☰
tcpdump抓包过滤器实战:语法、场景与性能优化
2026/10/16 1:23:17 网站建设 项目流程

1. 抓包过滤器到底在解决什么问题

很多人第一次接触 tcpdump,敲下命令之后屏幕上滚出来的东西能把人看晕——几千行数据包记录,密密麻麻的 IP、端口、标志位,真正想看的那几条淹没在里面。抓包过滤器就是干这个的:在数据包进入分析环节之前,先把不关心的流量扔掉,只留下你真正需要的那部分。

打个比方,抓包过滤器就像是在一条繁忙的高速公路上设了一个收费站,只放行特定类型的车辆通过。没有这个收费站,你面对的就是整条路的车流,根本没法分析。tcpdump 的过滤器基于 BPF(Berkeley Packet Filter)语法,这个语法被很多工具复用,包括 Wireshark 的捕获过滤器、tshark 等。学会一套,到处能用。

这篇文章适合谁看?如果你已经会用 tcpdump 抓包,但每次都是tcpdump -i any一把梭,然后对着满屏输出发呆,那这篇就是写给你的。如果你还没用过 tcpdump,也没关系,我会从过滤器的基础语法讲起,配合实际场景一步步展开。核心关键词就一个:tcpdump 抓包过滤器。围绕它,我会把语法规则、组合逻辑、常见场景、排查技巧全部拆开讲透。

先说清楚一个前提:tcpdump 的过滤器表达式是位置参数,写在所有选项之后。比如tcpdump -i eth0 -nn port 80,port 80就是过滤器。这个位置不能搞错,否则 tcpdump 会把过滤器当成接口名或者其他参数去解析,直接报错。

2. 过滤器语法核心:从类型到方向再到协议

2.1 三种限定类型:host、net、port

BPF 过滤器的基本构件是“限定词 + 值”的组合。最常用的三种限定类型是:

  • host:指定主机,可以是 IP 或主机名。例如host 192.168.1.100。
  • net:指定网段。例如net 192.168.1.0/24或net 10.0.0.0 mask 255.0.0.0。
  • port:指定端口。例如port 443、portrange 8000-9000。

这三种类型可以单独使用,也可以组合。单独使用时,host 192.168.1.100表示“源或目的地址是 192.168.1.100 的所有包”。注意这里默认是双向的,源和目的都匹配。

我见过不少人写tcpdump host 10.0.0.1 and host 10.0.0.2,以为能抓到两台主机之间的通信,实际上这个表达式抓的是“任意一端是 10.0.0.1 且任意一端是 10.0.0.2”的包,结果是对的,但逻辑上不够精确。更严谨的写法后面会讲。

2.2 方向限定词:src、dst 以及它们的组合

方向限定词用来缩小匹配范围:

  • src host 192.168.1.100:源地址是 192.168.1.100。
  • dst host 192.168.1.100:目的地址是 192.168.1.100。
  • src or dst host 192.168.1.100:等价于不加方向限定词。
  • src and dst host 192.168.1.100:源和目的都是这个地址,通常用于本机回环流量。

方向限定词同样适用于 port:src port 53表示源端口是 53,通常是 DNS 服务器的响应;dst port 53表示目的端口是 53,通常是 DNS 查询请求。这个区分在排查 DNS 问题时特别有用。

注意:src和dst只能修饰紧跟其后的那一个限定词。src host 10.0.0.1 and dst port 80是正确的,但src host 10.0.0.1 and port 80中的port 80不受src影响,仍然是双向匹配。

2.3 协议限定词:tcp、udp、icmp 等

协议限定词直接写协议名即可:tcp、udp、icmp、arp、ip、ip6。它们通常和其他限定词组合使用:

tcpdump -i eth0 -nn tcp and port 443 tcpdump -i eth0 -nn udp and port 53 tcpdump -i eth0 -nn icmp

如果不写协议限定词,tcpdump 默认匹配所有协议。但加上协议限定词能显著减少捕获量,尤其是在高流量环境下。比如你只想看 TCP 的重传情况,那就直接tcp,把 UDP 和 ICMP 全部过滤掉。

协议限定词还可以和方向、类型组合,形成非常精确的表达式:

tcpdump -i eth0 -nn 'tcp and src host 10.0.0.5 and dst port 3306'

这条命令抓的是:从 10.0.0.5 发出、目标端口是 3306 的 TCP 包。方向、协议、类型三个维度都用上了,精度很高。

2.4 逻辑运算符:and、or、not 的优先级陷阱

BPF 支持三种逻辑运算符:

  • and(也可以写成&&):与
  • or(也可以写成||):或
  • not(也可以写成!):非

优先级从高到低是:not>and>or。这个优先级顺序和大多数编程语言一致,但很多人会忽略它,导致过滤器行为不符合预期。

举个例子:

tcpdump -i eth0 -nn 'host 10.0.0.1 or host 10.0.0.2 and port 80'

由于and优先级高于or,这个表达式实际等价于:

tcpdump -i eth0 -nn 'host 10.0.0.1 or (host 10.0.0.2 and port 80)'

也就是说,所有与 10.0.0.1 相关的包都会被捕获,不管端口是什么;而 10.0.0.2 只捕获 80 端口的包。如果你想要的是“10.0.0.1 或 10.0.0.2 的 80 端口流量”,必须加括号:

tcpdump -i eth0 -nn '(host 10.0.0.1 or host 10.0.0.2) and port 80'

实操心得:我个人的习惯是,只要表达式里同时出现and和or,一律加括号。多打两个字符,省去后面排查过滤器逻辑的时间,非常划算。

3. 进阶过滤:字节偏移、标志位与协议头字段

3.1 用字节偏移精确匹配报文内容

BPF 允许你直接访问报文中的任意字节,语法是proto[offset:size]。其中proto是协议名(如ip、tcp、udp、icmp),offset是从协议头开始的字节偏移量,size是要读取的字节数(1、2、4)。

比如,想抓所有 TCP 标志位中 SYN 被置位的包:

tcpdump -i eth0 -nn 'tcp[13] & 2 != 0'

这里tcp[13]是 TCP 头的第 14 个字节(从 0 开始计数),也就是标志位字段。& 2是按位与操作,检查 SYN 位(值为 2)是否被置位。!= 0表示只要 SYN 位为 1 就匹配。

类似地,抓 RST 包:

tcpdump -i eth0 -nn 'tcp[13] & 4 != 0'

抓 FIN 包:

tcpdump -i eth0 -nn 'tcp[13] & 1 != 0'

抓 PSH+ACK 包:

tcpdump -i eth0 -nn 'tcp[13] & 24 = 24'

这些表达式在排查连接异常时非常有用。比如服务器收到大量 RST,你可以直接过滤出 RST 包,看是谁发的、发往哪里。

3.2 用 tcpdump 内置的 tcpflags 简化写法

如果你觉得字节偏移太底层,tcpdump 提供了更友好的写法:

tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0' tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0' tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'

最后一条抓的是“SYN 置位且 ACK 未置位”的包,也就是 TCP 三次握手的第一个包。这个写法比记字节偏移直观多了,推荐优先使用。

3.3 匹配 HTTP 请求方法

BPF 可以读取报文载荷的前几个字节,用来匹配应用层协议的特征。比如抓 HTTP GET 请求:

tcpdump -i eth0 -nn -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

这个表达式看起来复杂,拆开看就清楚了:

  • tcp[12:1]:读取 TCP 头的第 13 个字节,也就是数据偏移字段的高 4 位。
  • & 0xf0:取高 4 位,表示 TCP 头长度(单位是 4 字节)。
  • >> 2:右移 2 位,得到 TCP 头长度(单位是字节)。
  • tcp[((tcp[12:1] & 0xf0) >> 2):4]:从 TCP 头之后开始读取 4 个字节,也就是 HTTP 请求方法的前 4 个字节。
  • = 0x47455420:0x47 0x45 0x54 0x20对应 ASCII 的 "GET "。

同理,POST 是0x504f5354,HEAD 是0x48454144。这种写法在只需要抓特定 HTTP 方法时非常高效,避免了抓取大量无关流量。

注意:这个表达式只适用于 HTTP/1.x 的明文流量。HTTP/2 和 HTTPS 的载荷是加密的,无法用这种方式匹配。

4. 实战场景:从需求到过滤器表达式的完整推导

4.1 场景一:排查某台服务器与数据库之间的慢查询

需求:应用服务器 10.0.0.5 连接数据库 10.0.0.10 的 3306 端口,怀疑有慢查询导致连接堆积。需要抓取两者之间的 MySQL 流量。

分析:源地址是 10.0.0.5,目的地址是 10.0.0.10,目的端口是 3306,协议是 TCP。过滤器表达式:

tcpdump -i eth0 -nn -s 0 -w mysql_traffic.pcap 'tcp and src host 10.0.0.5 and dst host 10.0.0.10 and dst port 3306'

这里加了-s 0表示抓取完整报文,不截断。-w把结果写入文件,方便后续用 Wireshark 分析。如果不写-w,直接在终端输出,MySQL 的二进制协议内容会显示成乱码,意义不大。

如果还想同时抓数据库返回的响应包,可以放宽方向限定:

tcpdump -i eth0 -nn -s 0 -w mysql_traffic.pcap 'tcp and host 10.0.0.5 and host 10.0.0.10 and port 3306'

这样双向流量都能抓到。注意这里用了两个host加and,精确匹配两台主机之间的通信。

4.2 场景二:定位 DNS 解析异常

需求:某台机器解析域名时快时慢,需要抓取 DNS 查询和响应,看是查询没发出去,还是响应没回来。

分析:DNS 通常走 UDP 53 端口,源端口是随机高端口,目的端口是 53。过滤器表达式:

tcpdump -i eth0 -nn -s 0 'udp and port 53'

这条命令会抓取所有 DNS 流量。如果只想看某台机器的 DNS 请求:

tcpdump -i eth0 -nn -s 0 'udp and src host 10.0.0.20 and dst port 53'

如果 DNS 走 TCP(比如响应超过 512 字节时),需要单独抓:

tcpdump -i eth0 -nn -s 0 'tcp and port 53'

实操中我一般两条命令同时开两个终端,或者用or合并:

tcpdump -i eth0 -nn -s 0 '(udp or tcp) and port 53'

4.3 场景三:抓取特定网段的所有非本机流量

需求:某网段 192.168.10.0/24 内主机通信异常,需要抓取该网段与其他网段之间的流量,但排除本网段内部的互访。

分析:源或目的在 192.168.10.0/24,但源和目的不同时在 192.168.10.0/24。表达式:

tcpdump -i eth0 -nn -s 0 'net 192.168.10.0/24 and not (src net 192.168.10.0/24 and dst net 192.168.10.0/24)'

这个表达式用了not和括号,逻辑是:匹配网段流量,但排除源和目的都在该网段的包。实际抓包时,这个过滤器能有效减少内部广播和互访带来的噪音。

4.4 场景四:抓取 HTTP 错误响应

需求:某 Web 服务返回大量 5xx 错误,需要抓取包含 "HTTP/1.1 5" 的响应包。

分析:HTTP 响应状态行以 "HTTP/1.1 " 开头,后面跟状态码。5xx 错误的特征字符串是 "HTTP/1.1 5"。用字节偏移匹配:

tcpdump -i eth0 -nn -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):8] = 0x485454502f312e3120'

这个表达式匹配的是 "HTTP/1.1 " 这 9 个字符(0x48 0x54 0x54 0x50 0x2f 0x31 0x2e 0x31 0x20)。但这样只能匹配到所有 HTTP/1.1 响应,无法区分状态码。要精确匹配 5xx,需要再往后读一个字节:

tcpdump -i eth0 -nn -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):9] = 0x485454502f312e312035'

最后的0x35是字符 '5' 的 ASCII 码。这样就能只抓 5xx 响应了。不过说实话,这种表达式写起来费劲,实际工作中我更倾向于用-w抓包后用 Wireshark 过滤http.response.code >= 500,效率更高。tcpdump 的字节偏移过滤更适合在资源受限的环境下做初步筛选。

5. 过滤器组合技巧与性能优化

5.1 用括号控制逻辑优先级

前面提过and和or的优先级问题,这里再强调一次:只要表达式里同时有and和or,就加括号。这不是可选项,是必选项。我见过太多人因为少写一对括号,抓了半天发现数据不对,回头排查才发现是优先级问题。

正确的写法:

tcpdump -i eth0 -nn '(host 10.0.0.1 or host 10.0.0.2) and (port 80 or port 443)'

这个表达式抓的是:10.0.0.1 或 10.0.0.2 的 80 或 443 端口流量。逻辑清晰,不容易出错。

5.2 用 not 排除噪音流量

在高流量环境下,排除法往往比包含法更高效。比如你在抓 Web 流量,但 SSH 会话的包一直在干扰:

tcpdump -i eth0 -nn 'tcp and port 80 and not host 10.0.0.100'

或者排除整个网段的广播:

tcpdump -i eth0 -nn 'not net 192.168.1.0/24'

注意:not的优先级很高,如果后面跟的是复合表达式,一定要加括号。not host 10.0.0.1 and port 80实际是(not host 10.0.0.1) and port 80,而不是not (host 10.0.0.1 and port 80)。

5.3 过滤器对性能的影响

BPF 过滤器是在内核层面执行的,不匹配的包根本不会拷贝到用户空间。这意味着过滤器写得越精确,tcpdump 占用的 CPU 和内存就越少。在高流量服务器上,一个宽松的过滤器可能导致 tcpdump 自身成为性能瓶颈。

我做过一个简单的对比测试:在千兆网卡上抓包,不加过滤器时 tcpdump 的 CPU 占用率约为 15% 到 20%;加上tcp and port 443之后,CPU 占用率降到 3% 以下。差距非常明显。

所以,永远不要裸奔抓包。哪怕你暂时不确定要抓什么,也至少加一个tcp或udp限定词,把流量降下来。

5.4 常用过滤器速查表

需求过滤器表达式
抓取某主机所有流量host 10.0.0.1
抓取某主机发出的流量src host 10.0.0.1
抓取发往某主机的流量dst host 10.0.0.1
抓取某端口流量port 8080
抓取某网段流量net 192.168.1.0/24
抓取 TCP SYN 包tcp[tcpflags] & tcp-syn != 0
抓取 TCP RST 包tcp[tcpflags] & tcp-rst != 0
抓取 ICMP 包icmp
抓取 DNS 流量port 53
抓取 HTTP GET 请求tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420
排除某主机not host 10.0.0.1
抓取两台主机间流量host 10.0.0.1 and host 10.0.0.2
抓取非本网段流量not (src net 192.168.1.0/24 and dst net 192.168.1.0/24)

这张表建议存下来,日常排查时直接查,比每次重新推导快得多。

6. 常见问题与排查技巧实录

6.1 过滤器不生效?先检查引号

这是最常见的问题。如果你的过滤器里包含>、<、&、|等 shell 特殊字符,不加引号会被 shell 解释掉,tcpdump 收到的表达式就不完整了。

错误写法:

tcpdump -i eth0 tcp[13] & 2 != 0

shell 会把&解释为后台执行,2和!=和0变成其他命令的参数。正确写法是加单引号:

tcpdump -i eth0 'tcp[13] & 2 != 0'

实操心得:我个人的习惯是,所有过滤器表达式一律加单引号,不管里面有没有特殊字符。这样永远不会因为 shell 解释而出错。

6.2 抓不到包?检查接口和方向

如果过滤器写对了但抓不到包,按以下顺序排查:

  1. 接口是否正确:tcpdump -D列出所有可用接口,确认你抓的接口有流量经过。比如容器环境下,流量可能走docker0或veth接口,而不是eth0。
  2. 方向是否正确:如果你写的是src host 10.0.0.1,但实际流量是从 10.0.0.1 发往其他主机的,那应该能抓到。但如果流量是经过本机转发的,源地址可能不是 10.0.0.1。
  3. 是否被 VLAN 或隧道封装:如果流量带 VLAN 标签,host和port过滤器可能匹配不到,需要用vlan限定词。
  4. 是否走了回环接口:本机进程之间的通信走lo接口,抓eth0是抓不到的。

6.3 过滤器语法报错?用 -d 参数调试

tcpdump 提供了-d参数,可以把过滤器表达式编译成 BPF 指令码并打印出来。如果表达式有语法错误,-d会直接报错,帮你定位问题:

tcpdump -d 'tcp and port 80'

输出是一串 BPF 指令,看起来像汇编。虽然不直观,但至少能确认表达式是否合法。如果-d能正常输出,说明语法没问题,抓不到包就是其他原因。

6.4 常见错误速查表

错误现象可能原因解决方法
syntax error过滤器语法错误用-d检查,确认括号和运算符
抓不到任何包接口选错tcpdump -D列出接口,换接口重试
抓到的包不完整默认截断长度 262144 字节加-s 0抓完整报文
过滤器被 shell 解释未加引号表达式加单引号
and/or逻辑不符预期优先级问题加括号明确逻辑
高流量下丢包缓冲区不足加-B增大缓冲区,或写文件
抓不到 VLAN 流量未处理 VLAN 标签加vlan限定词

6.5 一个真实的排查案例

某次线上服务出现间歇性超时,应用日志显示连接数据库偶尔要等 3 秒以上。我在应用服务器上抓包:

tcpdump -i eth0 -nn -s 0 -w db_timeout.pcap 'tcp and host 10.0.0.5 and host 10.0.0.10 and port 3306'

抓了 5 分钟,把 pcap 文件拉到本地用 Wireshark 分析。过滤tcp.analysis.retransmission后发现,应用服务器发出的 SYN 包有重传,第一次 SYN 发出后 1 秒才收到 SYN-ACK。继续看发现是中间网络设备的问题,不是数据库本身慢。

这个案例说明:tcpdump 的过滤器帮你把无关流量排除掉,但真正的分析往往需要在 Wireshark 里做更细粒度的过滤。tcpdump 负责“抓得准”,Wireshark 负责“看得细”,两者配合才是完整的工作流。

7. 过滤器与抓包策略的配合

7.1 先粗后细:两阶段抓包法

在实际排查中,我通常分两步走:

第一步,用宽松过滤器快速抓一份样本:

tcpdump -i eth0 -nn -s 0 -c 1000 -w sample.pcap 'tcp'

-c 1000表示抓够 1000 个包就停,避免文件过大。拿到样本后用 Wireshark 快速浏览,确认问题流量的特征。

第二步,根据第一步的发现,写精确过滤器重新抓:

tcpdump -i eth0 -nn -s 0 -w target.pcap 'tcp and host 10.0.0.5 and port 3306 and tcp[tcpflags] & tcp-rst != 0'

这样既能快速定位问题,又不会抓一堆无关数据。

7.2 环形缓冲区:长时间抓包的利器

如果需要长时间抓包,但磁盘空间有限,可以用-C和-W参数实现环形缓冲区:

tcpdump -i eth0 -nn -s 0 -C 100 -W 10 -w /tmp/capture.pcap 'tcp and port 443'

-C 100表示每个文件最大 100MB,-W 10表示最多保留 10 个文件,写满后自动覆盖最旧的文件。这样总占用空间控制在 1GB 以内,适合在服务器上长期运行。

配合精确的过滤器,这个方案可以在不影响业务的前提下,持续捕获关键流量,出问题时随时回溯。

7.3 过滤器表达式的可读性维护

如果你需要把过滤器写进脚本或配置文件,建议把复杂表达式拆成变量,加上注释:

FILTER_HOST="host 10.0.0.5" FILTER_PORT="port 3306" FILTER_PROTO="tcp" FILTER_FLAGS="tcp[tcpflags] & tcp-rst != 0" tcpdump -i eth0 -nn -s 0 -w output.pcap "$FILTER_PROTO and $FILTER_HOST and $FILTER_PORT and $FILTER_FLAGS"

这样下次修改时一眼就能看出每个部分的含义,不用重新推导整个表达式。我吃过这个亏——半年前写的一个复杂过滤器,半年后回头看完全看不懂,只能重新写一遍。

8. 从过滤器到分析:抓完之后做什么

抓包只是第一步,过滤器帮你拿到干净的数据,接下来的分析才是重头戏。几个常用的后续操作:

  • 用 tshark 做命令行分析:tshark -r capture.pcap -Y 'tcp.analysis.retransmission',直接过滤出重传包。
  • 用 capinfos 看抓包统计:capinfos capture.pcap,快速了解抓包时长、包数量、平均速率。
  • 用 editcap 切割大文件:editcap -c 10000 capture.pcap split.pcap,把大文件切成小份,方便逐个分析。

这些工具和 tcpdump 配合使用,能覆盖从抓包到分析的完整链路。tcpdump 的过滤器是入口,把好入口这一关,后面的分析效率会高很多。

我个人在实际操作中的体会是:过滤器写得好不好,直接决定了排查效率。同样一个问题,过滤器写得精确,可能十分钟就定位到原因;过滤器写得粗糙,抓了一堆无关数据,光筛选就要花半小时。花五分钟研究一下过滤器语法,省下的是后面几十分钟的无效劳动。这个投入产出比,怎么算都划算。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询