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 知识体系总览

下面这张图帮你把本章的知识点串起来。你可以把它当成一个「过滤器设计地图」。

BPF 过滤器知识体系 BPF 过滤器知识体系 BPF 表达式 限定词(Qualifiers) 逻辑运算符 原语(Primitives) 类型(Type) 方向(Dir) 协议(Proto) and(&&) or(||) not(!) host/net port tcp/udp/icmp 💡 组合公式:协议 + 方向 + 类型 + 值 + 逻辑运算符 + 协议 + 方向 + 类型 + 值 示例:tcp and src host 10.0.0.1 and dst port 80

4.7 实战:从零写一个复合过滤器

咱们来一个真实场景。假设你要排查一个 Web 应用的问题:

  • 服务器 IP:10.0.0.100
  • Web 端口:8080
  • 数据库端口:3306
  • 你只想看来自内网客户端的请求,排除监控系统(IP:10.0.0.200)

一步步来:

  1. 先限定主机:host 10.0.0.100
  2. 再限定端口:port 8080 or port 3306
  3. 加上方向:dst host 10.0.0.100(只看发往服务器的包)
  4. 排除监控:not src host 10.0.0.200
  5. 组合起来:
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 的三要素(类型、方向、协议),学会了逻辑运算,就能写出精准的过滤规则。记住:

  • 先想清楚你要看什么(协议?主机?端口?)
  • 再决定方向(源?目标?双向?)
  • 最后用逻辑运算符组合
  • 拿不准的时候,加括号