河北石家庄建设网站,大气企业网站,廊坊网站搜索优化,用qq邮箱做网站文章目录 NDIS协议驱动开发指南1. 技术概览2. NDIS协议驱动2.1 BindAdapterHandlerEx2.2 SendNetBufferListsCompleteHandler2.3 ReceiveNetBufferListsHandler2.4 ProtocolNetPnpEvent 3. NET_BUFFER_LIST4. ndisprot实例5. 总结 NDIS协议驱动开发指南
我们知道#xff0c;在… 文章目录 NDIS协议驱动开发指南1. 技术概览2. NDIS协议驱动2.1 BindAdapterHandlerEx2.2 SendNetBufferListsCompleteHandler2.3 ReceiveNetBufferListsHandler2.4 ProtocolNetPnpEvent 3. NET_BUFFER_LIST4. ndisprot实例5. 总结 NDIS协议驱动开发指南
我们知道在以太网中所有的数据包都是通过以太网帧来发送的但是在网络上面的应用程序如果需要通过网络数据包交互就需要依赖网络协议来保障通信。平时我们用的最多的协议就是TCPIP协议。
其实在Windows中我们可以注册自己的协议开发自己的协议解析和封装驱动实现以太网帧的通信这就是本文的NDIS协议驱动。
以太网的以太包格式都是固定的格式如下
6字节6字节2字节其他长度源MAC目的MAC类型数据部分
NDIS协议驱动就是针对以太包的协议封装和解析过程对上层提供一个稳定的数据包通信的协议对下层提供一个可以供以太网发送的以太数据包。本文我们来看一下NDIS协议驱动的开发原理。
1. 技术概览
在Windows下面网络栈的基本架构如下
对于NDSI提供了三种功能能力的驱动
上层的协议驱动。中层的过滤驱动。下层的小端口驱动。
由于NDIS早期并没有直接提供过滤层的基本框架因此对于早期XP系统下面版本如果需要对NDIS层的数据包进行过滤需要在中间层实现协议驱动和小端口驱动
对上层创建小端口驱动来和上层协议层通信主要过滤数据包的发送。对下层创建协议驱动来和小端口驱动通信主要过滤数据包的接收。
对于协议层驱动实现比较简单只需要设置和处理好NDIS相关的协议层回调函数即可下面我们看一下协议层驱动的具体实现。
2. NDIS协议驱动
上面我们知道NDIS协议驱动主要处理NDIS的相关协议回调例程向NDIS注册回调例程的函数为NdisRegisterProtocolDriver该例程声明如下
NDIS_STATUS NdisRegisterProtocolDriver(NDIS_HANDLE ProtocolDriverContext,PNDIS_PROTOCOL_DRIVER_CHARACTERISTICS ProtocolCharacteristics,PNDIS_HANDLE NdisProtocolHandle
);其中NDIS_PROTOCOL_DRIVER_CHARACTERISTICS就是协议驱动的回调函数结构体该结构体如下
typedef struct _NDIS_PROTOCOL_DRIVER_CHARACTERISTICS {NDIS_OBJECT_HEADER Header;UCHAR MajorNdisVersion;UCHAR MinorNdisVersion;UCHAR MajorDriverVersion;UCHAR MinorDriverVersion;ULONG Flags;NDIS_STRING Name;SET_OPTIONS_HANDLER SetOptionsHandler;BIND_HANDLER_EX BindAdapterHandlerEx;UNBIND_HANDLER_EX UnbindAdapterHandlerEx;OPEN_ADAPTER_COMPLETE_HANDLER_EX OpenAdapterCompleteHandlerEx;CLOSE_ADAPTER_COMPLETE_HANDLER_EX CloseAdapterCompleteHandlerEx;NET_PNP_EVENT_HANDLER NetPnPEventHandler;UNINSTALL_PROTOCOL_HANDLER UninstallHandler;OID_REQUEST_COMPLETE_HANDLER OidRequestCompleteHandler;STATUS_HANDLER_EX StatusHandlerEx;RECEIVE_NET_BUFFER_LISTS_HANDLER ReceiveNetBufferListsHandler;SEND_NET_BUFFER_LISTS_COMPLETE_HANDLER SendNetBufferListsCompleteHandler;DIRECT_OID_REQUEST_COMPLETE_HANDLER DirectOidRequestCompleteHandler;
} NDIS_PROTOCOL_DRIVER_CHARACTERISTICS, *PNDIS_PROTOCOL_DRIVER_CHARACTERISTICS;在上述结构体中间对于一个简要的协议驱动只需要实现部分主要的回调函数即可包括
BindAdapterHandlerEx绑定回调函数当小端口驱动和协议驱动进行绑定的时候调用该函数通知协议驱动。UnbindAdapterHandlerEx解除绑定的回调函数和BindAdapterHandlerEx相反。OpenAdapterCompleteHandlerEx当使用NdisOpenAdapterEx绑定小端口驱动完成的时候被调用相当IRP的完成例程。CloseAdapterCompleteHandlerEx当使用NdisCloseAdapterEx解除协议驱动和小端口驱动完成的时候被调用。OidRequestCompleteHandlerNdisOidRequest请求完成的时候被调用的函数。SendNetBufferListsCompleteHandler表示使用NdisSendNetBufferLists发送完成数据包之后被调用的回调函数。ReceiveNetBufferListsHandler当小端口驱动接收到数据的时候就会通过该回调函数通知协议驱动数据包的到来。
2.1 BindAdapterHandlerEx
协议驱动是对网络数据包的封装当将网络数据包按照协议封装为以太网数据包之后就需要通过网卡发送出去那么协议驱动就需要和网卡驱动进行关联协议驱动的数据包知道如何发送给网卡驱动。
有两种情况需要进行协议的绑定
当协议驱动使用NdisRegisterProtocolDriver注册驱动的时候NDIS框架就会遍历当前系统所有的小端口驱动对每个小端口驱动调用BindAdapterHandlerEx回调函数。当有新的网卡设备插入并启动的时候(IRP_MN_START)就会对该小端口驱动遍历所有的协议驱动然后调用其BindAdapterHandlerEx回调函数进行绑定。
BindAdapterHandlerEx只是绑定的回调函数该函数声明如下
PROTOCOL_BIND_ADAPTER_EX ProtocolBindAdapterEx;NDIS_STATUS ProtocolBindAdapterEx(NDIS_HANDLE ProtocolDriverContext,NDIS_HANDLE BindContext,PNDIS_BIND_PARAMETERS BindParameters
)
{...}该函数只是将小端口驱动和协议驱动的信息当作回调函数的参数传递过来小端口驱动的信息通过PNDIS_BIND_PARAMETERS进行描述。真实的绑定是通过NdisOpenAdapterEx函数来完成的该函数如下
NDIS_STATUS NdisOpenAdapterEx(NDIS_HANDLE NdisProtocolHandle,NDIS_HANDLE ProtocolBindingContext,PNDIS_OPEN_PARAMETERS OpenParameters,NDIS_HANDLE BindContext,PNDIS_HANDLE NdisBindingHandle
);NdisOpenAdapterEx其实是建立小端口和协议驱动的桥梁大致如下 这样小端口的数据可以在NDIS框架中通过NDIS_OPEN_BLOCK回调给协议驱动协议驱动也可以通过NDIS_OPEN_BLOCK调用小端口驱动。
2.2 SendNetBufferListsCompleteHandler
在协议驱动中我们通过NdisSendNetBufferLists将以太包发送数据该函数声明如下
void NdisSendNetBufferLists(NDIS_HANDLE NdisBindingHandle,__drv_aliasesMem PNET_BUFFER_LIST NetBufferLists,NDIS_PORT_NUMBER PortNumber,ULONG SendFlags
);NET_BUFFER_LIST描述着我们需要发送的数据包集合当小端口驱动将数据包发送成功之后就会调用NdisMSendNetBufferListsComplete来通知协议驱动数据包被发送完成该函数如下
void NdisMSendNetBufferListsComplete(NDIS_HANDLE MiniportAdapterHandle,PNET_BUFFER_LIST NetBufferList,ULONG SendCompleteFlags
);NdisMSendNetBufferListsComplete完成回调的函数就是SendNetBufferListsCompleteHandler该函数如下
PROTOCOL_SEND_NET_BUFFER_LISTS_COMPLETE ProtocolSendNetBufferListsComplete;void ProtocolSendNetBufferListsComplete(NDIS_HANDLE ProtocolBindingContext,PNET_BUFFER_LIST NetBufferList,ULONG SendCompleteFlags
)
{...}SendNetBufferListsCompleteHandler这个函数将会重新获取NdisSendNetBufferLists发送的数据包在该函数中可以释放发送分配的NET_BUFFER_LIST内存。
不过这里需要注意的是NET_BUFFER_LIST是一个链表结构在底层可能会被断链发送因此SendNetBufferListsCompleteHandler这个函数中的NET_BUFFER_LIST可能是NdisSendNetBufferLists中的子链。
2.3 ReceiveNetBufferListsHandler
当我们的网卡接收到数据的时候就会通过硬件方式例如中断等通知数据包到来然后小端口驱动通过NdisMIndicateReceiveNetBufferLists将数据包传送给协议层驱动进行协议解析和数据包的传递该函数如下
void NdisMIndicateReceiveNetBufferLists(NDIS_HANDLE MiniportAdapterHandle,PNET_BUFFER_LIST NetBufferList,NDIS_PORT_NUMBER PortNumber,ULONG NumberOfNetBufferLists,ULONG ReceiveFlags
);在NdisMIndicateReceiveNetBufferLists内部就会调用ReceiveNetBufferListsHandler将接收到的网络数据包通过NET_BUFFER_LIST进行传递该函数声明如下
PROTOCOL_RECEIVE_NET_BUFFER_LISTS ProtocolReceiveNetBufferLists;void ProtocolReceiveNetBufferLists(NDIS_HANDLE ProtocolBindingContext,PNET_BUFFER_LIST NetBufferLists,NDIS_PORT_NUMBER PortNumber,ULONG NumberOfNetBufferLists,ULONG ReceiveFlags
)
{...}在该函数中NetBufferLists表示数据包通过解析该数据包我们就可以进行TCPIP网络栈的协议解析了。
2.4 ProtocolNetPnpEvent
改例程是NDIS框架对于网络PNP事件响应的回调函数该函数声明如下
PROTOCOL_NET_PNP_EVENT ProtocolNetPnpEvent;NDIS_STATUS ProtocolNetPnpEvent(NDIS_HANDLE ProtocolBindingContext,PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification
)
{...}NET_PNP_EVENT_NOTIFICATION描述了一个PNP事件的信息该结构如下
typedef struct _NET_PNP_EVENT_NOTIFICATION {NDIS_OBJECT_HEADER Header;NDIS_PORT_NUMBER PortNumber;NET_PNP_EVENT NetPnPEvent;ULONG Flags;NDIS_NIC_SWITCH_ID SwitchId;NDIS_NIC_SWITCH_VPORT_ID VPortId;
} NET_PNP_EVENT_NOTIFICATION, *PNET_PNP_EVENT_NOTIFICATION;typedef struct _NET_PNP_EVENT {NET_PNP_EVENT_CODE NetEvent;PVOID Buffer;ULONG BufferLength;ULONG_PTR NdisReserved[4];ULONG_PTR TransportReserved[4];ULONG_PTR TdiReserved[4];ULONG_PTR TdiClientReserved[4];
} NET_PNP_EVENT, *PNET_PNP_EVENT;NET_PNP_EVENT_CODE描述网络PNP事件的类型有如下
typedef enum _NET_PNP_EVENT_CODE
{NetEventSetPower,NetEventQueryPower,NetEventQueryRemoveDevice,NetEventCancelRemoveDevice,NetEventReconfigure,NetEventBindList,NetEventBindsComplete,NetEventPnPCapabilities,NetEventPause,NetEventRestart,NetEventPortActivation,NetEventPortDeactivation,NetEventIMReEnableDevice,NetEventNDKEnable,NetEventNDKDisable,NetEventFilterPreDetach,NetEventBindFailed,NetEventSwitchActivate,NetEventAllowBindsAbove,NetEventInhibitBindsAbove,NetEventAllowStart,NetEventRequirePause,NetEventUploadGftFlowEntries,NetEventMaximum
} NET_PNP_EVENT_CODE, *PNET_PNP_EVENT_CODE;NET_PNP_EVENT_CODE的具体值参见MSDN例如NetEventSetPower类型Buffer表示了NDIS_DEVICE_POWER_STATE结构描述电源状态。
3. NET_BUFFER_LIST
在NDIS中网络数据包通过NET_BUFFER_LIST来进行抽象这个结构表示着网络数据包的集合该数据结构如下
NET_BUFFER_LIST是一个NET_BUFFER_LIST的链表集合单个NET_BUFFER_LIST是NET_BUFFER的集合NET_BUFFER表示一个数据包该结构如下
NET_BUFFER其实就是使用MDL来描述数据包的真实类容因此对于NET_BUFFER_LIST的全部结构可以描述为如下
对于NET_BUFFER_LIST和NET_BUFFER提供了如下宏来操作该结构的成员
#define NET_BUFFER_LIST_NEXT_NBL(_NBL) ((_NBL)-Next)
#define NET_BUFFER_LIST_FIRST_NB(_NBL) ((_NBL)-FirstNetBuffer)#define NET_BUFFER_NEXT_NB(_NB) ((_NB)-Next)
#define NET_BUFFER_FIRST_MDL(_NB) ((_NB)-MdlChain)
#define NET_BUFFER_DATA_LENGTH(_NB) ((_NB)-DataLength)
#define NET_BUFFER_DATA_OFFSET(_NB) ((_NB)-DataOffset)
#define NET_BUFFER_CURRENT_MDL(_NB) ((_NB)-CurrentMdl)
#define NET_BUFFER_CURRENT_MDL_OFFSET(_NB) ((_NB)-CurrentMdlOffset)我们需要对接收或者发送的数据包进行处理都是解析NET_BUFFER_LIST的过程。
4. ndisprot实例
对于NDIS协议驱动WDK提供了一个示例ndisprot该示例展示了协议驱动的工作原理该实例提供如下功能
ndisprot驱动可以绑定到网卡上面。通过NdisprotReceiveNetBufferLists接收底层的网络数据包并将其放入队列中。用户层程序可以通过ReadFile读取协议驱动的网络数据包。用户层程序可以通过WriteFile往网络协议驱动写入数据包协议驱动通过NdisSendNetBufferLists将数据包发送到底层小端口驱动。
该协议否是可以支持网络通信呢本人没有进行实验验证但是从原理上来说是可行的只是它是一个面向非连接并且没有校验的原始通信手段的协议。
对于该驱动我们可以简单的使用如下方式手动安装和验证
5. 总结
对于NDIS协议层驱动平时我们的使用场景不多我们也没有能力也没必要设计一个完整的网络协议驱动。但是协议驱动是我们后面NDIS过滤驱动的基础NDIS过滤驱动可以帮助我们获取本机接收到的网络帧数据包并且对以太帧数据包进行过滤例如ARP数据包等。
因此NDIS协议驱动还是非常值得我们去学习的它是NDIS过滤驱动的基础也是以太帧数据包过滤的重要手段。