这里介绍如何从创建数据到最后发送数据的一个过程。
首先要做准你要发送的数据,注意你的数据大小至少要不少于64Byte。我们这里设置数据包的大小为80Byte.里面的内容包含一个14Byte的Ethernet Header,其他空位用无用数据填充。下面是具体流程:
一、创建缓冲区----用于保存自定义的数据
Ndis中创建新的一块内存的函数是:NdisAllocateMemoryWithTagPriority(),其他函数自行参考MSDN. 下面是用例:
pWriteBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle,BufferSize,FILTER_ALLOC_TAG, LowPoolPriority);
其中BufferSize等于80(比这个大也可以!根据需要。)
二、封装自定义数据
因为Ethernet Header是一个Struct结构,定义如下:
typedef struct _TESTPROV_ETH_HEADER
{
UCHAR DstAddr[TESTPROV_MAC_ADDR_LEN];
UCHAR SrcAddr[TESTPROV_MAC_ADDR_LEN];
USHORT EthType;
} TESTPROV_ETH_HEADER;
所以我们要把这些成员变量都赋值。首先申明一个TESTPROV_ETH_HEADER的一个指针变量,然后指向PwriteBuf所指的那块内存:
pEthHeader = (PTESTPROV_ETH_HEADER)pWriteBuf;
然后开始给TESTPROV_ETH_HEADER中的每个成员函数赋值,其中EthType= 0x8e88.
三、创建MDL
由于我们最终要把自定义的数据封装成NBL,然后发送出去。如果了解NBL结构,知道NBL由一个或者多个NB构成,每个NB又由一个或者多个MDL构成。所以这里我们先分配一个MDL,用来保存自定义的数据。
pMdl = NdisAllocateMdl(pFilter->FilterHandle,
pEthFrameNew,
BufferSize);
注意这里pEthFrameNew是一个Virtual Address.指向的是MDL缓冲区的基地址。需要事先分配:
pEthFrameNew = (PUCHAR)NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle,BufferSize,FILTER_ALLOC_TAG, LowPoolPriority);
四、拷贝数据到MDL,并且释放pWriteBuf
如果你的MDL创建成功之后,MDL中是没有内容的,需要把之前自定义在pWriteBuf中的数据拷贝到MDL中:
NdisMoveMemory(pEthFrameNew,pEthHeader, BufferSize);
之后释放pWriteBuf(勿忘)
NdisFreeMemory(pWriteBuf,BufferSize,0);
五、创建NBL
在创建NBL之前,有一件很重要的工作,那就是要分配好NetBufferListsPool.因为之后所有从Filter创建的NBL都要建立在这个Pool之上。从方便和多次利用考虑,我们把Pool分配工作在FilterAttach中完成:
NdisZeroMemory(&PoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS));
PoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
PoolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
PoolParameters.Header.Size = sizeof(PoolParameters);
PoolParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT ;
PoolParameters.ContextSize = sizeof(FILTER_SEND_NETBUFLIST_RSVD);
PoolParameters.fAllocateNetBuffer = TRUE;
PoolParameters.PoolTag = FILTER_ALLOC_TAG;
pFilter->SendNetBufferListPool = NdisAllocateNetBufferListPool(
NdisFilterHandle,
&PoolParameters);
需要注意的是,Ndis为每个Filter module instance 各自分配一个Pool. 还有sizeof(FILTER_SEND_NETBUFFER_RSVD)的值必须为8的整数倍(0亦可)
这里我们采用函数NdisAllocateNetBufferAndNetBufferList()来创建NBL,因为它不需要事先创建NB就可以直接跳过这一步创建NBL,有兴趣可以参考NdisAllocateNetBufferList().需要的参数为之前创建的Pool,以及预分配的MDL等。
pNetBufferList = NdisAllocateNetBufferAndNetBufferList(
pFilter->SendNetBufferListPool,
sizeof(FILTER_SEND_NETBUFLIST_RSVD),
0, // back fill size
pMdl,
0, // Data offset
BufferSize);
注意:如果没有成功创建NBL,需要释放MDL.免得内存泄露。
六、发送NBL
发送之前需要制定NBL的SourceHandle,为了后面处理需要,以辨别是自己定义的数据。其次制定发送的Dispatch_level.
pNetBufferList->SourceHandle = pFilter->FilterHandle;
//NDIS_MDL_LINKAGE(pMdl) = NULL;
SendFlags = NDIS_SEND_FLAGS_DISPATCH_LEVEL;
NdisFSendNetBufferLists(
pFilter->FilterHandle,
pNetBufferList,
NDIS_DEFAULT_PORT_NUMBER,
SendFlags);
七、处理NdisFSengNetBufferListComplete
这里需要注意:如果不是你自己发送的数据,需要通知Protocol层,该数据已经提交给底层了。如果是自己的创建和发送的,需要额外处理,销毁证据,因为上层Protocol没有让Filter发送该数据,如果也通知发送完毕就会出事,最显而易见的就是Blue Screen. 所以你要做的:
1. 判断是否是自己发送的数据:
if(NetBufferLists->SourceHandle == pFilter->FilterHandle){}
2. 如果是,销毁自定义数据
pMdl = NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(NetBufferLists));
FILTER_ASSERT(pMdl != NULL);
NdisQueryMdl(
pMdl,
(PVOID *)&pCopyData,
&BufferLength,
NormalPagePriority);
FILTER_ASSERT(pCopyData != NULL);
FILTER_DEREF_SEND_NBL(NetBufferLists, DispatchLevel);
NdisFreeMdl(pMdl);
pCopyData = NULL;
3. 如果不是,调用NdisFSendNetBufferListsComplete()通知上层驱动
NdisFSendNetBufferListsComplete(pFilter->FilterHandle, NetBufferLists, SendCompleteFlags);
到这里,该数据包发送完毕!
八、卸载释放Pool
这里是题外话:你在FilterAttach分配的Pool,你需要在FilterDetach时释放:
if(pFilter->SendNetBufferListPool !=NULL)
{
NdisFreeNetBufferListPool(pFilter->SendNetBufferListPool);
pFilter->SendNetBufferListPool = NULL;
}
不要担心,各自的Filter Module Instance都会各自释放自己的Pool.
九、整理暂时结束
一路风尘 整理