FilterSendNetBufferListsComplete:
NDIS calls the FilterSendNetBufferListsComplete function to complete a send request that a filter driver started by calling the NdisFSendNetBufferLists function.
FilterSendNetBufferLists:
NDIS calls the FilterSendNetBufferLists function to allow a filter driver to filter a linked list of NET_BUFFER_LISTstructures.
调用关系如下:
ndis6.0中NET_BUFFER_LIST和NET_BUFFER是两个很重要的数据结构。
如何在FilterSendNetBufferLists函数中读取数据包的内容呢,步骤如下:
1.pCurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(CurrNbl)
2.pMdl = NET_BUFFER_CURRENT_MDL(pCurrentNetBuffer)
3.NdisQueryMdl(
pMdl,
&pEthHeader,
&ulBufferLength,
NormalPagePriority);可以获得Mdl的地址指针保存在pEthHeader中,同时返回MDL内存块的大小ulBufferLength。
4.pEthHeader = (struct ether_header *)((PUCHAR)pEthHeader + NET_BUFFER_CURRENT_MDL_OFFSET(pCurrentNetBuffer)),这样pEthHeader 即指向我们熟悉的数据帧了(包含以太头+TCP/IP层头部)。
在获取数据包的过程中需要注意的是:
1).一个NET_BUFFER_LIST结构指向的是一连串的NET_BUFFER,这些NET_BUFFER中保存的数据包内容属于同一个stream(the same src-ip,dst-ip,src-port,dst-port and protocol)。通过NET_BUFFER_NEXT_NB(pCurrentNetBuffer)可以获取下一个NET_BUFFER。
2).在NET_BUFFER中包含NET_BUFFER_DATA结构
typedef struct _NET_BUFFER_DATA {
PNET_BUFFER Next;
PMDL MdlChain;
ULONG DataLength;
ULONG DataOffset;
PMDL CurrentMdl;
ULONG CurrentMdlOffset;
} NET_BUFFER_DATA, *PNET_BUFFER_DATA;
数据包的内容保存在MDL中,在上面的步骤2和3中,可通过NET_BUFFER信息获取到保存数据包内容的地址。因为MDL指示的那一块内存会有一个MDL头部信息(我并不确定),所以需要用pEthHeader + NET_BUFFER_CURRENT_MDL_OFFSET(pCurrentNetBuffer),这时才指向真正意义上的数据包内容。
3).一个NET_BUFFER因为MDL指向的内存大小有限制(我的观察是100个字节左右),所以一个NET_BUFFER会含有多个MDL(如一个1000字节的数据包,可能就会分为10个MDL进行保存),用MdlChain表示(即一个Mdl链表)。
4).通常情况下,NET_BUFFER中MdlChain与NET_BUFFER_CURRENT_MDL(pCurrentNetBuffer)获取到的CurrentMdl相同,同样的NET_BUFFER_CURRENT_MDL_OFFSET(pCurrentNetBuffer)与NET_BUFFER中DataOffset相同。
5).NET_BUFFER中DataLength代表数据包的总长度。在上面的步骤3中,获取到的ulBufferLength - NET_BUFFER_CURRENT_MDL(pCurrentNetBuffer),若所得值小于DataLength,则说明NET_BUFFER中含有多个MDL,若想获取完整包的信息则通过pMdl.next获取下一个MDL指针,在进行相关操作。
下面是我改写filter中FilterSendNetBufferLists函数的代码:
VOID
FilterSendNetBufferLists(
IN NDIS_HANDLE FilterModuleContext,
IN PNET_BUFFER_LIST NetBufferLists,
IN NDIS_PORT_NUMBER PortNumber,
IN ULONG SendFlags
)
/*++
Routine Description:
Send Net Buffer List handler
This function is an optional function for filter drivers. If provided, NDIS
will call this function to transmit a linked list of NetBuffers, described by a
NetBuferList, over the network. If this handler is NULL, NDIS will skip calling
this fitler when sending a NetBufferList and will call the next lower fitler
in the stack with a non_NULL FilterSendNetBufferList handleror the miniport driver.
A filter that doesn't provide a FilerSendNetBufferList handler can not initiate a
send o its own.
Arguments:
FilterModuleContext: Pointer to our filter context area.
NetBufferLists: Pointer to a List of NetBufferLists.
PortNumber - Port Number to which this send is targetted
SendFlags- Specifies if the call is at DISPATCH_LEVEL
Return Value:
NDIS_STATUS_SUCCESS:
NDIS_STATUS_PENDING:
NDIS_STATUS_INVALID_PACKET:
NDIS_STATUS_RESOURCES:
NDIS_STATUS_FAILURE:
NOTE: The filter will act like a passthru filter.
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
BOOLEAN DispatchLevel;
BOOLEAN bFalse = FALSE;
PNET_BUFFER_LIST CurrNbl, NextNbl;
PNET_BUFFER pCurrentNetBuffer;
PMDL pMdl;
ULONG ulOffset;
struct ether_header *pEthHeader = NULL;
ULONG ulBufferLength = 0;
ULONG NBDO;
ULONG NBDL;
//DEBUGP(DL_TRACE, ("===>SendNetBufferList: NBL = %p.\n", NetBufferLists));
do
{
DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags);
#if DBG
//
// we should never get packets to send if we are not in running state
//
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
//
// If the filter is not in running state, fail the send
//
if (pFilter->State != FilterRunning)
{
DEBUGP(4, ("NOT SURE WHETHER IN THIS PATH.\n"));
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
CurrNbl = NetBufferLists;
while (CurrNbl)
{
NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_PAUSED;
CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
}
NdisFSendNetBufferListsComplete(pFilter->FilterHandle,
NetBufferLists,
DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
break;
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
#endif
if (pFilter->TrackSends)
{
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
CurrNbl = NetBufferLists;
while (CurrNbl)
{
pFilter->OutstandingSends++;
FILTER_LOG_SEND_REF(1, pFilter, CurrNbl, pFilter->OutstandingSends);
NextNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
DEBUGP(4, ("===>BufferList: NBL = %p, begin the NB process.\n", NetBufferLists));
for(pCurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(CurrNbl);
pCurrentNetBuffer != NULL;
pCurrentNetBuffer = NET_BUFFER_NEXT_NB(pCurrentNetBuffer))
{
pMdl = NET_BUFFER_CURRENT_MDL(pCurrentNetBuffer);
ulOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pCurrentNetBuffer);
NBDL = NET_BUFFER_DATA_LENGTH(pCurrentNetBuffer);
NBDO = NET_BUFFER_DATA_OFFSET(pCurrentNetBuffer);
do
{
ASSERT(pMdl != NULL);
if (pMdl)
{
NdisQueryMdl(
pMdl,
&pEthHeader,
&ulBufferLength,
NormalPagePriority);
}
if (NULL == pEthHeader)
{
//
// The system is low on resources. Set up to handle failure
// below.
//
ulBufferLength = 0;
break;
}
if (0 == ulBufferLength)
{
break;
}
ASSERT(ulBufferLength > ulOffset);
//if the ulBufferLength < NBDL, then there are some more MDLs.
DbgPrint("NBDL IS %d, NBD0 IS %d, MDL_OFFSET IS %d, MDL NET BUFFER LENGTH IS %d\n", NBDL, NBDO, ulOffset, ulBufferLength);
ulBufferLength -= ulOffset;
pEthHeader = (struct ether_header *)((PUCHAR)pEthHeader + ulOffset);
if (ulBufferLength < sizeof(struct ether_header))
{
DEBUGP(DL_WARN,
("ReceiveNetBufferList: runt nbl %p, first buffer length %d\n",
CurrNbl, ulBufferLength));
break;
}
DbgPrint("DstMAC: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x\n",pEthHeader->ether_dhost[0],pEthHeader->ether_dhost[1],pEthHeader->ether_dhost[2],pEthHeader->ether_dhost[3],pEthHeader->ether_dhost[4],pEthHeader->ether_dhost[5]);
DbgPrint("srcMAC: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x\n",pEthHeader->ether_shost[0],pEthHeader->ether_shost[1],pEthHeader->ether_shost[2],pEthHeader->ether_shost[3],pEthHeader->ether_shost[4],pEthHeader->ether_shost[5]);
}while (bFalse);
DbgPrint("End one NB process!\n");
}
//DbgPrint("IF NOT SHOW THIE, MAYBE SOME PROBLEM.\n");
CurrNbl= NextNbl;
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
//
// If necessary, queue the NetBufferList in a local structure for later processing
//
NdisFSendNetBufferLists(pFilter->FilterHandle, NetBufferLists, PortNumber, SendFlags);
}
while (bFalse);
DEBUGP(4, ("<===SendNetBufferList. \n"));
}
其中用到了一个新的数据类型,可以添加到filter.h头文件中:
#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */
struct ether_header {
UCHAR ether_dhost[ETHER_ADDR_LEN];
UCHAR ether_shost[ETHER_ADDR_LEN];
USHORT ether_type;
};