3. 捕获过滤器实战:BPF语法详解
各位好,我是老张。今天咱们聊聊捕获过滤器。说白了,就是告诉Wireshark:「我只想看这些包,其他的别收」。在高并发场景下,这招能救命。
我记得刚带团队时,有个兄弟抓了个1GB的pcap文件,打开直接卡死。我问他:「你用了捕获过滤器吗?」他一脸懵。嗯,从那以后,我要求所有人必须先配过滤器,再点开始。
3.1 BPF语法:核心四要素
BPF(Berkeley Packet Filter)是捕获过滤器的底层语言。你不需要背所有关键字,但必须掌握四个核心维度:
| 维度 | 关键字 | 示例 |
|---|---|---|
| 协议 | tcp / udp / icmp / arp | tcp |
| 方向 | src / dst / src or dst | src 10.0.0.1 |
| 端口 | port / portrange | port 80 |
| 逻辑 | and / or / not | tcp and port 443 |
tcp and dst port 80。这样读起来顺,也不容易漏条件。
3.2 捕获特定端口:80与443
高并发连接里,80和443是绝对主力。我建议你这样写:
# 只抓HTTP(80)和HTTPS(443)
tcp port 80 or tcp port 443
# 更简洁的写法
tcp port 80 or 443
# 如果你只想看从客户端发来的请求
tcp src port 80 or tcp src port 443
为什么会强调方向?我在项目里遇到过一个问题:抓了半天,发现全是服务器回包,请求包一个没抓到。后来发现忘了写 src 和 dst。你想想看,高并发下双向流量巨大,不加方向过滤,数据量直接翻倍。
3.3 捕获特定IP段
高并发场景下,你往往只关心某个子网。比如公司内网 10.0.0.0/8,或者某个云服务商的IP段。
# 抓来源是10.0.0.0/8的包
src net 10.0.0.0/8
# 抓目标在192.168.1.0/24的包
dst net 192.168.1.0/24
# 抓两个网段之间的通信
net 10.0.0.0/8 and net 192.168.1.0/24
我曾经帮一个电商团队排查问题。他们线上有个服务,只跟某个CDN节点通信。我直接写 host 203.0.113.50,抓了30秒,问题就定位了。如果没加IP过滤,那流量得翻几十倍。
避坑指南: 我曾经用 net 10.0.0.0/8 抓包,结果发现少了很多包。后来一查,原来有些机器用的是 10.0.0.0/16 的子网掩码。BPF的 net 关键字是按你写的掩码匹配的,不是自动适配。所以,写之前先确认子网掩码。
3.4 捕获TCP标志位组合
高并发连接里,TCP标志位是诊断问题的金钥匙。比如你想看三次握手,就抓SYN包;想看连接断开,就抓FIN包。
BPF支持直接访问TCP头部标志位字段。语法是:
# 抓SYN包(不包含ACK)
tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0
# 抓SYN-ACK包
tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0
# 抓RST包(连接重置)
tcp[tcpflags] & tcp-rst != 0
# 抓FIN包(正常关闭)
tcp[tcpflags] & tcp-fin != 0
我个人最常用的是 tcp[tcpflags] & tcp-rst != 0。为什么?因为高并发下,RST包往往意味着异常。比如后端服务挂了、连接超时、或者防火墙拦截。抓到RST,基本就找到问题了。
| 标志位 | BPF写法 | 典型场景 |
|---|---|---|
| SYN | tcp-syn != 0 and tcp-ack == 0 |
连接发起 |
| SYN-ACK | tcp-syn != 0 and tcp-ack != 0 |
连接响应 |
| RST | tcp-rst != 0 |
异常断开 |
| FIN | tcp-fin != 0 |
正常关闭 |
| ACK | tcp-ack != 0 |
确认包(几乎每个包都有) |
tcp[tcpflags] & tcp-ack != 0 and len == 0。高并发下,纯ACK包占比很高,但通常不携带业务数据。过滤掉它们,能让你更聚焦在数据包上。
3.5 综合实战:一个高并发场景的过滤器
假设你正在排查一个Web服务的高延迟问题。服务跑在 10.0.1.100:443,客户端来自 10.0.0.0/16。你想看所有SYN和RST包,排除纯ACK。
我会这样写:
tcp and host 10.0.1.100 and port 443 and src net 10.0.0.0/16 and (tcp[tcpflags] & (tcp-syn|tcp-rst) != 0)
拆开看:
tcp— 只抓TCPhost 10.0.1.100— 只跟这台机器有关port 443— 只看HTTPSsrc net 10.0.0.0/16— 客户端来源tcp[tcpflags] & (tcp-syn|tcp-rst) != 0— 只抓SYN或RST
这个过滤器在高并发下,能把流量降到原来的1/10甚至更低。我当年用这个过滤器,在每秒10万连接的场景下,只抓了200MB数据,就找到了问题根源。
核心原则: 捕获过滤器越精确,分析越轻松。不要怕写长表达式,怕的是抓了一堆垃圾数据,最后还得用显示过滤器再筛一遍。记住:捕获过滤器是「丢包」,显示过滤器是「藏包」。在高并发下,丢比藏重要得多。
3.6 BPF语法速查图
下面这张图是我自己整理的BPF语法结构。每次写复杂过滤器前,我都会扫一眼,确保没漏条件。
这张图我贴在了工位上。每次写过滤器,我就按这个顺序来:协议 → 方向 → 端口 → 逻辑 → 高级标志位。嗯,基本不会漏。
3.7 写在最后
捕获过滤器是Wireshark的第一道防线。在高并发场景下,它决定了你是「精准打击」还是「大海捞针」。我个人建议:每次抓包前,花30秒写一个精确的过滤器。这30秒,能帮你省下后面30分钟的分析时间。