使用 tcpdump 捕获网络数据包:Red Hat 实战指南 - 夜莺博客

使用 tcpdump 捕获网络数据包:Red Hat 实战指南

网络故障排查到数据包层面时,抓包的质量直接决定分析效率——抓错接口、用文本输出、过滤太狠,都可能让一份抓包文件失去价值。Red Hat 客户门户的这篇知识库文章总结了一套经过支持团队验证的抓包规范:在连接两端同时抓包、问题发生时再抓、保存二进制文件、限制每个包的大小、必要时使用滚动捕获,以及如何把抓包文件正确压缩后上传给支持团队。本文把这份指南整理成可直接照做的清单。

下面在原指南的基础上补齐两个容易被忽略的环节:过滤器怎么写才不会把证据滤掉,以及抓到的包该看哪些字段才能下结论。

捕获提示(关键规则)

  • 在连接两端(客户端和服务端)同时运行抓包。
  • 确保在问题发生时抓包。
  • 保存到本地文件系统,并记录系统时区,便于与日志关联。
  • 难以复现的问题或文件过大时,使用滚动捕获。
  • 使用 tcpdump -s 限制每个包保存的信息量。
  • 不要用文本输出,使用二进制格式(.cap / .pcap)。
  • 避免 tcpdump -i any,确保在正确的接口上抓包。
  • 尽量不过滤;必须过滤时,注意别丢弃对话的某一个方向。

这几条规则背后的理由是统一的:抓包是一次性的机会,问题发生的时间窗口错过就无法重来,所以在开始之前要把「抓得全、抓得对、将来还能读」这三件事一次性做对。

为什么必须在两端同时抓包

单端抓包只能证明「本机发出了什么」或「本机收到了什么」,无法区分丢包发生在中间链路、对端主机,还是本机协议栈。两端都抓以后,用同一个特征包(比如一个特定的 TCP 序列号或一个 ICMP 请求的 ID)就能判断它是在哪一跳消失的:客户端有、服务端没有,说明丢在网络上或对端之前;服务端也收到了但客户端没收到回应,说明问题在对端处理或返回路径。只抓一端时,这两种情况在文件里长得一模一样。

为什么不要用 -i any

-i any 让 tcpdump 走 Linux 的抓包「伪设备」,保存的链路层头部是 Linux cooked 格式(SLL)而非标准 Ethernet,在 Wireshark 里看不到正常的 MAC 地址和以太类型,跨 VLAN、跨 VRF 时尤其难分析,同一个包还可能出现两次。正确做法是先用 ip link show 确认业务流走的接口,再明确指定。

基本抓包命令

# 抓取 eth0 端口 80 的流量并写入文件
tcpdump -i eth0 port 80 -w /tmp/http.cap

# 限制单个包长度,避免超大包
tcpdump -i eth0 -s 128 -w /tmp/sample.cap

# 用 -r 验证已抓到的流量
tcpdump -r /tmp/client.cap

过滤器怎么写才不丢证据

抓包文件越大越难分析,所以过滤几乎是必需的;但过滤器写错,丢掉的往往是唯一能说明问题的那个方向。BPF 过滤语法的核心是表达式之间用 and、or、not 组合,方向用 src、dst 限定:

# 只抓一对主机的双向对话(最常用的写法)
tcpdump -i eth0 -nn host 10.0.0.5 and host 10.0.0.9 -w /tmp/pair.cap

# 只抓某个网段到某端口
tcpdump -i eth0 -nn net 10.0.0.0/24 and port 443 -w /tmp/https.cap

# 只抓 TCP 建连/断连标志位,用来数连接次数
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'

# 排除噪声:不要 ARP、不要管理 SSH,保留剩下的一切
tcpdump -i eth0 -nn not arp and not port 22 -w /tmp/clean.cap

# 只抓 ICMP,看是否有「需要分片」或不可达消息
tcpdump -i eth0 -nn icmp

最容易犯的错误是在一端只写 src 或 dst:服务端只抓 dst port 443 时返回流量完全不在文件里,看起来像「服务端没回应」,实际只是没抓到。所以在磁盘和时间允许时,先按宿主或网段抓全量,再在分析阶段用 Wireshark 的显示过滤器细化更安全。

读懂抓包里的证据:常见故障特征

抓包本身不是结论,能从里面读出什么才是。下面这张对照表是排障时最常用的几个特征:

抓包中的现象 含义 下一步查什么
只有 SYN,没有 SYN-ACK 请求到达了对端或被中间设备丢弃 对端是否在听、防火墙规则、回程路由
立即出现 RST 端口未监听,或被策略主动拒绝 服务进程、监听地址、安全组/ACL
同一序列号反复重传 丢包或对端接收窗口问题 链路误码、拥塞、对端缓冲
窗口值为 0(Zero Window) 接收方应用处理不过来 应用线程、慢查询、GC 停顿
ICMP「fragmentation needed」 路径 MTU 小于报文长度,典型 MTU 黑洞 隧道/PPPoE 的 MTU 与 MSS 钳制
ICMP unreachable / admin prohibited 中间设备明确拒绝 防火墙策略与路由黑洞
TCP 三次握手正常但应用层无响应 问题在应用而非网络 应用日志、后端依赖

重传与乱序看起来很像,含义完全不同:重传说明包丢了,要查链路和拥塞;乱序说明包只是走散了,通常无害。判断方法见 Wireshark 重传与乱序分析。

# 快速统计:每秒包数,用来判断问题发生的时间点
tcpdump -r /tmp/client.cap -nn -tttt | head -20
tshark -r /tmp/client.cap -q -z io,stat,1
tshark -r /tmp/client.cap -Y "tcp.analysis.retransmission" | wc -l
tshark -r /tmp/client.cap -Y "icmp.type == 3" -T fields -e ip.src -e icmp.mtu

时间戳与时区对齐

抓包的价值取决于能不能和日志、监控、变更记录对上时间,所以要做两件事:用 -tttt 输出带日期的绝对时间(而不是默认的相对秒数),并在抓包前确认两端系统时间已同步;同时把时区写进抓包文件说明——同一个时间戳在 UTC 与本地时间之间相差 8 小时,足以让跨午夜的故障分析跑偏。

包长截断(-s)怎么选

-s 决定每个包最多保存多少字节。选得太小,分析时看不到需要的内容;选得太大,磁盘很快写满。经验规则:

  • -s 0:保存完整包,只在需要看应用层载荷(HTTP 头、TLS 握手、私有协议)时使用。
  • -s 128:覆盖 TCP/IP 头加常见应用层头部,是 Red Hat 支持团队建议的折中值,文件体积通常只有全量抓包的十分之一。
  • -s 96:只够 IPv4 + TCP 头,适合纯连接性排查。

截断的包在 Wireshark 里显示为「truncated」,事后无法补救,只能重抓;不确定时宁可加大 -s,用滚动捕获控制体积。

滚动捕获(长时间抓包)

长时间抓包时让文件无限增长不是好主意。-w path -W n -C m 指示 tcpdump 创建每个约 m MB 的 n 个文件(path0 到 path(n-1)),写满后循环覆盖。注意:

  • tcpdump 默认以 tcpdump 用户运行,需保证该用户有写权限。
  • 用 -Z userID 可切换运行身份。
  • 文件大小和数量组合必须满足捕获需求——在相关文件被覆盖前要有足够时间抓到目标事件。
tcpdump -i eth0 -w /tmp/rolling.cap -W 10 -C 100

上面是「10 个 100 MB 文件,总计 1 GB」。1 GB 大约只相当于几十秒到几分钟的全速流量,对间歇性故障要留足轮转窗口:-W 20 -C 200(4 GB)更实用。希望文件名带时间戳时,可用 -G 按时长切分:

# 每 300 秒切一个文件,文件名带时间戳
tcpdump -i eth0 -w '/tmp/cap-%Y%m%d-%H%M%S.pcap' -G 300 -W 48 -Z root

# 查看目录占用,避免写满根分区
df -h /tmp && ls -lh /tmp/cap-*.pcap | tail

另外一点:别把文件写到 /tmp,很多发行版的 /tmp 是占内存的 tmpfs,应写到 /var/tmp 或专门的数据盘,避免抓包把磁盘写满引入二次故障。

权限与常见报错

抓包需要网络管理权限,普通用户直接运行会失败。常见报错与处理:

# 报错:You don't have permission to capture on that device
sudo tcpdump -i eth0 -nn -w /tmp/a.cap

# 或者给二进制授予必要的能力,让普通用户也能抓包
sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump

# 报错:No such device 或 interface not found
ip link show                    # 确认真实接口名(ens5/enp1s0 等)
ip -br addr                     # 看接口是否 UP,是否拿到地址
  • 云主机上接口名往往不是 eth0,而是 ens5、enp1s0 之类,脚本里写死 eth0 是常见错误。
  • 容器里抓包要进对应的 network namespace(nsenter -t <pid> -n tcpdump ...),否则看到的只是宿主机默认网络。

压缩与上传

大文件(几百 MB 甚至几 GB)用 gzip 压缩(bzip2 可能让 tshark 等工具无法读取):

gzip /tmp/client.cap
gzip /tmp/server.cap

把客户端和服务端的 *.cap.gz 附加到支持问题单或上传到支持团队的 sFTP 服务器。

文件过大无法一次上传时,用 split -b 100M 分片并说明重组顺序;压缩前先算 sha256sum 并记录,传输损坏可以自证。命名建议带角色与时间,例如 client-web01-20260914-0312.cap.gz,一眼就能分清哪端是哪端。

安装 tcpdump

# RHEL 5 及以上
yum install tcpdump
# 或
dnf install tcpdump

只想做图形化分析时,同一台机器上通常还需要 wireshark-cli(提供 tshark、capinfos)和 wireshark 本体;Debian/Ubuntu 上的包名是 tcpdump 与 tshark。生产服务器上不建议安装图形界面套件,把 .cap.gz 拉到本机分析即可。安装后用 tcpdump --version 确认 libpcap 版本,某些加密或隧道协议的解析依赖较新的 libpcap。

结合命令选型,可先读本站的《17 个最佳 Linux 网络和故障排除命令》;设备侧抓包可参考 SONiC 排障指南。如果抓包里看到 ICMP「需要分片」或大包发不出去,请对照 TCP MSS 钳制与 PMTUD 排障 一起分析——这类问题在应用侧通常表现为「小请求正常、大响应卡死」。

原文链接:https://access.redhat.com/solutions/1382953