NDIS Filter驱动可能是最简单的内核驱动之一,以至于windows甚至直接在内核编程中给出了案例,默认情况下,安装了WDK的VS编译器甚至可以直接生成一个NDIS Filter驱动,它也被认为是大量网络过滤驱动实现的基础。
然而这个是一个漫长的过程,在DDK的时代(XP),NDIS5.x有中间层驱动,那个驱动是一个非常奇特的驱动,对上层表现得像小端口驱动,对下层表现的像协议驱动,这意味着需要实现两层接口,非常麻烦,但是后来微软发现似乎没有这个必要,于是重写了NDIS的核心部分,将Filter抽象出来,这就是NDIS Filter驱动的来源:
可以在驱动中直接生成这一类驱动:
生成的结果如下图:
由于这一类驱动非常简单,我们就从实用性和代码分析方面来看吧。
关于Filter驱动
Filter驱动的位置非常特殊,它可以被加载到window系统中当前所有存在的网络适配器之上,无论是Wifi还是网卡,它的实现方式几乎一摸一样,故它主要用来做两件事:
1. 过滤网络数据包: 由于它能够拦截所有的网络数据包,所以它非常适合用来做防火墙一类的应用;
2. 提高socket效率: 在之前文章中讲述过,在内核中直接拦截数据包可以比在应用层拦截数据包高效40%,通常10%的效率就可以在商业和技术上形成断层了。事实上,在xp时代,NI曾经提供基于socket传输的工业相机,同时灰点相机首先推出了基于内核Filter的驱动,二者的效率差是40%,笔者当时也在几乎同时间开始了这一类驱动的开发,深刻体会到一项新技术对市场的巨大变革。
NDIS主要实现 OSI 模型的底层四层:
NDIS Filter驱动位于网络层和传输层之间,这带来的直接效果就是,NDIS Filter获取的是数据链路层的数据包,我们还需要解析Mac首部、IP首部、TCP/UDP首部。
初始化和卸载
初始化的代码如下:
_Use_decl_annotations_
NTSTATUS
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
加载此驱动程序时调用的第一个入口点。
向 NDIS 注册为筛选器驱动程序并创建设备
用于与用户模式进行通信。
Arguments:
DriverObject - pointer to the system's driver object structure
for this driver
RegistryPath - system's registry path for this driver
Return Value:
STATUS_SUCCESS if all initialization is successful, STATUS_XXX
error code if not.
--*/
{
NDIS_STATUS Status;
NDIS_FILTER_DRIVER_CHARACTERISTICS FChars;
NDIS_STRING ServiceName = RTL_CONSTANT_STRING(FILTER_SERVICE_NAME);
NDIS_STRING UniqueName = RTL_CONSTANT_STRING(FILTER_UNIQUE_NAME);
NDIS_STRING FriendlyName = RTL_CONSTANT_STRING(FILTER_FRIENDLY_NAME);
BOOLEAN bFalse = FALSE;
UNREFERENCED_PARAMETER(RegistryPath);
DEBUGP(DL_TRACE, "===>DriverEntry...\n");
FilterDriverObject = DriverObject;
do
{
NdisZeroMemory(&FChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS));
FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS);
#if NDIS_SUPPORT_NDIS61
FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2;
#else
FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
#endif
FChars.MajorNdisVersion = FILTER_MAJOR_NDIS_VERSION;
FChars.MinorNdisVersion = FILTER_MINOR_NDIS_VERSION;
FChars.MajorDriverVersion = 1;
FChars.MinorDriverVersion = 0;
FChars.Flags = 0;
FChars.FriendlyName = FriendlyName;
FChars.UniqueName = UniqueName;
FChars.ServiceName = ServiceName;
//TODO:大多数处理程序都是可选的,
// 但是此示例包括它们全部用于说明目的。
// 如果您不需要特定的处理程序,
// 请将其设置为 NULL,
// NDIS 将更有效地代表您传递操作。
FChars.SetOptionsHandler = FilterRegisterOptions;
FChars.AttachHandler = FilterAttach;
FChars.DetachHandler = FilterDetach;
FChars.RestartHandler = FilterRestart;
FChars.PauseHandler = FilterPause;
FChars.SetFilterModuleOptionsHandler = FilterSetModuleOptions;
FChars.OidRequestHandler = FilterOidRequest;
FChars.OidRequestCompleteHandler = FilterOidRequestComplete;
FChars.CancelOidRequestHandler = FilterCancelOidRequest;
FChars.SendNetBufferListsHandler = FilterSendNetBufferLists;
FChars.ReturnNetBufferListsHandler = FilterReturnNetBufferLists;
FChars.SendNetBufferListsCompleteHandler = FilterSendNetBufferListsComplete;
FChars.ReceiveNetBufferListsHandler = FilterReceiveNetBufferLists;
FChars.DevicePnPEventNotifyHandler = FilterDevicePnPEventNotify;
FChars.NetPnPEventHandler = FilterNetPnPEvent;
FChars.StatusHandler = FilterStatus;
FChars.CancelSendNetBufferListsHandler = FilterCancelSendNetBufferLists;
DriverObject->DriverUnload = FilterUnload;
FilterDriverHandle = NULL;
// Initialize spin locks
FILTER_INIT_LOCK(&FilterListLock);
InitializeListHead(&FilterModuleList);
Status = NdisFRegisterFilterDriver(DriverObject,
(NDIS_HANDLE)FilterDriverObject,
&FChars,
&FilterDriverHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_WARN, "Register filter driver failed.\n");
break;
}
Status = NDISFilterRegisterDevice();
if (Status != NDIS_STATUS_SUCCESS)
{
NdisFDeregisterFilterDriver(FilterDriverHandle);
FILTER_FREE_LOCK(&FilterListLock);
DEBUGP(DL_WARN, "Register device for the filter driver failed.\n");
break;
}
}
while(bFalse);
DEBUGP(DL_TRACE, "<===DriverEntry, Status = %8x\n", Status);
return Status;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
NDIS_STATUS
NDISFilterRegisterDevice(
VOID
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
UNICODE_STRING DeviceName;
UNICODE_STRING DeviceLinkUnicodeString;
PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceAttribute;
PFILTER_DEVICE_EXTENSION FilterDeviceExtension;
PDRIVER_OBJECT DriverObject;
DEBUGP(DL_TRACE, "==>NDISFilterRegisterDevice\n");
NdisZeroMemory(DispatchTable, (IRP_MJ_MAXIMUM_FUNCTION+1) * sizeof(PDRIVER_DISPATCH));
// 初始化派发例程
DispatchTable[IRP_MJ_CREATE] = NDISFilterDispatch;
DispatchTable[IRP_MJ_CLEANUP] = NDISFilterDispatch;
DispatchTable[IRP_MJ_CLOSE] = NDISFilterDispatch;
DispatchTable[IRP_MJ_DEVICE_CONTROL] = NDISFilterDeviceIoControl;
NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING);
NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING);
// Create a device object and register our dispatch handlers
NdisZeroMemory(&DeviceAttribute, sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES));
DeviceAttribute.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES;
DeviceAttribute.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1;
DeviceAttribute.Header.Size = sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES);
DeviceAttribute.DeviceName = &DeviceName;
DeviceAttribute.SymbolicName = &DeviceLinkUnicodeString;
DeviceAttribute.MajorFunctions = &DispatchTable[0];
DeviceAttribute.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION);
// 创建符号链接
Status = NdisRegisterDeviceEx(
FilterDriverHandle,
&DeviceAttribute,
&NdisDeviceObject,
&NdisFilterDeviceHandle
);
if (Status == NDIS_STATUS_SUCCESS)
{
FilterDeviceExtension = (PFILTER_DEVICE_EXTENSION) NdisGetDeviceReservedExtension(NdisDeviceObject);
FilterDeviceExtension->Signature = 'FTDR';
FilterDeviceExtension->Handle = FilterDriverHandle;
// Workaround NDIS bug
DriverObject = (PDRIVER_OBJECT)FilterDriverObject;
}
DEBUGP(DL_TRACE, "<==NDISFilterRegisterDevice: %x\n", Status);
return (Status);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
NDISFilterDeregisterDevice(
VOID
)
{
if (NdisFilterDeviceHandle != NULL)
{
NdisDeregisterDeviceEx(NdisFilterDeviceHandle);
}
NdisFilterDeviceHandle = NULL;
}
1. NDIS和常规的WDF有很大区别,例如说在代码中有关于自旋锁的部分:
// NDIS初始化自旋锁
FILTER_INIT_LOCK(&FilterListLock);
// NDIS宏如下
#define FILTER_INIT_LOCK(_pLock) NdisAllocateSpinLock(_pLock)
// WDF 初始化自旋锁
NTSTATUS WdfSpinLockCreate(
[in, optional] PWDF_OBJECT_ATTRIBUTES SpinLockAttributes,
[out] WDFSPINLOCK *SpinLock
);
事实上,NDIS自旋锁的定义如下:
typedef struct _NDIS_SPIN_LOCK
{
KSPIN_LOCK SpinLock;
KIRQL OldIrql;
} NDIS_SPIN_LOCK, * PNDIS_SPIN_LOCK;
typedef NDIS_SPIN_LOCK FILTER_LOCK;
二者功能一样,但是此处使用NDIS函数的原因是NDIS库对很多代码都进行了封装,这个封装本身就是为了避免一些不恰当的操作。
2. 这里主要的函数就是 NdisFRegisterFilterDriver以及NdisRegisterDeviceEx,这也是NDIS和WDM之类驱动比较特殊的地方,它会处理NDIS_FILTER_DRIVER_CHARACTERISTICS结构中的一系列函数,而不是先处理AddDevice例程(在这里,被认为是NdisRegisterDeviceEx)。
这是因为这个行为被NDIS封装了,Filter驱动分为上下沿,但是NDIS Filter总是位于协议驱动和小端口驱动之间,故它们没有必要去增加新设备,这并不是网卡驱动不支持PNP,新设备可以作为PNP来增加,NDIS会将Filter挂接上去,就是下面的两个函数:
FChars.AttachHandler = FilterAttach;
FChars.DetachHandler = FilterDetach;
代码中的技巧
一个是双链表和filterFindFilterModule:
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
typedef struct _MS_FILTER
{
LIST_ENTRY FilterModuleLink;
//Reference to this filter
ULONG RefCount;
NDIS_HANDLE FilterHandle;
NDIS_STRING FilterModuleName;
NDIS_STRING MiniportFriendlyName;
NDIS_STRING MiniportName;
NET_IFINDEX MiniportIfIndex;
NDIS_STATUS Status;
NDIS_EVENT Event;
ULONG BackFillSize;
FILTER_LOCK Lock; // Lock for protection of state and outstanding sends and recvs
FILTER_STATE State; // Which state the filter is in
ULONG OutstandingSends;
ULONG OutstandingRequest;
ULONG OutstandingRcvs;
FILTER_LOCK SendLock;
FILTER_LOCK RcvLock;
QUEUE_HEADER SendNBLQueue;
QUEUE_HEADER RcvNBLQueue;
NDIS_STRING FilterName;
ULONG CallsRestart;
BOOLEAN TrackReceives;
BOOLEAN TrackSends;
#if DBG
BOOLEAN bIndicating;
#endif
PNDIS_OID_REQUEST PendingOidRequest;
}MS_FILTER, * PMS_FILTER;
// 这是双链表结构,事实上这是非常高效的内核结构
_IRQL_requires_max_(DISPATCH_LEVEL)
PMS_FILTER
filterFindFilterModule(
_In_reads_bytes_(BufferLength)
PUCHAR Buffer,
_In_ ULONG BufferLength
)
{
PMS_FILTER pFilter;
PLIST_ENTRY Link;
BOOLEAN bFalse = FALSE;
FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse);
Link = FilterModuleList.Flink;
while (Link != &FilterModuleList)
{
// 这是典型的C语言指针技术,利用结构中成员的地址反向解析出拎一个成员的地址
pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
if (BufferLength >= pFilter->FilterModuleName.Length)
{
if (NdisEqualMemory(Buffer, pFilter->FilterModuleName.Buffer, pFilter->FilterModuleName.Length))
{
FILTER_RELEASE_LOCK(&FilterListLock, bFalse);
return pFilter;
}
}
Link = Link->Flink;
}
FILTER_RELEASE_LOCK(&FilterListLock, bFalse);
return NULL;
}
我们观察到,MS_FILTER结构的第一个成员就是ENTRY_LIST,按照常理,结构对象的地址和结构对象中第一个成员的地址是同一个,但这里使用了下面的宏来获取结构对象的地址:
// 根据给定结构类型计算结构基址,以及结构内字段的地址。
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
这是因为这个宏是用来解决任一结构成员反推地址的,这项技术在内核中非常常见。
另外一个是do-while结构:
do
{
...
Status = NdisFRegisterFilterDriver(DriverObject,
(NDIS_HANDLE)FilterDriverObject,
&FChars,
&FilterDriverHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
Status = NDISFilterRegisterDevice();
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
}
while(bFalse);
这里是利用了 do-while 必然运行一次的语言特性,这种方式在初始化代码中非常常见,它用于替代常见的Goto语句。
数据包收发
数据包的收发主要是FilterSendNetBufferLists和FilterReceiveNetBufferLists两个函数:
_Use_decl_annotations_
VOID
FilterSendNetBufferLists(
NDIS_HANDLE FilterModuleContext,
PNET_BUFFER_LIST NetBufferLists,
NDIS_PORT_NUMBER PortNumber,
ULONG SendFlags
)
/*++
Routine Description:
发送网络缓冲区列表处理程序
此函数是Filter驱动程序的可选函数。如果提供,NDIS
将调用此函数通过网络传输由
NetBufferList 描述的 NetBuffers 链接列表。如果此处理程序为 NULL,NDIS 将在发送 NetBufferList 时跳过调用
此Filter,并将调用堆栈中的下一个较低
驱动程序。不提供 FilerSendNetBufferList
处理程序的Filter无法自行发起发送。
Arguments:
FilterModuleContext - our filter context area
NetBufferLists - a List of NetBufferLists to send
PortNumber - Port Number to which this send is targeted
SendFlags - specifies if the call is at DISPATCH_LEVEL
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
PNET_BUFFER_LIST CurrNbl;
BOOLEAN DispatchLevel;
BOOLEAN bFalse = FALSE;
DEBUGP(DL_TRACE, "===>SendNetBufferList: NBL = %p.\n", NetBufferLists);
do
{
DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags);
#if DBG
// 如果我们不处于运行状态,我们不应该发送数据包
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
// 如果Filter未处于运行状态,则发送失败
if (pFilter->State != FilterRunning)
{
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);
CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
// 如果需要,将 NetBufferLists 排队到本地结构中以供稍后处理。
// 但是,不要将它们排队太久,否则系统的性能可能会下降。
// 如果您需要无限期地保留 NBL,则分配内存,执行深度复制,
// 然后完成原始 NBL。
NdisFSendNetBufferLists(pFilter->FilterHandle, NetBufferLists, PortNumber, SendFlags);
}
while (bFalse);
DEBUGP(DL_TRACE, "<===SendNetBufferList. \n");
}
_Use_decl_annotations_
VOID
FilterReceiveNetBufferLists(
NDIS_HANDLE FilterModuleContext,
PNET_BUFFER_LIST NetBufferLists,
NDIS_PORT_NUMBER PortNumber,
ULONG NumberOfNetBufferLists,
ULONG ReceiveFlags
)
/*++
Routine Description:
FilerReceiveNetBufferLists 是Filter驱动程序的可选函数。
如果提供,此函数将处理底层 NIC 或较低级别Filter驱动程序发出的接收指示。此函数也可以作为环回的结果调用。如果此处理程序为 NULL,NDIS 将在处理接收指示时跳过调用此Filter,并将调用堆栈中的下一个更高驱动程序。不提供 FilterReceiveNetBufferLists 处理程序的Filter无法提供 FilterReturnNetBufferLists 处理程序,也无法自行启动原始接收指示。
Arguments:
FilterModuleContext - our filter context area.
NetBufferLists - a linked list of NetBufferLists
PortNumber - Port on which the receive is indicated
ReceiveFlags -
N.B.: It is important to check the ReceiveFlags in NDIS_TEST_RECEIVE_CANNOT_PEND.
This controls whether the receive indication is an synchronous or
asynchronous function call.
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
BOOLEAN DispatchLevel;
ULONG Ref;
BOOLEAN bFalse = FALSE;
#if DBG
ULONG ReturnFlags;
#endif
DEBUGP(DL_TRACE, "===>ReceiveNetBufferList: NetBufferLists = %p.\n", NetBufferLists);
do
{
DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags);
#if DBG
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
if (pFilter->State != FilterRunning)
{
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags))
{
ReturnFlags = 0;
if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags))
{
NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL);
}
NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags);
}
break;
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
#endif
ASSERT(NumberOfNetBufferLists >= 1);
// 如果您想丢弃接收到的数据包,则必须小心地
// 修改 NBL 链,如下所示:
// if NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags):
// 对于每个未丢弃的 NBL,暂时将其从链接列表中断开,
// 并使用NdisFIndicateReceiveNetBufferLists 和
// NDIS_RECEIVE_FLAGS_RESOURCES 标志集单独指示它。
// 然后立即将 NBL 重新链接到链中。当所有 NBL 都已
// 指示为已启动时,您可以从此函数返回。
// 否则(NDIS_TEST_RECEIVE_CANNOT_PEND 为 FALSE):
// 将 NBL 的链接列表分为两个链:一个链
// 包含要丢弃的数据包,另一个链中包含其他所有内容。
// 使用 NdisFReturnNetBufferLists 返回第一个链,
// 并使用 NdisFIndicateReceiveNetBufferLists 指示其余链。
// 注意:在以太网数据包的接收路径上,一个 NBL 将只有一个 NB。
// 因此假设您正在以太网上接收,
// 或者连接到 Native WiFi 上,您不必担心丢弃一个 NB,
// 而是尝试指示同一 NBL 上的其余 NB。
// 换句话说,如果第一个 NB 应该被丢弃,则丢弃整个 NBL。
// 如果您想修改数据包,并且可以快速完成,您可以在此处执行此操作。
// 但是,请确保您保存了足够的信息以撤消 FilterReturnNetBufferLists 处理程序中的修改。
// 如有必要,将 NetBufferLists 排队到本地结构中以供稍后处理。
// 但是,不要将它们排队太久,否则系统的性能可能会下降。
// 如果您需要无限期地保留NBL,则分配内存,执行
// 深度复制,并返回原始 NBL。
if (pFilter->TrackReceives)
{
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
pFilter->OutstandingRcvs += NumberOfNetBufferLists;
Ref = pFilter->OutstandingRcvs;
FILTER_LOG_RCV_REF(1, pFilter, NetBufferLists, Ref);
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
NdisFIndicateReceiveNetBufferLists(
pFilter->FilterHandle,
NetBufferLists,
PortNumber,
NumberOfNetBufferLists,
ReceiveFlags);
if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags) &&
pFilter->TrackReceives)
{
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
pFilter->OutstandingRcvs -= NumberOfNetBufferLists;
Ref = pFilter->OutstandingRcvs;
FILTER_LOG_RCV_REF(2, pFilter, NetBufferLists, Ref);
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
} while (bFalse);
DEBUGP(DL_TRACE, "<===ReceiveNetBufferList: Flags = %8x.\n", ReceiveFlags);
}
1. 在函数说明中明确指出了FilterSendNetBufferLists和FilterReceiveNetBufferLists都是可选的,但是如果为NULL,那么过滤驱动的某些功能就缺失了,所以一般也会增加这两个函数。
同时,这里也明确指出了,这两个函数不要长期排队,因为整个NDIS库将轮流使用这一套NET_BUFFER_LIST,如果持有太长时间就会有不可预测的问题。
2. 还有一个函数是FilterReturnNetBufferLists,如果不实现这个函数也会导致无法发起原始接受请求...
_Use_decl_annotations_
VOID
FilterReturnNetBufferLists(
NDIS_HANDLE FilterModuleContext,
PNET_BUFFER_LIST NetBufferLists,
ULONG ReturnFlags
)
/*++
Routine Description:
FilterReturnNetBufferLists 处理程序。
FilterReturnNetBufferLists 是一个可选函数。如果提供,NDIS 会调用
FilterReturnNetBufferLists 来将一个或多个 NetBufferLists 及其嵌入的 NetBuffers 的所有权返回给Filter驱动程序。如果此处理程序为 NULL,NDIS
将在将 NetBufferLists 返回底层
微型端口时跳过调用此Filter,并调用堆栈中的下一个较低驱动程序。不提供 FilterReturnNetBufferLists 处理程序的Filter无法自行发起接收指示。
Arguments:
FilterInstanceContext - our filter context area
NetBufferLists - a linked list of NetBufferLists that this
filter driver indicated in a previous call to
NdisFIndicateReceiveNetBufferLists
ReturnFlags - flags specifying if the caller is at DISPATCH_LEVEL
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
PNET_BUFFER_LIST CurrNbl = NetBufferLists;
UINT NumOfNetBufferLists = 0;
BOOLEAN DispatchLevel;
ULONG Ref;
DEBUGP(DL_TRACE, "===>ReturnNetBufferLists, NetBufferLists is %p.\n", NetBufferLists);
// 如果您的Filter将任何接收数据包注入到要接收的数据路径中,
// 则必须在此处识别它们的 NBL 并将其从链中删除。
// 不要尝试将您的 NBL 接收并返回到较低层。
// 如果您的Filter修改了 FilterReceiveNetBufferLists 处理程序中的
// 任何 NBL(或 NB、MDL 等),则必须在此处撤消修改。
// 通常,NBL 必须以您接收时的相同条件返回。
// (例外:NBL 可以在链接的列表中重新排序,并且暂存字段不重要)。
if (pFilter->TrackReceives)
{
while (CurrNbl)
{
NumOfNetBufferLists ++;
CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
}
}
// 返回收到的 NBL。如果您从链中删除了任何 NBL,
// 请确保链不为空(即 NetBufferLists!=NULL)。
NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags);
if (pFilter->TrackReceives)
{
DispatchLevel = NDIS_TEST_RETURN_AT_DISPATCH_LEVEL(ReturnFlags);
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
pFilter->OutstandingRcvs -= NumOfNetBufferLists;
Ref = pFilter->OutstandingRcvs;
FILTER_LOG_RCV_REF(3, pFilter, NetBufferLists, Ref);
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
DEBUGP(DL_TRACE, "<===ReturnNetBufferLists.\n");
}
这个例程唯一奇怪的一点是,它没有提供如何访问缓冲区代码,这个问题会在驱动改造的过程中中体现。