抓包过滤器详解:BPF语法基础与实战
说实话,很多刚接触Wireshark的朋友,一上来就被海量数据包淹没了。你想想看,一个千兆网口上跑的数据,每秒成千上万个包,全抓下来根本没法看。这时候,过滤器就是你唯一的救星。
我个人习惯把过滤器分成两类:抓包过滤器和显示过滤器。这一章我们先啃硬骨头——抓包过滤器。它背后的核心就是BPF(Berkeley Packet Filter)语法。别被这个术语吓到,说白了,它就是一套让你告诉网卡“我只想要这些包,其他的别给我”的指令集。
核心概念:BPF语法工作在数据链路层,在数据包进入操作系统协议栈之前就进行过滤。这意味着,如果你用BPF过滤掉了某些包,Wireshark根本看不到它们。好处是节省资源,坏处是一旦过滤掉,就再也找不回来了。
BPF语法基础:三个核心要素
BPF表达式由三部分组成:协议(proto)、方向(dir)、类型(type)。我习惯把它记成“什么协议、从哪来、是什么”。
| 要素 | 可选值 | 示例 |
|---|---|---|
| 协议(proto) | ether, ip, ip6, arp, tcp, udp, icmp 等 | tcp |
| 方向(dir) | src, dst, src or dst(默认) | src |
| 类型(type) | host, net, port, portrange | host 192.168.1.1 |
组合起来就是:[协议] [方向] [类型] [值]。比如 tcp src port 80,意思就是“只抓TCP协议、源端口是80的包”。
我的小习惯:在写BPF表达式时,我总会在脑子里默念一遍这个顺序。如果漏了协议,默认就是ip;如果漏了方向,默认就是src or dst。很多新手在这里翻车,抓了半天发现包不对,其实就是方向写反了。
常见过滤表达式详解
1. host:按主机过滤
这是最常用的。你想只看某台机器的通信,就用它。
# 只看192.168.1.1的流量(无论源还是目标)
host 192.168.1.1
# 只看从192.168.1.1发出的流量
src host 192.168.1.1
# 只看发往192.168.1.1的流量
dst host 192.168.1.1
我记得有一次排查一个应用延迟问题,同事抓了半小时包,发现全是广播包。我过去一看,他用的就是 host 10.0.0.5,但没加方向。结果抓到了大量从10.0.0.5发出去的广播响应,真正的问题流量反而被淹没了。加上 dst host 后,问题立刻浮出水面。
2. port:按端口过滤
端口过滤是排查应用层问题的利器。
# 只看HTTP流量
tcp port 80
# 只看DNS查询和响应
udp port 53
# 只看SSH连接
tcp port 22
注意:port 80 默认包含TCP和UDP。如果你只想看TCP的80端口,一定要写成 tcp port 80。我曾经见过有人用 port 443 抓HTTPS,结果抓到了一堆QUIC(UDP 443)的包,自己还纳闷为什么解密不了。
3. net:按网段过滤
当你需要监控整个子网的流量时,net就派上用场了。
# 抓取整个192.168.1.0/24网段的流量
net 192.168.1.0/24
# 只抓从该网段发出的流量
src net 192.168.1.0/24
# 只抓发往该网段的流量
dst net 192.168.1.0/24
嗯,这里要注意:net 后面可以跟CIDR表示法,也可以跟子网掩码。比如 net 192.168.1.0 mask 255.255.255.0 效果是一样的。我个人更习惯用CIDR,少打几个字。
4. proto:按协议过滤
除了直接写协议名,你还可以用协议号。这在抓一些不常见协议时特别有用。
# 等价写法
ip proto 6 # TCP
ip proto 17 # UDP
ip proto 1 # ICMP
# 或者直接用名字
tcp
udp
icmp
为什么会有两种写法?因为有些协议没有对应的名字。比如你想抓GRE协议(协议号47),就只能写成 ip proto 47。我在做VPN排障时经常用这个。
组合条件:and/or/not 的实战用法
单个条件往往不够用。你需要把多个条件组合起来,才能精准定位目标流量。
| 操作符 | 含义 | 示例 |
|---|---|---|
| and (&&) | 两个条件都满足 | tcp and port 80 |
| or (||) | 满足任意一个条件 | port 80 or port 443 |
| not (!) | 排除某个条件 | not arp |
来看几个实战中我经常用的组合:
# 抓取从192.168.1.100发往任何主机的HTTP流量
src host 192.168.1.100 and tcp port 80
# 抓取除了SSH之外的所有TCP流量
tcp and not port 22
# 抓取DNS或HTTP流量
port 53 or port 80
# 抓取192.168.1.0/24网段内,除了广播和组播之外的流量
net 192.168.1.0/24 and not broadcast and not multicast
避坑指南:我曾经在排查一个DHCP问题时,写了 udp port 67 or port 68。结果抓到了一堆乱七八糟的包。为什么?因为 or 的优先级比 and 低,但比隐式连接低。正确的写法应该是 udp and (port 67 or port 68)。记住:用括号明确优先级,永远不要依赖默认规则。
BPF语法知识体系
下面这张图帮你理清BPF语法的整体结构。我每次培训都会画一遍,因为它能帮你建立全局视角。
实战:从零构建一个精准的抓包过滤器
光说不练假把式。我们来看一个真实场景:
场景:公司内部Web服务器(192.168.1.10)响应变慢,怀疑是某个客户端(192.168.1.200)在发大量请求。你需要抓取这两个IP之间的HTTP流量,排除其他干扰。
第一步:确定协议——TCP(HTTP基于TCP)。
第二步:确定方向——两个IP之间的双向通信。
第三步:确定类型——主机地址和端口。
最终表达式:
tcp and host 192.168.1.10 and host 192.168.1.200 and port 80
等等,这个表达式有点啰嗦。我们可以简化:
tcp and (host 192.168.1.10 or host 192.168.1.200) and port 80
或者更简洁的写法:
tcp and net 192.168.1.0/24 and port 80
但这样会把整个网段的HTTP流量都抓进来。如果网段里还有其他Web服务器,就会抓到多余的数据。所以,精确永远比简洁重要。
我的建议:在写BPF表达式时,先写一个宽松的版本抓一小段时间,看看都有哪些流量。然后逐步收紧条件。不要一上来就写一个极其复杂的表达式,万一写错了,你连错在哪都不知道。
常见错误与避坑
- 方向搞反:
src host和dst host的区别。抓包前想清楚:你是想看从某台机器发出的包,还是发往它的包? - 协议遗漏:只写
port 80会同时匹配TCP和UDP。虽然UDP 80很少见,但万一有呢? - 括号缺失:
tcp port 80 or 443会被解析成(tcp port 80) or 443,而不是你想要的tcp and (port 80 or port 443)。 - 大小写敏感:BPF关键字是小写的。
TCP不会被识别,必须写成tcp。
重要提醒:抓包过滤器一旦设置,被过滤掉的包就永远消失了。如果你不确定该过滤什么,我建议先用显示过滤器。显示过滤器只是隐藏了包,并没有丢弃它们。等确认了过滤条件,再改用抓包过滤器来提升性能。