1.3 WinPcap驱动内部说明

WinPcap的结构如图2-4所示,NPF是WinPcap的组件,用来处理网络上传输的数据包,并对用户层导出数据包捕获、发送与分析的能力。下面将描述NPF与操作系统与其基础结构体的交互。
clip_p_w_picpath001
图2-4 NPF的结构

1.3.1 NPF与NDIS

网络驱动程序接口规范(NDIS)标准,定义了网络适配器(或者,更好的说法是管理网络适配器的驱动程序)与协议驱动程序(如TCP/IP就为一个实现实例)之间的通信。NDIS的主要目的是扮演一个包装层,允许协议驱动程序不依赖特定适配器或特定Win32操作系统来发送和接收网络(LAN或WAN)上的数据包。
NDIS支持三种类型的网络驱动程序:
1) 网络接口卡或NIC驱动程序
NIC驱动直接管理网络接口卡(network interface cards,NIC)。NIC驱动程序在其下面直接与硬件打交道,在其上面提供一个接口,允许高层(驱动)在网络上发送数据包,处理中断,复位网卡,停止NIC,请求与设置驱动的操作特性。NIC驱动即可为微端口( miniports),也可为遗留的全NIC驱动。
微端口驱动程序
微端口驱动程序仅实现特定的硬件操作来管理NIC,包括发送与接收NIC上的数据。对所有底层NIC驱动程序通用的操作,诸如同步管理,是由DICS提供的 。微端口不直接调用操作系统的例行程序;它们与操作系统的接口是NDIS.
微端口不保持绑定的跟踪。它仅把数据包向上传递给NIDIS,并由NDIS确定这些数据包传递给合适的协议。
NIC 驱动程序
全NIC驱动程序被编制为即执行特定硬件操作,也执行所有同步与排队这些通常由NDIS完成的操作。全NIC驱动程序,例如,需要维护它们自己的绑定信息以标识所接收的数据。
2) 中间层驱动程序
中间层驱动程序是一个上层驱动程序(诸如协议驱动程序)与微端口驱动程序之间的接口。对上层驱动程序而言,一个中间层驱动程序看起来像一个微端口驱动程序。对一个微端口驱动程序而言,一个中间层驱动程序看起来像一个协议驱动程序。一个中间层协议驱动程序可以位于另一个中间层驱动程序的顶部,尽管这样的分层对系统性能有负面影响。开发中间层驱动程序的典型原因,是在一个已有的遗留协议驱动程序与一个微端口驱动程序之间介质转换任务,而该微端口驱动程把NIC作为一种对协议驱动程序而言是未知的新介质类型来管理。例如,一个中间层驱动程序能够从LAN协议转换到ATM协议。一个中间层驱动程序不能与用户层的应用程序通信,只能与其它NDIS驱动程序进行通信。
3) 传输层驱动 程序或协议驱动程序.
一个协议驱动程序实现一个网络协议栈,诸如 IPX/SPX 或TCP/IP,它在一个或多个网络接口卡上提供服务。一个协议驱动程序对其上面应用程序层的客户提供服务,同时与一个或多个NIC驱动程序或其下面的中间层NDIS驱动程序连接。
NPF作为一个协议驱动程序实现。从性能的角度这可能并不是最好的选择,但是其允许与MAC层具有合理的独立,同时也能完全访问原始流量。
NPF在NDIS栈中的位置如图2-5所示。
clip_p_w_picpath002
图2-5 NDIS 中的NPF
正常情况下与操作系统的交互为异步。这意味着驱动提供了一个回调函数集,在一些操作需要NPF处理时被系统调用。NPF为应用程序的所有I/O操作:open、 close、 read、write、 ioctl等导出对应的回调函数。
同样与NDIS的交互也是异步的。如一个新数据包到来的事件就通过一个回调函数(此时为Packet_tap())通知NPF。此外,NDIS与NIC驱动程序的交互总是依靠非阻塞函数发生:当NPF调用一个NDIS函数,该调用立即返回;当处理结束后,NDIS调用一个特定的NPF回调函数(也叫完成函数)来通告该函数已经完成。驱动程序为任何底层的操作,如发送数据包,对NIC设置或请求参数等,导出一个回调函数。

1.3.2 NPF的主要功能

NPF能够执行许多操作:捕获、监视、转储到磁盘、数据包发送。下面将简短的描述这些操作。

1.3.2.1 数据包捕获

NPF最重要的操作是数据包的捕获。在一个捕获过程中,驱动程序利用一个网络接口嗅探数据包,并把它们完整无缺的递送给用户层应用程序。
捕获过程依赖两个主要组件:
1) 数据包过滤器
一个数据包过滤器决定一个到来的数据包是否需要被接收与复制到一个应用程序。大多数使用NPF的应用程序拒收比它所接收多得多的数据包,因此对于普遍较好的性能,一个通用与有效率的数据包过滤器是关键。
一个数据包过滤器是一个布尔值输出的函数,对一个数据包而言函数的值为true,捕获驱动器将复制数据包到应用程序,如果是false数据包将被丢弃。NPF数据包过滤器有一点复杂,因为它决定的不仅仅只是数据包是否应该保留,同时也包括应该保留的字节数。
NPF驱动器所采用过滤系统来自于BSD数据包过滤器(BPF),一个虚拟处理器能够执行过滤程序,该程序采用伪汇编程序表达并在用户层创建。应用程序采用一个用户定义的过滤器(例如,获得所有的UDP数据包),利用wpcap.dll,并编译它们到一个BPF程序(例如,如果数据包是IP,同时协议类型字段值为17,那么返回true)。接着,应用程序使用BIOCSETF的 IOCTL把过滤器传给内核。到此为止,该程序对进来的所有数据包执行过滤,并只接受遵循过滤条件的数据包。
不像传统的解决方案,NPF不解释过滤器,但是它执行它们。为了性能的原因,在使用过滤器之前,NPF把它给JIT编译器,该编译器把它转换成一个本地80X86的函数。 当一个数据包被捕获后,NPF调用该本地函数,而不是调用该过滤器的解释器,这使得处理过程很快。该优化的背后观念与JAVA jitters的一个优化非常相似。
2) 一个循环缓冲区来存储数据包并避免丢包
存储在缓冲区的一个数据包带有一个包头,该头维护如时间戳、包大小的信息。此外,为了提高应用程序访问数据的速度,数据包之间填充了一些填充数据。在单个读操作中,一组数据包能从NPF复制到应用程序。这提高了性能,因为最小化了读的次数。当一个新的数据包到来时,缓冲区已满,该包被丢弃,因此造成掉包。为了最大化多功能性(maximum versatility),内核与用户层的缓冲区在运行时能够被改变。packet.dll与wpcap.dll提供实现该功能的函数。
用户缓冲区的大小非常重要,因为它决定了在单个系统调用内,能够从内核空间复制到用户空间的最大数据量。另一方面,在单个调用中能够复制的最小数据量也非常重要的。该变量如果为一个巨大的数值,内核在把数据复制到用户层前需要等待几个数据包的到来。这保证了低数量的系统调用,也就是,低的处理器占用,这对诸如嗅探器的应用程序是一个很好的设置。在另一方面,一个小的数值意味着只要应用程序准备好接收数据包,内核就将复制数据包。这对于实时应用程序是极好的(如ARP重定向器(redirectors)或网桥)方案,这些程序需要内核做出更好的响应。从这些方面看来,NPF具有一个可配置的特性,允许用户在最好的效率与好的响应度(或任何中间情况)之间做出选择。
库wpcap.dll包括两个系统调用,能够用来设置读超时时间与内核能够传输到应用程序的数据的最小数量。缺省状态下,读超时时间为1秒,内核与应用程序之间数据复制的最小数量为16K。

1.3.2.2 数据包发送

NPF允许把原始数据包发送到网络上。为发送数据,一个用户层应用程序在NPF设备上执行一个WriteFile()系统调用。该数据被发送到网络,并不对数据作任何协议封装,因此应用程序将不得不构建每个数据包的不同协议头。应用程通常不需要生成FCS,它通常由网络适配器硬件计算,并在发送到网络前自动添加到一个数据包的尾部。
正常情况下,网络数据包的发送率并不是非常高,因为每个数据包都需要一个系统调用。因为该原因,WinPcap添加了使用一次写系统调用就能把单个数据包发送多次的功能。通过IOCTL调用(控制码为pBIOCSWRITEREP),用户层应用程序能够设置单个数据包发送的次数。例如,该值设为1000,应用程序所写的每个原始数据包在驱动设备文件上都将会发送1000次。能够用该特性可为测试的目的生成高速的网络流量:上下文切换的负载不再出现,因此性能显著性的变好。

1.3.2.3 网络监视

WinPcap提供一个内核层的可编程模块,能够对网络流量计算简单的统计信息。该模块背后的思想如图2-4所示:统计信息不需要复制数据包到应用程序就能被收集,从监视引擎获取统计结果简化了接收与显示。这允许避免捕获的大部分工作耗费在内存与CPU时钟上。
监视引擎由一个分类器与紧接分类器的一个计数器所组成。数据包使用NPF的过滤引擎被分类,这提供了一种配置方式来选择一个网络流量的子集。经过过滤器的数据到计数器,计数器保存一些变量,如数据包的数目与过滤器接收到字节的数量,并根据网络所接收的数据包的数据来更新这些变量。这些变量在固定时间间隔时传递给用户层应用程序,用户可以配置时间间隔的周期。该模式下在内核与用户空间中没分配缓冲区。

1.3.2.4 转储到磁盘

WinPcap提供转储到磁盘的能力,可以在内核模式下把网络数据直接存储到磁盘。
clip_p_w_picpath003
图2-6 数据包捕获与内核层转储
在传统的系统中,图2-6中黑色箭头所表示的就是把数据包存储到磁盘的流程。每个数据包被复制几次,正常情况下,分配4个缓冲区:一个是捕获驱动程序的,一个是应用程序保持所捕获的数据,一个是应用程序写文件所用的stdio函数(或类似)使用的,最后一个是文件系统的。
当NPF的内核层网络流量记录特性(traffic logging feature)激活时,捕获驱动程序直接访问文件系统,这样红色虚线箭头所表示的就是把数据包直接存储到磁盘的流程。只有两个缓冲区与一次复制是必须的,系统调用的数目彻底减少,因此性能显著的更好。
当前,转储到磁盘的功能采用广泛使用的libpcap格式。为了选择存入磁盘的数据包,也提供了在网络流量转储过程前就被过滤的特性。