4. 捕获过滤器实战:基于IP、端口、协议的BPF语法精讲与实战演练

抓包排错,第一步就是「抓对包」。

很多人打开Wireshark直接点开始,结果几秒钟就被海量数据淹没了。你想想看,在千兆网络里,哪怕只是闲聊流量,每秒也能产生成千上万个包。手动找问题?那真是大海捞针。

所以,捕获过滤器必须得会。它不是在抓完包之后过滤,而是在网卡驱动层面就把不想要的包扔掉。说白了,它让你的抓包工具只关注你关心的流量,节省性能,也节省你的眼睛。

4.1 BPF语法:捕获过滤器的灵魂

捕获过滤器的底层语言叫BPF(Berkeley Packet Filter)。别被这个名字吓到,它其实是一套非常简洁的规则表达式。Wireshark、tcpdump、tshark都共用这套语法。

我个人习惯把BPF语法拆成三要素:类型、方向、协议。你只要掌握了这三样,就能组合出90%以上的过滤规则。

要素 关键字 说明
类型 host, net, port, portrange 指定过滤的对象是IP地址、网段还是端口
方向 src, dst, src or dst 指定数据包的来源或去向
协议 ether, ip, ip6, arp, tcp, udp, icmp 指定链路层、网络层或传输层的协议

核心公式: [协议] [方向] [类型] [值]

例如:tcp src host 192.168.1.1 表示「只抓取源IP为192.168.1.1的TCP包」。

4.2 基于IP地址的过滤

这是最常用的场景。你想看某个服务器或者某个客户端的通信,直接写IP就行。

# 抓取所有与192.168.1.100相关的流量(无论源还是目标)
host 192.168.1.100

# 只抓取源IP为10.0.0.1的包
src host 10.0.0.1

# 只抓取目标IP为172.16.0.1的包
dst host 172.16.0.1

# 抓取整个网段(192.168.1.0/24)
net 192.168.1.0/24

# 抓取非某个IP的流量(排除法)
not host 192.168.1.1

我的经验: 有一次排查DNS解析慢的问题,我直接用了 host 8.8.8.8,结果发现流量根本没走到公网DNS,而是被内网劫持了。如果当时不加这个过滤器,我可能还在看一堆无关的广播包。

4.3 基于端口和协议的过滤

很多时候,你知道问题出在哪个应用层协议上。比如HTTP走80端口,HTTPS走443,MySQL走3306。这时候直接过滤端口,效率最高。

# 抓取所有80端口的流量(HTTP)
port 80

# 抓取源端口为443的流量
src port 443

# 抓取目标端口为53的流量(DNS)
dst port 53

# 组合协议和端口:只抓TCP 80端口
tcp port 80

# 抓取UDP 53端口(DNS查询通常走UDP)
udp port 53

嗯,这里要注意一个坑。很多人写 port 80 以为只抓HTTP,其实它抓的是所有协议在80端口的包。如果某个应用用了TCP 80端口但不是HTTP,它也会被抓到。所以,我建议你养成习惯,加上协议限定:tcp port 80。

我曾经踩过的坑: 有一次排查数据库连接超时,我写了 port 3306,结果抓了半天没抓到任何包。后来才发现,数据库监听的是3307端口,而应用配置写错了。所以,抓包前一定要确认端口号,别想当然。

4.4 逻辑运算符:组合你的过滤条件

单个条件往往不够用。比如你想同时看Web服务器和数据库服务器的流量,或者想看除了某个IP之外的所有流量。这时候就需要逻辑运算符:and、or、not。

运算符 示例 含义
and (&&) tcp and port 80 同时满足两个条件
or (||) port 80 or port 443 满足任意一个条件
not (!) not arp 排除满足条件的包
# 抓取192.168.1.1的HTTP流量
host 192.168.1.1 and tcp port 80

# 抓取DNS或NTP流量(UDP 53或123)
udp port 53 or udp port 123

# 抓取除了广播包之外的所有流量
not broadcast

# 抓取来自10.0.0.0/8网段且不是SSH的流量
src net 10.0.0.0/8 and not tcp port 22

实战技巧: 在Wireshark的捕获过滤器输入框里,你不需要写引号。但在tcpdump命令行里,如果条件里有空格或特殊字符,记得用单引号包起来。例如:tcpdump -i eth0 'tcp port 80 and not host 192.168.1.1'

4.5 高级BPF:直接操作数据包偏移量

如果你觉得上面的语法还不够灵活,BPF允许你直接读取数据包中某个字节的值。这听起来有点底层,但在某些特殊场景下非常有用。

比如,你想抓取TCP标志位中SYN=1且ACK=0的包(也就是TCP握手的第一个包)。用普通语法很难精确表达,但用偏移量就可以:

# TCP头部第13个字节的第1位是SYN,第4位是ACK
# 抓取SYN=1且ACK=0的包
tcp[13] & 0x02 != 0 and tcp[13] & 0x10 == 0

为什么会用到这个?我记得有一次排查SYN Flood攻击,我需要快速确认攻击包的标志位特征。用这个过滤器,直接就把正常的握手包和攻击包区分开了。

小抄: 常用的偏移量过滤:

  • icmp[0] == 8 抓取ICMP Echo Request (ping请求)
  • icmp[0] == 0 抓取ICMP Echo Reply (ping响应)
  • tcp[13] & 0x02 != 0 抓取SYN包
  • tcp[13] & 0x10 != 0 抓取ACK包

4.6 实战演练:一个完整的排错场景

假设你接到一个工单:用户反馈访问公司官网(假设IP是203.0.113.10)非常慢。你需要抓包分析。

你会怎么做?

我的做法是分三步:

  1. 先粗筛: 用 host 203.0.113.10 抓所有与官网的通信,确认是否存在丢包或重传。
  2. 再精筛: 如果发现TCP重传很多,用 tcp port 80 and host 203.0.113.10 只看HTTP流量,分析请求响应时间。
  3. 最后深挖: 如果怀疑是DNS解析慢,用 udp port 53 and host 203.0.113.10 单独看DNS查询过程。

你看,一个复杂的排错问题,被拆解成了几个简单的BPF过滤器。这就是捕获过滤器的价值——让你在正确的时间,看到正确的数据。

BPF捕获过滤器知识体系 BPF 语法 类型 (host/net/port) 方向 (src/dst) 协议 (tcp/udp/icmp) 逻辑运算符 (and/or/not) 高级BPF (偏移量) 实战:粗筛 → 精筛 → 深挖 核心公式: [协议] [方向] [类型] [值]

4.7 常见错误与避坑指南

  • 语法顺序写反: 比如写成 80 port tcp。BPF语法对顺序有要求,必须是「协议 方向 类型 值」。写反了Wireshark会报错。
  • 忘记加协议限定: 只写 port 53,结果抓到了TCP 53的包(虽然很少见,但确实存在)。建议加上 udp port 53。
  • 在捕获过滤器里写显示过滤器的语法: 比如 http.request 是显示过滤器,不能用在捕获过滤器里。捕获过滤器只认BPF语法。
  • 括号使用不当: 复杂条件建议加括号明确优先级。例如:tcp and (port 80 or port 443)。

我曾经犯过的错: 有一次在tcpdump里写 tcp port 80 or 443,结果只过滤了80端口,443端口根本没抓到。因为 or 443 被解析成了 port 443 但缺少协议限定。正确写法是 tcp port 80 or tcp port 443。

好了,这一章的内容就到这里。BPF语法不难,但需要多练。你可以在自己的测试环境里,用不同的过滤器抓包看看效果。记住,抓对包,是排错成功的一半。