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。你想想看,高并发下双向流量巨大,不加方向过滤,数据量直接翻倍。

注意: 端口过滤器只对TCP/UDP头部有效。如果你抓的是ARP或ICMP包,端口条件会被忽略。Wireshark不会报错,但结果可能不是你想要的。

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 确认包(几乎每个包都有)
小技巧: 如果你想抓「纯ACK包」(没有数据,只有确认),可以写 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 — 只抓TCP
  • host 10.0.1.100 — 只跟这台机器有关
  • port 443 — 只看HTTPS
  • src net 10.0.0.0/16 — 客户端来源
  • tcp[tcpflags] & (tcp-syn|tcp-rst) != 0 — 只抓SYN或RST

这个过滤器在高并发下,能把流量降到原来的1/10甚至更低。我当年用这个过滤器,在每秒10万连接的场景下,只抓了200MB数据,就找到了问题根源。

核心原则: 捕获过滤器越精确,分析越轻松。不要怕写长表达式,怕的是抓了一堆垃圾数据,最后还得用显示过滤器再筛一遍。记住:捕获过滤器是「丢包」,显示过滤器是「藏包」。在高并发下,丢比藏重要得多。

3.6 BPF语法速查图

下面这张图是我自己整理的BPF语法结构。每次写复杂过滤器前,我都会扫一眼,确保没漏条件。

BPF语法结构图 BPF捕获过滤器语法结构 BPF表达式 协议:tcp/udp/icmp 方向:src/dst/host 端口:port/portrange 逻辑:and/or/not 高级:tcp[tcpflags] & 标志位 提示:四个维度可以任意组合,顺序不重要。例如:tcp and dst port 80 and src net 10.0.0.0/8 高并发下建议先写协议,再写IP,最后写端口,最后加标志位过滤。

这张图我贴在了工位上。每次写过滤器,我就按这个顺序来:协议 → 方向 → 端口 → 逻辑 → 高级标志位。嗯,基本不会漏。

3.7 写在最后

捕获过滤器是Wireshark的第一道防线。在高并发场景下,它决定了你是「精准打击」还是「大海捞针」。我个人建议:每次抓包前,花30秒写一个精确的过滤器。这30秒,能帮你省下后面30分钟的分析时间。