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)非常慢。你需要抓包分析。
你会怎么做?
我的做法是分三步:
- 先粗筛: 用
host 203.0.113.10抓所有与官网的通信,确认是否存在丢包或重传。 - 再精筛: 如果发现TCP重传很多,用
tcp port 80 and host 203.0.113.10只看HTTP流量,分析请求响应时间。 - 最后深挖: 如果怀疑是DNS解析慢,用
udp port 53 and host 203.0.113.10单独看DNS查询过程。
你看,一个复杂的排错问题,被拆解成了几个简单的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语法不难,但需要多练。你可以在自己的测试环境里,用不同的过滤器抓包看看效果。记住,抓对包,是排错成功的一半。