过滤的艺术:Wireshark显示过滤器语法详解

说实话,抓包这件事,百分之八十的功夫不在抓,而在过滤。你想想看,一个线上故障,网卡上每秒几千个包,眼睛看花了也找不到问题。我刚开始用Wireshark那会儿,就吃过这个亏——抓了一大堆数据,结果在列表里翻来翻去,啥也没看出来。

后来我明白了,显示过滤器就是你的第二双眼睛。用好了,几秒钟就能锁定目标。今天我就把这套过滤的功夫,掰开了揉碎了讲给你听。

核心认知:捕获过滤器是“拦路虎”,在数据进门之前就筛掉;显示过滤器是“显微镜”,在已捕获的数据中精准定位。排错时,我几乎只用显示过滤器——因为你不确定哪些包有用,先全抓下来再说。

4.1 基础过滤:从地址和端口开始

先讲最常用的几个过滤字段。这些你每天都会用到,记牢了能省一半时间。

ip.addr —— 只看某个IP的通信

这个字段最常用,也最容易踩坑。我直接说结论:

ip.addr == 192.168.1.100

这条过滤会显示所有包含该IP的包——不管它是源地址还是目标地址。我个人习惯用这个,因为排错时我关心的是“这个IP参与的所有通信”。

但如果你只想看某个方向,就得用更精确的写法:

ip.src == 192.168.1.100    # 只看这个IP发出的包
ip.dst == 192.168.1.100    # 只看发给这个IP的包

我曾经踩过的坑:有一次排查DNS问题,我用 ip.addr == 8.8.8.8 过滤,结果发现包特别少。折腾了半天才发现,DNS请求是发往8.8.8.8没错,但响应包是从另一个IP回来的(用了DNS转发)。所以记住:ip.addr 匹配的是“包含”,不是“等于两端”。

tcp.port —— 锁定应用层协议

端口过滤是定位应用层问题的利器。比如看HTTP:

tcp.port == 80 || tcp.port == 443

嗯,这里要注意,tcp.port 同样匹配源端口和目标端口。如果你想精确控制方向:

tcp.srcport == 80    # 源端口是80(通常是服务端响应)
tcp.dstport == 80    # 目标端口是80(通常是客户端请求)

我一般在排错时这么用:先 tcp.port == 3306 看MySQL通信,再根据情况细化。说白了,端口过滤是快速缩小范围的第一刀。

4.2 应用层过滤:直击问题本质

基础过滤只能看到“谁在和谁说话”,应用层过滤才能看到“他们在说什么”。

http.request —— 只看HTTP请求

这个字段特别实用。比如你想看某个URL的请求:

http.request.uri contains "/api/login"

或者只看GET请求:

http.request.method == "GET"

我在项目中遇到过一个问题:前端说接口返回慢,后端说没收到请求。我抓包后用 http.request 过滤,发现请求确实发出了,但TCP层有重传——原来是中间某个交换机丢了包。如果没有这个过滤,我可能还在那翻几百个包呢。

dns.qry.name —— 精准定位DNS查询

DNS问题排查,这个字段是王牌:

dns.qry.name == "www.example.com"

如果你想看某个域名的所有DNS记录:

dns.qry.name contains "example.com"

小技巧:用 dns.flags.response == 0 只看查询请求,dns.flags.response == 1 只看响应。配合 dns.qry.name 使用,效果翻倍。

4.3 逻辑运算符:把过滤条件串起来

单个条件往往不够用。你需要把多个条件组合起来,才能精准定位。

运算符 写法 含义 示例
与 and 或 && 两个条件都满足 ip.addr == 10.0.0.1 and tcp.port == 80
或 or 或 || 满足任一条件 tcp.port == 80 or tcp.port == 443
非 not 或 ! 排除满足条件的包 not arp

举个例子,我想看 192.168.1.100 的HTTP通信,但排除健康检查的请求:

ip.addr == 192.168.1.100 and tcp.port == 80 and not http.request.uri contains "/health"

你看,三个条件串起来,一下子就精确了。

4.4 过滤链构建:从模糊到精确

真正的排错高手,不会一上来就写复杂的过滤条件。我一般分三步走:

  1. 粗筛:用 ip.addr 或 tcp.port 锁定通信双方
  2. 细筛:用应用层字段缩小范围
  3. 精筛:用逻辑运算符组合条件,排除干扰

比如排查一个“网页加载慢”的问题:

# 第一步:看这个IP的所有通信
ip.addr == 10.0.0.5

# 第二步:只看HTTP
ip.addr == 10.0.0.5 and tcp.port == 80

# 第三步:只看某个资源的请求,排除图片等静态资源
ip.addr == 10.0.0.5 and tcp.port == 80 and http.request.uri contains "/api" and not http.request.uri contains ".jpg"

我的习惯:在Wireshark的显示过滤器栏里,我从来不会一次性输入长串条件。我会先在脑子里想清楚“我要看什么”,然后从粗到细逐步添加。每加一个条件,看一眼结果,确认方向对不对。

下面这张图,是我自己总结的过滤链构建逻辑:

显示过滤器构建流程 第一步:粗筛 ip.addr / tcp.port 第二步:细筛 http.request / dns.qry.name 第三步:精筛 and / or / not 实际排错示例:网页加载慢 粗筛: ip.addr == 10.0.0.5 → 看到这个IP的所有通信,发现主要是HTTP流量 细筛: ip.addr == 10.0.0.5 and tcp.port == 80 → 只看HTTP,发现大量请求都在等一个API响应 精筛: ip.addr == 10.0.0.5 and tcp.port == 80 and http.request.uri contains "/api" → 锁定API请求,发现TCP重传,问题出在网络层

4.5 避坑指南:我踩过的那些坑

最后分享几个实战中容易犯的错误:

  • 大小写问题:Wireshark的字段名是大小写敏感的。http.request 是对的,HTTP.Request 就不行。我刚开始时经常在这上面浪费时间。
  • 引号的使用:字符串值必须用双引号,比如 http.request.method == "GET"。数字和布尔值不用,比如 tcp.port == 80。
  • contains vs ==:== 是精确匹配,contains 是包含匹配。比如 dns.qry.name contains "google" 会匹配所有包含google的域名,而 == 只匹配完全一致的。
  • 括号的使用:多个逻辑条件组合时,用括号明确优先级。比如 (ip.addr == 10.0.0.1 or ip.addr == 10.0.0.2) and tcp.port == 80,不加括号结果可能完全不一样。

我的建议:在Wireshark的过滤栏里输入时,每输入一个字段,留意一下语法高亮。如果字段名变成红色,说明写错了。这个功能很贴心,别忽略它。