首页/网络/抓包与流量分析速查/BPF 过滤表达式基础

BPF 过滤表达式基础

tcpdump/pcap-filter 使用的表达式由"限定符 + 原语"构成:类型(host/net/port/portrange)、方向(src/dst)、协议(tcp/udp/ip6)三类限定符可组合出精确匹配;理解结构就不再靠试错写过滤器。

BPF 表达式是什么

tcpdump、dumpcap 以及 Wireshark 的捕获过滤器共用同一套 libpcap 表达式语法,习惯上称为 BPF。它由限定符加原语组成:类型限定符(host、net、port、portrange)决定匹配什么对象,方向限定符(src、dst)决定哪个方向,协议限定符(tcp、udp、ip、ip6、arp、ether)决定哪一层。多个原语用 and、or、not 连接,可以用括号分组。理解这个结构之后,写过滤器就从试错变成了翻译。

基本信息

基本结构协议限定符 方向限定符 类型限定符 值
关系运符!= 表示包含该位,= 表示精确相等
常用原语host、net、port、portrange、less、greater、ether 及协议名
连接词and / or / not,也可写 && / || / !
引用表达式含括号或竖线时必须用单引号包住
手册man 8 pcap-filter

限定符对照

类别取值说明
类型host / net / port / portrange决定匹配地址、网段、端口还是端口区间
方向src / dst / src or dst默认双向,加 src 或 dst 限定单向
协议tcp / udp / icmp / ip / ip6 / arp / ether限定匹配发生在哪一层
组合规则一个原语最多一个类型加一个方向加一个协议同类限定符不可叠加,如 tcp tcp 是非法写法
字节访问tcp[13] 这类下标写法读取指定偏移的字节,用于标志位与载荷匹配

典型使用场景

  • 只看某台主机:host 10.0.0.9,双向匹配最省心
  • 只看某个网段:net 10.10.0.0/16,排查机房级问题
  • 只看某个端口:port 443,或 dst port 3306 收窄方向
  • 按协议号过滤:ip proto 47 抓 GRE、ip proto 89 抓 OSPF
  • 按载荷字节匹配:tcp 偏移加四字节比较,精确抓 HTTP GET
  • 用排除法减噪音:not (arp or icmp)

从简单到复杂

从简单到复杂
tcpdump -i eth0 -nn host 10.0.0.9                              # 一台主机
tcpdump -i eth0 -nn 'host 10.0.0.9 and tcp port 443'            # 主机加端口
tcpdump -i eth0 -nn 'host 10.0.0.9 and (port 80 or port 443)'   # 用括号分组
tcpdump -i eth0 -nn 'not (arp or icmp)'                         # 排除法
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'              # 标志位
tcpdump -i eth0 -nn 'ip proto 47'                               # 协议号
tcpdump -i eth0 -nn 'udp dst portrange 5000-5100'               # 端口区间
tcpdump -i eth0 -nn 'ether host 00:11:22:33:44:55'              # 二层地址
写复杂过滤器时先把需求拆成四问:谁发的、哪个方向、什么协议、哪个端口,再逐项翻译成原语。这个方法比直接憋一个长表达式可靠得多。
括号、竖线、尖括号在 shell 里都是特殊字符,含这些字符的表达式必须用单引号包住整体,否则过滤器会被 shell 提前拆解。

字节偏移示例

字节偏移示例
tcpdump -i eth0 -nn -s 0 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'   # 匹配 GET 请求
arp[6:2] = 1                       # 只看 ARP 请求,等于 2 是应答
icmp[icmptype] = icmp-echo        # 只看 ping 请求

两条表达式的等价关系

  • host 10.0.0.9 等于 src host 10.0.0.9 or dst host 10.0.0.9,方向默认为双向
  • port 443 等于 tcp port 443 or udp port 443,协议默认为传输层两者都含
  • not (arp or icmp) 与 not arp and not icmp 等价,前者可读性更好
  • tcp[tcpflags] & tcp-syn != 0 与 tcp[tcpflags] & 0x02 != 0 等价,名字写法更直观

常见需求与写法

想做什么表达式写法要点
只看一台主机与某服务端口host 10.0.0.9 and dst port 443方向修饰只作用于紧跟其后的原语
只看两个网段之间net 10.10.0.0/16 and net 172.16.0.0/12两个 net 分别匹配源与目的,双向都命中
排除某类协议not icmp6IPv6 邻居发现依赖 ICMPv6,排除前想清楚影响
抓带 VLAN 标签的帧vlan and tcp port 443vlan 原语会跳过一次标签,写完整需要考虑双层标签
抓分片报文ip[6:2] & 0x1fff != 0首片带传输层头部,后续分片没有端口信息
只抓某个方向的响应src port 443 and dst host 10.0.0.9用源端口加目的地址两步限定最明确
复杂表达式建议先在命令行里用 -c 少量取样验证命中效果,再挂到落盘命令上。写错条件直接跑长时间抓包,最坏的结果是抓了一个小时却什么也没抓到。

写表达式的顺序

  • 先确定分析目标:是要统计数量、看内容,还是确认某类报文是否存在
  • 再选粒度:越靠前的条件越便宜,先用主机与网段收窄,再加端口与协议
  • 然后确认方向:只有需要区分请求与响应时才加 src 或 dst 限定
  • 最后考虑排除:用 not 去掉已知噪音,但要确认排除项不会误伤目标流量
  • 写完先用少量计数验证命中量级,量级明显不对说明条件写反了

易错点

  • 三个同类限定符不能叠加,tcp tcp 这类写法会直接报语法错误
  • and 优先级高于 or,混合书写有歧义时必须用括号明确分组
  • 表达式要放在所有选项之后,放在 -w 前面可能被解析成参数值
  • 括号与竖线需要单引号保护,否则被 shell 解释成管道或子命令,得到完全不同的命令
  • BPF 不认识 tcp.port 这类字段名,那是显示过滤器语法,填进捕获过滤器只会报错

数据来源与参考

  • man 8 pcap-filter(表达式语法)
  • pcap-filter 手册(tcpdump.org)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌