抓包过滤器详解: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语法的整体结构。我每次培训都会画一遍,因为它能帮你建立全局视角。

BPF抓包过滤器知识体系 BPF表达式 协议 (proto) 方向 (dir) 类型 (type) ether ip / ip6 tcp / udp icmp / arp src / dst src or dst (默认) host net port portrange 组合条件:and / or / not src host 10.0.0.1 and tcp port 80 net 192.168.1.0/24 and not port 22 💡 用括号明确优先级:tcp and (port 80 or port 443)

实战:从零构建一个精准的抓包过滤器

光说不练假把式。我们来看一个真实场景:

场景:公司内部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。

重要提醒:抓包过滤器一旦设置,被过滤掉的包就永远消失了。如果你不确定该过滤什么,我建议先用显示过滤器。显示过滤器只是隐藏了包,并没有丢弃它们。等确认了过滤条件,再改用抓包过滤器来提升性能。