当前位置: 首页 >> 程序设计 >> 基于PassThru的NDIS中间层驱动程序扩展
 

基于PassThru的NDIS中间层驱动程序扩展

作者:      来源:zz     发表时间:2008-06-02     浏览次数:      字号:    

中国源码网内相关主题链接
  • 基于PassThru的NDIS中间层驱动...

  • 三 NDIS驱动程序的数据处理流程
          

    3.1 三种NDIS驱动程序的关系。                    
        通常一个NDIS Protocol Driver 的上边沿导出TDI接口,并在其下边沿向NDIS注册一组Protocolxxx操作例程;一个NDIS Miniport Driver则在其下边沿通过NDIS接口操作物理网络设备,并在其上边沿向NDIS注册一组Miniportxxx操作例程。当一个中间层介入的时候,必需遵守这个规则,因此,中间层驱动对上层来说,扮演一个Miniport Driver的角色,它在上边沿向NDIS注册一组Miniportxxx函数;对于下层Miniport Driver来说,中间层驱动扮演一个Protocol Driver的角色,因此它在下边沿向NDIS注册一组Protocolxxx函数。Miniport Driver通过调用NdisMRegisterMiniport向NDIS注册一组MiniportXxx函数。其中原型如下:
        NDIS_STATUS NdisMRegisterMiniport(
            IN NDIS_HANDLE  NdisWrapperHandle,
             IN PNDIS_MINIPORT_CHARACTERISTICS  MiniportCharacteristics,
             IN UINT  CharacteristicsLength
             );
        其中,NdisWrapperHandls是之前通过调用NdisMInitializeWrapper取得的句柄MiniportCharacteristics包含一组MiniportXxx函数指针。

        Protocol Driver 通过调用 NdisRegisterProtocol向NDIS注册一组ProtocolXxx函数。其中原型如下:
        VOID NdisRegisterProtocol(
            OUT PNDIS_STATUS  Status,
            OUT PNDIS_HANDLE  NdisProtocolHandle,
            IN PNDIS_PROTOCOL_CHARACTERISTICS  ProtocolCharacteristics,
            IN UINT  CharacteristicsLength
            );
        其中,ProtocolCharacteristics包含一组ProtocolXxx函数。由于NDIS Intermediate Driver的双重性,它需要调用 NdisIMRegisterLayeredMiniport 向NDIS注册,并向上层导出一组MiniportXxx函数,之后,调用NdisRegisterProtocol向NIDS注册,并向下层导出一组 ProtocolXxx函数。当一个NDIS中间层介入后,如图二所示。

    3.2 NDIS数据发送流程:
        当上层协议驱动要发数据时,调用NdisSend/NdisSendPackets请求NDIS发送数据包,NDIS将会调用紧接其下的中间层驱动的MiniportSend/MiniportSendPackets,中间层驱动MiniportSend/MiniportSendPackets 有机会在这里对包进行必要的操作,然后,中间层驱动再次调用NdisSend/NdisSendPackets请NDIS发送数据包,NDIS将调用其下层的Miniport Driver的MiniportSend/MiniportSendPackets,底层MiniportSend /MiniportSendPackets通过NDIS接口控制物理网络设备,将数据发送出去。在上层请求发送数据包时,上层分配了相关的资源(如内存),希望在下层完成发送动作后,能够及时的收回相关的资源,所以,当上层调用NdisSend/NdisSendPackets返回 NDIS_STATUS_PENDING以外的任何值时,上层就可以释放资源了,如果得到返回的结果是NDIS_STATUS_PENDING话,说明下层还没有完成发送请求,以后,等下层最终完成发送请求时,下层调用NdisMSendComplete请求NDIS通知上层可以释放资源了,于是NDIS 调用上层注册的ProtocolSendComplete函数,上层在这它的ProtocolSendComplete中释放了资源后,再次调用 NdisMSendComplete请求NDIS通知更上层。

    3.3 NDIS数据接收流程:
        当底层网络设备有数据到来的时候,将触发中断,相应的中断处理程序接管中断后,将可能调用Miniport Driver所注册的中断处理例程(ISR),Miniport Driver通常在这里把网卡上的数据考贝到Miniport Driver缓冲区队列中去,出于效率的考虑,Miniport Driver这时可能不会立即通知上层处理新的数据,因为很可能,马上还有随后的新的数据到来,当接收到的包的数量达到一定程度的时候,Miniport Driver会调用NdisMIndicateReceivePacket指示新的NDIS新数据的到来,这时候,NdisMIndicateReceivePacket的调用将导致NDIS调用位于Miniport上层的中间层向NDIS注册的下边沿 ProtocolReceivePacket函数。中间层驱动程序的ProtocolReceivePacket可以对收到的数据包进行相应的处理,之后,可以选择再次调用NdisMIndicateReceivePacket请求NDIS通知更上层数据包的到来,这时,NDIS调用更上层注册的 ProtocolReceive函数,上层的ProtocolReceive对数据包进行必要的处理后,继续调用 NdisMIndicateReceivePacket请求NDIS,通知更上层,最终数据包传到协议驱动中,由相关的协议栈进行处理。
        有时候,在这种级连的上传过程中,并不是那么的顺利,例如,由于某种原因,如:网络设备的驱动程序的可用缓冲区数量减少到某个指定的数量时,网络设备的驱动程序调用NdisMxxxIndicateReceivePacket请求NDIS通知上层数据的到来,这时NDIS将调用上层注册的 ProtocolReceive,上层在ProtocolReceive中进行必要的处理后,进一步调用 NdisMxxxIndicateReceivePacket使得NDIS调用上层的协议驱动注册的ProtocolReceive。在一些不太理想的情况下,一次中断,从网络设备中接收到的数据对某个协议来说并不是一个完整的协议数据包。(一般情况下,其余的数据bit已经在链路途中了,并随后立即就会到达网络设备,这有可能就发生在一个CPU在处理网络设备中断的同时,网络设备的板载存储器上就已经收到了其余的数据了,甚至DMA控制器可能已经把这些数据传到了系统的主存储器上了),这时,上层的ProtocolReceive都无法进行正常的处理(一般对某个包进行处理,都要以相关的协议为依据,进行分析)。这时,在上传到某一层时,可以调用NdisTransferData请求NDIS把随后的信息传上来,这时,NDIS又将在向上传递的途中回过头来向下调用下面的MiniportTransferData,下层重复调用NdisTransferData把这个请求传送到底程的Miniport Driver。如果在上层调用NdisTransferData时不是返回NDIS_STATUS_PENDING,则上层可以继续它的处理,而如果返回 NDIS_STATUS_PENDING,底程在最终完成请求时,调用NdisMTransferDataComplete请求NDIS通知上层传送完成,这将导致上层注册的ProtocolTransferDataComplete被调用,上层调用NdisMTransferDataComplete 请求NDIS通知更上层。由于硬件技术的发展,网络设备板载存储的增加,系统主存储器的增加,以及网络传输能力的改善,NDIS那么迂回的通过 MdisMxxxIndicateReceivePacket,ProtocolReceive,MiniportTransferData这一条坎坷的路径进行数据处理的情况似乎越来越少见了。
        接收过程是由下层传递上去的,同样,底层的Miniport分配了一些资源,如用于存储这个数据包内容的内存,我们希望这些资源最终能极时的被归还,以供以后使用。一个包在从下到上的传递过程时,如果某一层的ProtocolReceive/ProtocolReceivePacket有兴趣对这个包进行处理的话,则需要检查这个包的OOB信息段是不是携带NDIS_STATUS_RESOURCES,如果是的话,说明其下层资源紧缺,希望上层在处理的时候,自己考贝一份副本,以供自己使用,因为下层希望自己能够尽快收回这个包的资源,在这里,上层以后可以用自己的那份拷贝指示上层数据包的到来,这样的话,以后,中间层希望上层处理完后,能够收回所有权。另一方面,底层的Miniport并不是每一次都会在OOB信息段中设置 NDIS_STATUS_RESOURCES标志的,这在很多情况下是不必要的。当上层协议驱动完成处理时,可以调用NdisReturnPackets 通知NDIS,相应包已经处理完成,可以安全的释放相关资源了,于是NDIS将调用其下层注册的MiniportReturnPackets,下层在这里释放与这个包相关的资源,并继续调用NdisReturnPackets,请求NDIS把这个通知传给更下层。
        
        这样一来,发数据由上层发起请求往下传,而接收数据从下层往上传,有没有可能一次接收的过层是由上面发起往下传的呢?这是没有必要的,为什么?我的应用程序不就主动调用WSARecv或WSARecvFrom等函数主动向下传递的一次收数据的请求的吗?事实上是这样的,你的接收请求经过 SPI,TDI,到了最终的协议驱动时,如果协议驱动中,指定的套接字上(最重要的是端口和目的IP了)有相应的数据的能满足你的这一次的读请求的话,就完成你的请求,如果不能的话,则阻塞了。当下层有数据来的时候,数据传递到协议驱动时,协议驱动会检查包头的信息并查看当前不是有应用程序打开过相应的端口,以及和对应的目标建立过连接,如果有的话,就把数据存到协议栈中的缓冲区中去,如果对应套接字上有阻塞的接收请求的话,就判断是不是能完成它的请求了,如果能了,就完成他,如果不能,继续等以后下层传上来再重复这个过程。否则就抛弃了,另外,如果,一个应用程序建立一个套接字,并与一台机器建立连接,对方发送了数据,协议栈会把数据保存起来,也许连接超时后,协议栈的缓冲区中还有应用程序没有接收的数据的话,也被抛弃了。所以,协议驱动从来都没有必要主动往下传递一个接收数据的请求。这一点,对于有一点网络程序调试经验的人来说,似乎可以直接找到一个的证据,在调试器的调用WSARecv前下一个断点,用Sniffer抓包,可能数据已经到来了,然后,再回到调试器中执行WSARecv,可以看到收到的就是先前Sniffer下来的数据了。
    (注:用一个工作在NDIS协议层或是NDIS中间层的Sniffer来观查。)

    [1] [2] [3]

    编辑 webmaster

     
     
     
    评论更多>>
     
    沙发!!!
     
    发表
     
    姓名: QQ:
    性别: MSN:
    E-mail: 主页:
    评分: 1 2 3 4 5
    评论内容:
    验证码:
      
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •