本文转自http://eslxf.blog.51cto.com/918801/278880
数据包过滤过程
数据包到达网络接口时,链路层的设备驱动程序通常是将数据包直接传送给协议栈进行处理。当
NPF
以一个协议驱动程序注册后,也类似于一个协议栈。
链路层驱动程序在将数据包传送给协议栈时也会传递给NPF。
NPF
负责将此数据包指针传递至内核过滤器(可能有多个),过滤器决定数据包是否被接收以及数据包中的哪些内容将被接收。对于每一个决定接受数据包的过滤器,
NPF
才执行数据复制工作,将所需的数据复制到与过滤器相关的缓存中,以供用户进程读取。
NPF
独立
于诸如TCP-IP协议的主机协议来捕获网络数据包。这意味着WinPcap不能阻塞、过滤或操纵同一机器上其它应用程序的通信,它只是简单地"嗅探"网络上所传输的数据包。
注意到NPF是先对数据包进行过滤再缓冲,避免先缓冲每个数据包直到用户读数据时再进行过滤所造成的效率问题。
过滤表达式
Winpcap
的过滤表达式
是一个ASCI字符串形式的高层布尔表达式(与
libpcap
、Tcpdump和 WinDump的相同
)
,
原语(primitive)是其基本组成单元。一个表达式由一个或多个原语组成,原语之间通过and、or和not连接。基本语法结构是:
[not]
原语[and|or[not] 原语...]
包过滤机制实际上是针对数据包的布尔值操作函数,如果函数最终返回
true
,则通过过滤,反之则被丢弃。形式上包过滤由一个或多个谓词判断的并操作(
AND
)和或操作(
OR
)构成。每一个谓词判断基本上对应数据包的协议类型或某个特定值,例如,只需要
TCP
类型且端口为
110
的数据包或
ARP
类型的数据包。
过滤过程从一个用户所定义的过滤表达式(比如,捕获起所有UDP数据包)开始,由wpcap.dll中的pcap_compile函数把过滤表达式编译成虚拟机指令(比如,如果数据包是IP协议的,并且协议类型字段为17,那么返回true),编译后的指令由wpcap.dll中的pcap_setfilte函数将其应用到某个已打开的网络捕获设备(网卡)上。其转换过程如图10-6所示。
![](https://i-blog.csdnimg.cn/blog_migrate/833a9655ce08b73f43e8643f93bde0c3.jpeg)
图10-6 过滤表达式转换为底层代码
内核模块必须能够执行这些指令,因此对所有进入的数据包它必须有一个类似的“
BPF
虚拟机”来执行指令。该内核中与
BPF
兼容的过滤机
(
“
NPF
虚拟机”
)
是获得高性能的一个关键点。
过滤时,NPF会检查每个网络数据包首部相应字段的值,检查范围包括数据链路层首部(如以太帧的源/目的MAC地址和Type字段)、网络层首部(如源/目的IP地址和协议字段等)、传输层首部(如源/目的端口号字段等),所有的符合要求的数据包(即与过滤条件比较后,布尔表达式为真的包)将会被WinPcap继续传递。
过滤代码的编译
WinPcap
使用了gencode.c、optimize.c、grammar.c、scanner.c源文件与tokdefs.h头文件完成编译操作。
其中scanner.c、grammar.c与 tokdefs.h文件由flex 和 bison 生成。
在WinPcap中
wpcap/libpcap/
目录下的
scanner
.l
与grammar.y就分别对应于flex与bison的源程序。通过下列指令生成对应的
scanner
.c
、grammar.c与tokdefs.h文件。
flex -Ppcap_ -t scanner.l > $$.scanner.c; mv $$.scanner.c scanner.c
bison -y -p pcap_ -d grammar.y
mv y.tab.c grammar.c
mv y.tab.h tokdefs.h
应用程序
编译过滤表达式调用getcode.c文件中的pcap_compile()函数,通过该函数生成所需的字节码。
当使用完该过滤程器后,需要调用配对的pcap_freecode函数,来释放存储字节代码的结构体bpf_program所用的内存空间。
过滤代码的安装
应用程序调用pcap_setfilter()函数将一个过滤器与一个捕获实例关联,把过滤器的字节代码传递给内核的NPF驱动程序中,当进行数据捕获时,驱动程序执行该过滤器对来自网络的所有数据包进行过滤,所有符合要求的数据包,将会复制给应用程序,否则丢弃该数据包。详细的处理过程践后续章节的分析。