1.6 NPF中对应的函数接口
1.6.1 关键结构体 _OPEN_INSTANCE
结构体 _OPEN_INSTANCE包含NPF驱动程序一个运行实例的状态;这是NPF最重要的结构体:它被几乎所有驱动程序的函数使用;一个_OPEN_INSTANCE结构体与每个用户层会话关联,并允许对该驱动程序的并发访问。
结构体的具体定义如下:
typedef
struct _OPEN_INSTANCE
{
PDEVICE_EXTENSION DeviceExtension; //
指向该实例所缚设备的_DEVICE_EXTENSION结构体的指针
NDIS_HANDLE AdapterHandle; //
该实例使用适配器的NDIS标识符
UINT Medium; //
底层NDIS驱动使用的物理媒体类型。
//
参见微软WDK的NdisOpenAdapter()函数的文档了解详情。
NDIS_HANDLE PacketPool; //NDIS_PACKET
结构体的存储池,
//
用来与NIC驱动传输数据包。
KSPIN_LOCK RequestSpinLock; //
用来同步OID请求的自旋锁
LIST_ENTRY RequestList; //
挂起的OID请求列表
LIST_ENTRY ResetIrpList; //
挂起的适配器复位请求列表
INTERNAL_REQUEST Requests[MAX_REQUESTS]; //
封装每单个OID请求的结构体数组
PMDL BufferMdl; //
指向一个内存描述列表(MDL)的指针,
//MDL
映射了环形缓冲区的内存
PKEVENT ReadEvent; //
指向事件的指针,在该事件上对该实例的读调用必须等待
PUCHAR bpfprogram; //
指向与当前实例相关的过滤伪代码的指针
//
该代码只能在特定情景中使用(例如,当从NIC驱动程序接收的数据包存储在两个非连续的缓冲区中。)在正常情况下,使用被JIT编译器创建并被下一个字段(Filter)所指向的 过滤例程。参见NPF了解过滤过程的细节。
#ifdef
_X86_
JIT_BPF_Filter *Filter; //
指向由jitter所创建的本机过滤函数的指针。参见BPF_jitter()函数了解细节。
#endif
//_X86_
UINT MinToCopy; //
环形缓冲区中没被锁定的最小可读的数据数量,通过IOCTL的BIOCSMINTOCOPY控制码设置
LARGE_INTEGER TimeOut; //
读操作的超时值,即使缓冲区的数据数量低于MinToCopy(通过IOCTL控制码 BIOCSRTIMEOUT设置),读操作也返回。
int
mode; //
驱动程序的工作模式。参见PacketSetMode()函数了解细节
LARGE_INTEGER Nbytes; //
当该实例在统计模式下时,记录被过滤器接收的字节数量。
LARGE_INTEGER Npackets; //
当该实例在统计模式下时,记录被过滤器接收的数据包数量。
NDIS_SPIN_LOCK CountersLock; //
保护统计模式计数器的自旋锁
UINT Nwrites; //
一个数据包需要被重复发送的次数。参见NPF了解细节
ULONG Multiple_Write_Counter; //
对一个单个写操作已做物理重复的次数进行计数
NDIS_EVENT WriteEvent; //
同步多个写进程的事件
BOOLEAN WriteInProgress; //
如果正在写的过程中为TRUE。NPF当前允许在同一个open实例上执行一个单独的写操作
NDIS_SPIN_LOCK WriteLock; //
保护WriteInProgress 变量的自旋锁
NDIS_EVENT NdisRequestEvent; //
用来同步NDIS的回调结构体与I/O请求的事件
BOOLEAN SkipSentPackets; //
如果该实例不应该捕获它自己所传输的数据包,则为True
NDIS_STATUS IOStatus; //
维护OID请求调用的状态 ,这将传递给应用程序
HANDLE DumpFileHandle; //
在转储模式下使用的文件句柄
PFILE_OBJECT DumpFileObject; //
指向转储模式下使用的文件对象的指针
PKTHREAD DumpThreadObject; //
指向转储模式下使用的线程对象的指针
HANDLE DumpThreadHandle; //
转储模式下创建的线程句柄,为了异步地把数据从缓冲区搬移到磁盘
NDIS_EVENT DumpEvent; //
转储模式下,用来同步转储线程与探针(tap)读取的事件
LARGE_INTEGER DumpOffset; //
转储文件当前的偏移
UNICODE_STRING DumpFileName; //
转储文件的文件名
UINT MaxDumpBytes; //
转储文件的能存储的最大字节数。如果转储文件达到该大小限定,它将被关闭 。0值意味着无大小限制。
UINT MaxDumpPacks; //
所能存储到转储文件中最大的数据包包数,如果转储文件达到该限定,它将被关闭 。0值意味着无此限制。
BOOLEAN DumpLimitReached; //
如果转储文件的最大大小(MaxDumpBytes或MaxDumpPacks)达到了,该值为TRUE。
#ifdef
HAVE_BUGGY_TME_SUPPORT
MEM_TYPE mem_ex; //
被TME虚拟协处理器使用的内存
TME_CORE tme; //
包含TME协处理器虚拟机的数据结构体
#endif
//HAVE_BUGGY_TME_SUPPORT
NDIS_SPIN_LOCK MachineLock;//
保护BPF过滤器与TME引擎的自旋锁,如果在使用中
UINT MaxFrameSize; //
底层MAC所能接收的最大帧大小。用来检测通过NPF_Write()或NPF_BufferedWrite()发送的帧大小。
// KAFFINITY
被用来作为一个位掩码,是为了系统的亲缘性。因此在每个被支持的操作系统上对所有系统上的CPU都是足够大的(x86上为32位,x64上为64位?).
//
我们使用它的大小计算CPU的最大数目。
CpuPrivateData CpuData[sizeof(KAFFINITY) * 8]; //
内核缓冲区结构体的内存池,每个CPU一个
ULONG ReaderSN; //
下一个将被从内核缓冲池中读取的数据包的序号
ULONG WriterSN; //
下一个将被写入内核缓冲池中的数据包的序号。
//
这两个序号对每个捕获实例都是唯一的。
ULONG Size; //
包含在CpuData字段中的每个内核缓冲区的大小
ULONG AdapterHandleUsageCounter;
NDIS_SPIN_LOCK AdapterHandleLock;
ULONG AdapterBindingStatus; ////
描述NPF是否仍然被该实例使用的适配器所束缚,它是未绑定的或它是未束缚的。
NDIS_EVENT NdisOpenCloseCompleteEvent;
NDIS_EVENT NdisWriteCompleteEvent; //
事件,当所有数据包被NdisSend成功发送后产生该事件通知(并且,对应的sendComplete函数被调用)
NTSTATUS OpenCloseStatus;
ULONG TransmitPendingPackets; //
说明正被挂起的待传输数据包的数目,比如,已被提交给NdisSendXXX,但是SendComplete仍没被调用。
}OPEN_INSTANCE, *POPEN_INSTANCE;
其中Requests[MAX_REQUESTS]的类型为INTERNAL_REQUEST结构体,该结构体存储一个OID请求。驱动程序使用该结构体在底层NIC驱动程序上执行OID请求或者设置操作。通常仅网络驱动程序执行OID操作,但NPF通过一个IOCTL接口导出该机制给用户层应用程序。驱动程序使用该结构体来封装一个NDIS_REQUEST结构体。
结构体的具体定义如下:
typedef
struct _INTERNAL_REQUEST {
LIST_ENTRY ListElement; //
用来操控请求链表的句柄
NDIS_EVENT InternalRequestCompletedEvent;//
请求完成事件
NDIS_REQUEST Request; //
带有实际请求的结构体,将由NdisRequest调用传递。
NDIS_STATUS RequestStatus;
} INTERNAL_REQUEST, *PINTERNAL_REQUEST;
NPF
有四种工作模式,分别定义如下:
#define MODE_CAPT 0x0
//
捕获工作模式
#define MODE_STAT 0x1
//
统计工作模式
#define MODE_MON 0x2
//
内核监视模式
#define MODE_DUMP 0x10
//
内核转储工作模式
1.6.2 IRP的说明
NPF
驱动程序入口DriverEntry中的下列代码指定了IRP_MJ_CREATE、IRP_MJ_CLOSE、IRP_MJ_CLEANUP、IRP_MJ_DEVICE_CONTROL的IRP对应的处理函数。
DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = NPF_Close;
DriverObject->MajorFunction[IRP_MJ_CLEANUP]= NPF_Cleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NPF_IoControl;
函数PacketOpenAdapterNPF的CreateFileA系统调用导致NPF_Open函数调用。
lpAdapter->hFile=CreateFileA(SymbolicLinkA,
GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_EXISTING,0,0);
函数
PacketCloseAdapter
的
CloseHandle
系统调用导致NPF_Cleanup与NPF_Close函数调用。
CloseHandle(lpAdapter->hFile);
诸如PacketSetBuff函数中DeviceIoControl的系统调用导致NPF_IoControl函数调用。
Result = (BOOLEAN)DeviceIoControl(AdapterObject->hFile,
BIOCSETBUFFERSIZE,&dim,sizeof(dim),
NULL,0,&BytesReturned,NULL);