4. 抓包过滤器精讲:BPF语法入门、协议过滤、端口过滤、主机过滤、复合条件过滤
过滤器这东西,说白了就是帮你从海量数据包里「捞针」的工具。我刚入行那会儿,抓个包动不动就几十万条,眼睛都看花了。后来才明白——不会用过滤器,等于白抓。
这一章,咱们就把 BPF 语法掰开揉碎。你学完就能写出自己的过滤规则,指哪打哪。
核心认知:BPF(Berkeley Packet Filter)是 tcpdump、Wireshark 底层通用的过滤语言。学会它,你在任何抓包工具里都能用同一套逻辑。
4.1 BPF 语法基础:三要素
BPF 表达式由三个核心部分组成:限定词(Qualifiers)。记住这三类,你就掌握了骨架。
| 限定词类型 | 关键词 | 作用 |
|---|---|---|
| 类型(Type) | host, net, port, portrange | 指定过滤的对象是什么(主机、网段、端口) |
| 方向(Dir) | src, dst, src or dst | 指定数据流的方向(源、目标、双向) |
| 协议(Proto) | tcp, udp, icmp, arp, ip, ip6 | 指定网络协议类型 |
举个例子:tcp src port 80 就是「只看源端口是 80 的 TCP 包」。从左到右依次是:协议 → 方向 → 类型 + 值。这个顺序别搞反了。
我的习惯:写复杂过滤器时,我会先在纸上写出「协议 + 方向 + 类型 + 值」的模板,再往里填参数。这样不容易漏掉限定词。
4.2 协议过滤:tcp / udp / icmp
协议过滤是最常用的。你想想看,排查 HTTP 问题肯定只看 TCP,测 DNS 就得看 UDP,查网络连通性就得用 ICMP。
4.2.1 TCP 过滤
# 只看 TCP 流量
tcp
# 只看 TCP 且带有 SYN 标志的包(三次握手第一步)
tcp[tcpflags] & tcp-syn != 0
# 只看 TCP RST 包(连接被重置)
tcp[tcpflags] & tcp-rst != 0
我在项目中遇到过一个问题:服务器偶尔返回 RST,客户端直接报错。我用了上面第二条规则,只抓 RST 包,很快就定位到是后端连接池超时配置有问题。
4.2.2 UDP 过滤
# 只看 UDP 流量
udp
# 只看 UDP 且长度大于 100 字节的包(过滤掉小包确认)
udp and len > 100
UDP 过滤有个坑——它没有像 TCP 那样的标志位。所以如果你想过滤特定行为,通常得结合端口或应用层特征。
4.2.3 ICMP 过滤
# 只看 ICMP 流量
icmp
# 只看 ICMP Echo Request(ping 请求)
icmp[icmptype] = 8
# 只看 ICMP Echo Reply(ping 响应)
icmp[icmptype] = 0
我曾经踩过的坑:在 Wireshark 里写 icmp 没问题,但在 tcpdump 里有些老版本需要写成 icmp[0] = 8 而不是 icmp[icmptype]。建议你在新环境里先跑一次 tcpdump -d 看看语法是否被正确解析。
4.3 端口过滤:精准定位服务
端口过滤是排查应用层问题的利器。我个人的工作流是:先确定服务端口,再过滤对应流量。
# 只看 80 端口(双向)
port 80
# 只看源端口是 443
src port 443
# 只看目标端口是 53(DNS 查询)
dst port 53
# 端口范围:从 8000 到 8999
portrange 8000-8999
你可能会问:为什么有时候 port 80 抓到的包比预期多?因为 port 是双向的,既包括源端口 80,也包括目标端口 80。如果你只想看服务端发出去的包,用 src port 80。
实战技巧:排查 Web 服务慢的问题时,我习惯用 tcp port 80 and host 10.0.0.1。这样既过滤了端口,又限定了具体的主机,数据量一下就降下来了。
4.4 主机过滤:锁定通信双方
主机过滤用来限定 IP 地址。不管是 IPv4 还是 IPv6,语法都一样。
# 只看与 192.168.1.1 的通信(双向)
host 192.168.1.1
# 只看从 10.0.0.1 发出的包
src host 10.0.0.1
# 只看发往 172.16.0.1 的包
dst host 172.16.0.1
# 网段过滤:只看 192.168.1.0/24 网段
net 192.168.1.0/24
# IPv6 也一样
host 2001:db8::1
嗯,这里要注意:host 默认是双向的。如果你只想看某个方向,一定要加上 src 或 dst。我在一次排障中,就是因为没加方向限定,抓到了大量回程的广播包,干扰了判断。
4.5 复合条件过滤:逻辑运算符
单个条件往往不够用。BPF 支持三种逻辑运算:and(&&)、or(||)、not(!)。
# 与运算:来自 10.0.0.1 的 TCP 80 端口流量
host 10.0.0.1 and tcp port 80
# 或运算:DNS 查询或 HTTP 请求
udp port 53 or tcp port 80
# 非运算:排除 DHCP 流量
not (udp port 67 or udp port 68)
# 复合:来自内网且不是 SSH 的 TCP 流量
src net 192.168.0.0/16 and tcp and not port 22
优先级陷阱:BPF 中 and 优先级高于 or。所以 tcp port 80 or udp port 53 and host 10.0.0.1 会被解析为 tcp port 80 or (udp port 53 and host 10.0.0.1)。如果你想让两个条件都受主机限制,必须加括号:(tcp port 80 or udp port 53) and host 10.0.0.1。
4.6 知识体系总览
下面这张图帮你把本章的知识点串起来。你可以把它当成一个「过滤器设计地图」。
4.7 实战:从零写一个复合过滤器
咱们来一个真实场景。假设你要排查一个 Web 应用的问题:
- 服务器 IP:10.0.0.100
- Web 端口:8080
- 数据库端口:3306
- 你只想看来自内网客户端的请求,排除监控系统(IP:10.0.0.200)
一步步来:
- 先限定主机:
host 10.0.0.100 - 再限定端口:
port 8080 or port 3306 - 加上方向:
dst host 10.0.0.100(只看发往服务器的包) - 排除监控:
not src host 10.0.0.200 - 组合起来:
dst host 10.0.0.100 and (port 8080 or port 3306) and not src host 10.0.0.200
调试小技巧:在 tcpdump 里加一个 -d 参数,它会打印出 BPF 编译后的伪汇编代码。如果你看到 (000) ldh ... 这样的输出,说明语法正确。如果报错,就是某个限定词写错了。
4.8 常见错误与避坑
| 错误写法 | 问题 | 正确写法 |
|---|---|---|
tcp and port 80 and 443 |
缺少逻辑运算符连接两个端口 | tcp and (port 80 or port 443) |
host 10.0.0.1 and 10.0.0.2 |
不能同时写两个 host | host 10.0.0.1 or host 10.0.0.2 |
not host 10.0.0.1 and port 80 |
优先级导致先执行 and | not (host 10.0.0.1 and port 80) |
src or dst port 80 |
方向不能和类型写在一起 | src port 80 or dst port 80 |
我曾经犯过的错:有一次我在生产环境抓包,写了个 not port 22 想排除 SSH 流量。结果发现抓到的包还是很多——因为我忘了排除监控系统的 SNMP 端口(161/162)。所以,排除条件一定要列全,否则数据量还是很大。
4.9 本章小结
过滤器是抓包分析的「第一道筛子」。你掌握了 BPF 的三要素(类型、方向、协议),学会了逻辑运算,就能写出精准的过滤规则。记住:
- 先想清楚你要看什么(协议?主机?端口?)
- 再决定方向(源?目标?双向?)
- 最后用逻辑运算符组合
- 拿不准的时候,加括号