windows 驱动实例分析系列: NDIS 6.0的Filter 驱动讲解

NDIS Filter驱动可能是最简单的内核驱动之一,以至于windows甚至直接在内核编程中给出了案例,默认情况下,安装了WDK的VS编译器甚至可以直接生成一个NDIS Filter驱动,它也被认为是大量网络过滤驱动实现的基础。

然而这个是一个漫长的过程,在DDK的时代(XP),NDIS5.x有中间层驱动,那个驱动是一个非常奇特的驱动,对上层表现得像小端口驱动,对下层表现的像协议驱动,这意味着需要实现两层接口,非常麻烦,但是后来微软发现似乎没有这个必要,于是重写了NDIS的核心部分,将Filter抽象出来,这就是NDIS Filter驱动的来源:

79e216c3ebf748fa887b683c5166b46f.png

可以在驱动中直接生成这一类驱动:

0d3befc6fc944b77bf662d42e15616ac.png

生成的结果如下图:

4dcad96d69514928951ca31a08113af5.png

由于这一类驱动非常简单,我们就从实用性和代码分析方面来看吧。

关于Filter驱动

Filter驱动的位置非常特殊,它可以被加载到window系统中当前所有存在的网络适配器之上,无论是Wifi还是网卡,它的实现方式几乎一摸一样,故它主要用来做两件事:

1. 过滤网络数据包: 由于它能够拦截所有的网络数据包,所以它非常适合用来做防火墙一类的应用;

2. 提高socket效率: 在之前文章中讲述过,在内核中直接拦截数据包可以比在应用层拦截数据包高效40%,通常10%的效率就可以在商业和技术上形成断层了。事实上,在xp时代,NI曾经提供基于socket传输的工业相机,同时灰点相机首先推出了基于内核Filter的驱动,二者的效率差是40%,笔者当时也在几乎同时间开始了这一类驱动的开发,深刻体会到一项新技术对市场的巨大变革。

NDIS主要实现 OSI 模型的底层四层:

02a4b2419e56479cbb9f5d22a1ea0a4f.png

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");
}

这个例程唯一奇怪的一点是,它没有提供如何访问缓冲区代码,这个问题会在驱动改造的过程中中体现。

  对于在“蓝网之家”影响下蠢蠢而动搞 Windows 95 远程启动的朋友可能不少,那么大家一定对 NDIS 这几个字母不会感到陌生。其实不只是在远程启动这一层,只要是网卡的驱动盘,大家都会在里面发现有类似 NDISNDIS2、NDIS3、NDIS4一样的目录,只是大家在 Windows 9x 或 NT 中安装、设置网卡时没有注意到它罢了。但即使大搞特搞 RPL 的朋友对其大概也是只知其然而不其所以然。    NDIS 是什么?有什么作用?       NDIS 的全称是 Network Device Interface Specification,中文意思就是网络设备接口规范。    根据 OSI 七层模型,物理层定义了对网卡、线缆以及其它物理硬件设备进行配置的标准。节点间建立通信时是由物理层在传输介质上传送二进制信息实现的,因此,在发送端和接收端都还必须有一个程序来格式化这种信息流并将其传送给上一层。NDIS 的作用就是避免在访问网卡每次进行传输时都编写相应的代码。由此说来,NDIS 本质上是一种软件接口,有了 NDIS ,所有的传输就可以采用一种通用的方式来访问由不同厂商制造的网卡了,即它是用来连接协议堆栈和网卡的。   与此相关的软件还有重定向器(Redirector)和服务器(Server)。前者的目的是截获来自 OSI 会话层的网络调用,并通过将其传送到相应的协议驱动程序接口而格式化成 NDIS 能够识别和使用的命令。后者则负责接收从重向器传过来的来自于远程计算机的请求,再将这一请求传送给相应的本地文件系统驱动程序,最后再由该“服务器”将数据沿协议堆栈向下传递给客户机。    TCP协议也是通过调用 NDIS 接口服务来完成传输操作的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值