<Filter Module Send & Receive>
Filter Module不直接支持老的基于NDIS_PACKET结构的发送请求和接收指示,替代老的结构现在采用新的NET_BUFFER结构
来进行发送请求和指示。(NET_PACKET->NET_BUFFER)
<Filter Driver 的缓冲区管理>
Filter Driver创建缓冲区用于拷贝从其它驱动获得的数据或用于发起一个发送请求和接收指示。
如果一个Filter Driver不能创建一个缓冲区那就不能管理缓冲区池,这样就只能传递发送请求和接收指示。
Filter Driver创建缓冲区用于发送和接受就必须创建NET_BUFFER_LIET结构池和NET_BUFFER结构池。
NdisAllocateNetBufferListPool;
NdisAllocateNetBufferPool;
从池里分配置结构的例程:
(设置NdisAllocateNetBufferList中AllocateNetBuffer为TRUE)
NdisAllocateNetBufferAndNetBufferList;(仅在NET_BUFFER_LIST中创建一个NET_BUFFER结构)
NdisAllocateNetBufferList;
NdisAllocateNetBuffer;
调用NdisAlloocateNetBufferAndNstBufferList比调用NidsAllocateNetBufferList后再调用NdisAllocateNetBuffer更有效。
Filter Driver要发起一个发送请求或者接收指示时应先确定内容和下层驱动对回填空间的要求,一个Filter Driver根据重
启属性(在FilterRestart被调用是可以获得)来决定下层驱动回填空间的要求。
//获得重启属性,来决定下层驱动回填空间的要求。
//Filter Driver应该在Restarting状态时记录回填空间的要求
NdisRestartAttributes = RestartParameters->RestartAttributes;
<在驱动中发送数据>
Filter Driver 可以发起一个发送请求或过滤上层驱动引发的一个发送请求。当一个协议驱动调用NdisFSendBufferLists时,
会提交一个NET_BUFFER_LIST结构给在驱动栈上最顶层的Filter Module。
<自身引发的发送请求>
Filter Driver可以调用NdisFSendNetBufferLists来发送一个定义在NET_BUFFER_LIST结构中的网络数据。
但是Filter Driver 必须设置NET_BUFFER_LIST结构中SourceHandle的值和NdisFSendNetBufferLists参数中NidsFilterHandle
的值是一样的。(SourceHandle:NDIS uses SourceHandle to return the NET_BUFFER_LIST structure to that driver. )
(NdisFilterHandle:The NDIS handle that identifies this filter module. NDIS passed the handle to the filter
driver in a call to the FilterAttach function. )
如果不是自己引发的NET_BUFFER_LIST结构数据,那么就不应该修改它其中的SourceHandle成员的值。
在调用NdisFSendNetBufferLists发送前可以利用NET_BUFFER_LIST_INFO来设置发送请求的附加信息。
注:
1)一个Filter Driver一旦发送,它就放弃了对NET_BUFFER_LIST及所有链接资源的所有权。NDIS会控制发送请求并传递给下层
驱动处理。
2)同时,NDIS会调用FilterSendNetBufferListsComplete例程把发送出的结构及数据返还给Filter Driver。
NDIS可以将收集多次NdisFSendNetBufferLists发送的结构和数据形成一个单链表传递给FilterSendNetBufferListsComplete。
3)除非调用NdisSendNetbufferListsComplete,则调用过NdisSendNetBufferLists的发送请求当前状态是未知的。
4)一个Filter Driver是不能在NDIS调用FilterSendNetBufferListsComplete返回结构之前就对NET_BUFFER_LIST和其关联的数据
做检查或修改甚至释放它。只能在FilterSendNetBufferListsComplete中来完成后继处理资源回收等操作。
5)当NDIS调用FilterSendNetBufferListsComplete时,Filter Driver重新获得对结构或者资源的控制权及所有权。
可以在FilterSendNetBufferListsComplete中释放所用资源,并且准备下一个NdisFSendNetBufferLists调用。
回环发送:
NDIS总是按照Filter Driver调用NidsFSendNetBufferLists提交顺序把数据传递给下层驱动,但是返回
FilterSendNetBufferListsComplete的顺序则是任意的,这时需要注意它们之间没有必然的顺序关系。
Filter Driver也可以请求一个回环发送请求,只要把NdisFSendNetBufferLists的SendFlags 设置成
NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK就行,这时NDIS会引发一个包含发送数据的接收包指示。
<过滤发送请求>
NDIS会调用Filter Driver的FilterSendNetBufferLists例程来让驱动过滤上层驱动的发送请求。
Filter Driver不能改变其它驱动传来的NET_BUFFER_LIST结构中的SourceHandle(这个结构最开始的拥有者)成员的值。
但是,可以过滤数据或发送过滤的数据到下层驱动。
1)可以把缓冲区通过NdisFSendBufferLists传递给下层驱动,NDIS来保证上下文空间对Filter Driver的有效性。Filter Drvier可以
在发送前修改缓冲区的内容。可以像处理自己引发的发送请求的缓冲区一样处理这个缓冲区。
2)可以调用NdisFSendNetBufferListsComplete拒绝传递这个包。
3)排队缓冲区内容到本地供以后处理。例如要在一定超时后处理或要接收到特定包后才处理等。9
注:如果支持这种处理方式就要支持取消请求的操作。
4)可以拷贝缓冲区并引发一个发送请求。它类似自己引发一个发送请求。但必须先调用NdisFSendNetBufferListsComplete返回上层驱
动的缓冲区。
如果Filter Driver不提供FilterSendNetBufferListsComplete,它还是可以引发一个发送操作的,但是必须提供一个
FilterSendNetBufferListsComplete并且不能在这个例程里把这个事件传递给上层驱动。一个Filter Driver可以传递或过滤一个上层驱
动的回环请求,如果传递的是一个回环请求,NDIS会设置FilterSendNetBufferLists的SendFlags参数为
NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK,Filter Driver在调用NdisFSendNetBufferLists时把这个标记传给它即可。在回环请求情况下,
NDIS就会指示一个包含发送数据的接收包。
如果一个Filter Driver任何的修改行为不是NDIS提供的标准服务,那么它应该自己提供相应的一切服务。
例如:
如果一个Filter Driver修改了一个硬件地址请求,就必须处理直接到这个新地址回环包。在这种情况下,因为Filter Driver已
经更改了硬件地址,NDIS是不能提供基于这个硬件地址的回环服务的。
<取消一个发送请求>
一个Filter Driver可以取消一个自己引发的或者上层驱动引发的发送请求。
1)取消自己引发的发送请求
过滤驱动调用NDIS_SET_BUFFER_LIST_CANCEL_ID宏为每一个NET_BUFFER_LIST标记一个取消ID。在为网络数据分配取消ID之前,
必须先调用NdisGeneratePartialCancelId来获得取消ID的高字节。
这是为了确保不是驱动不会把一个取消ID同时分配给两个驱动。驱动通常在DriverEntry调用NdisGeneratePartialCancelId,
但是驱动可以在不同的时间多次调用它来获得多个取消ID。
驱动可以调用NdisFCancelSendNetBufferLists例程来完成取消一个被标记过取消ID且正在传输的数据。
要获得取消ID就得用NDIS_GET_NET_BUFFER_LIST_CANCEL_ID宏来完成。如果一个Filter Driver对所有发送的NET_BUFFER_LIST标
记了相同的取消ID那么就可以调用一次NdisFCancelSendNetBufferLists来取消这部分发送请求。
在实现这个功能时,NDIS会调用下层驱动的取消发送功能。
中断正在执行的发送任务后,下层驱动会调用发送完成全程(如:NdisMSendNetBufferListComplete)返回指定的NET_BUFFER_LIST,
并指定返回状态NDIS_STATUS_CANCELLED,NDIS依次调用Filter Driver的FilterSendNetBufferListsComplete例程。
在FilterSendNetBufferListsComplete例程中要用NDIS_SET_NET_BUFFER_LIST_CANCEL_ID来取消LIST_BUFFER_LIST中被标记的取消
ID,这样时为了防止这个ID在NET_BUFFER_LIST被再次分配时使用。
2)取消上层驱动引发的发送请求
上层驱动取消一个未完成的发送请求时也必须对这个发送请求的NET_BUFFER_LIST结构设定取消ID。
上层驱动在取消一个发送请求,NDIS会传递给Filter Driver的FilterCancelSendNetBufferLists一个取消ID来取消发送的
NET_BUFFER_LIST发送请求。
下面是驱动对需要传递的数据做了排除或者缓存的情况下,FilterCancelSendNetBufferLists要执行一些操作:
(1).遍历Filter Driver的发送队列,用NDIS_GET_NET_BUFFER_LIST_CANCEL_ID获得队列中NET_BUFFER_LIST的取消ID与
FilterCancelSendBufferLists的取消ID比较。
(2).移除队列中取消ID和FilterCancelSendBufferLists中取消ID相同的元素。
(3).调用NdisFSendNetBufferListsComplete来完成这些NET_BUFFER_LIST并设定返回状态NDIS_STATUS_CANCELLED.
(4).调用NdisFCancelSendNetBufferLists传递取消发送请求给下层驱动。传递取消ID给下层驱动就Filter Driver取消自己引发的一
样。
<在驱动中接收数据>
在Filter Driver中可以引发一个接收指示或过滤下层驱动引发的接收指示。
当下层驱动微端口驱动调用NdisMIndicateReceiveNetBufferLists时,NDIS会提交一个NET_BUFFER_LIST给在驱动栈上离微端口驱动最近的那个
Filter Module。
1)自己引发一个接收指示
Filter Driver调用NdisFIndicateReceiveNetBufferLists来给上层驱动指示接收的数据。
这个函数是通过NET_BUFFER_LIST结构给上层驱动指示数据的,因此Filter Driver需要从池中分配这个结构。如果
NdisFIndicateReceiveNetBufferLists的调用被上层驱动接收成功,NDIS通过驱动的FilterReturnNetBufferLists返回
NdisFIndicateReceiveNetBufferLists指示数据。
Filter Driver调用NdisFIndicateReceiveNetBufferLists后Filter Driver失去了对NET_BUFFER_LIST的所有权直到
FilterReturnNetBuffer被调用才恢复。
如果Filter Driver在调用NdisFIndicateReceiveNetBufferLists时设置ReceiveFlags为NDIS_RECEIVE_FLAGS_RESOURCES
(当前驱动资源紧张),在函数返回后 Filter Driver会立即恢复对NET_BUFFER_LIST的所有权,这时Filter Driver必须立即处理这个
NET_BUFFER_LIST的返回,因此NDIS在这种情况下是不会调用FilterReturnNetBufferLists返回NET_BUFFER_LIST结构的。
2)过滤接收的指示数据
Filter Driver可以在指示数据给上层驱动之前对这个数据进行过滤。可以对每一个提交到FilterReceiveNetBufferLists的数据做以下
操作:
(1).传递给上层之前改动缓冲区内容,NDIS保证上下文空间的有效性。
如果NDIS设置了FilterReceiveNetBufferLists参数ReceiveFlags的NDIS_RECEIVE_FLAGS_RESOURCES标记,Filter Drvier要
传递这个指示给上层驱动,并且只有恢复了数据的原始状态后才能从FilterReturnNetBufferLists返回给下层驱动。
(2).拒绝数据指示。
如果FilterReceiveNetBufferLists的ReceiveFlags没有设置NDIS_RECEIVE_FLAGS_RESOURCES标记,Filter Driver调用NdisFReturnNetBufferLists返回这个缓冲区数据;
如果设置了则可以什么都不做直接返回就可达到拒绝目的。
(3).排队在本地数据结构中以后再处理。
如果FilterReceiveNetBufferLists的ReceiveFlags设置NDIS_RECEIVE_FLAGS_RESOURCES标记,Filter Driver需要拷贝一份
NET_BUFFER_LIST后再返回(原样返回给下层驱动)。
Filter Driver调用NdisFIndicateNetBufferLists传递接收指示给上层驱动,类似于Filter Driver直接引发的接收指示。
如果上层驱动完成了对缓冲区的接收,NDIS会调用Filter Driver的FilterReturnNetBufferLists例程。
在Filter Driver 的FilterReturnNetBufferLists中应该撤销在接收路径上做的操作(如在FilterReceiveNetBufferLists中的处理).
当最底层的FilterModule完成对缓冲区的处理后,NDIS把缓冲区返回给MiniportDriver。
如果FilterReceiveNetBufferLists的ReceiveFlags没有设置NDIS_RECEIVE_FLAGS_RESOURCE标记,Filter Driver调用
NdisFReturnNetBufferList返回这个缓冲区数据,如果设置了FilterReceiveNetBufferLists直接返回时就把缓冲区返还给下层的微端口
驱动,就不会再经过FilterReturnNetBufferLists例程了。
最后贴下自己实现的代码,经检验没有错误(代码关于NBLcopy和NBLallocate):
PNET_BUFFER_LIST filterAllocateNetBufferList(
PMS_FILTER pFilter,
ULONG OldMDLLength
)
/*
Mark:
Allocate a new NBL. Accroding to PMS_FILTER's Parameters and OldMDLLength.
Return:
NewNBL
*/
{
BOOLEAN pFalse = FALSE;
PNET_BUFFER_LIST NewNBL = NULL;
PMDL NewMDL = NULL;
PUCHAR NewMDLAddress = NULL;
ULONG NewOffSet = 0;
ULONG NewMDLLength = 0;
ULONG TotalLength = 0;
KdPrint(("<----------Allocate NBL START-------->\n"));
do{
//Copy the First NB's MDL to New
NewMDLAddress = NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle,
OldMDLLength,
FILTER_ALLOC_TAG,
LowPoolPriority);
if (NewMDLAddress == NULL) { KdPrint(("NewMDLAddress Allocate Failed.\n")); break; }
else KdPrint(("NewMDLAddress Allocate SuccessFul.\n"));
NdisZeroMemory(NewMDLAddress, OldMDLLength);
NewMDL = NdisAllocateMdl(pFilter->FilterHandle, NewMDLAddress, OldMDLLength);
//NewNBL Allocate
NewNBL = NdisAllocateNetBufferAndNetBufferList(pFilter->SendNetBufferListPool, 0, 0, NewMDL, 0, OldMDLLength);
NewNBL->SourceHandle = pFilter->FilterHandle;
if (NewNBL == NULL){
KdPrint(("NewNBL Allocate Failed!\n"));
break;
}
else{
KdPrint(("NewNBL Allocate Successful!\n"));
//break;
}
} while (pFalse);
KdPrint(("<----------Allocate NBL END-------->\n"));
return NewNBL;
}
PNET_BUFFER_LIST filterCopyNetBufferList(
PNET_BUFFER_LIST OldNBL,
PMS_FILTER pFilter
)
/*
Mark:
Copy NetBufferList From OldNBL to NewBufferList.
Return:
NewNBL
*/
{
BOOLEAN pFalse = FALSE;
BOOLEAN FirstNB_MDL = TRUE;
PNET_BUFFER_LIST NewNBL = NULL;
PMDL NewMDL = NULL;
PUCHAR NewMDLAddress = NULL;
PMDL NewNMDL = NULL;
PUCHAR NewNMDLAddress = NULL;
ULONG NewNMDLLength = 0;
ULONG NewOffSet = 0;
ULONG NewMDLLength = 0;
ULONG TotalLength = 0;
PNET_BUFFER OldNB = NULL;
PMDL OldMDL = NULL;
ULONG OldOffSet = 0;
PUCHAR OldMDLAddress = NULL;
ULONG OldMDLLength = 0;
PMDL OldNMDL = NULL;
ULONG OldNOffSet = 0;
PUCHAR OldNMDLAddress = NULL;
KdPrint(("<----------Copy NBL START-------->\n"));
do{
//Copy the First NB's MDL to New
OldNB = NET_BUFFER_LIST_FIRST_NB(OldNBL);
OldMDL = NET_BUFFER_FIRST_MDL(OldNB);
NdisQueryMdl(OldMDL, (PVOID *)&OldMDLAddress, &OldMDLLength,NormalPagePriority);
KdPrint(("OldMDLLength :%d \n",OldMDLLength));
NewNBL = filterAllocateNetBufferList(pFilter,OldMDLLength);
if (NewNBL == NULL){
KdPrint(("NewNBL Allocate Failed.\n"));
}
//Success----------------------------------->
NewOffSet = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(NewNBL));
NewMDL = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(NewNBL));
NdisQueryMdl(NewMDL, (PVOID *)&NewMDLAddress, &NewMDLLength, NormalPagePriority);
KdPrint(("NewMDLLength : %d", NewMDLLength));
if (NewMDLAddress == NULL){
KdPrint(("NewMDLAddress Query Failed .\n"));
break;
}
KdPrint(("NewMDLAddress Query Successful .\n"));
OldOffSet = NET_BUFFER_CURRENT_MDL_OFFSET(OldNB);
KdPrint(("OldOffSet: %d \n", OldOffSet));
OldMDLLength = OldMDLLength - OldOffSet;
if (OldMDLLength > NewMDLLength){
OldMDLLength = NewMDLLength;
KdPrint((" OldMDLLength > NewMDLLength.\n"));
}
else if (OldMDLLength == NewMDLLength){
KdPrint((" OldMDLLength == NewMDLLength.\n"));
}
else{
KdPrint((" OldMDLLength < NewMDLLength.\n"));
}
KdPrint(("OldMDLLength : %d; \nNewMDLLength : %d;\n", OldMDLLength, NewMDLLength));
KdPrint(("Move Memory \n"));
NdisMoveMemory(NewMDLAddress, (OldMDLAddress + OldOffSet), OldMDLLength);
KdPrint(("Move Memory Success.\n"));
//------------------------------>Success
OldNMDL = OldMDL->Next;//第一个NB的第二个MDL
if (OldNMDL == NULL) KdPrint(("!!!!!!!!OldMDL Next is Emty!\n"));
else KdPrint(("!!!!!!!!OldMDL Next Have Somthing.\n"));
OldNOffSet = 0;
//Circulation
for (;;){//NNB
for (;;){//NMDL
if (OldNMDL == NULL) break;//If OldNMDL is Empty,break and get next NB;
KdPrint(("-----------Start Move Memory-----------\n"));
NdisQueryMdl(OldNMDL, (PVOID *)&OldNMDLAddress, &OldMDLLength, NormalPagePriority);
NewNMDLAddress = NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle,
OldMDLLength,
FILTER_ALLOC_TAG,
LowPoolPriority);
if (NewNMDLAddress == NULL) KdPrint(("-------NewNMDLAddress is NULL--------\n"));
if(NewNMDLAddress == NULL){
KdPrint(("NewMDLAddress Allocate Failed.\n"));
break;
}else
KdPrint(("NewMDLAddress Allocate SuccessFul.\n"));
NdisZeroMemory(NewNMDLAddress, OldMDLLength);
NewNMDL = NdisAllocateMdl(pFilter->FilterHandle, NewNMDLAddress, OldMDLLength);
if (NewNMDL == NULL){ KdPrint(("--------NewNMDL is NULL----------\n")); }
else KdPrint(("----------NewNMDL Create Successful---------\n"));
OldMDLLength = OldMDLLength - OldNOffSet;
KdPrint(("OldMDLLength: %d.\nOldOffSet: %d.\n",OldMDLLength,OldNOffSet));
if (OldMDLLength > NewNMDLLength){
OldMDLLength = NewNMDLLength;
KdPrint((" OldMDLLength > NewNMDLLength.\n"));
}
else if (OldMDLLength == NewMDLLength){
KdPrint((" OldMDLLength == NewNMDLLength.\n"));
}
else{
KdPrint((" OldMDLLength < NewNMDLLength.\n"));
}
KdPrint(("Move Memory \n"));
NdisMoveMemory(NewNMDLAddress, (OldNMDLAddress + OldNOffSet), OldMDLLength);
KdPrint(("Move Memory Success.\n"));
//
OldNOffSet = 0;
TotalLength += OldMDLLength;
NewMDL->Next = NewNMDL;
NewMDL = NewMDL->Next;
OldNMDL = OldNMDL->Next;
KdPrint(("-----------End Move Memory-----------\n"));
}
OldNB = NET_BUFFER_NEXT_NB(OldNB);
if (OldNB != NULL){
OldNMDL = NET_BUFFER_FIRST_MDL(OldNB);
OldNOffSet = NET_BUFFER_CURRENT_MDL_OFFSET(OldNB);
KdPrint(("Have Another NB in OldNBL"));
}
else{ KdPrint(("Not Have NB in OldNBL any all.\n")); break; }
}
}while(pFalse);
NewNBL->FirstNetBuffer->DataLength = TotalLength;
KdPrint(("<----------Copy NBL END-------->\n"));
return NewNBL;
}
(代码关于过滤单个NBL):
BOOLEAN filterScanSingleNBL(
PNET_BUFFER_LIST NetBufferLists,
//UCHAR RulesPort,
PMS_FILTER pFilter
)
/*
Mark:
Filter & Scan everyNB in a single NBL
Return:
A new NBL
*/
{
//UNREFERENCED_PARAMETER(RulesPort);
BOOLEAN Match = TRUE;
BOOLEAN Doit = FALSE;
PNET_BUFFER_LIST MatchNBL = NULL;
PNET_BUFFER MatchNB = NULL;
PMDL MatchMDL = NULL;
ULONG MatchMDLOffSet = 0;
PUCHAR MatchMDLAddress = NULL;
PUCHAR Data = NULL;
ULONG DataOffSet = 0;
ULONG Length = 0;
ULONG MatchLength = 0;
do{
KdPrint(("--------------------Scan a NBL Start-----------------------\n"));
//将NetBufferLists拷贝进一个空的NBL中,并将其赋值给MatchNBL
MatchNBL = filterCopyToNewNetBufferList(NetBufferLists, pFilter);
if (MatchNBL == NULL) break;
//获得NBL中数据总长度
Length = filterGetNBLLength(MatchNBL);
//分配UCHAR 数据空间
Data = NdisAllocateMemoryWithTagPriority(pFilter->FilterHandle, Length, FILTER_ALLOC_TAG, LowPoolPriority);
if (Data == NULL) break;
NdisZeroMemory(Data,Length);
//Read NBL
MatchNB = NET_BUFFER_LIST_FIRST_NB(MatchNBL);
KdPrint(("MatchNB 's DataLength: %d\nNetBufferLists 's DataLengthLength: %d",filterGetNBLLength(MatchNBL),filterGetNBLLength(NetBufferLists)));
MatchMDL = NET_BUFFER_CURRENT_MDL(MatchNB);
MatchMDLOffSet = NET_BUFFER_CURRENT_MDL_OFFSET(MatchNB);
//将所有读取出来的数据复制到缓冲区Data中
for (;;){
if (MatchMDL == NULL) { KdPrint(("------>Next MatchMDL == NULL----->\n")); break; }
NdisQueryMdl(MatchMDL, (PVOID *)&MatchMDLAddress, &MatchLength, NormalPagePriority);
KdPrint(("MatchLength: %d\nMatchMDLOffSet: %d\n", MatchLength, MatchMDLOffSet));
MatchLength -= MatchMDLOffSet;
//输出所有当前MDL数据
for (int i = 0; i<MatchLength; i++)
KdPrint(("%2x\n", MatchMDLAddress[i]));
if (MatchLength > Length)
KdPrint(("MatchLength > Length.\n"));
if (MatchLength <= 0)
KdPrint(("MatchLength <= 0 .\n"));
NdisMoveMemory((Data + DataOffSet), (MatchMDLAddress + MatchMDLOffSet), MatchLength);
DataOffSet += MatchLength;
MatchMDLOffSet = 0;
MatchMDL = MatchMDL->Next;
}
//输出拷贝进数据的一个Data内存
for (int i = 0; i<MatchLength; i++) KdPrint(("Data: %2x \n", Data[i]));
//解析Data中的数据
//如果其中有一项匹配,则return
do{
} while (Doit);
//释放申请的Data内存
ExFreePoolWithTag(Data,FILTER_ALLOC_TAG);
} while (Doit);
KdPrint(("--------------------Scan a NBL End-----------------------\n"));
return Match;
}
刚入驱动,若有错误还望前辈们多多指出。