NDIS Filter Drivers指南

NDIS Filter Drivers


译者序 
本文是根据DDK中相关章节翻译,本人英语水有限文中难免有翻译及写的不当之处,
果有任何问题可以通LZIOG@163.com
邮件联系和交流。

1 介绍NDIS Filter Drivers

ilter Drivers提供了针对微端口驱动(Miniport Drivers)的过滤服务(filtering service),
NDIS驱动栈上必须包含微端口驱动(Miniport Drivers)和协议驱动(Protocol Drivers),可选
的包含 Filter  Drivers。更多关于 NDIS驱动栈的信息可查看 DDK的 MSDN中的 Driver  Stack 
Management。下面列举基本的栈配置图示: 
名称:  1.png查看次数: 0文件大小:  6.3 KB

下面的应用可能就需要一个 Filter Drivers 来完成: 

1) 基于安全或其它目录的数据过滤应用
 
2) 对网络数据进行监视,收集及统计的应用 

下面的几个小节将介绍 Filter Drivers 的一些特性和服务。

1.1 Filter Drivers  特性 

Filter Drivers 主要包括以下特性: 

1) 一个 Filter Drivers 实例叫 Filter Module。Filter Module 附加在一个适配器的微端口驱动上,
来自相同或不同 Filter Drivers 的多个 Filter Module 都可以被堆叠在一个网络适配器上 

2)在 Filter Module 被安装到驱动栈时,之上的协议驱动和之下的微端口驱动都不需要提供
额外的支持功能 

3)因为 Filter Module 不像中间层驱动(intermediate driver)那样提供一个虚拟的微口,也
不与某个设备对象联结,所以在微端口适配器(miniport adapter)之上的 Filter Module  功
能相当于一个修过过版本的微端口适配器(miniport adapter)。 原文:Because filter drivers 
do  not  implement  virtual  miniports  like  an  intermediate  driver,  filter  drivers  are  not 
associated with a device object. A miniport adapter with overlying filter modules functions as 
a modified version of the miniport adapter.)

4)NDIS 使用配置信息来到决定一个 Filter Module 附加到一个网络适配器栈上的顺序 

5)在不用拆除整驱动栈的情况下,NDIS 可以动态的插入、删除 Filter Module 或进行重新配


6)当 NDIS 重起驱动栈的时候协议驱动可以获得在栈上的 Filter Module 列表 

7)Filter Drivers 可以过滤下层网络适配器上绝大部分的通信。Filter Module 不联结某特定的
绑定(Filter  modules  are  not  associated  with  any  particular  binding  between  overlying 
protocol drivers and the miniport adapter.) 

8)Filter  Drivers  可以选择为过滤服务也可以选择为分流的不过滤服务,选择为哪一种是可
以动态配置的(Filter drivers can select the services that are filtered and can be bypassed for 
the  services  that  are  not  filtered.  The  selection  of  the  services  that  are  bypassed  and  the 
services that are filtered can be reconfigured dynamically.) 

9)NDIS  保证有效的上下文空间,也不就是说 Filter Drivers 不要需要通代码 COPY 缓冲区来
获得上下文空间 

1.2 Filter Drivers  服务 

Filter Drivers  提供发下服务: 

1) 发起一个发送请求和接收指示 

2) 改变数据缓冲区的顺序或对发送和接收数据进行调速 

3) 在一个驱动栈的接收或发送路径上更改、删除、添加数据 

4) 发起查询或设置 OID 的请求给下层驱动 

5) 过滤对下层驱动的 OID 查询或设置请求 

6) 过滤从下层驱动传来的 OID 查询和设置请求的应答 

7) 发起一个状态指示给上层的设备 

8) 过滤从下层传来的状态指示 

9) 管理注册表中每一个微端口适配器和其接口的参数 

1.3 Filter Drivers  类型

下面是两种主要类型的 Filter Drivers: 

1) Monitoring 
这种类型可以在驱动栈上做监视行为,但是它不能在驱动栈上进行数据修改行为。
Monitoring Filter Drivers 不在修改或发起数据。 

2) Modifying
这种类型可以在驱动栈上做更改行为,该类修改类型是特定于驱动的( The  type  of 
modification is driver‐specific)。 
在驱动的安装 INF 文件中 FilterType 的值是 0x00000001 就是一个 monitoring filter,如果
是 0x00000002 就是一个 modifying filter。 

你可以指定一个过滤驱动是 mandatory(强制)的(也是在配置 INF 文件中指定个特征),
它通常用在 modifying filter 驱动上,如果一个 mandatory 的 Filter  驱动不能被加载,那么它
联结的驱动栈将被拆除。

1.4 Mandatory Filter Drivers

Mandatory  Filter  Drivers 必须存在于驱动栈上,驱动栈才能正常工作。如果一个
Mandatory Filter Module 不附加,那么驱动栈也将会被拆除。Modifying 或 Monitoring 类型的
Filter Drivers 可以被指定为 Mandatory(强制)的。  所有中间层过滤驱动(filter intermediate 
drivers)都是可选的(与 Mandatory 相对,就是说如果不存在驱动栈也不被拆除) 

要附加一个 Mandatory Filter Drivers 在一个驱动栈上,NDIS 先解绑定所有协议驱动,附
加 Filter Module 后再重新绑定协议驱动。如果不能成功附加 Filter Drivers 那么拆除驱动栈的
下边界。 

要分离一个 Mandatory Filter Drivers 在一个驱动栈上,NDIS  先解绑定所有协议驱动,分
离 Filter Module 后重新绑定协议驱动。要分离一个 optional Filter Drivers(可选 Filter Drivers,
与 Mandatory(强制)  Filter Drivers 相对)时,NDIS 只暂停驱动栈,分离后重起驱动栈这时
并不解绑定协议驱动。 

如果计算机重起,如果一个 Mandatory  Filter  Drivers 还没有附加到微端口适配器
(miniport adapter)这时 NDIS 是不会绑定协议驱动的。 

在安装时用的 INF 文件中指是驱动是 Mandatory 还是 Optional。在 INF 文件在指定
FilterRunType 为 0x00000001 代表驱动是 Mandatory,指定 0x00000002 代表是 Optional。 

下面列出 DDK 所有例子 filter 中的 INF 文件的一个相关节,内容如下: 
代码:
;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Ndi installation support 
;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisMon" 
HKR, Ndi,CoServices,0x00010000,"NdisMon" 
HKR, Ndi,HelpText,,%NdisMon_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000001 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1  
标蓝的行是指定它是一个 Modifying 类型的 filter 驱动,标红的行就是指定它是一个
Mandatory Filter Drivers。关于 INF 的语法可以参看 DDK 帮助文档中的相关节。

2 编写NDIS Filter Drivers 

在写一个 Filter 驱动之前,应该先了解一下 Miniport Drivers 和 Protocol Drivers 的相关知
识。可以参见 DDK 帮助文件的相关章节。下面各小节将提供一个如何编写一个 Filter Drivers
的参考。

2.1 初始化Filter Driver

系统加载 Filter  Driver 后初始化立即发生,Filter  Dreivers 被加载为系统服务。系统加载
Filter  Drivers 可在 Miniport  Drivers 被加载的之前、期间和之后。如果一个微端口适配器
(miniport adatpter)所支持的类型与 Filter Drivers 所要绑定的类匹配且 Filter Drivers 也完成
了初始化,那么 NDIS 可以加附加一个 Filter Module 到这个微端口适配器上。 

如果一个 Filter Drivers 没有加载,那即使现在驱动栈启动了那么系统也会加载它。详细
的信息可查看 DDK 帮助文档中的 Starting a Driver Stack。

Filter Drivers 被加载后,系统会调用驱动的 DriverEntry 例程。在 DriverEntry 例程在应用
当立即调用 NdisFRegisterFilterDriver 注册驱动的 FilterXXX 入口例程。NdisFRegisterFilterDriver
成功将会返回 STATUS_SUCCESS。然后申请相关资源,如果一个资源申请分配置失败那么记
得释放已经申请分配到资源。系统通过两个参数调用 DriverEntry:

1) 一个指向被 I/O 系统事先创建好的驱动对像的指针 

2) 一个指向存储驱动特定参数注册表路径的指针 

Filter  Drivers 通过 DriverEntry 的 DriverObject 参数做 NdisFRegisterFilterDriver 向 NDIS 注
册一个 NDIS Filter Driver。Filter Drivers 使用注册表路径参数来获得相关的运行参数。

一个 Filter  Drivers 在 DriverEntry 中调用 NdisFRegisterFilterDriver,它导出一系统的
FilterXXX 的例程序集合 ,并通过一 个 NDIS_FILTER_DRIVER_CHARACTERISTICS 结构做 为
NdisFRegisterFilterDriver 的 FilterCharacteristics 参数。NDIS_FILTER_DRIVER_CHARACTERISTICS
结构指定 Mandatory 和 Optional    FilterXXX 的例程入口。有些 Optional  例程可以被 BYPASS。

NdisFRegisterFilterDriver 定义: 
代码:
NDIS_STATUS

NdisFRegisterFilterDriver(

IN PDRIVER_OBJECT DriverObject,

IN NDIS_HANDLE FilterDriverContext,

IN PNDIS_FILTER_DRIVER_CHARACTERISTICS FilterCharacteristics,

OUT PNDIS_HANDLE NdisFilterDriverHandle

);
NDIS_FILTER_DRIVER_CHARACTERISTICS 结构内容:
代码:
typedef struct _NDIS_FILTER_DRIVER_CHARACTERISTICS {
 
NDIS_OBJECT_HEADER  Header;
 
UCHAR  MajorNdisVersion;
UCHAR  MinorNdisVersion;
UCHAR  MajorDriverVersion;
UCHAR  MinorDriverVersion;
ULONG  Flags;
NDIS_STRING  FriendlyName;
NDIS_STRING  UniqueName;
NDIS_STRING  ServiceName;
SET_OPTIONS_HANDLER  SetOptionsHandler;
FILTER_SET_FILTER_MODULE_OPTIONS_HANDLER  SetFilterModuleOptionsHandler;
FILTER_ATTACH_HANDLER  AttachHandler;
FILTER_DETACH_HANDLER  DetachHandler;
FILTER_RESTART_HANDLER  RestartHandler;
FILTER_PAUSE_HANDLER  PauseHandler;
FILTER_SEND_NET_BUFFER_LISTS_HANDLER  SendNetBufferListsHandler; 
FILTER_SEND_NET_BUFFER_LISTS_COMPLETE_HANDLER  SendNetBufferListsCompleteHandler;
FILTER_CANCEL_SEND_HANDLER  CancelSendNetBufferListsHandler;
FILTER_RECEIVE_NET_BUFFER_LISTS_HANDLER  ReceiveNetBufferListsHandler;
FILTER_RETURN_NET_BUFFER_LISTS_HANDLER  ReturnNetBufferListsHandler;
FILTER_OID_REQUEST_HANDLER  OidRequestHandler;
FILTER_OID_REQUEST_COMPLETE_HANDLER  OidRequestCompleteHandler;
FILTER_CANCEL_OID_REQUEST_HANDLER  CancelOidRequestHandler;
FILTER_DEVICE_PNP_EVENT_NOTIFY_HANDLER  DevicePnPEventNotifyHandler;
FILTER_NET_PNP_EVENT_HANDLER  NetPnPEventHandler;
FILTER_STATUS_HANDLER  StatusHandler;
FILTER_DIRECT_OID_REQUEST_HANDLER  DirectOidRequestHandler;
FILTER_DIRECT_OID_REQUEST_COMPLETE_HANDLER  DirectOidRequestCompleteHandler;
FILTER_CANCEL_DIRECT_OID_REQUEST_HANDLER  CancelDirectOidRequestHandler;
 

} NDIS_FILTER_DRIVER_CHARACTERISTICS, *PNDIS_FILTER_DRIVER_CHARACTERISTICS;
NDIS_FILTER_DRIVER_CHARACTERISTICS 结构中 Mandatory 例程:

FilterAttach   

FilterDetach   

FilterRestart   

FilterPause   

NDIS_FILTER_DRIVER_CHARACTERISTICS 结构中 Optional 且不能在运行时变更的例程: 

FilterSetOptions   

FilterSetModuleOptions   

FilterOidRequest   

FilterOidRequestComplete 

FilterStatus   

FilterNetPnPEvent   

FilterDevicePnPEventNotify   

FilterCancelSendNetBufferLists   

NDIS_FILTER_DRIVER_CHARACTERISTICS 结构中 Optional 且能在运行时变更的例程: 

FilterSendNetBufferLists   

FilterSendNetBufferListsComplete   

FilterReturnNetBufferLists   

FilterReceiveNetBufferLists   

上述四个例程也定义在 NDIS_FILTER_PARTIAL_CHARACTERISTICS 中,这个结构中指定的例程
可以在运行时的 FilterSetModuleOptions 例在中调用 NdisSetOptionalHandles 来改变。如果
Filter Drivers 要在例中改自身的一个特性那么就必须提供 FilterSetModuleOpitons 例程。部分
特性每一个不同的 Filter Module 可以各不相同。

如果 NdisFRegisterFilterDriver 调用成功,NDIS 会填充 NdisFilterDriverHandle 变量返回一
个 Filter  Driver 句柄。Filter  Driver 应该保存这个句柄,后面调有 NDIS  例程时会使这个句柄
做为输入参数,例如 NdisFDeregisterFilterDriver 例程就用它做输入参数。在驱动被卸载时会
调用驱动的卸载例程,必须在卸载例程中调用 NdisFDeregisterFilterDriver 来释放这个句柄,
同时也要释放所以在申请分配的资源。

FilterSetOptions 返回后,Filter  Moudle 处于 Detached 状态。NDIS 可以在 FilterSetPtions
返回后的任何时间调用 FilterAttach 例程。Filter  Module 在 FilterAttach 中完成和它相关的特
定初始化。 

Filter  Drivers 也可以在 DriverEntry 中选择一些其它(如全局的)初始化。但一定要在
FilterDriverUnload 例程中释放所有初始化时或运行时申请分配的资源。正面是 DDK 中所带例
子的 DriverEntry 例程的源码。

代码:
NDIS_STATUS
DriverEntry(
IN PDRIVER_OBJECT   DriverObject,
IN PUNICODE_STRING  RegistryPath
)
{
NDIS_STATUS               Status;
NDIS_FILTER_DRIVER_CHARACTERISTICS   FChars;
NDIS_STRING               ServiceName;
NDIS_STRING               UniqueName;
NDIS_STRING               FriendlyName;


DEBUGP(DL_TRACE,("===>DriverEntry...\n"));


RtlInitUnicodeString(&ServiceName, FILTER_SERVICE_NAME);
RtlInitUnicodeString(&FriendlyName, FILTER_FRIENDLY_NAME); 
RtlInitUnicodeString(&UniqueName, FILTER_UNIQUE_NAME);
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);
FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
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;


//
// for the time being, there is no additional options to register
// but let's have this handler anyway
//
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;


FILTER_INIT_LOCK(&FilterListLock);


InitializeListHead(&FilterModuleList);


Status = NdisFRegisterFilterDriver(DriverObject,
(NDIS_HANDLE)FilterDriverObject,
&FChars,
&FilterDriverHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_WARN, ("MSFilter: Register filter driver failed.\n"));
break;
}
//
// Initilize spin locks
//


Status = FilterRegisterDevice();


if (Status != NDIS_STATUS_SUCCESS)
{
NdisFDeregisterFilterDriver(FilterDriverHandle);
FILTER_FREE_LOCK(&FilterListLock);
DEBUGP(DL_WARN, ("MSFilter: Register device for the filter driver
failed.\n"));
break;
}



}
while(FALSE);



DEBUGP(DL_TRACE, ("<===DriverEntry, Status = %8x\n", Status));
return Status;


} 
2.2 卸载Filter Drivers

一个与 Filter Drivers 相关联的驱动对象联结一个 Unload 例程,系统会在这个 Filter Driver 
在所有微端口适配器(Miniport Adapter)上的过滤服务被移除后调用这个 Unload 例程。Filter 
Drivers 的 Unload 例程被命名为 FilterDriverUnload。 

FilterDriverUnload 应该释放与自己驱动相关的资源。任何 Filter  Driver 创建的驱动对象
都要就被销毁。当 FilterDriverUnload 返回后系统就可以完成 Filter Driver 的卸载操作了。

卸载例程的功能是和驱动相关的,一般的规则是,卸载例程应该撤销初始化时执行的所
有 操 作 。 一 个  Filter  Driver 例 程 必 须 在  FilterDriverUnload 例 程 中 调 用
NdisFDeregisterFilterDriver 来撤销 NdisFRegisterFilterDriver 的注册。

注意:系统只会在调用 FilterDetach 分离了所有和本 Filter Driver 相关的 Filter Module 后
才会调用 FilterDriverUnload 例程。下面贴出 DDK 中例子 filter 的 FilterDriverUnload 源码。 

代码:
VOID
FilterUnload(
IN PDRIVER_OBJECT   DriverObject
)
{
DEBUGP(DL_TRACE, ("===>FilterUnload\n"));
//
// Should free the filter context list
//
FilterDeregisterDevice();
NdisFDeregisterFilterDriver(FilterDriverHandle);


#if DBG
FILTER_ACQUIRE_LOCK(&FilterListLock, FALSE);
ASSERT(IsListEmpty(&FilterModuleList));


FILTER_RELEASE_LOCK(&FilterListLock, FALSE);


#endif


FILTER_FREE_LOCK(&FilterListLock);


DEBUGP(DL_TRACE, ("<===FilterUnload\n"));


return;


} 
2.3 Filter Module  状态和操作

一个 Filter Driver 的 Filter Module(Filter Driver 的一个实例)必须支持下列操作状态。
 
1) Detached 

Detached 状态是每一个 Filter  Module 的初始状态。当一个 Filter  Driver 停留在这个状态
时,它可以附加一个 Filter Module 到一个驱动栈上。 

2) Attaching 

这个状态是标识一个 Filter Driver 正准备附加一个 Filter Module 到一个驱动栈上。 

3) Paused 

在这个状态时,Filter Driver 不能执行接收和发送操作。
 
4) Restarting 

在这个状态时,一个 Filter  Driver 必须为一个 Filter  Module 完成重启发送和接收数据时
所需要的所有准备工作。 

5) Running 

在这个状态时,一个 Filter Driver 执行 Filter Module 正常的发送和接收入处理。 

6) Pauseing
 
在这个状态要,一个 Filter  Driver 要为一个 Filter  Module 完成停止发送和接收处理所需
要的所有准备工作。 

在下面表格中,标题列出了 Filter Module 的所有状态,第一列是主要的事件。其余的条目是
要进入的一下个状态,它是事件发生时的内部状态。空白条目表示无效状态/事件的组合。
点击图片以查看大图图片名称:	2.png查看次数:	2文件大小:	50.6 KB文件 ID :	86989
名称:  3.png查看次数: 0文件大小:  2.1 KB

主要的过滤驱动事件定义如下: 

1) FilterAttach 

当 NDIS 调用 FilterAttach 例程向一个驱动栈附加一个 Filter Module 时发这个事件。 

2) Attach is complete 

当 Filter Module 在 Attaching 状态时且 Filter Driver 初始化了 Filter Module 所需要的所有
资源时,Filter Module 进入 Paused 状态。 

3) FilterDetach 

当 NDIS 调用 Filter  Driver 的 FilterDetach 从一个驱动栈上分离一个 Filter  Module 时发生
这个事件。 

4) FilterRestart 

当 NDIS 调用 Filter Driver 的 FilterRestart 把一个暂停的 Filter Modue 的重启动时发生这个
事件。 

5) Restart is complete 

当 Filter  Module 在 Restarting 状态时且完成所有发送和接收所需要的准备工作时,Filter 
Moduel 就进入了 Running 状态。 

6) FilterPause 

当 NDIS 调用 Filter Driver 的 FilterPause 暂停一个 Filter Module 时发生此事件 

7) Pause is complete 

当 Filter  Driver 为 Filter  Module 完成了所有发送和接收操作的停止工作时,暂停完成同
时也进入了 Paused 状态 

8) Attach fail 

当 NDIS 调用 FilterAttach 操作失败时(比如,Filter  Module 所有需要的资源申请分配失
败等等),这时就进入了 Detached 状态。 

9) Restart fail 

当 NDIS 调用 FilterRestart 操作失败时,Filter Module 就进入了 Paused 状态。 
 
10)Send and Receive Operations 
 
Filter Module 可以在 Pauseing 状态和 Running 状态进行发送和接收操作的处理。 
 
11)OID Requests 
 
Filter Module 可以在 Runnig 状态、Restarting 状态、Paused 状态和 Pauseing 状态进行 OID
的控制和处理。 

2.4 附加Filter Module

在向一个驱动动栈插入一个 Filter Module 时,NDIS 会调用 Filter Driver 的 FilterAttach 全
程。在进入执行 FilterAttach 时,Filter Module 就进入了 Attaching 状态。下面列出 FilterAttach
在 DDK 中的声明。
代码:
NDIS_STATUS
FilterAttach(
 
IN NDIS_HANDLE  NdisFilterHandle,
IN NDIS_HANDLE  FilterDriverContext, 
IN PNDIS_FILTER_ATTACH_PARAMETERS  AttachParameters
);
NDIS 通过 NdisFilterHandle 传过来一个句柄,这个句柄将用于所有 Filter  Driver 中对
NdisXXX 类例程的调用时引用指示这个 Filter  Module,这些例程有状态指示、发送请求、接
收指示、OID 请求等等。当一个 Filter Module 进入 Attaching 时驱动可以:

1) 创建一个环境上下文区域并且初始化一个缓冲区池以及其 Filter Module 特定的资源 

2) 用 NDIS 传来给 Filter Attach 的 NdisFilterHandle 做为输入来调用 NdisFSetAttributes 例程,
其中 NdisFSetAttributes 的 FilterModuleContext  参数的作用是为 FilterModule 指定环境上
下文用的(注意:这里要与 NdisFRegisterFilterDriver 中的 FilterDriverContext 参数区别开
来)。NDIS 会把这个环境上下文传递给 Filter  Drivers 注册的 FilterXXX 例程(注意:
FilterAttach 中的 FilterDriverContext 是由 NdisFRegisterFilterDriver 的 FilterDriverContext
指定的),下面列出 NdisFSetAttributes 例程在 DDK 中的声明 

代码:
NDIS_STATUS
NdisFSetAttributes(
 
IN NDIS_HANDLE  NdisFilterHandle,
IN NDIS_HANDLE  FilterModuleContext,
IN PNDIS_FILTER_ATTRIBUTES  FilterAttributes
);
3) 这步是可选的,Filter Module 可以从注册表中读取一些参数。 

4) 如果上述的操作完成成功那么 Filter Module 就进入了 Paused 状态 

5) 如果上述的操作完成失败那么要释放已经成功申请分配的资源返回后进 Detached 状态 

6) 返回时可以返回 NDIS_STATUS_SUCCESS 或适当的错误码,如果驱动返回失败则 NDIS 会
结束这个驱动栈。 

注意:注册中可以包含一下 Filter Module 需要的执行标记这是可选的。如果 Filter Driver
被安装时指定为可选的(Optional,与 Mandatory 相对)那么 FilterAtt 工 ch 返回失败 NDIS
是不会么结束这个驱动栈的其它部分的。 

一个 Filter  Driver 在 Attaching 状态时不能进行发送请求、接收指示、状态指示和 OID 请
求操作。接收和发送操作只支持在 Running 和 Pauseing 两种状态下进行。OID 请求和状态指
示支持在 Paused、Restarting、Running 和 Pauseing 这四种状态下进行。NDIS 调用 FilterDetach
例程来分离之前调用 FilterAttach 附加的 Filter  Module,更多分离 Filter  Module 的信息参看
下节。这边列举一下 DDK 例子 filter 中 FilterAttach 的代码。 
代码:
NDIS_STATUS
FilterAttach(
IN NDIS_HANDLE           NdisFilterHandle,
IN NDIS_HANDLE           FilterDriverContext,
IN PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters
)
{
PMS_FILTER       pFilter = NULL;
NDIS_STATUS      Status = NDIS_STATUS_SUCCESS;
PFL_NDIS_FILTER_LIST FilterHandleEntry;
NDIS_FILTER_ATTRIBUTES FilterAttributes;
ULONG         Size; 

DEBUGP(DL_TRACE, ("===>FilterAttach: NdisFilterHandle %p\n",
NdisFilterHandle));
do
{
ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject);
if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject)
{
Status = NDIS_STATUS_INVALID_PARAMETER;
break;
}


if (AttachParameters->MiniportMediaType != NdisMedium802_3)
{
DEBUGP(DL_ERROR, ("MSFilter: Doesn't support media type other than
NdisMedium802_3.\n"));


Status = NDIS_STATUS_INVALID_PARAMETER;
break;
}


Size = sizeof(MS_FILTER) +
AttachParameters->FilterModuleGuidName->Length +
AttachParameters->BaseMiniportInstanceName->Length +
AttachParameters->BaseMiniportName->Length;


pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size);
if (pFilter == NULL)
{
DEBUGP(DL_WARN, ("MSFilter: Failed to allocate context
structure.\n"));
Status = NDIS_STATUS_RESOURCES;
break;
}


NdisZeroMemory(pFilter, sizeof(MS_FILTER));


pFilter->FilterModuleName.Length =
pFilter->FilterModuleName.MaximumLength =
AttachParameters->FilterModuleGuidName->Length;
pFilter->FilterModuleName.Buffer = (PWSTR)((PUCHAR)pFilter +
sizeof(MS_FILTER));
NdisMoveMemory(pFilter->FilterModuleName.Buffer,
AttachParameters->FilterModuleGuidName->Buffer,
pFilter->FilterModuleName.Length);





pFilter->MiniportFriendlyName.Length =
pFilter->MiniportFriendlyName.MaximumLength =
AttachParameters->BaseMiniportInstanceName->Length;
pFilter->MiniportFriendlyName.Buffer =
(PWSTR)((PUCHAR)pFilter->FilterModuleName.Buffer +
pFilter->FilterModuleName.Length);
NdisMoveMemory(pFilter->MiniportFriendlyName.Buffer,
AttachParameters->BaseMiniportInstanceName->Buffer,
pFilter->MiniportFriendlyName.Length);



pFilter->MiniportName.Length = pFilter->MiniportName.MaximumLength =
AttachParameters->BaseMiniportName->Length;
pFilter->MiniportName.Buffer =
(PWSTR)((PUCHAR)pFilter->MiniportFriendlyName.Buffer +


pFilter->MiniportFriendlyName.Length);
NdisMoveMemory(pFilter->MiniportName.Buffer,
AttachParameters->BaseMiniportName->Buffer,
pFilter->MiniportName.Length);


pFilter->MiniportIfIndex = AttachParameters->BaseMiniportIfIndex;
//
// The filter should intialize NoTrackReceives and NoTrackSends properly,
for this
// driver, since its default characteristic has both send and receive
handler, they
// are initiazed to FALSE.
//
pFilter->TrackReceives = TRUE;
pFilter->TrackSends = TRUE;
pFilter->FilterHandle = NdisFilterHandle;



NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
FilterAttributes.Flags = 0; 
Status = NdisFSetAttributes(NdisFilterHandle,
pFilter,
&FilterAttributes);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_WARN, ("MSFilter: Failed to set attributes.\n"));
break;
}


pFilter->State = FilterPaused;


FILTER_ACQUIRE_LOCK(&FilterListLock, FALSE);
InsertHeadList(&FilterModuleList, &pFilter->FilterModuleLink);
FILTER_RELEASE_LOCK(&FilterListLock, FALSE);


}
while (FALSE);


if (Status != NDIS_STATUS_SUCCESS)
{
if (pFilter != NULL)
{
FILTER_FREE_MEM(pFilter);
}
}
 


DEBUGP(DL_TRACE, ("<===FilterAttach:
 


Status %x\n", Status));
 
return Status;
}
2.5 分离Filter Module

NDIS 调用 Filter  Driver 的 FilterDetach 例程来对驱动栈引发一个分享 Filter  Module 的操
作。在开始执行 FilterDetach 例程时 Filter Module 进入了 Detached 状态。在从驱动栈上分离
一个 Filter  Module 时,NDIS 会先暂停这个驱动栈(这意味之在调用 FilterDetach 之前 NDIS
已经使 Filter Module 进入了 Paused 状态)。 

在这个例程中在释放和这个 Filter Module 相关的环境上下文和其它资源。这个全程不能
返回失败。因此,必须保证成功释放附加时申请分配及运行时分配的所有资源。当
FilterDettach 返回后 NDIS 会重启被暂停的驱动栈。下面列出 DDK 中 filter 实现的 FilterDetach
例程。
代码:
VOID
FilterDetach(
IN NDIS_HANDLE  FilterModuleContext
) 
{
 
PMS_FILTER
PFL_NDIS_FILTER_LIST
PLIST_ENTRY
 
pFilter = (PMS_FILTER)FilterModuleContext;
pEntry;
pLink;
 



DEBUGP(DL_TRACE, ("===>FilterDetach:
 



FilterInstance %p\n",
 
FilterModuleContext));



//
// Filter must be in paused state
//
FILTER_ASSERT(pFilter->State == FilterPaused);



//
// Don't come up anything that would prevent the filter from detaching
//


//
// Free filter instance name if allocated.
//
if (pFilter->FilterName.Buffer != NULL)
{
FILTER_FREE_MEM(pFilter->FilterName.Buffer);
}



FILTER_ACQUIRE_LOCK(&FilterListLock, FALSE);
RemoveEntryList(&pFilter->FilterModuleLink);
FILTER_RELEASE_LOCK(&FilterListLock, FALSE);



//
// Free the memory allocated
FILTER_FREE_MEM(pFilter);


//
// Alway return success
//
DEBUGP(DL_TRACE, ("<===FilterDetach Successfully\n"));


return; 
} 
2.6 重启和暂停Filter Module

在进行即插即用操作时 NDIS 会暂停 Filter  Module,例如添加、删除一个 Filter  Module
或添加一个新的绑定等等。NDIS 从一个 Paused 状态启动一个 Filter Module 到 Running 状态。
当一个 Filter  Module 的附加和暂停操作完成时它就进入了 Paused 状态。下面的小节将讨论
启动和暂停 Filter Module。

2.6.1 重启Filter Module

要启动一个 Paused 状态的 Filter  Module,如果有 FilterSetModuleOptions 就先调用它,
接着调用 FilterRestart 例程。一个 Filter  Module 执行 FilterRestart 例程时就进入了 Restarting
状态。如果一个 Filter Driver 提供了 FilterSetModuleOptions 例程那驱动就可以在这个例程中
更改当前的 Filter Module 的部分特性。更多的信息可以看 Data Bypass Model 小节。

当调用一个 Filter  Driver 的 FilterRestart 例程时,NDIS 通过 FilterRestartParameters 的
RestartAttributes 成员传递一个 NDIS_RESTART_ATTRIBUTES 结构。Filter  Drivers 可以修改由底
层驱动指定的重启属性。更多修改重启属性的信息可以参考 DDK 中对 FilterRestart 例程的说
明。 

注意:在调用栈中的任何 Filter  Module 的 FilterRestart 例程之前都会调用,它们的
FilterSetModuleOptions 例程. 

NDIS 启动一个 Filter  Module 是即插即用操作重启一个驱动栈的一部分。当一个 Filter 
Module 是 Restarting 状态时,它可以:
 
1) 完成任何正常发送和接收所需要的准备工作 

2) 可读写 Filter Module 的配置参数 

3) 可以接收网络数据指示,拷贝和排队数据稍后指示给上层驱动或丢弃数据。
 
4) 不能发起任何新的接收指示 

5) 应该立即调用 NdisFSendNetBufferListsComplete 例程来拒绝 FilterSendNetBufferLists 传来
的发送的请求。应该设置每一个 NET_BUFFER_LIST 的完成状态为 NDIS_STATUS_PAUSED 

6) 可以使用 NdisFIndicateStatus 例程进行状态指示 

7) 可以控制 OID 请求操作 

8) 不能发起任何新的发送请求 

9) 应该调用 NdisFReturnNetBufferLists 反回所有新的接收指示。如果需要的话可以在接收指
示返回之前拷贝它们。 
 
10)可以制作 OID 请求发送给下层驱动设置或查询配置信息。

11)可以在 FilterStatus 中控制状态指示 

12)返回时指示 NDIS_STATUS_SUCCESS 或失败状态,如果不 Filter Module 不能启动返回
了失败,而它又是一个 Mandatory 的 Filter Driver 那个 NDIS 将会结束这个驱动栈 
在一个 Filter Driver 完成对发送和接收的重启后必须指示完成这个重启操作。Filter Driver
的重启操作的完成可以是同步也可以是异步的,同步时返回 NDIS_STATUS_SUCCESS 异步时返
回 NDIS_STATUS_PENDING 。 如 果 返 回 的 是 NDIS_STATUS_PENDING 就 必 须 调 用
NdisFRestartComplete 例 程 在 重 启 操 作 完 成 后 。 在 这 种 情 况 下 , 驱 动 需 要 传 递 给
NdisFRestartComplete 一个固定状态(标识重启结果的成功或失败状态)。

重启操作完成 Filter Module 就进入了 Running 状态,恢得一切正常的发送和接收外理。
在 Filter  Driver 的 FilterRestart 例程执行的时候 NDIS 不会发起任即插即用操作,如附加,分
离,暂停请求等等。Ndis 可以在 Filter Module 进入 Running 状态后发起一个暂停请求,下节
将介绍。下面列出 DDK 中 Filter 的 FilterRestart 例程的实现代码。 
代码:
NDIS_STATUS
FilterRestart(
IN NDIS_HANDLE           FilterModuleContext,
IN PNDIS_FILTER_RESTART_PARAMETERS RestartParameters
)
{
NDIS_STATUS  Status;
PMS_FILTER   pFilter = (PMS_FILTER)FilterModuleContext; // BUGBUG, the cast
may be wrong
NDIS_HANDLE  ConfigurationHandle = NULL;



PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes;
PNDIS_RESTART_ATTRIBUTES    NdisRestartAttributes;
NDIS_CONFIGURATION_OBJECT    ConfigObject;
 


DEBUGP(DL_TRACE, ("===>FilterRestart:
 


FilterModuleContext %p\n",
 
FilterModuleContext));


FILTER_ASSERT(pFilter->State == FilterPaused);


ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT);
ConfigObject.NdisHandle = FilterDriverHandle;
ConfigObject.Flags = 0;


Status = NdisOpenConfigurationEx(&ConfigObject, &ConfigurationHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
//
// Filter driver can choose to fail the restart if it cannot open the
configuration
//


#if 0
//
// The code is here just to demonstrate how to call NDIS to write an eventlog.
If drivers need to write
// an event log.
//
 
PWCHAR
 
ErrorString = L"NdisMon";
 


DEBUGP(DL_WARN, ("FilterRestart: Cannot open configuration.\n"));
NdisWriteEventLogEntry(FilterDriverObject,
EVENT_NDIS_DRIVER_FAILURE,
0,
1,
&ErrorString,
sizeof(Status),
&Status);
#endif


}



if (Status == NDIS_STATUS_SUCCESS)
{
NdisCloseConfiguration(ConfigurationHandle);
}


NdisRestartAttributes = RestartParameters->RestartAttributes;


//
// If NdisRestartAttributes is not NULL, then miniport can modify generic
attributes and add
// new media specific info attributes at the end. Otherwise, NDIS restarts the
miniport because
// of other reason, miniport should not try to modify/add attributes
//
if (NdisRestartAttributes != NULL)
{
PNDIS_RESTART_ATTRIBUTES NextAttributes;


ASSERT(NdisRestartAttributes->Oid ==
OID_GEN_MINIPORT_RESTART_ATTRIBUTES);


NdisGeneralAttributes =
(PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data;


//
// Check to see if we need to change any attributes, for example, the driver
can change the current
// MAC address here. Or the driver can add media specific info attributes. 
//
NdisGeneralAttributes->LookaheadSize = 128;


//
// Check the next attributes to see whether the filter need to modify
//
NextAttributes = NdisRestartAttributes->Next;


while (NextAttributes != NULL)
{
//
// If somehow the filter needs to change a attributes which requires
more space then
// the current attributes:
// 1. Remove the attribute from the Attributes list:
// TempAttributes = NextAttributes;
// NextAttributes = NextAttributes->Next;
// 2. Free the memory for the current attributes:
NdisFreeMemory(TempAttributes, 0 , 0);
// 3. Dynamically allocate the memory for the new attributes by calling
// NdisAllocateMemoryWithTagPriority:
// NewAttributes = NdisAllocateMemoryWithTagPriority(Handle, size,
Priority);
// 4. Fill in the new attribute
// 5. NewAttributes->Next = NextAttributes;
// 6. NextAttributes = NewAttributes; // Just to make the next statement
works.
//
NextAttributes = NextAttributes->Next;
}

//

}


//
// If everything is OK, set the filter in running state
// If it get preempted, it doesn't matter
//
pFilter->State = FilterRunning; // when successful



Status = NDIS_STATUS_SUCCESS;


if (Status != NDIS_STATUS_SUCCESS)
{
pFilter->State = FilterPaused;
}



DEBUGP(DL_TRACE, ("<===FilterRestart: FilterModuleContext %p, Status %x\n",
FilterModuleContext, Status));
return Status;
}
2.6.2 暂停Filter Module

要暂停一个Running中的Filter Module,NDIS会调用Filter Driver的FIlterPause例程。当Filter 
Driver执行FilterPause全程时它就进入了Pauseing状态。NDIS暂停Filter  Module的操作是即插
即用操作暂停一个驱动栈工作的一部分。详细信息可参考DDK中的Pausing  a  Driver    Stack。
一个在Pauseing中的Filter Driver  有如下约束。 

1) Filter Module 不能发起任何新的接收指示,但是可以传递下层驱动的接收指示。
 
2) 如果这里有 Filter Module 发起的接收指示还没有完成,那么必须等待它们全部完成。有
只当 FilterReturnNetBufferLists 完成所有外部的接收指示后暂停操作才能完成 

3) 要 返 回 任 何 未 处 理 的 由 下 层 驱 动 引 发 的 接 收 指 示 给 NDIS , 只 有 等 到
NdisFReturnNetBufferLists 返回了所有未处理的接收指示后暂停操作才能完成。这里也可
以排队缓冲这些未完成接收指示。 

4) 立即用 NdisFReturnNetBufferLists 返回所有下层驱动新传来的接收指示,如果需要可以在
返回之前拷贝和排队这些接收指示的。 

5) 不能发起任何新的发送请求 

6) 这时如果有 Filter Driver 引的但 NDIS 还未完成的发送操作,必须在这里等待他们完成。 

7) 应该在 FilterSendNetBufferLists 例程中立即用 NdisFSendNetBufferListsComplete 返回那些
新到达的发送请求。并且为每一个 NET_BUFFER_LIST 设置 NDIS_STATUS_PAUSED 返回状
态。
 
8) 这时可以用 NdisFIndicateStatus 提供状态指示 

9) 可以在 FilterStatus 中处理状态指示 

10) 可以在 FIlterOidRequest 中处理 OID 请求 

11) 可以发起一个 OID 请求 

12) 不能释放分配的相关资源,和 Filter Module 相关的资源最好放在 FilterDetach 中释放。 

13) 如果有用于发送和接收的定时器也要停止它 

当成功停止发送和接收操作后就必须完成暂停操作。暂停操作的完成可以是同步的也可
以是异步的,分别从 FilterPaused 返回 NDIS_STATUS_SUCCUSS 和 NDIS_STATUS_PENDING。如
果返回 NDIS_STATUS_PENDING 就必须调用 NdisFPauseComplete 来完成这个暂停操作,可以
传递一个返回状态给这个例程。暂停操作完成后代表 Filter  Module 进入了 Paused 状态,这
里它有以下约束。

1) 不能发起任何接收指示,但是可以传递底层驱动发来的接收指示 

2) 需要立即调用 NdisFReturnNetBufferLists 返回底层驱动的接收指示给 NDIS,如果需要可
以在返回之前可以拷贝和排队这些指示。 

3) 不能引发任何新的发送指示 

4) 要立即调用 NdisFSendNetBufferListsComplete 完成那些在 FilterSendNetBufferLists 中收到
的发送请求,并为每一个 NET_BUFFER_LIST 结构设置 NDIS_STATUS_PAUSED 状态。 

5) 这时可以用 NdisFIndicateStatus 发起状态指示 

6) 可在 FilterStatus 中进行状态指示处理 

7) 可在 FIlterOidRequest 中进行 OID 请求处理 

8) 可以发起 OID 请求 

在 Filter  Module 进行 Pauseing 状态时 NDIS 不会发起其它 PnP 操作如附加,分离,重起
等。只当 Filter Module 进入了 Paused 状态后 NDIS 才能对它进行分离和重启操作。下面列出
DDK 中 Filter 的 FilterPause 实现。
代码:
NDIS_STATUS
FilterPause(
IN NDIS_HANDLE           FilterModuleContext,
IN PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters
)
{
PMS_FILTER     pFilter = (PMS_FILTER)(FilterModuleContext);
NDIS_STATUS    Status;


UNREFERENCED_PARAMETER(PauseParameters);


DEBUGP(DL_TRACE, ("===>NDISMON FilterPause: FilterInstance %p\n",
FilterModuleContext));


//
// Set the flag that the filter is going to pause
//
FILTER_ASSERT(pFilter->State == FilterRunning);


FILTER_ACQUIRE_LOCK(&pFilter->Lock, FALSE);
pFilter->State = FilterPausing;
FILTER_RELEASE_LOCK(&pFilter->Lock, FALSE);



Status = NDIS_STATUS_SUCCESS; 
pFilter->State = FilterPaused;


DEBUGP(DL_TRACE, ("<===FilterPause: Status %x\n", Status));
return Status;
} 
2.7 Data Bypass Mode

Filter  Driver 的 Data  Bypass  Mode 能为系统示供更好的性能。在 Data  Bypass  Mode 下
NDIS 不调用相关的 FilterXXX 例程。例如,一个 Filter Driver 对发送和接收进行过滤操作是不
需要的,那就可以 Bypass 发送和接收。(即在注册时不指定其入口,哪些例程可以 Bypass
参看 NdisFRegisterFilterDriver 在 DDK 中的说明)

当系统初始化调用 NdisFRegisterFilterDriver 时,要为可以 Bypass 的例程指定默认入口,
如果不为它们指示入口,那么就默认它们是 Bypass 的。也可以在运行时改变 Bypass 状态,
但是必须要在初始化时为驱动注册 FilterSetModuleOptions 例程,驱动可以在这个例程中初
始化 NDIS_FILTER_PARTIAL_CHARACTERISTICS 结构来调用 NdisSetOptionalHandlers 来完成必
变。这个例程如果存在那么在调用 Filter  Driver 的 FilterRestart 例程之前调用它。下面是在
NDIS_FILTER_PARTIAL_CHARACTERISTICS 结构中可以被 Bypass 的可选全程。 

FilterSendNetBufferLists

FilterSendNetBufferListsComplete

FilterCancelSendNetBufferLists

FilterReturnNetBufferLists

FilterReceiveNetBufferLists

要设置例程为 Bypass 的就把它的入口设成 NULL,但是注意一点如果驱动用任何和
FilterXXX 联 结 的 NDIS 例 程 时 , 就 必 须 提 供 相 应 的 入 口 。 例 如 , 如 果 要 调 用
NdisFIndicateNetBufferLists 就必须为驱动注册 FilterReturnNetBufferLists 例程。 

如 果 驱 动 在 FilterSendNetBufferLists 中 排 队 发 送 请 求 那 么 它 就 必 须 提 供FilterCancelSendNetBufferLists例 程 。 如 果 指 定 了FilterReceiveNetBufferLists
和FilterReturnNetBufferLists 例程就必须提供人 FilterStatus 例程。 

要在运行时更变 Bypass 状态需要调用 NdisFRestartFilter。NdisRestartFilter 可以计划一个
暂停操作后做一个重启操作。这样 NDIS 就会调用指定 Filter Module 的 FilterSetModuleOptions
例程,在其中调用 NdisSetOptionalHandlers 可以重设 FilterXXX 的入口。 

注意:暂停和重启操作将可能引发一个包在传输和接收路径上被丢弃。网络协议中有些
有重试机制如 TCP,而其它协议通常没有这种机制。 

Filter Driver 可以注册附加选项例程来支持驱动选项服务(Optional Driver Service),驱动
动注册的选项服务为 FilterSetOptions 例程。 

2.8 Filter Driver的选项配置

NDIS 通过 FilterSetOptions 例程来配置可选过滤驱动服务(Optional Filter Driver Service)。
传给 FilterSetOptions 的环境上下文是 NdifFRegisterFilterDriver 注册时设置的环境上下文。
 
FilterSetOption 可以注册选项服务(Optional  Service)需要的 FilterXXX 例程的默认入口
和分配相关资源。这里注册可选服务项也是通 NdisSetOptionalHandlers 例程来注册(会影响到
所有 Filter Module)。在当前操作系统版中并没有可选的过滤驱动服务(Optional Filter Driver 
Service)。 

也可以会对某一个 Filter Module 来更改一些 FilterXXX 函数,可以用在 Data Bypass Mode
小节中讲的那样。如果 NdisSetOptionalHandlers 在 FilterRestart 例程中调用,只影响本 Filter 
Module 的配置,其它 Filter Module 的配置并不会受到影响。

2.9 Filter Module发送和接收

在这一节将介绍 NDIS  6.0  Filter  Driver 的发送和接收操作。Filter  Driver 可以发起一个发
送主请求和接收指示或过滤其它驱动的接收和发送指示。Filter Module 在一个微端口适配器
(Miniport  Adapter)的上层。一个 Filter  Module 可以在一个驱动栈上过滤他所联结的下层
适配器的所有发送请求和接收指示。绑定在这个适配器上的协议驱动的发送请求也会经过
它。 

Filter  Driver 不直接支持老的基于 NDIS_PACKET 结构的发送请求和接收指示。替代老的
结构现在采新的 NET_BUFFER 结构来进行发送请求和接收指示。也就是说,NDIS 控制由
NET_BUFFER 向 NDIS_PACKET 的转换。 

注意:Filter  Driver 来说它可以动态改变一个 Filter  Module 的发送和接收的 FilterXXX 例
程。 

下面的小节将介绍关于 Filter Driver 如果进行发送请求和接收指示处理的内容。 

2.9.1 Filter Driver的缓冲区管理

Filter  Driver 创建缓冲区用于拷贝从其它驱动获得的数据或用于发起一个发送请求和接
收指示。如果一个 Filter  Driver 不能创建一个缓冲区那就不能管理缓冲区池,这样就只能传
递 发 送 请 求 和 接 收 指 示 。 Filter  Driver 创 建 缓 冲 区 用 于 发 送 和 接 收 就 必 须 创 建
NET_BUFFER_LIST 结构池和 NET_BUFFER 结构池,下面是创建这些池的例程。 

NdisAllocateNetBufferListPool   

NdisAllocateNetBufferPool   

下面是从池里分配置结构的例程。 

NdisAllocateNetBufferAndNetBufferList   

NdisAllocateNetBufferList   

NdisAllocateNetBuffer   

调用 NdisAllocateNetBufferAndNetBufferList 比调用 NdisAllocateNetBufferList 后再调用
NdisAllocateNetBuffer 更 有 高 效 。 然 而 NdisAllocateNetBufferAndNetBufferList 仅 在
NET_BUFFER_LIST中 创 建 一 个NET_BUFFER结 构 。 要 使 用
NdisAllocateNetBufferAndNetBufferList 必 须 在 调 用 NdisAllocateNetBufferListPool 时 设 置 
AllocateNetBuffer 为 TRUE。 

Filter  Driver 要发起一个发送请应该先确害内容和下层驱动对回填空间的要求,一个
Filter Driver 根据重启属性(在 FilterRestart 被调用时可以获得)来决定下层驱动回填空间的
要求,一个 Filter Driver 应该在 Restarting 状态时决定回填和内容的需求。Filter Driver 应该为
整个栈分配足够的回填和内容空间。如果需要一个 Filter Driver 可以在 Restarting 状态释放缓
冲区池并重新分配它们。下面是释放池和释放人池中分配的结构的例程。 

NdisFreeNetBufferListPool   

NdisFreeNetBufferPool   

NdisFreeNetBufferList   

NdisFreeNetBuffer  

驱动在释放 NET_BUFFER_LIST 之前应该先释放用 NdisAllocateNetBuffer 的
NET_BUFFER。如果用 NET_BUFFER 是用 NdisAllocateNetBufferAndNetBufferList  申请的那
个直接调用 NdisFreeNetBufferList 释放 NET_BUFFER_LIST。

2.9.2 在Filter Driver中发送数据

Filter  Driver 可以发起一个发送请求或过滤上层驱动引发的一个发送请求。当一个协议
驱动调用 NdisFSendNetBufferLists 时,提交一个 NET_BUFFER_LIST 结构给在驱动栈最顶层的
Filter Module。

1)一个由 Filter Driver 引发的发送请求

下面图中示例了一个 Filter Driver 引发的一个发送操作 
名称:  4.png查看次数: 0文件大小:  10.5 KB

Filter  Driver 调用 NdisFSendNetBufferLists 发一个定义为 NET_BUFFERLIST 结构的网络数
据 。 Filter  Driver 必 须 设 置  NET_BUFFER_LIST 结 构 中  SourceHandle 的 值 和
NdisFSendNetBufferLists 参数中 NdisFilterHandle 的值一样。对 NDIS 驱动来说如果不是自己引
发的 NET_BUFFER_LIST 结构数据,那么就不应该修改它其中的 SourceHandle 成员的值。在调
用 NdisFSendNetBufferLists 发送前可以,利用 NET_BUFFER_LIST_INFO 宏来设置陪伴发送请求
的信息。下层的驱动同样可以通这个宏来找到这些信息。一个 Filter  Driver 一调用
NdisSentNetBufferLists,它就放弃了对 NET_BUFFER_LIST 及所有联结资源的所有权。NDIS 会
控制发送请求并传递给下层驱动。

NDIS 调用 FilterSendNetBufferListsComplete 把发送的结构和数据返还给 Filter  Driver。
NDIS 可以收集多次 NdisFSendNetBufferLists 发送的结构和数据形成一个单链表传递给
FilterSendNetBufferListsComplete。除非到 NDIS 调用 FilterSendNetBufferListsComplete,否则
一 个 发 送 请 求 的 当 前 状 态 总 是 未 知 的 。 一 个 Filter  Driver 是 不 能 在 NDIS 调 用
FilterSendNetBufferListsComplete 返回结构之前对 NET_BUFFER_LIST 和其关联的数据做检查
的。FilterSendNetBufferListsComplete 要完成一个发送请求完成后的任何必要的后继处理。当
NDIS 调用 FilterSendNetBufferListsComplete 时,Filter Driver 就重新获地对结构及结构相关资
源的所有权。可以在 FilterSendNetBufferListsComplete 中释放所有相关的资源和准备下一个
NdisFSendNetBufferLists 调用。

NDIS 总是按照 Filter Driver 调用 NdisFSendNetBufferLists 提交的顺序传递给下层驱动,但
是回返 FilterSendNetBufferListsComplete 的顺序则是任意的。Filter  Driver 可以请求一个回环 
发 送 请 求 , 只 要 把NdisFSendNetBufferLists的SendFlags设 置 成
NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK 就行了。NDIS 会引发一个包含发送数据的接收包
指示。
 
注意:一个 Filter  Driver 应该对自己引发的发送请求保持跟踪并确保在完成时不调用
NdisFSendNetBufferComplete 例程。

2)过滤发送请求

下面图示例了过滤一个上层驱动引发的发送请求的过程 

名称:  5.png查看次数: 0文件大小:  16.5 KB

NDIS 调用一个 Filter Driver 的 FilterSendNetBufferLists 例程来过滤上层驱动的发送请求。
Filter  Driver 不能改变其它驱动传来的 NET_BUFFER_LIST 结构中的 SourceHandle 成员的值。
它可以过滤数据并发送过滤的数据到下层驱动。对每一个提交到 FilterSendNetBufferLists 的
NDIS_BUFFER_LIST,我们可做下面的操作。 

1) 可以把缓冲区通过 NdisFSendBufferLists 传递给下层驱动,NDIS 保证上下文空间对 Filter 
Driver 的有效性。过滤驱动可以在发送前修改缓冲区的内容。可以像处理自己引发的发
送请求的缓冲区一样处理这个缓冲区。 

2) 可以调用 NdisFSendNetBufferListsComplete 拒绝传递这个包 

3) 排队缓冲区内容到本地的供以后处理。例如要在一定超时后处理或要接收到特定包后才
处理等。注意:如果支持这种处理方式就要支持取消请求的操作。 

4) 可以拷贝缓冲区并引发一个发送请求。它类似自己引发一个发送请求,但必须先调用
NdisFSendNetBufferComplete 返回上层驱动的缓冲区。 
发送请求在驱动栈继续完成,当一个微端口驱动调用 NdisMSendNetBufferListsComplete
完 成 一 个 发 送 请 求 时 , NDIS 会 调 用 微 端 口 驱 动 之 上 最 近 的 Filter  Driver 的
FilterSendNetBufferLists 例 程 。 在 一 个 发 送 操 作 完 成 后 , Filter  Driver 可 以 做 在
FilterSendNetBufferLists 中所有修改的相反操作。 FilterSendNetBufferComplete 返回一个 
NET_BUFFER_LIST 结构的单链表和发送请求的最终状态给上层的驱动。当最顶层的 Filter 
Module 的 FilterSendNetBufferListsComplete 被调用完成后 NDIS 会调用引发发送请求的协议
驱动的 ProtocolSendNetBufferListsComplete。如果 Filter Driver 不提供 FilterSendNetBufferLists
它还是可以引发一个发送操作的,但它必须提供一个 FilterSendNetBufferListsComplete 并且
不能在这个例程里把这个事件传递给上层驱动。一个 Filter  Driver 可以传递或过滤一个上层
驱动的回环请求,要传递一个回环请求,NDIS 会设置 FilterSendNetBufferLists 的 SendFlags
参 数 为NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK , Filter  Driver在 调 用
NdisFSendNetBufferLists 时把这个标记传给它即可。在回环请求的情况下 NDIS 会指示一个包
含发送数据的接收包。

通常情况下,如果一个 Filter Driver 修改的任何行为不是 NDIS 提供的标准服务,那么它
应该当自己为 NDIS 提供相应的服务。例如,如果一个 Filter Driver 修改了一个硬件地址请求,
就必须处理直接到这个新地址回环包。在这种情况下,因为 Filter Driver 已经更改了地址 NDIS
是不能提供一个回环服务的。还有就是如果 Filter  Driver 设置了混杂模式那它就不能传递额
外的数据给上层接收。

2.9.3 在Filter Driver中取消一个发送请求

一个 Filter Driver 可以取消一个自己引发的或上层驱动引发的发送请求

1)取消 Filter Driver 的发送请求

下面图示中展示了一个 Filter Driver 的发送请求被取消的过程
名称:  6.png查看次数: 0文件大小:  16.0 KB

过滤驱动调用 NDIS_SET_NET_BUFFER_LIST_CANCEL_ID 宏为每一个 NET_BUFFER_LIST 标
记一个取消 Id。在为网络数据分配取消 ID 之前,必须先调用 NdisGenratePartialCanceId 获得
取消 ID 的高字节。这是为了确保不是驱动不会把一个取消 ID 分配给两个驱动。驱动通常在
DriverEntry 调用 NdisGenratePartialCanceld,但是驱动可以在不同的时间多次调用它来获得多
个 取 消 ID 。 要 取 消 被 标 记 过 取 消 ID 且 正 在 传 输 的 数 据 , 驱 动 可 以 调 用
NdisFCancelSendNetBufferLists例 程 来 完 成 。 要 获 得 取 消ID可 以 用
NDIS_GET_NET_BUFFER_LIST_CANCEL_ID 宏来完成。如果一个 Filter  Driver 对所有发送的
NET_BUFFER_LIST 标记了相同的取消 ID 那它可以用一个 NdisFCancelSendNetBufferLists 来取
消所有的发送请求。如果把一部发送请求的 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 设置取消的 NET_BUFFER_LIST 的取消 ID 为 NULL,这
样是为了防止这个 ID,在 NET_BUFFER_LIST 被再次分配时使用。

2)取消上层驱动引发的发送请求

下面图例中展示了这种情况
名称:  7.png查看次数: 0文件大小:  15.2 KB

上层驱动在取消一个未完成的发送请求时也必须对这个发送请求的 NET_BUFFER_LIST
结构设定取消 ID。NDIS 会传递给 Filter  Driver 的 FilterCancelSendNetBufferLists 一个取消 ID
来取消发送请求的  NET_BUFFER_LIST 发送。FilterCanCelSendNetBufferLists 下执行下列操作。

1)遍历 Filter Driver 的发送队列,用 NDIS_GET_NET_BUFFER_LSIT_CANCEL_ID 获得队列
中 NET_BUFFER_LIST 的取消 ID 与 FilterCancelSendBufferLists 的取消 ID 比较 

2)移除队列中取消 ID 和 FilterCancelSentBufferLists 中取消 ID 相同的元素 

3)调用 NdisFSendNetBufferListsComplete 来完成这些 NET_BUFFER_LIST 并设定返回状
态为 NDIS_STATUS_CANCELLED 

4)调用 NdisFCancelSendNetBufferLists 传递取消发送请求给下层驱动。传递取消 ID 给
下层驱动就 Filter Driver  取消自己引发的发关请求一样

2.9.4 在Filter Driver中接收数据

在 Filter  Driver 中可以引发一个接收指示或过滤下层驱动引发的接收指示。当下层驱动微端
口驱动调用 NdisMIndicateReceiveNetBufferLists 时,NDIS 会提交一个 NET_BUFFER_LIST 给在
驱动栈上离微端口驱动最近的那个 Filter Module。

1) Filter Driver 引发一个接收指示

下面图例中展示了这个过程
名称:  8.png查看次数: 0文件大小:  11.6 KB

Filter  Driver 调用 NdisFIndicateReceiveNetBufferLists 来指示发送数据。这个函数通过
NET_BUFFER_LIST 结构给上层驱动指示数据。Filter  Driver 可以从池中分配这个结构。如果
Filter Driver 设置了 NdisFIndicateReceiveNetBufferLists 的状态为 NDIS_STATUS_SUCCESS,NDIS
通过驱动的 FilterReturnNetBufferLists 返回指示数据。在这种情况下 Filter  Driver 失去了对
NET_BUFFER_LIST 的所有权直到 FilterReturnNetBufferLists 被调用。如果 Filter  Driver 在调用
NdisFIndicateReceiveNetBufferLists 时设置 ReceiveFlags 为 NDIS_RECEIVE_FLAGS_RESOURCES,
在函数返回后 Filter Driver 会立即恢复对 NET_BUFFER_LIST 的所有权,这时 Filter Driver 必须
立 即 处 理 这 个 NET_BUFFER_LIST 的 返 回 因 为 NDIS 在 这 种 表 情 下 是 不 会 调 用
FilterReturnNetBufferLists 返回 NET_BUFFER_LIST 结构的。

注意:一个 Filter Driver 应该跟踪自己引发的接收指示确保它在 FilterReturnNetBufferLists
中不调用 NdisFReturnNetBufferLists。

2) 过滤接收指示

下面图例中展示了这个过程
名称:  9.png查看次数: 0文件大小:  13.1 KB

NDIS 调用 FilterReceiveNetBufferLists 来处理从下层驱动而来的接收指示。当下层驱动调
用 接 收 指 示 例 程 指 示 一 个 网 络 数 据 或 回 环 数 据 时 NDIS 就 会 调 用 Filter  Driver 的
FilterReceiveNetBufferLists 例程。

如 果FilterReceiveNetBufferLists的ReciverFlags没 有 设 置
 NDIS_RECEIVE_FLAGS_RESOURCES 这个标志,那么 Filter Driver 可以保持这个 NET_BUFFER_LIST
的所有权一直到 FilterReturnNetBufferLists 调用完成。如果设置了 Filter  Driver 将不能持有它
的所有权同时说明下层的驱动运行低资源模式下,Filter  Driver 应该以最快的速度处理这个
接收指示。 

注意:这个标记设置了后 Filter  Driver 必须保持 NET_BUFFER_LIST 的原有集合设置和状
态。例如:在这种情况下可以在 FilterReceiveNetBufferLists 返回前对 NET_BUFFER_LIST 进行
处理但是必须对它的原始状态进行保存,处理完后必须恢复。

Filter  Driver 可以在指示数据给上层驱动之前对这个数据进行过滤。可以对每一个提交
到 FilterReceiveNetBufferLists 的数据做下列操作。

1) 可以传递这个指示给上层驱动,并且可以在传递之前修改缓冲区中的内容,NDIS 保证上
下文空间的有效性,注意:如果 NDIS 设置了 FilterReceiveNetBufferLists 参数 ReceiveFlags
的 NDIS_RECEIVE_FLAGS_RESOURCES 标记,Filter  Driver 要传递这个指示给上层驱动并且
只有恢复了对数据的所有权后才能从 FilterReturnNetBufferLists 返回。 
 
2) 拒 绝 数 据 。 如 果FilterReceiveNetBufferLists的ReceiveFlags没 有 设 置
NDIS_RECEIVE_FLAGS_RESOURCES 标记,Filter  Drivre  调用disFReturnNetBufferList 返回
这个缓冲区数据,如果设置了则可以什么都不做直接返回来达到拒绝的上的。 

3) 排除在本地数据结构中以后再处理。如果 FilterReceiveNetBufferLists 的 ReceiveFlags 设置
NDIS_RECEIVE_FLAGS_RESOURCES 标记,需要拷贝一份 NET_BUFFER_LIST 后再返回(在这
中情况下是能不能直接使用传来的 NET_BUFFER_LIST 的,它必须原样返回给下层驱动)。 
Filter Driver 调用 NdisFIndicateNetBufferLists 传递接收指示给驱动栈上的上层驱动,类似
于 Filter Driver 直接引发的接收指示。如果上层驱动保留了对缓冲区(NET_BUFFER_LIST)的
所 有 权 , NDIS 会 调 用  Filter  Driver 的  FilterReturnNetBufferLists 例 程 。 在
FilterReturnNetBufferLists 中应该撤消在接收路径上(如在 FilterReciveNetBufferLists 中做的一
些处理)的操作。当最底层的 Filter  Module 完成对缓冲区(NET_BUFFER_LIST)的处理后,
NDIS 把缓冲区返回给微端口驱动。如果 FilterReceiveNetBufferLists 的 ReceiveFlags 没有设置
NDIS_RECEIVE_FLAGS_RESOURCES 标记,Filter Drivre  调用 NdisFReturnNetBufferList 返回这个
缓冲区数据,如果设置了 FilterReceiveNetBufferLists 直接返回时就把缓冲区返还给了下层微
端口驱动。  

2.10 Filter Module OID请求

NDIS 为每一个适配器定义了对象 ID 值(OID),它包括设备特征,配置设置和统计信息。
更多的信息可查看 NDIS OIDs。Filter  Driver 可以查询设置下层驱动的参数据或过滤上层驱动
的 OID 请求。NDIS 为 6.1 以后的版本提供直接 OID 请求接口(Direct OID Request Interface)。
直接 OID 请求方法支持频繁经常性的 OID 查询设置请求。例如:IPSec  Offload    Version  2
(IPSecV2)接口提供的 OID_TCP_TASK_IPSEC_OFFLOAD_V2_ADD_SA OID 直接 OID 请求,直接
OID 请求在 NDIS 中提可选的。 

下面小节介绍关 OID 请求的信息。

2.10.1在Filter Driver中过滤OID请求

Filter Driver 可以处理上层驱动引发的 OID 请求,NDIS 调用 Filter Driver 的 FilterOidRequest
例程来处理 OID 请求,Filter Driver 需要调用 NdisFOidRequest 例程来转发请求给下层驱动。
 
NDIS 调 用 FilterCancelOidRequest 来 取 消 一 个 OID 请 求 , 当 NDIS 调 用
FilterCancelOidRequest 时,Filter Driver 应该尽可能快的调用 NdisFCancelOidRequest(原文是
NdisFOidRequest,我理解这里是一外笔误应该为 NdisFCancelOidRequest)。 

下图示例了一个 OID 请求 
名称:  10.png查看次数: 0文件大小:  14.3 KB

Filter  Driver 可以从 FilterOidRequest 同步和异 步完成 一 个 OID 请求,分 别返 回
NDIS_STATS_SUCCESS 和 NDIS_STATUS_PENDING 即可。FilterOidRequest 可以用同步的直接完
成一个 OID 请求并返回一个错误状态。 

如 果 Filter  Driver 成 功 控 制 ( 处 理 ) 了 一 个 OID 请 求 那 它 必 须 设 置 OID 请 求 传 来
NDIS_OID_REQUEST结构的SupportRevision成员。这个成员是通知OID的发起者所支持的驱动
修订版本。更多信息可以参看Specifying NDIS Version Information。 

如果 FilterOidRequest 返回 NDIS_STATUS_PENDING,就必须在 OID 请求完成后调用
NdisFOidRequestComplete 来通知上层驱动求请求完成。在这种情况下,请求的结果通过
NdisFOidRequestComplete 的 OidRequest 参数返回给上层驱动,并通过 Status 参数返回请求
完成的最终状态。 

如果 FilterOidRequest 返回 NDIS_STATUS_SUCCESS,通过 FIlterOidRequest 的 OidRequest
参数返回一个查询结果能上层。这时不调用 NdisFOidRequestComplete 例程。 

要转发 OID 请求到下层驱动,Filter  Driver 必须调用 NdisFOidRequest。如果一个 OID 请
求不能被转发到下层驱动应该当立即返回。要完成一个请求且不转发可以直接返回
NDIS_STATUS_SUCCESS 或 其 它 错 误 状 态 或 返 回 NDIS_STATUS_PENDING 后 调 用
NdisFOidRequestComplete。
 
转发 OID 请求类似于自己引发 OID 请求可见下节介绍。 

当下层驱动完成这个转发的请求时,如果有必要 Filter Driver  可以修改这个请求的结果
并递给上层驱动。 

一个 Filter Driver 可以从上层驱动接收 OID 请求在 Restarting,Running,Pausing 和 Paused
状态时。 

注意:像微端口驱动和 Filter Driver 在同一时间只可以接收到一个 OID 请求。NDIS 串行
化 OID 请求发送给 Filter Module,在一个 OID 请求没有完成之前是不会收到下一个 OID 请求
的。 

下面这个例子是在 Filter Driver 中修改一个 OID 请求: 

Filter  Driver 会 给 数 据 包 添 加 一 个 头 的 情 况 。 在 这 个 种 情 况 下 , Filter  Driver 在
OID_GEN_MAXIMUM_FRAME_SIZE 从下层驱动返回结果后,返回的结果中减去要插入的头的
长度。因为在发送数据时 Filter Driver 要插入一个头,而在接收数据时要移除一个头。

2.10.2在Filter Driver发起OID请求

Filter Driver 可以调用 NdisFOidRequest 引发一个 OID 查询和设置请求给下层驱动。下列
图示了这个过程。
名称:  11.png查看次数: 0文件大小:  12.2 KB

当 Filter  Driver 调用 NdisFOidRequest 时 NDIS 会调用下层驱动的请求功能。列多信息可
以能微端口驱动程序是怎么处理 OID 请求的。 

如果 NdisFOidRequest 是同步完成的 OID 请求它会返回 NDIS_STATUS_SUCCESS,如果是
异步完成它会返回 NDIS_STATUS_PENDING。 

要确定哪些信息被下层驱动成功处理了,就要检查返回结果的SupportRevision成员的
值。更多信息可以参看Specifying NDIS Version Information。 

如果 NdisFOidRequest 返回 NDIS_STATUS_PENDING, NDIS 在 OID 请求完成后调用
FilterOidRequestComplete 来 通 知 求 请 求 完 成 。 在 这 种 情 况 下 , 请 求 的 结 果 通 过
NdisFOidRequestComplete 的 OidRequest 参数返回给上层驱动,并通过 Status 参数返回请求
完成的最终状态。 

如果 NdisFOidRequest 返回 NDIS_STATUS_SUCCESS,通过 NdisFOidRequest 的 OidRequest
参数返回一个查询结果能上层。这时不调用 FilterOidRequestComplete 例程。 

一个 Filter Driver 可以调用 NdisFOidRequest 引发 OID 请求在 Restarting,Running,Pausing
和 Paused 状态。 

注意: Filter  Driver 必须跟踪这个请求确保不在 FilterOidRequestComplete 中调用
NdisFOidRequestComplete(因为请求是自己引发的不能传到上层)。

2.10.3 Filter Module的直接OID请求(Direct OID Request)

要支持直接 OID 请求,要在 Filter  Driver 的 NDIS_FILTER_DRIVER_CHARACTERISTICS 中提
供相应的 FilterXXX 接口的入口且 NDIS 的版本要支持 NdisFDirectXXX 例程。
 
直接 OID 请求的接口类似于标准的 OID 请求接口,例如:  NdisFDirectOidRequest  和
FilterDirectOidRequest  例程类似于 NdisFOidRequest  和 FilterOidRequest 例程。 

注意:只有 NDIS  6.1 指定一些 OID 在才可以使用直接 OID 请求,6.1 版本之前的和 6.1
中的一些 OID 是不支持直接 OID 请求的。哪些 OID 支持直接 OID 请求可以参看 OID 接口页。
例如看看 OID_TCP_TASK_IPSEC_OFFLOAD_V2_ADD_SA。 

直接 OID 请求不是串行的,它不像标准的 OID 请求那样被 NDIS 串行化,一个直接 OID
发生时可能存在另一个直接 OID 或标准 OID 请求也发生了。此外,直接 OID 请求例程工作
在小于等等 DISPATCH_LEVEL 的中断级别上。 

要支持直接 OID 请求就要使用文档中规定的标准备接口,下面列出直接 OID 接口与标
准 OID 请求接口的对应。
名称:  12-1.png查看次数: 0文件大小:  7.4 KB
名称:  12-2.png查看次数: 0文件大小:  21.7 KB

2.11 Filter Module PnP事件通知

Filter  Driver  可以接收到所有下层微端口驱动要接收的 PnP 事件通知,此外还可以接收
到上层协议驱动要接收的网络事件通知道。它可以控制驱动指定的通知事件。 

下图展示了一个 Filter Driver 接收 PnP 通知的情形 
名称:  13.png查看次数: 0文件大小:  6.1 KB

Filter Driver 提供 FilterPnpEventNotify 来接收 NDIS 传递的 PnP 和电源管理事件,它类似
于微端口的 MiniportDevicePnPEventNotify 全程。 

Filter Driver 要下层驱动转发收到的事件,转发事件要用到 NdisFDevicePnPEventNotify 例
程。 

下面图例是 Filter Driver 接收网络通知事件的过程
名称:  14.png查看次数: 0文件大小:  5.3 KB

Filter  Driver 提供了 FilterNetPnpEvent 例程来处理网络 PnP 和电源管理事件通知。它类
似于协议驱动的 ProtocolNetPnPEvent。 

Filter  Driver 需要转发网络 PnP 和电源管理事件给上层驱动。转发这些事件是通
NdisFNetPnpEvent 来完成的。

Filter Driver应该处理驱动栈上的更改(当NDIS改变一个正在运行的栈时会先发送相关的
PnP和电源管理事件,根据实际情况Filter Driver也要在自己的程序中处理相应的事件)。更多
关于更改运行中的驱动栈的信息可以参看DDK中Modifying a Running Driver Stack。 

如查有必要可以处理这些事件,因为在一个PnP和电源管理事件通知后NDIS才可以执行
暂停操作。关于暂停操作在DDK中的Pausing a Driver Stack里有详细的介绍。

2.12 Filter Module  状态指示

Filter Driver 可以提供 FilterStatus 例程,当下层驱动报告状态的时候 NDIS 会调用它。此
外,Filter Driver 还可以自己引发一个状态指示。 

下图示例一个状态指示 
名称:  15.png查看次数: 0文件大小:  5.6 KB

当下层驱动调用一个状态指示例程时(NdisMIndicateStatusEx或NdisFIndicateStats),NDIS
会调用Filter  Driver的FilterStatus例程。更多的关于如何从微端口驱动指示状态的信息可以查
看DDK中的Adapter Status Indications。 

Filter Driver 在 FilterStatus 中调用 NdisFIndicateStatus 传递一个状态指示给上层驱动。此
外,还可以过滤状态指示(不用调用 NdisFIndicateStatus)或在调用 NdisFIndicateStatus 之前
修改状态信息。 

Filter  Driver 要自己引发一个状态报告,可以在 NDIS 未调用 FilterStatus 的情况下调用
NdisFIndicateStatus。 

在这种情况下, Filter  Driver 要设置 SourceHandle 成员为 FilteAttech 参数提供的
NdisFilterHandle 句柄。如果一个状态指示是一个 OID 请求相关的(下层请求一个 OID 下层
要做相应的状态指示),那么状态的 DestinationHandle  和 RequestId 成员要设置成上层的 OID
请求包携带的数据。 

Filter  Driver 调 用 NdisFIndicateStatus 后 NDIS 会 调 用 相 邻 上 层 的 状 态 指 示 函 数
(ProtocolStatusEx 或 FilterStatus)。 

3 安装NDIS Filter Drivers

这节主要介绍如何安装 NDIS Filter Driver 的信息。Filter Driver 安装不同于中间层过滤驱
动,配置管理器把 Filter  Driver 安装成自启动的服务类似于协议驱动。对于每一个微端口适
配器(Miniport Adapter)为 NDIS 提供一个 Filter Module 的列表。Filter Driver 不像中间层过
滤驱动那样联结一个虚拟设备(或虚拟微端口(virtual miniport))。 

要安装一个 Filter  Driver 需要提供一个 INF 的配置文件。配置管理器从这个配置文件中
读取相关的配置信息并复制到注册表。与此不同的是中间层过滤驱动,它有两个配置文件一
个提供上边缘协议驱动的接口配置信息,另一个提供下边缘虚拟微端口驱动的接口配置信
息。 

Filter  Driver 的 INF 配置文件类似于中间层过滤驱动的协议 INF 配置文件(Prolocol  INF 
File),它没有微端口 INF 配置文件(Miniport INF File)。 

下面的小节将介绍如何为一个 Filter Driver 生成一个 INF 文件。

3.1 指定Filter Driver的绑定关系

在一个网络驱动的 INF 文件中,UpperRanget 条目列出可能的上边缘绑定和 LowerRange
条目列出可能的上边缘绑定。这些条目包含都是系统预定义的值。 

对 Filter  Driver 来说,你必须设置 UpperRange 和 LowerRange 条目分别为 noupper 和
nolower。下面示例了一个 Filter Driver 的 INF 文件的条目。 

HKR, Ndi\Interfaces,UpperRange,,"noupper"
HKR, Ndi\Interfaces,LowerRange,,"nolower"

在一个 Filter Driver 的 INF 文件中 FilterMediaTypes 条目定义了 Filter Driver 要绑定的驱
动,它指定了 Filter Driver 要服务的媒体类型。下面图示列出在一个 Filter Driver 的 INF 文件
中的 FilterMediaTypes 定义及 FilterMediaTypes 可取值的列表。 

HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet"

FilterMediaTypes 可取值的列表,它和 LowerRange 的一样。

名称:  16-1.png查看次数: 0文件大小:  36.5 KB
名称:  16-2.png查看次数: 0文件大小:  44.9 KB

当计算机加载一个 Filter  Driver 时,Filter  Driver 要被插入到已经存在的协议到适配器的
绑定(Protocol‐to‐Adapter Banding),这时就依赖 FilterMediaTypes 提供一个列表来决定插入
到哪些协议到适配器的绑定上。

3.2 Filter Driver的INF文件设置

一个 Filter Driver 的 INF 设置指定了它的特性,例如:可以指定这个驱动是 Modifying 还
是 Monitoring 的,是 Mandatory 还是 Optional 的。此处,在 INF 文件中还可以指定 Filter Driver
的 FilterModule 的通用配置参数和其它有具体微端口适配口相关联的细节信息。Filter  Drivr
还要指定一个唯一的过滤器识别 GUID(FID GUID) 
 
下面两个小节分别示例如何配置 Monitoring 和 Modifying 两种类型的 Filter Driver。

3.2.1 Monitoring Filter Driver的INF文件配置

下面 Filter Driver 的安装节指定它为一个 Monitoring 类型的 Filter Driver: 

1) 设置 INF 文件的 Class 条目为 NetService(同时要指定 ClassGUID)下面是一个示例。

代码:
Class       = NetService 
 
ClassGUID      = {4D36E974‐E325‐11CE‐BFC1‐08002BE10318} 
2)   必须设置安装节(DDInstall,这个名字不 INF 文件中可能有不同)的 Characteristics 条目,
下边示例一个设置 
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys 
0x40000 这个是指 NCF_LW_FILTER 集合,一个 Filter  Driver 不设置为 NCF_FILTER(0x400)
这个标记

3)   设置 NetCfgInstanceId 条目,这个 Id 和 Filter Driver 中调用 NdisFRegisterFilterDriver 是指
定 Filter Driver 的唯一名保持一样。它可以用 Uuidgent.exe 来生成。下面示例这个条目的
配置。
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys
4) 一个安装节(DDInstall,这个名字不 INF 文件中可能有不同)必须指定一个 AddReg 条目
用于配置 Ndi 键的信息,在 Ndi 节下面必须指定 Service 条目。下面是一个示例。
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys 
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisMon" 
HKR, Ndi,CoServices,0x00010000,"NdisMon" 
HKR, Ndi,HelpText,,%NdisMon_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1 
5)   Filter  Driver 的安装节必须指定 FilterType 和 FilterRunType 条目,下面示例了将 Filter 
Driver 配置为 Monitoring 且 Mandatory 的配置方法 
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisMon" 
HKR, Ndi,CoServices,0x00010000,"NdisMon" 
HKR, Ndi,HelpText,,%NdisMon_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000001 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1  
FilterType 为 0x00000001 说是一个 Monitoring 类型的 Filter Driver,FilterRunType 为 1 是
以 Mandatory 加载,为 2 是以 Optional 方式加载。 

6) 下面示例如何为一个 Filter Driver 在系统中添加一个服务。
代码:
[Install.Services] 
AddService=NdisMon,,NdisMon_Service_Inst 
 
[NdisMon_Service_Inst]
DisplayName          = %NdisMon_Desc% 
ServiceType          = 1 ;SERVICE_KERNEL_DRIVER 
StartType              = 1 ;SERVICE_SYSTEM_START 
ErrorControl        = 1 ;SERVICE_ERROR_NORMAL 
ServiceBinary      = %12%\NdisMon.sys 
LoadOrderGroup    = NDIS 
Description          = %NdisMon_Desc% 
AddReg                    = Common.Params.reg 
7)INF 文件中要在 CoService 条目中为 Filter Driver 指定主服务名(即上一步创建的服务),
下面示例。
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisMon" 
HKR, Ndi,CoServices,0x00010000,"NdisMon" 
HKR, Ndi,HelpText,,%NdisMon_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1 
8)在 INF 文件中定 FilterClass 的值,指定 Filter  Driver 在驱动栈上的顺序(位置),但是
Monitoring 的 Filter Driver 不必指定,它固定加载到离微端口适配口最近的位置。

9)必须为 Filter 指定绑定的信息
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisMon" 
HKR, Ndi,CoServices,0x00010000,"NdisMon" 
HKR, Ndi,HelpText,,%NdisMon_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1 
10)为 Filter  Driver 指定通用参数,这些参数可以是与具体网络适配器相关的也可以是具体
实例(Filter Module)相关的,下面为示例。
代码:
[Common.Params.reg] 
 
HKR, FilterDriverParams\DriverParam,  ParamDesc,    , "Driverparam for Mon"
HKR, FilterDriverParams\DriverParam,  default,    , "5" 
HKR, FilterDriverParams\DriverParam,  type,       , "int"
HKR, FilterAdapterParams\AdapterParam,     ParamDesc,    , "Adapterparam for Mon"
HKR, FilterAdapterParams\AdapterParam,  default,    , "10" 
HKR, FilterAdapterParams\AdapterParam,  type,   , "int"
HKR, FilterInstanceParams\InstanceParam,        ParamDesc, , "Instance param for lwf" 
HKR, FilterInstanceParams\InstanceParam, default, , "15" 
HKR, FilterInstanceParams\InstanceParam, type, , "int" 
3.2.2 Modifying Filter Driver的INF文件配置

下面 Filter Driver 的安装节指定它为一个 Modifying 类型的 Filter Driver: 

1) 设置 INF 文件的 Class 条目为 NetService(同时要指定 ClassGUID)下面是一个示例。
代码:
Class     = NetService 
 
ClassGUID      = {4D36E974‐E325‐11CE‐BFC1‐08002BE10318}
2)   必须设置安装节(DDInstall,这个名字不 INF 文件中可能有不同)的 Characteristics 条目,
下边示例一个设置
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys
0x40000 这个是指 NCF_LW_FILTER 集合,一个 Filter  Driver 不设置为 NCF_FILTER(0x400)
这个标记

3)   设置 NetCfgInstanceId 条目,这个 Id 和 Filter Driver 中调用 NdisFRegisterFilterDriver 是指
定 Filter Driver 的唯一名保持一样。它可以用 Uuidgent.exe 来生成。下面示例这个条目的
配置。
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys 
4) 一个安装节(DDInstall,这个名字不 INF 文件中可能有不同)必须指定一个 AddReg 条目
用于配置 Ndi 键的信息,在 Ndi 节下面必须指定 Service 条目。下面是一个示例。
代码:
[Install] 
AddReg=Inst_Ndi 
Characteristics=0x40000 
NetCfgInstanceId="{5cbf81bd‐5055‐47cd‐9055‐a76b2b4e3697}" 
Copyfiles = NdisMon.copyfiles.sys 
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisLwf" 
HKR, Ndi,CoServices,0x00010000,"NdisLwf" 
HKR, Ndi,HelpText,,%NdisLwf_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper"
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1 
5) Filter  Driver 的安装节必须指定 FilterType 和 FilterRunType 条目,下面示例了将 Filter 
Driver 配置为 Monitoring 且 Mandatory 的配置方法

代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisLwf" 
HKR, Ndi,CoServices,0x00010000,"NdisLwf" 
HKR, Ndi,HelpText,,%NdisLwf_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1   
FilterType 为 0x00000002 说是一个 Modifying 类型的 Filter  Driver,FilterRunType 为 1 是
以 Mandatory 加载,为 2 是以 Optional 方式加载。

6)下面示例如何为一个 Filter Driver 在系统中添加一个服务。
代码:
[Install.Services] 
AddService=NdisLwf,,NdisLwf_Service_Inst 
 
[NdisLwf_Service_Inst] 
DisplayName          = %NdisLwf_Desc% 
ServiceType          = 1 ;SERVICE_KERNEL_DRIVER 
StartType              = 1 ;SERVICE_SYSTEM_START 
ErrorControl        = 1 ;SERVICE_ERROR_NORMAL 
ServiceBinary      = %12%\NdisLwf.sys 
LoadOrderGroup    = NDIS 
Description          = %NdisLwf_Desc% 
AddReg                    = Common.Params.reg  
7)INF 文件中要在 CoService 条目中为 Filter Driver 指定主服务名(即上一步创建的服务),
下面示例。
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisLwf" 
HKR, Ndi,CoServices,0x00010000,"NdisLwf" 
HKR, Ndi,HelpText,,%NdisLwf_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1 
8)在 INF 文件中定 FilterClass 的值,指定 Filter Driver 在驱动栈上的顺序(位置),Modifying
类型的 Filter Driver 必须为它指定一个值。  这个值可以是下面值中的一个。
名称:  17.png查看次数: 0文件大小:  94.3 KB

注意:  多个 Modifying 类型的 Filter  Driver 可以存在于同一个栈层上。例如,两个
Modifying 类型的 Filter Driver 可以都指定 FilterClassic 为“scheduler”并时存在于这个驱动栈
的这个层上。Filter  Driver 后安装的在先安装的下层,也就是说后安装的离微端口适配器最
近。此外,多个 Filter Driver 在不同微端口适配器上的顺序是一相同的。 

看下面的示例:
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisLwf" 
HKR, Ndi,CoServices,0x00010000,"NdisLwf" 
HKR, Ndi,HelpText,,%NdisLwf_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet" 
HKR, Ndi,FilterRunType, 0x00010001, 1   
9) 必须为 Filter 指定绑定的信息
代码:
[Inst_Ndi] 
HKR, Ndi,Service,,"NdisLwf" 
HKR, Ndi,CoServices,0x00010000,"NdisLwf" 
HKR, Ndi,HelpText,,%NdisLwf_HelpText% 
HKR, Ndi,FilterClass,, compression 
HKR, Ndi,FilterType,0x00010001,0x00000002 
HKR, Ndi\Interfaces,UpperRange,,"noupper" 
HKR, Ndi\Interfaces,LowerRange,,"nolower" 
HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet"
HKR, Ndi,FilterRunType, 0x00010001, 1
10) 为 Filter  Driver 指定通用参数,这些参数可以是与具体网络适配器相关的也可以是具体
实例(Filter Module)相关的,下面为示例。
代码:
[Common.Params.reg] 
 
 
HKR, FilterDriverParams\DriverParam,   ParamDesc,    , "Driverparam for Lwf" 
HKR, FilterDriverParams\DriverParam,  default,    , "5" 
HKR, FilterDriverParams\DriverParam,  type,       , "int" 
HKR, FilterAdapterParams\AdapterParam,     ParamDesc,    , "Adapterparam for Lwf "
HKR, FilterAdapterParams\AdapterParam,   default,    , "10" 
HKR, FilterAdapterParams\AdapterParam,   type,   , "int" 

HKR, FilterInstanceParams\InstanceParam,        ParamDesc, , "Instance param for lwf" 
HKR, FilterInstanceParams\InstanceParam, default, , "15" 
HKR, FilterInstanceParams\InstanceParam, type, , "int" 
3.3 Filter Driver访问配置信息

NDIS 支持一个函数集合用来访问 Filter Driver 的注册表参数。Filter Driver 可以在附加和
重启操作时或接收到 PnP 和电源管理通知事件时访问这些参数。
代码:
NDIS_STATUS 
  NdisOpenConfigurationEx( 
    IN PNDIS_CONFIGURATION_OBJECT  ConfigObject, 
    OUT PNDIS_HANDLE  ConfigurationHandle 
    ); 
过 滤 驱 动 通 过 调 用 NdisOpenConfigurationEx 来 访 问 注 册 表 设 置 。 调 用 用 
NdisFRegisterFilterDriver时 会 获 得 一 个 句 柄 , 如 果 用 这 个 句 柄 初 始 
NDIS_CONFIGURATION_OBJECT 结构的 NdisHandle 成员并调用NdisOpenConfigurationEx 可以
得 到 一 个 标 识 参 数 存 储 点 的 注 册 表 句 柄 。 Filter  Driver 可 以 使 用 这 个 句 柄 一 直 到
NdisDeregisterFilterDriver 调用。

如 果 用 FilterAttech 中 系 统 传 来 的 句 柄 同 初 始 化 NdisHandle 成 员 那 么 调 用
NdisOpenConfigurationEx 得到的就是 Filter  Module 的配置存储点。获得的这个句柄到
FilterDetach 调用完成就失效了。当有多个 Filter  Module 配置在相同的微端口适配器上时,
如果 Monitoring 类型的 Filter  Driver 设置 NDIS_CONFIGURATION_OBJECT 的 Flags 值为
NDIS_CONFIG_FLAG_FILTER_INSTANCE_CONFIGURATION  ,这样驱动就可以访问特定的 Filter 
Module 配置信息。Modifying 类型的 Filter Driver 不能应用这个标记。
 
一个 Filter Driver 在访问过配置信息后一定要用 NdisClosConfiguration 关闭配置句柄和相
关的资源。 

4 附录 


4.1 NET_BUFFER体系结构 


4.1.1 网络数据结构
 

网络数据由在网络上发送和接收的数据包组成。NDIS 提供了数据结构来描述它们,NDIS 
6.0 提供了如下数据结构: 

1) NET_BUFFER   

2) NET_BUFFER LIST   

3) NET_BUFFER_LIST_CONTEXT   

下图说明了这些结构之间的关系: 
名称:  18.png查看次数: 0文件大小:  22.1 KB

在 NDIS  6.0 中,NET_BUFFER 是封闭网络数据的基本构建块。每一个 NET_BUFFER 都有
一个 MDL 链。这些 MDL 映射的缓冲区地址是 NET_BUFFER 指定的数据空间。这种数据映射
同 NDIS  5.X 及之前的版本使用的 NDIS_PACKET 中的映射是一样的。NDIS 提供函数来管理这
些 MDL 链。 

多个 NET_BUFFER 可以被附加到(可以被串到)一个 NET_BUFFER_LIS 上。这些 NET_BUFFER
被组织成一个以 NULL 结束的单链表。驱动或 NDIS 仅创建一个 NET_BUFFER_LIST,应该修改
它的链表给它插入或删除一些 NET_BUFFER 结构。 

NET_BUFFER_LIST 结构中包含的信息中描述了所有被串到一个链表上的 NET_BUFFER 结
构 。 如 果 NET_BUFFER_LIST 需 要 上 下 文 信 息 , 可 以 分 配 额 外 的 空 间 来 存 储 一 个
NET_BUFFER_LIST_CONTEXT 结构。NDIS 提供函数来分配、访问 NET_BUFFER_LIST_CONTEXT
中的数据。 

多个 NET_BUFFER_LIST 结构可以串成一个 NET_BUFFER_LIST 链表。它们可以被组织成一
个以 NULL 结尾的单向链表。驱动可以对这个链表直接进行插入、删除操作。 

4.1.1.1NET_BUFFER结构

NDIS  6.0 中使用的 NET_BUFFER 结构相似于 NDIS  5.X 以前使用的 NDIS_PACKET 结构,每
一个 NET_BUFFER 结构中都包装了一个网络数据包。 

下图显示了 NET_BUFFER 的各个域:
名称:  19.png查看次数: 0文件大小:  20.0 KB

NET_BUFFER 结构包含一个 NetBufferHeader 成员它是一个 NET_BUFFER_HEADER 结构。
这个成员又包含一个 NET_BUFFER_DATA 结构的 NetBufferData 成员。应该用 NDIS 提供宏和
相关函数来访问 NET_BUFFER 结构。下面是相关宏的列表。

NET_BUFFER_NEXT_NB 
NET_BUFFER_FIRST_MDL 
NET_BUFFER_DATA_OFFSET 
NET_BUFFER_DATA_LENGTH 
NET_BUFFER_PROTOCOL_RESERVED 
NET_BUFFER_MINIPORT_RESERVED 
NET_BUFFER_CHECKSUM_BIAS 
NET_BUFFER_CURRENT_MDL
NET_BUFFER_CURRENT_MDL_OFFSET 
NdisGetPoolFromNetBuffer

NET_BUFFER 中的一些成员仅仅被 NDIS 使用,下面列出一些编写驱动中常会用到的成
员。

ProtocolReserved 

它是为协议驱动保留的。(也可以理解为是为一个发送包的引发者保留的) 

MiniportReserved 

它是为微端口驱动保留的。(也可以理解为是为一个接收指示的引发者保留的) 

NdisPoolHandle 

它指示了 NET_BUFFER 结构是从哪一个 NET_BUFFER 池里分配的 

Next 
以一个 NET_BUFFER 链表中它是指向下一个 NET_BUFFER 结构的指针。如果它是最后个
元素那它的这个成员为 NULL 

DataLength 

它指明了网络数据在 MDL 链中的长度,长度以字节为单位。 

DataOffset 

它表示的是从 MDL 链所描述的缓冲区的开始位置到网络数据(当前驱动要使用的数据)
开始位置之间的偏移。它能字节为单位。
 
CurrentMdl 

它是一个指向当前驱动在 MDL 链中要使用的第一个 MDL 的指针。它是 NDIS 提供的一
个优化(跳任何驱动不使用的 MDL 直接到驱动要使用的第一个 MDL。) 

CurrentMdlOffset 

它是一个从 CurrentMdl 指向 MDL 所描述的地址空间的开始位置到网络数据(当前驱动
要使用的数据)开始位置之间的偏移。它能字节为单位。 

下面是各数据成员之间的一个关系图。  
名称:  20.png查看次数: 0文件大小:  16.6 KB

NDIS 提供了一个系列的函数来访问这些在 MDL 链中的数据。有些时候一些数据空间对
当前驱动来说是未使用的,虽然是未使用的但也可能包含了有效的数据。例如,在一些应用
中的接收路径中这个未使用的空间可能包含有下层驱动使用的头信息。 

驱动使用 Retreat 和 Advance 操作来减少和增加使用的数据完间。更多关于这方面的信
息可在“Retreat 和 Advance 操作”小节看到。 

下列术语和定义描述了 NET_BUFFER 的数据空间中的元素。

Used Data Space 

这里包含了当前驱动当前时间可以使用的数据空间,要增加使用的空间可以执行
Retreat 操作,要减少使用的空间可以执行 Advance 操作。
 
Unused Data Space 

当前驱动当前时间不使用的数据空间。
 
Total Data Size
 
这是当前 Used Data Space 和 Unused Data Space 空间大小的和。如果要计算这个总的空
间大小就把 DataOffset 和 DataLength 两个成员相加就可得到。 

Retreat 

这是一个增加使用空间的操作 

Advance 

这是一个减少使用空间的操作

4.1.1.2NET_BUFFER_LIST结构

一 个 NET_BUFFER_LIST 结 构 中 包 装 了 一 个 NET_BUFFER 的 单 向 链 表 。 下 面 展 示
NET_BUFFER_LIST 中的各个域。
名称:  21.png查看次数: 0文件大小:  24.2 KB

在 NET_BUFFER_LIST 结构中包含一个 NET_BUFFER_LIST_HEADER 的 NetBufferListHeader
成员,这个成员里包含一个 NET_BUFFER_LIST_DATA 的 NetBufferListData 的子成员。应该使
用 NDIS 提供的宏和函数来访问这些成员,下面列出这些宏和函数。

NET_BUFFER_LIST_NEXT_NBL 
NET_BUFFER_LIST_FIRST_NB 
NET_BUFFER_LIST_FLAGS 
NET_BUFFER_LIST_MINIPORT_RESERVED 
NET_BUFFER_LIST_CONTEXT_DATA_START 
NET_BUFFER_LIST_CONTEXT_DATA_SIZE 
NET_BUFFER_LIST_INFO 
NET_BUFFER_LIST_STATUS 
NET_BUFFER_LIST_PROTOCOL_RESERVED 
NdisGetPoolFromNetBufferList 

在 NET_BUFFER_LIST 结构中大部成员仅仅是 NDIS 来使用的。下面列出的成员是我们在
编写驱动的时候最有可能用的一些成员。

ParentNetBufferList 

如果一个 NET_BUFFER_LIST 是一个从父结构派生(主要是指一个对结构做的克隆、分片、
重组作操作)过来的一个子结构那么它就指向父结构。其它情况下它为 NULL。 

NdisPoolHandle 

分配结构时所用池的句柄。

ProtocolReserved 

它是为协议驱动保留的。(也可以理解为是为一个发送包的引发者保留的) 

MiniportReserved 

它是为微端口驱动保留的。(也可以理解为是为一个接收指示的引发者保留的) 

SourceHandle 

它标识了分配这个 NET_BUFFER_LIST 的驱动。NDIS 利用它来返加这个结构,我们不应
该当在驱动中读这个句柄。
 
ChildRefCount 

如果结构是一个父结构,它代表着它有几个子结构。其它情况下为 0。 

Flags 

保留给 NET_BUFFER_LIST 未来标识属性用的,目前版本没有相关的属性规范。 

Status 

标识一个网络操作的最终状态,如果微端口操作在完成发送后写这个状态返回给上层发
送操作的引发者。 

NetBufferListInfo 

是关于 NET_BUFFER_LIST 结构的信息,这个些信息通常是链表中的所有 NET_BUFFER 的
信息 

Next 

在一个 NET_BUFFER_LIST 链表中它是指向一个结构的指针,如果它是最后一个元素那
Next 为 NULL。 

FirstNetBuffer 

它指向一个联结在 NET_BUFFER_LIST 上的 NET_BUFFER 链表的第一个元素。

 
注意:Context 成员是指一个 NET_BUFFER_LIST_CONTEXT 结构的,NDIS 提供宏和函数来维护

这个结构,下面列出这些宏和函数。 

NdisAllocateNetBufferListContext   
NdisFreeNetBufferListContext   
NET_BUFFER_LIST_CONTEXT_DATA_START   
NET_BUFFER_LIST_CONTEXT_DATA_SIZE  

4.1.1.3NET_BUFFER_LIST_CONTEXT结构

NDIS 使用 NET_BUFFER_LIST_CONTEXT 结构来为所联结的 NET_BUFFER_LIST 存储一些附
加数据。NET_BUFFER_LIST 的 Context 成员指一个 NET_BUFFER_LIST_CONTEXT 结构,这个结
构存储的信息对 NDIS 和栈上的其它驱动是不透明的。下面列出了结构的各个域。
名称:  22.png查看次数: 0文件大小:  3.1 KB

这个结构的 ContextData 成员包含了环境信息,这个环境信息可以是 NET_BUFFER_LIST
所需要的任何信息。我们应该使用下面的宏和函数来维护 NET_BUFFER_LIST_CONTEXT 中的
成员。 

NdisAllocateNetBufferListContext   
NdisFreeNetBufferListContext   
NET_BUFFER_LIST_CONTEXT_DATA_START   
NET_BUFFER_LIST_CONTEXT_DATA_SIZE

4.1.2 Retreat和Advance操作

NDIS 提供 Retreat 和 Advance 功能来维护 NET_BUFFER 结构。Retreat 操作标记更多当前
驱动可用的数据空间。而 Advance 则释放一些当前驱动可用的数据空间。 

一个 Retreat 操作必需发生成发送数据或返回发送数据给下层数据时。例如,在发送操
作期间一个驱动调用 NdisRetreatNetBufferDataStart 给头部数据预留出空间。
 
一个 Advance 操作必需发生成发送完成和从底层驱动接收数据时。例如,在接收数据期
间驱动调用 NdisAdvanceNetBufferStart 直接跳上层驱动不关心的下层驱动的头部数据。在这
种情况下,这些头部数据保留在未使用数据空间内。 

下图展示了网络数据和这些操作的关系。
名称:  23.png查看次数: 0文件大小:  9.8 KB

4.1.2.1Retreat操作

Retreat 操作是增加一个 NET_BUFFER 或所有在 NET_BUFFER_LIST 中的 NET_BUFFER 的可
用数据空间。下面是 Retreat 操作相关的函数。 

NdisRetreatNetBufferDataStart   
NdisRetreatNetBufferListDataStart  

Retreat 操作可以同时为 NET_BUFFER 分配一些 MDL,要提供一个分配机制,驱动可以
指定一个可选的 NetAlloceMdl 函数入口,如果把这个参数设为 NULL 则用默认的分配机制。
被分配的 MDL 必须被 NetFreeMdl 释放,提供了相应的分配机制也要有相应的释放机制。 

要得到新的 DataLength,调用函数 NDIS 会添加驱动指定的 DataOffsetDelta 到当前的
DataLength 。 如 果 扩 展 的 空 间 小 于 未 使 用 的 空 间 , 那 么 就 用 当 前 的 DataOffset 减 去
DataOffsetDelta 得到一个新的 DataOffset 并用当前的 DataLengh 加上 DataOffsetDelta 得到一
个新的 DataLength。 

如果扩展的空间大于未使用的空间,Retreat 操作会分配新的数据空间。这时 NDIS 会调
整 DataOffset。
 
对一个发送操作来说,如果没有足够的未使用空间给 Retreat 操作 NDIS 会分配置。如果
不需要分配,NDIS 只是简单的调整 DataOffset 和 DataLength。要得到最好性能,驱动应该分
配足够的空间供下层驱动执行 Retreat 操作。 

对一个接收返回的操作来说,Retreat 操作可以只简单的调整 DataOffset 和 DataLength。
它反转了接收处理中的 Advance 操作。在 Retreat 操作完成后又恢复了下层驱动信息到可使
用数据空间。

4.1.2.2Advance操作

Aduvance 操作减少了 NET_BUFFER 和一个 NET_BUFFER_LIST 中的所有 NET_BUFFER 的可 
使用数据空间。下面列出了相关的操作函数。

NdisAdvanceNetBufferDataStart   
NdisAdvanceNetBufferListDataStart 

Advanced 操作可以在些时候释放掉一些关联在 NET_BUFFER 上的数据空间。驱动可以在
调用函数时提供一个 NetFreeMdl 函数入口来提供一种释放机制。如果不提供 NDIS 会使用默
认的方法进行释放。对 MDL 的释放必须使用与分配相对应用的释放方法。 

要获得一个新的 DataLength,NDIS 会用当前的 DataLength 减去 DataOffsetDelta 得到一
个新的。如果在 Retreat 操作时分配了新的空间那么,Advance 操作时可以释放这些新分配
的空间。如果不释放内存那么 NDIS 只是简单的调整一下 DataLength 和 DataOffset。
 
对于发送操作完成的情况,Advance 操作来可释放在 Retreat 操作时分配的内存。为了
更好的性能,驱动应该分配足够的空间给下层执行 Retreat 操作。 

对于接收的情况,Advance 操作可以只简单的调整一下 DataLength 和 DataOffset。在
Advance 操作完成之后,低层驱动的头信息被保留到了未使用数据空间。 

4.1.3 获得池句柄

下列 NDIS 池分配函数需要一个句柄来分配资源。 

NdisAllocateNetBufferPool   
NdisAllocateNetBufferListPool   

NDIS 6.0 可以通过下面的方式来获得这个句柄。 

协议驱动 

协议驱动在调用 NdisRegisterProtocolDriver 时可以获得一个句柄,它用这个句柄来分配
池资源。 

微端口驱动 

NDIS 会调用微端口驱动的 MiniportInitializeEx 例程并传递一个句柄,它可以用这个句柄
来分配池资源。 

中间层驱动 

中间层驱动调用 NdisRegisterProtocolDriver 时可以获得一个句柄用它可以分配用于发送
操作的池。NDIS 会调用它的 MiniportInitializeEx 例程并传递一个句柄给它,它可以用这个句
柄分配用于接收操作的池。 

过滤驱动(Filter Driver) 
 
NDIS 在调用 FilterAttach 进会传递一个句柄,它可以用这个句柄来分配池。 
 
其它驱动 

如果一个驱动不能用上述方法获得句柄,那它可以调用 NdisAllocateGenericObject 来获
得一个句柄来分配池。

4.1.4 分发IRQL跟踪

为了提升系统性能,一些 NDIS 函数都包含一个分发级别用于指示函数执行时的 IRQL。
清楚及正确使用这些 IRQL 可以帮助我们避免必要的 IRQL 设定。
 
这些其它标记(other flag)是用来控制一些其它属性(other abbritues)的,它们的名字中
都包含了对应的 IRQL。 

NDIS_SEND_FLAGS_DISPATCH_LEVEL
NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL 
NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL 
NDIS_RETURN_FLAGS_DISPATCH_LEVEL 
NDIS_RWL_AT_DISPATCH_LEVEL 

不对 IRQL 进行测试,调用者必须清楚当前执行的 IRQL 才能设置这些标记。例如,我知
道它是因为它在驱动设计中是固定的,或者在驱动中可以保存这个 IRQL。 

如果知道当前的 IRQL 是 DISPATCH_LEVEL,调用者就必须设置这个标记。如果当前 IRQL
未知或调用者不运行在 DISPATCH_LEVEL 上,则不用设置这些标记(清除这个些标记)。如果
调用者是 NDIS,那被调用函数应该测试这些标记避免更改这些标记。 

驱动不应该测试 IRQL 来决定标记的设置,测试将会出错。如果必要可以直接调用函数
做自我测试。决定应该不应该设置这些标记是驱动设计的工作。 

4.1.5 发送和接收操作

在 一 次 函 数 是 调 用 中 , NDIS6.0 驱 动 可 以 发 送 多 个 NET_BUFFER_LIST , 而 每 一 个
NET_BUFFER_LIST 都 可 以 是 多 个 NET_BUFFER 。 此 外 , 同 样 可 以 指 示 完 成 多 个 这 样 的
NET_BUFFER_LIST。 

在接收路径中,微端口驱动可以用 NET_BUFFER_LIST 指示一个接收。每一个被微端口驱
动指示的 NET_BUFFER_LIST 中包含一个 NET_BUFFER。但是本地 802.11 驱动可以包含多个
NET_BUFFER 结构。因为不同的协议绑定可以处理每一个 NET_BUFFER_LIST,而 NDIS 也可以
独立的返回每一个 NET_BUFFER_LIST 给微端口驱动。 

对于 NDIS5.X 和更早的版本,NDIS 在基于 NDIS_PACKET 和基于 NET_BUFFER 之间提供了
一个转换层(用于做两者的兼容转换)。NDIS 在其中进行两者的转换。要避免性能下降就应
该在所有路径上支持 NET_BUFFER 结构和 NET_BUFFER_LIST 结构(即应用新的 NDIS 接口模
式)。 

4.1.5.1发送数据

下面是一个发送操作的图示,它包含协议驱动,NDIS 和微端口驱动。
名称:  24.png查看次数: 0文件大小:  15.1 KB

协议驱动在一个绑定中调用 NdisSendNetBufferLists 发送一个 NET_BUFFER_LIST 结构。
NDIS 调用 MiniportSendNetBufferLists 将这个 NET_BUFFER_LIST 结构传递给下层驱动程序。 

所 有 基 于 NET_BUFFER 的 发 送 操 作 都 是 异 步 的 , 微 端 口 驱 动 调 用
NdisMSendNetBufferListsComplete 返回一个 NET_BUFFER_LIST 并返回一个适当的状态。每一
个 被 发 送 的 NET_BUFFER_LIST 结 构 都 可 以 被 独 立 的 完 成 。 微 端 口 驱 动 每 一 次 调 用
NdisMSendNetBufferListsComplete时 , NDIS都 会 去 调 用 协 议 驱 动 的 
ProtocolSendNetBufferListsComplete 例程。

当 NDIS 调用用协议驱动的 ProtocolSendNetBufferListsComplete 时,协议驱动可以收回对
NET_BUFFER_LIST 及它所联结 NET_BUFFER 及其它资源的所有权。 

微端口驱动和 NDIS 返回 NET_BUFFER_LIST 的顺序是不确定的(并不一定按发送的顺序
来)。在这个期间协议驱动应该保证与 NET_BUFFER_LIST 相联结的 NET_BUFFER 不被修改。
 
任何 NDIS 驱动都可以解除 NET_BUFFER 与 NET_BUFFER_LIST 的关系也可以解除
NET_BUFFER 与 MDL 的关系统(就是说可以在发送路径上对数据包做修改),但是必须返回
原 来 的 NET_BUFFER_LIST ( 做 了 修 改 当 完 成 后 返 回 给 上 层 时 要 返 回 原 来 那 个
NET_BUFFER_LIST)。例如,一个中间层驱动可以把一个 NET_BUFFER_LIST 分解成两个
NET_BUFFER_LIST 发送给下层驱动。但是当这两个 NET_BUFFER_LIST 发送完成后必须向上层
返回 NET_BUFFER_LIST 时必须返回原来(要求 NET_BUFFER 及相关 MDL 都要和原来的一样)
的那个 NET_BUFFER_LIST(中间层驱动直接产生的 NET_BUFFER_LIST 不能返回给上层驱动)。 

协议驱动要用调用 NdisOpenAdapterEx 时 NDIS 提供的 NdisBindingHandle 来设置
NET_BUFFER_LIST 的 SourceHandle 成 员 。 NDIS 使 用 这 个 成 员 返 回 协 议 驱 动 发 送 的
NET_BUFFER_LIST 结构。 

中间层驱动也需要设置这个 NET_BUFFER_LIST 结构中的 SourceHandle 成员为自己调用
NdisOpenAdapterEx 时返回的 NdisBindingHandle。如果中间层驱动转发一个 NET_BUFFER_LIST
要在设置它的 SourceHandle 之前保存上层驱动设置的 SourceHandle。并前在转发完成后恢
复原先的 SourceHandle 后返回给上层驱动。

4.1.5.2取消发送

下面图例中是一个取消发送操作的过程。
名称:  25.png查看次数: 0文件大小:  15.6 KB

驱动可以调用 NDIS_SET_NET_BUFFER_LIST_CANCEL_ID 给每一个要传输到下层驱动的
NET_BUFFER_LIST 设置一个取消 ID。 

在分配取消 ID 时我们应该先调用 NdisGeneratePartialCancelId 获得取消 ID 的最高位字
节。这是为了保证系统在任意两个驱动的取消 ID 不相同。典型的用法是在 DriverEntry 中调
用它一次来获得取消 ID 的最高字节,但是也可以在不同的地方调用多次获得多个取消 ID 的
最高字节。

要 取 消 一 个 未 完 成 的 发 送 操 作 , 驱 动 可 以 通 过 取 消 ID 来 调 用 过
NdisCancelSendNetBufferLists 函数(它其中一个参数是要取消的发送 NET_BUFFER_LIST 被设
置的取消 ID)。驱动可以用 NDIS_GET_NET_BUFFER_LIST_CANCEL_ID 获得 NET_BUFFER_LIST
的取消 ID。 

如果驱动对所有发送的 NET_BUFFER_LIST 设置相同的取消 ID 那么只要一次取消调用就
会取消所有的发送。对一组 NET_BUFFER_LIST 设置相同的取消 ID 对这个取消 ID 的一次取消
调用则会取消这一组的发送。 

NDIS 会调用在绑定上的适当低层驱动的 MiniportCancelSend,取消正在进行的传输后,
微 端 口 驱 动 调 用 NdisMSendNetBufferListsComplete 返 回 NET_BUFFER_LIST 和 一 个
NDIS_STATUS_CANCELLED 状态。NDIS  会调用适当驱动的 PotocolSendNetBufferListsComplete
函数。在这个函数中回收资源时应该设置相关 NET_BUFFER_LIST 的取消 ID 为 NULL,防止它
再次被使用时出现问题。 

4.1.5.3接收数据 

下面图例是基于一个接收操作的,包括微端口驱动、NDIS 和协议驱动。
名称:  26.png查看次数: 0文件大小:  11.6 KB

微端口驱动可以调用 NdisMIndicateReceiveNetBufferLists 来给上层驱动指示数据。这些
数据组 NET_BUFFER_LIST 来组织。协议驱动接收到 NET_BUFFER_LIST 后可以对它进行修改转
发给客户端。同样的驱动也可以,附加更多附加信息在 NET_BUFFER_LIST 中。 

可 以 把 所 有 要 指 示 给 上 层 驱 动 的 NET_BUFFER_LIST 链 接 到 一 起 通 过
NdisMIndicateReceiveNetBufferLists 来指示给上层驱动。NDIS 会校验这些 NET_BUFFER_LIST
结 构 并 调 用 每 一 个 绑 定 协 议 驱 动 的 ProtocolReceiveNetBufferLists 。 NDIS 仅 仅 把
NET_BUFFER_LIST 传递给那下正确有绑定了的协议驱动。同时会匹配 NET_BUFFER_LIST 中
NetBufferListFrameType(协议驱动在注册是会注册相应该的帧类型与之匹配后 NDIS 才转发
给它)来确定转发给某个协议驱动。 

如果设置了给 ReceiveFlag 设置了 NDIS_RECEIVE_FLAGS_RESOURCES 标记,它会传递给上
层的协议驱动。NDIS 会在协议驱动的 ProtocolReceiveNetBufferLists 调用返回后收回资源(传
给协议驱动的 NET_BUFFER_LIST 及与之关联的所有资源)的所有权。 

注意:如果设置了这个标记上层驱动应该尽快处理返回资源给下层驱动。同时在返回时
要恢复对资源的修改。例如,一个收如此标记的上层驱动,可以对 NET_BUFFER_LIST 及相关
资源进行修改,但返回 NDIS 进要确保 NET_BUFFER_LIST 是原始的内容(这里主要指资源本
身,如果其中的 NET_BUFFER 链,及 NET_BUFFER 中的 MDL 链,而其中的数据内容则不必要
恢复原始中)。  

如果没有设置这个标记那么协议驱动则持有对这个 NET_BUFFER_LIST 的所有权。协议驱
动可以对它进行排队延后处理(注意,如果传递了标记则必须在函数内完成处理因为函数返
回后这个 NET_BUFFER_LIST 不用属于协议驱动了)。在这种情况下协议驱动必须调用
NdisReturnNetBufferLists 来返还资源给下层驱动。 

如果下层的微端口驱动运行在低资源情况下,可以在指示数据给上层时传递这个标记。
这个微端口驱动就可以在指示结束后恢复所有指示所用资源的所有权。 

当 协 议 驱 动 调 用 NdisReturnNetBufferLists 后 NDIS 会 调 用 微 端 口 驱 动 的
MiniportRetrunNetBufferLists 例程。 

注意:如果一个微端口驱动指示数据给上层时传递了 NDIS_RECEIVE_FLAGS_RESOURCES
标记那么 NDIS 将不会调用它的 MiniportRetrunNetBufferLists。微端口对资源的所有权会在
NdisMIndicateReceiveNetBufferLists 返回时自动恢复。 
 
NDIS返 回NET_BUFFER_LIST的 顺 序 并 不 和 微 端 口 驱 动 调 用 
NdisMIndicateReceiveNetBufferLists 的顺序一致,NDIS 可以以任何顺序返回NET_BUFFER_LIST。 

微端口驱动在指示数据给上层时应该设置 NET_BUFFER_LIST 的 SourceHandle 成员。这个
成员应该设置为 NDIS 在微端口驱动的 MiniprotInitEx 中传递的那个 MiniprotAdapterHandle
句柄。NDIS 要利用这个成员返回 NET_BUFFER_LIST 结构微端口驱动。 
 
中间层驱动也要在指示数据给上层时应该设置 NET_BUFFER_LIST 的 SourceHandle 成员。
这 个 成 员 应 该 设 置 为 NDIS 在 中 间 层 驱 动 的 MiniprotInitEx 中 传 递 的 那 个
MiniprotAdapterHandle 句柄。如果要转发这个接收指示,驱动要保存下层驱动设置的
SourceHandle 值后再设置这个句柄为中间层驱动的。当 NDIS 返回 NET_BUFFER_LIST 给中间
层驱动时,要重新恢复原来的 SourceHandle 值再调用 NdisMReturnNetBufferLists 返回给下层
驱动。 

4.1.5.4NDIS回环包

如果 NET_BUFFER_LIST 的成员 NblFlags 设置了 NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET
标记,那说明这个包是一个回环包。协议驱动和 Filter  Driver 可以检查这个标记判断这个包
是不是回环包。 

回环包要被回传回来要满足下面三个条件:

1) 下层微端口的类型必须是 NdisMedium802_3  或 NdisMedium802_5 

2) 下面三个条件之一必须满足: 
 
a)  协 议 绑 定 设 置 了 OID 的 OID_GEN_CURRENT_PACKET_FILTER  请 求 设 定 了
NDIS_PACKET_TYPE_PROMISCUOUS 标记或下面条件为真。 
多个绑定到微端口驱动 
一个 Filter Module 附加到微端口并且注册了接收控制器 

b)  协 议 绑 定 设 置 了 OID 的 OID_GEN_CURRENT_PACKET_FILTER  请 求 设 定 了
NDIS_PACKET_TYPE_ALL_LOCAL 标记或下面条件为真。 
多个绑定到微端口驱动 
一个 Filter Module 附加到微端口并且注册了接收控制器 
 
c)  调 用 者 设 置NdisSendNetBufferLists的SendFlags参 数 的 
NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK 标记 

3) 包符合微端口的包过滤设置。例如下面: 
 
a)  如果是一个直接包,它的目的 MAC 地址和微端口的 MAC 地址一样。 

b)如果是一个多播包,微端口的过滤设置了 NDIS_PACKET_TYPE_ALL_MULTICAST
标记或目标地址匹配微端口的多播地址列表且端口的过滤设置了
NDIS_PACKET_TYPE_ALL_MULTICAST 标记 

c)如果是一个广播包,微端口的过滤设置了 NDIS_PACKET_TYPE_BROADCAST 标
记。 
 
d) 微 端 口 的 过 滤 设 置 了NDIS_PACKET_TYPE_PROMISCUOUS或  
NDIS_PACKET_TYPE_ALL_LOCAL 标记。 

一个协议绑定要接收回环包要满足下面条件: 

1) 协议绑定是回环包原始发送者。 

2) 协议绑定不能设置 NDIS_PACKET_TYPE_NO_LOCAL 过滤。 

一个协议绑定不接收回环包要满足下面条件: 

1) 协议绑定设置了 NDIS_PACKET_TYPE_NO_LOCAL 过滤且协议绑定是回环包原始
发送者。 
 
2) 协 议 绑 定 是 原 始 发 送 者 但 没 有 不 能 设 置
NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK 标记。 

下面图显示了回环包的运算逻辑:
点击图片以查看大图图片名称:	27.png查看次数:	6文件大小:	87.6 KB文件 ID :	87016

4.1.6 以太网发送和接收操作

4.1.6.1发送以太网帧


Windows  Vista 的 TCP/IP 协议支持对一组以太网帧的发送。任何引发发送或修改发送的
上层驱动都必须遵循 TCP/IP 协议规范。 

注意:如果任何上层驱动不遵循下面的规则那么下层的驱动的行为将不可预测。 

1) 如果要发送一个以太网帧必须分配一个 NET_BUFFER_LIST 结构用于存放以太网帧。每一
个 NET_BUFFER_LIST 必须包含一个带外数据块(OOB),它被定义在 NET_BUFFER_LIST 是
NetBufferListInfo 成员中,被一些特殊处理使用。 

2) 如果要发送一个以太网帧必须为以太网帧分配一个或多个 NET_BUFFER 结构且链接到帧
的 NET_BUFFER_LIST 结构中。 

3) 必须保证 NET_BUFFER_LIST 中的 NET_BUFFER 描述的是同一种类形的以太网帧。 

4) 必须保证 NET_BUFFER_LIST 中的 NET_BUFFER 描述的以太网帧是有相同源和目的 MAC 的。 

5) 如果发送的是 TCP 或 UDP 包,那么 NET_BUFFER_LIST 中的所有 NET_BUFFER 必须是同一
TCP 或 UDP 链接的包。 

6) 不能把一个 MAC 头分别放在多个 MDL 中。如果这样做部分 MAC 头会被当成 VLAN 标记。 

7) 如果驱动改变了在 NET_BUFFER 中的 MDL 链或在 NET_BUFFER_LIST 中的 NET_BUFFER_LIST
则需要在返加 NET_BUFFER_LIST 给上层驱动之前时恢复原始的状态。但 NET_BUFFER_LIST
之间的链接关系不需要和原来的一样(如果返回多个 NET_BUFFER_LIST 时)。

4.1.6.2接收以太网帧

Windows  Vista 的 TCP/IP 协议支持对一组以太网帧的接收。任何引发接指示或修改接收
指示的下层驱动都必须遵循 TCP/IP 协议规范。 

注意:如果存在驱动不做下面这些需求英,那上层驱动可能行为不可预测。 
驱动接收以太网帧必须遵循下面的规范。 

1) 驱动必须分配一个 NET_BUFFER_LIST 结构用于存放以太网帧。每一个 NET_BUFFER_LIST
必须包含一个带外数据块(OOB),它被定义在 NET_BUFFER_LIST 是 NetBufferListInfo 成
员中,被一些特殊处理使用。 

2) 必须为以太网帧分配 NET_BUFFER 结构且链接到帧的 NET_BUFFER_LIST 结构中。注意:
一个以太网帧可以被分离成多块存储在 MDL 中链接到 NET_BUFFER 结构中。 

3) 对于 VISTA 版本不允许把一个接收的以太网帧分成多份存储在 MDL 中。但是以后的版本
会允许这样做。 

4) 任何 Windows 版本中都不能分离(在一个 NET_BUFFER_LIST 中把数据分别存储到多个
MDL 中)IP 头,IPV4 选项,IPSEC 头,IPV6 扩展头或上层协议头,除非第一个 MDL 中包
含了个 NDIS 支持的前视数据(LOOKHEAD)长度的数据 

5) 如果下层驱动遵循上述规则那到任何 NDIS6.0 以后的协议驱动和 Filter Driver 都应该支持
以太网帧的分片存储(在一个 NET_BUFFER_LIST 中把数据分别存储到多个 MDL 中)。这
样做是为了这些驱动在以后的 Windows 版本中兼容运行。

4.1.7 派生NET_BUFFER_LIST结构

NDIS6.0 提供函数来管理 NET_BUFFER_LIST 结构还可以从其它的 NET_BUFFER_LIST 派生
结构。这些函数经常被中间层驱动使用。 

下面这些函数就支持从一个已经存在的 NET_BUFFER_LIST 中创建一个派生结构。 

NdisAllocateCloneNetBufferList   
NdisAllocateFragmentNetBufferList   
NdisAllocateReassembledNetBufferList 

这些函数改善了系统性能,因为这些函数在派生的过程中并不复制网络数据。可以对已
经存在的 NET_BUFFER_LIST 做以下三种派生操作。 

克隆(Clone) 

克隆一个 NET_BUFFER_LIST 的副本(它还是引用原来的数据),可以用克隆的副本发送
到多个传输路径。 

分片(Fragrance) 
 
一个分片 NET_BUFFER_LIST 结构包含一个 NET_BUFFER 集(它还是引用原来的数据)。
但是,数据被划分成不超过最大大小的单元。驱动可以有效将大缓冲区分解在小的。 

重组(Reassembled) 

一个重组的 NET_BUFFER_LIST 结构包含一个 NET_BUFFER_LIST 结构(它是引用原来多个
NET_BUFFER 结构的原始数据)。驱动可以利用这个操作来合并多个小的 NET_BUFFER_LIST 成
一个大的 NET_BUFFER_LIST。 

4.1.7.1NET_BUFFER_LIST结构的派生关系

驱动的编写者应该知道和维护经过克隆,分片,重组后派的子结构与父结构之间的关系。
这些关系主要是为了保证在父结构被释放后才能释放子结构。它应用下列规则。 

1) 当一个驱动从 NET_BUFFER_LIST 创建了一个子结构后,驱动应该保留父结构的所有权,
传递时传递子结构给其它驱动。驱动决不能把父结构传递给其它驱动。 

2) 驱动应该仅更新父结构中的子结构数。因为父结构不传递给它其它驱动,所以子结构数
被覆盖的机会很小。驱动应该设置子结构中的父指针指向父结构。 

3) 当驱动从其它驱动收到一个 NET_BUFFER_LIST 时是不能更改它的父指示的。如果接收到
的是一个子结构那它的父指针是被设置过的。 

4) NDIS 不强制上述规则。当前 NET_BUFFER_LIST 的所有者必须管理维护子结构数和父指针。
例如,一个驱动对当前的 NET_BUFFER_LIST 进行了克隆和分片,就必须管理维护好相关
的父指针和子结构总数。 

5) NDIS 在分配一个 NET_BUFFER_LIST 时总是置它的父指针和子结构数据为 0。NDIS 在任何
时间向其它驱动传递这个结构时也不会修改它们。

4.1.7.2克隆

一个驱动从一个已经存在的 NET_BUFFER_LIST 克隆一个新的结构,这样新克隆的结构引
用的还是原始的数据。驱动可以利用这种方法在多个路径上有效的传输相同的数据。 

下面展示了父结构和克隆结构之间的关系。
名称:  28.png查看次数: 0文件大小:  18.6 KB

驱动调用 NdisAllocateCloneNetBufferList 函数来克隆一个已经存在的 NET_BUFFER_LIST,
它分新分配一个 NET_BUFFER_LIST 和 NET_BUFFER 及 MDL 同时引用原来的数据,这里并不复
制数据(注意:这里并没有复制网络数据,即 MDL 描述的数据只是被引用了)。但是它不会
克隆 NET_BUFFER_LIST 的 NET_BUFFER_LIST_CONTEXT 结构成员。要释放这个克隆的新结构调
用 NdisFreeCloneNetBufferList。

4.1.7.3分片

下面图展示了父结构与分片子结构之间的关系。
点击图片以查看大图图片名称:	29.png查看次数:	0文件大小:	46.7 KB文件 ID :	87018

驱 动 可 以 调 用 NdisAllocateFragmentNetBufferList 来 分 片 一 个 已 经 存 在 的
NET_BUFFER_LIST 结构。它新分配一个 NET_BUFFER_LIST 结构和多个 NET_BUFFER 及 MDL 结
构,不会分配 NET_BUFFER_LIST_CONTEXT 结构成员。其网络数据也不复制,只是引用原来
的。 

如果父结构有多个 NET_BUFFER 和单个的分片方法是一样的。例如,如果有一个父结构
有两个 NET_BUFFER,第一个 NET_BUFFER 分片后剩下的空间小于分片的长度,这时也不会
把它同下一个 NET_BUFFER 分并再分片,会像一个 NET_BUFFER 那做处理。 

驱动使用 NdisFreeFragmentNetBufferList 来释放分片生成的 NET_BUFFER_LIST 结构。 

4.1.7.4重组
下图展示了重结构包与父结构之间的关系。
点击图片以查看大图图片名称:	30.png查看次数:	0文件大小:	41.7 KB文件 ID :	87019

驱动可以调用 NdisReassemledNetBufferList 重组一个已经存在的 NET_BUFFER_LIST 结构, 
它 会 新 分 配 一 个 NET_BUFFER_LIST 和 NET_BUFFER 及 MDL , 但 是 不 分 配
NET_BUFFER_LIST_CONTEXT 成员结构。重组的结构只是引用原来的数据并不复制数据。 

驱动可以调用 NdisFreeReassemledNetBufferList 释放重组时生成的 NET_BUFFER_LIST,同
时释放与之前链的 NET_BUFFER 和 MDL 链。

4.2 指定NDIS版本信息 

大多数 NDIS 结构都有一个版本信息的头(Header)成员,NDIS 和 Ndis 驱动都要设置这
个成员结构。NDIS 驱动应该在访问结构的其它成员之前先检查这个结构。此外,NDIS 驱动
在初始化其间也应该要 NDIS 指示版本信息。

4.2.1 概览NDIS支持的版本

Header 成员是一个 NDIS_OBJECT_HEADER 结构,这个结构包含保留成员,类型和长度三
个成员。 

它的这些成员要符合下面的需求: 

1) 在一个结构中如果在新的 NDIS 版本中为其添加了新的成员那它的保留(revision)成员
会有一个新的值。例如,在 NDIS6.1 中在结构的末尾添加了一个联合体或一个字节域,
那个 Header 成员中的保留成员会有一个新的值。 

2) 一个结构被改变后,后面版本结构的长度可能会大于早期版本结构的长度,但是不能小
于早期版本的长度。后面版本新添加的成员要放到结构的末尾。 

3) 如果结构早期版本的信息仍然有效且完整结构仅新加一个新的版本号。在这种情况下应
该对老的版成员支持。注意:如果上述条件不满足 NDIS 会提供一个新命名的结构来替
代原来的结构。 

4) NDIS 驱动应该使用预定义的保留版本值。 

5) Header.Size 的值应该和 Header.Revision 的值保持一致。这就是说如果它们应该保持是同
下版本相对应的对值。

4.2.2 NDIS驱动需要支持的版本信息

NDIS 结构提供一了个 Header 成员来保存版本信息且 NDIS 驱动也需要对它进行支持。
NDIS 支持驱动运行在不同版本操作系统所支持的 NDIS 接口上。此处,高版本的 NDIS 驱动
可以在低版本的 NDIS 上注册。例如:一个 NDIS5.X 或 6.1 的驱动可以注册在只支持 NDIS6.0
版本的操作系统上。对于 NDIS6.1 的驱动则必须检查当前 NDIS 的版本同时注册为驱动后对
上级别的驱动做适当的支持。注意:驱动不需要对之前版本的所有结构特性进行支持。例如
一个驱动创建了 revision 2 版本的一个结构只需要适当的支持 revision1 版本的成员即可。 

要访问结构的成员信息必须完成下列处理: 

1) 在访问任何成员之前先检查 Header.revision 和 Header.size。 

2) 对于早期版的结构其 Header.revision 和 Header.size 必须匹配,同时必须根据这两个对应
的 NDIS 版对结构进行正确的访问。 

3) 对于一个之后版本的结构,可以使用这个结构里的信息。对于高版本的结构总是兼容低
版本的结构。 

4) 一个驱动使用结构的版本信息必须和其注册的版本信息相匹配。如一个驱动注册为 6.1
的 NDIS 驱动那么它也应该使用 NDIS6.1 的结构。 

5) NDIS 驱动成功的完成一个 OID 请求后必须设置 NDIS_OID_REQUEST 的 SupportedRevision
成员。这个值是要设置为请求发起者的 revision 版本。 

6) 要决定请求是不是被下层驱动完成了,要检查 OID 请求返回来的 SupportedRevision 值。 
 
4.2.3 NDIS需要的版本信息 

NDIS 通过头部标识的不同版信息来保证在不同的 NDIS 版上使驱动兼容。在支持头版本
信息上 NDIS 主要负责下面两点 

1) 被操纵结构的版本信息低于 NDIS 的版本;NDIS 检查头部信息并对基于相应的修订版对
结构进行解释。 

2) 如果一个驱动使用了错误的版本信息则使调用失败并返回适当的错误码;比如在在
NDIS6.1 上使用了 XXX_REVISION_2 这种版本信息 NDIS 就会使调用失败。 

4.2.4 获得NDIS版本 

NDIS 的版本在相同的计算机上也可能是不同的,例如我们可以通 RtlGetVersion 和
RtlVerifyVersionInfo 来获系统的版本信息,但我们不能依此来判断 NDIS 的版本信息。NDIS
驱动要获得 NDIS 的版本信息可以用 NdisGetVersion。 

4.2.5 NDIS  对象版本发布到WMI

这个主题是关于 NDIS 对像版本对 WMI 的支持。WMI 的管理对像格式化文件是没有版
本的,因此如果对 NDIS 结果有了新的修订,添加了多个域就要添加到管理对像格式化文件
中去。同时应用程序在查询和设置一个版本时也必须检查了提供相关的修订版本信息。 
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值