1.NDIS_PACKET 包描述符结构
结构中包含3个域,Head,Tail,NdisPacketOobOffset,包描述符和一个或者多个链式数据缓冲区(MDL)关联,这个缓冲区包含真正的网咯数据。Head指向第一个缓冲区地址,Tail指向最后一个为NULL
,可以通过NdisQueryPacket,NdisGetNextBuffer,NdisGetFirstBufferFromPacketSafe等函数获得链式缓冲区信息。带内数据是正常的网络数据,oob带外数据是除正常数据外,需要传达的信息。
NDIS_PACKET_EXTENSION是oob的扩展。
2.DriverEntry 入口函数
初始化小端口包装句柄。使用NdisIMRegisterLayeredMiniport注册小端口特征。它返回NDIS_HANDLE把小端口和协议驱动关联起来。注册
协议驱动特征。通过NdisIMAssociateMiniport关联两个驱动接口。可以认为每个中间层驱动分两层,上层小端口驱动,下层协议驱动。
3.动态绑定NIC设备
由PNP管理器发起,依次调用它们的AddDevice函数。在PtBindAdapter中实现协议和小端口的绑定。ndis驱动不通过设备栈绑定而是直接的函数调用来进行分层传递。
在PtBindAdapter函数内部,使用NdisAllocatePacketPoolEx分配缓冲池,通过函数NdisOpenAdapter实现绑定下层NIC。这个调用是在NDIS的内核对象中,建立起中间层驱动和下层被绑定驱动之间注册
函数的调用关系。建立关系后,中间层和下层驱动就可以互相调用收发包。这是NDIS的绑定实质。因为bind可能异步完成,在bind前调用事件初始化函数,在调用后等待事件,在
PtOpenAdapterComplete中激活事件。调用NdisIMInitializeDeviceInstanceEx初始化驱动的虚拟NIC。
4.小端口miniport初始化MPInitialize
上面绑定到真实的底层NIC设备后,自己创建一个虚拟的NIC设备,随时过滤从上层过来的请求包。然后NdisMSetAttributesEx设置适配器上下文,不处理超时。
5.发送数据包
当上层驱动将包发到中间层驱动时它能在MpSend回调函数中收到这些包描述符。它可以在这个回调中调用过滤规则函数发送,或者拒绝,或者过滤。发送包的时候调用NdisSend,NdisSendPackets或者
NdisCoSendPackets系列函数。NdisSend函数的第二个参数NdisBindingHandle即包含真实目标NIC或者下层驱动的虚拟NIC的句柄。
6. 包描述符“重利用”和“重申请”
收到的包描述符不修改,传递下去是重利用。根据收到的包描述符,重建新的包描述符,是重申请。重申请只需要申请包描述符后,修改包描述符结构,数据部分把原来的Head和Tail指针复制过来。
7.发送数据包的异步完成
在PtSendComplete中,判断是否重利用则根据包描述符是否是从包池中申请来的。如果是重利用,调用NdisMSendComplete通知上层驱动包发送的完成。如果是重申请,则使用原始包描述符通知上层驱
动,并对正在发送包计数减一。
8. 中间层驱动接收数据包
底层驱动调用NdisMIndicateReceivePacket向中间层驱动通知接收数据包描述符指针。中间层驱动如果提供了PtReceivePacket函数,则该函数被调用。否则,PtReceive函数被调用。如果底层驱动调
用NdisMXxxIndicateReceive函数向中间层驱动通知接收包,则PtReceive函数被调用。协议驱动中PtReceive函数侧重接收包,中间层驱动中侧重于向上通知包。可以在此函数中对收到的数据包进行规
则的处理,比如接收,丢弃,重定向等。
上层驱动收到网络包接收通知后,回调用NdisTransferData要求底层驱动将完整的包数据发送给他。中间层驱动在MPTransferData中收到这个请求。可以在这个函数
中对包数据做规则的处理,比如接收,丢弃,重定向等。如果是异步完成接收包,则在PtTransferDataComplete处理完成。
同步使用PtReceivePacket接收完整的包描述符数据。
9. 对包的过滤
通过NdisQueryPacket获取第一个包的ndisbuffer地址,根据NdisGetNextBuffer获取包的下一个链式数据部分,然后判断是ip,arp包等,如果是ip包,根据ip头判断具体的icmp,udp,tcp等协议,进
行过滤。
10. 中间层驱动的查询和设置。
查询请求:如果NdisRequest异步完成,则返回NDIS_STATUS_PENDING,系统会查询命令完成后调用NDIS函数NdisMQueryInformation,使得中间层驱动的PtRequestComplete被调用。如果是同步,则自
己调用PtRequestComplete.在PtRequestComplete后处理中,将有用信息保存在中间层驱动中,然后调用NdisMQueryInformationComplete完成。设置请求类似。
11.生成普通的控制设备
使用NidsMRegisterDevice生成设备对象,有一个参数可以输入一个分发函数表,这样就不会破坏ndis中间层驱动本身的分发函数。或者先获取原来的分发函数,然后把分发函数指针设置为hook后函数
,在hook函数中调用原来的分发函数。
----写于2014.1.21