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驱动使用的物理媒体类型。

//参见微软WDKNdisOpenAdapter()函数的文档了解详情。

    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;          //环形缓冲区中没被锁定的最小可读的数据数量,通过IOCTLBIOCSMINTOCOPY控制码设置

    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;    //如果正在写的过程中为TRUENPF当前允许在同一个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;   //如果转储文件的最大大小(MaxDumpBytesMaxDumpPacks)达到了,该值为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_CREATEIRP_MJ_CLOSEIRP_MJ_CLEANUPIRP_MJ_DEVICE_CONTROLIRP对应的处理函数。

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;

 

函数PacketOpenAdapterNPFCreateFileA系统调用导致NPF_Open函数调用。

lpAdapter->hFile=CreateFileA(SymbolicLinkA,

GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_EXISTING,0,0);

 

函数PacketCloseAdapterCloseHandle系统调用导致NPF_CleanupNPF_Close函数调用。

CloseHandle(lpAdapter->hFile);

 

诸如PacketSetBuff函数中DeviceIoControl的系统调用导致NPF_IoControl函数调用。

Result = (BOOLEAN)DeviceIoControl(AdapterObject->hFile,

BIOCSETBUFFERSIZE,&dim,sizeof(dim),

NULL,0,&BytesReturned,NULL);

 

本文出自 “千江月” 博客,请务必保留此出处http://eslxf.blog.51cto.com/918801/209247