捕获过滤器实战:BPF语法详解、常见过滤场景与调试技巧
很多朋友刚开始用Wireshark时,习惯把所有流量都抓下来,然后再用显示过滤器慢慢筛。说实话,在高流量环境下这么干,机器很容易卡死,甚至直接丢包。我早年在一个千兆出口做故障排查,就因为没设捕获过滤器,结果抓了3分钟,Wireshark直接崩了——从那以后,我养成了“先过滤、后抓包”的习惯。
这一章,我们就来啃下捕获过滤器的核心——BPF语法。你把它学会了,抓包效率至少提升一倍。
1. BPF语法:捕获过滤器的灵魂
BPF的全称是Berkeley Packet Filter,伯克利包过滤器。它最早是BSD系统上的东西,后来成了几乎所有抓包工具的标准底层引擎。Wireshark的捕获过滤器,底层就是靠BPF表达式工作的。
BPF语法本质上是一种“布尔表达式”。你告诉内核:哪些包我要,哪些包我不要。匹配的包才会从内核拷贝到用户空间。说白了,它是在源头做减法。
核心原则:捕获过滤器工作在数据链路层之后、协议栈处理之前。它只能做“是否保留”的判断,不能修改包内容,也不能做复杂的协议字段匹配(那是显示过滤器的事)。
1.1 基本元素:原语与运算符
一个BPF表达式由若干原语(primitive)组成,每个原语通常包含一个限定符(qualifier)和一个标识符(identifier)。限定符又分三种:
| 限定符类型 | 关键字 | 说明 |
|---|---|---|
| 类型 | host / net / port / portrange | 指定要匹配的对象类型(主机、网段、端口、端口范围) |
| 方向 | src / dst / src or dst / src and dst | 指定流量方向(源、目的、双向) |
| 协议 | ether / ip / ip6 / arp / rarp / tcp / udp / icmp | 指定链路层或网络层/传输层协议 |
举个例子:
host 192.168.1.1
这个表达式省略了方向和协议限定符,默认等价于 ip host 192.168.1.1,也就是匹配所有进出该IP的IP包。
如果你想更精确:
src host 192.168.1.1 and tcp dst port 80
意思是:只抓源IP是192.168.1.1、且目的端口是80的TCP包。
1.2 逻辑运算符与组合
BPF支持标准的逻辑运算:and(&&)、or(||)、not(!)。你可以用括号改变优先级。
我个人习惯在复杂表达式里显式加括号,避免歧义:
(src net 10.0.0.0/8 or src net 172.16.0.0/12) and not port 53
这个表达式抓取来自两个私有网段、且排除DNS(53端口)的所有流量。我在一次内网渗透排查中用过类似的写法,效果很好。
小技巧:Wireshark的捕获过滤器输入框有语法高亮。如果输入完发现整行变红,说明表达式有语法错误。这时候别慌,检查括号是否匹配、关键字是否拼错。
2. 常见过滤场景:从入门到实战
下面我列几个高频场景,都是我在项目里反复用到的。你直接拿去用,改改IP和端口就行。
2.1 按IP地址过滤
抓取与特定主机的所有通信:
host 10.10.10.5
只抓某个方向:
src host 10.10.10.5
dst host 10.10.10.5
抓取一个网段:
net 192.168.1.0/24
2.2 按MAC地址过滤
这个场景在排查二层环路或ARP攻击时特别有用。我曾经遇到一个案例,某台机器的网卡疯狂发送重复MAC帧,用以下过滤器直接锁定了问题设备:
ether host 00:1a:2b:3c:4d:5e
也可以只抓源MAC或目的MAC:
ether src 00:1a:2b:3c:4d:5e
ether dst 00:1a:2b:3c:4d:5e
注意:MAC地址过滤只对以太网帧有效。如果你的网络环境是PPPoe或802.11无线,ether关键字可能不适用,需要改用对应的链路层类型。
2.3 按端口过滤
抓取HTTP流量:
tcp port 80
抓取多个端口(比如HTTP和HTTPS):
tcp port 80 or tcp port 443
抓取端口范围(比如常见服务端口1-1024):
tcp portrange 1-1024
嗯,这里要注意:portrange是BPF的扩展语法,老版本libpcap可能不支持。如果你在旧系统上遇到报错,可以拆成多个port用or连接。
2.4 按协议类型过滤
只抓ARP包(排查IP冲突时必备):
arp
只抓ICMP包(测试连通性时用):
icmp
只抓UDP广播包:
udp and dst host 255.255.255.255
2.5 组合场景:精准定位问题
有一次,客户反馈数据库响应慢。我怀疑是某台应用服务器发起了大量非正常查询。我用了这个过滤器:
tcp and src host 10.0.1.100 and dst port 3306
只抓那台应用服务器发往MySQL端口(3306)的TCP包。抓了30秒,就发现每秒有上千个短连接——原来是连接池配置出了问题。
实战建议:捕获过滤器越精确越好。你每多一个条件,内核需要过滤的包就少一批,抓包工具的CPU和内存压力就越小。在高吞吐环境(比如10Gbps链路),一个粗糙的过滤器可能导致丢包率超过50%。
3. 过滤器调试技巧:别让表达式坑了你
BPF表达式写错了,要么抓不到想要的包,要么抓了一堆垃圾。我总结了几条调试经验,帮你快速定位问题。
3.1 用tcpdump测试表达式
Wireshark的捕获过滤器输入框虽然能提示语法错误,但它不会告诉你“逻辑对不对”。我的习惯是先在命令行用tcpdump测试:
tcpdump -i eth0 -c 10 'tcp and src host 10.0.1.100 and dst port 3306'
-c 10表示抓10个包就退出。如果tcpdump能正常输出,说明表达式语法和逻辑都没问题。如果tcpdump报错,它会给出具体的错误位置——比Wireshark的提示更详细。
3.2 注意关键字冲突
BPF有一些保留关键字,比如host、port、net、mask。如果你把主机名或变量命名为这些词,表达式会解析失败。我曾经见过有人写:
host host
这显然是个错误。正确的写法是:
host 192.168.1.1
3.3 区分捕获过滤器和显示过滤器
这是新手最容易搞混的地方。捕获过滤器用BPF语法,显示过滤器用Wireshark自己的语法(基于字段名)。
举个例子:
- 捕获过滤器:
tcp port 80—— 只抓80端口的TCP包 - 显示过滤器:
tcp.port == 80—— 从已抓到的包中筛选出80端口的包
如果你把显示过滤器的写法填到捕获过滤器输入框里,Wireshark会直接报错。反过来,把BPF语法填到显示过滤器里,也不会生效。
我的习惯:捕获过滤器只做“粗筛”,比如按IP、端口、协议过滤。显示过滤器做“精筛”,比如按TCP标志位、HTTP方法、响应码过滤。两者配合,效率最高。
3.4 使用Wireshark的“编译BPF”功能
Wireshark在捕获选项里有一个“编译BPF”按钮(在输入框旁边)。点击后,它会显示BPF表达式被编译成的内核指令码。虽然一般人看不懂这些指令,但你可以通过它确认表达式是否被正确解析。如果编译后的指令条数异常多(比如超过100条),说明你的表达式可能过于复杂,建议简化。
4. BPF语法知识图谱
下面这张图总结了BPF语法的核心结构,以及从表达式到内核过滤的完整流程。你可以把它当作速查表。
5. 避坑指南:我踩过的那些坑
最后分享几个真实教训,你遇到了能少走弯路。
- 坑一:忘记指定协议。 写
host 10.0.0.1默认匹配IP包,但如果链路上有非IP流量(比如ARP、STP),这些包会被放过。如果你只想抓IP包,记得加ip前缀:ip host 10.0.0.1。 - 坑二:端口过滤不生效。 我曾经写
port 80,以为只抓TCP 80,结果UDP 80的包也进来了。因为port默认匹配所有传输层协议。正确写法是tcp port 80。 - 坑三:在无线网卡上使用MAC过滤。 无线网卡的MAC地址是BSSID,不是终端的真实MAC。如果你在Wi-Fi环境下用
ether host过滤终端MAC,大概率抓不到包。需要用wlan host或wlan addr。 - 坑四:表达式太长导致截断。 BPF表达式在内核中有长度限制(通常是4096字节)。如果你写了一个超长的表达式,libpcap会静默截断,导致过滤结果不完整。我建议复杂场景拆成多个捕获会话,或者改用显示过滤器做二次筛选。
重要提醒:捕获过滤器一旦设置,抓包过程中不能修改。你必须停止抓包、修改过滤器、重新开始。所以在生产环境抓包前,先用tcpdump测试一下表达式,确认无误再上Wireshark。