2902 字
约 9 分钟
14
S02-J05 · 抓包分析实战:tcpdump 与 Wireshark 的判读方法

"接口慢"是最容易吵起来的一句话:网络说应用写得烂,应用说网络在丢包,两边都有"证据"。抓包的价值不是"看到有包在飞",而是把抱怨切成三个可判定的问题:发出去没有、到没到、时间花在哪一段。抓错位置或读错标志位,只会把吵架升级成甩锅。


1. 抓包要回答的三类问题

问题 判据 该看什么
有没有发 本机出口有没该流的 SYN 抓本机网卡,看目标四元组
有没有到 对端是否回 SYN-ACK / 响应 抓对端或中间点,比同一序号
为什么慢 握手、首字节、重传、窗口、TLS 各占多少 时间差分解,不看总量看分段

先定问题再开抓。没问题驱动的抓包,只会产出一个几 GB 的文件和一句"看起来挺正常"。

2. 抓包点怎么选

位置 能看到 看不到 / 风险
本机网卡 eth0 本机发出的与收到的 回环流量、已被转发的流量
本机回环(lo) 同机进程间、代理前后 物理链路问题
容器内 eth0 容器视角收发 是否经过 NAT、qdisc 丢包
宿主机网桥 cni0 容器与外部之间的包 已被 DNAT 改写的地址
代理层 应用层请求与上游耗时 代理自身排队时间

抓错位置的典型错误结论:在宿主机 cni0 上只看到 ClusterIP,就判定"应用连错地址";实际是 kube-proxy 的 DNAT 在后面才改写。只在应用侧抓包,看到"SYN 已发出、无 SYN-ACK"就断言"对端没开端口"——也可能是防火墙静默丢弃(DROP 而非 REJECT,本机连 RST 都收不到)。判据:每个抓包点要能说清"之后包还经过谁",说不清就再抓一个点。

3. tcpdump 的最小命令集

# 全量抓、落盘、不做解析(生产首选:抓完再分析)
tcpdump -i eth0 -s 0 -w /tmp/cap-$(date +%H%M).pcap 'host 10.0.0.5 and port 5432'

# 只看握手与挥手,不落盘
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0'

# 环形缓冲:单文件 100MB,保留 20 个(约 2GB 上限)
tcpdump -i any -s 0 -C 100 -W 20 -w /tmp/ring.pcap 'port 8080'

# 读文件并加时间差,直接看间隔
tcpdump -nn -tttt -r /tmp/cap.pcap 'tcp and host 10.0.0.5'
参数 作用 不写会怎样
-s 0 抓完整帧 老默认 68 字节,应用层被切掉
-w 写 pcap 不解析 高流量下解析不过来,丢包
-nn 不解析名字 逐包查 DNS,抓包自己变慢
-i any 所有接口 cooked 模式,无以太头
-C / -W 按 MB 轮转、保留个数 单文件写满磁盘
-Z tcpdump 抓完降权 长期以 root 跑

-i any 的副作用:链路层类型变成 SLL,ether host、vlan 这类二层表达式失效;高流量下它聚合所有接口,一个 pcap 里可能有两条路径的同一条流,读时要靠 tcp.stream 区分。

4. BPF 写错导致的漏包

漏包比抓不到更危险:你以为看到了全部,其实只看到一部分。

写法 实际含义 问题
tcp port 80 or 443 (tcp port 80) or (port 443) 443 也匹配 UDP/QUIC
not port 22 and host 10.0.0.5 组合多了易错 复杂表达式必须加括号
host 10.0.0.5 and port 443 只匹配该地址 走代理时四元组是代理的
vlan and host 10.0.0.5 带标签才匹配 内核多已自动处理,加了反而漏
-s 100 截断到 100 字节 应用层解析失败,误判"无请求体"
# 只抓 SYN(不含 SYN-ACK)
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'

# 只抓 RST
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

# 抓两台主机之间的双向流量(括号不能省)
tcpdump -i eth0 -nn 'host 10.0.0.5 and (port 3306 or port 6379)'

5. Wireshark 的五个判读点

现象 过滤器 含义
三次握手 tcp.flags.syn==1 SYN/SYN-ACK/ACK,看 RTT 与重试
重传 tcp.analysis.retransmission 同 seq 再发,发送方认为丢了
快速重传 tcp.analysis.fast_retransmission 3 个 Dup ACK 后立即重发
Dup ACK tcp.analysis.duplicate_ack 接收方有缺口,重复确认同一 seq
复位 tcp.flags.reset==1 被拒或中断,看是谁发的

tcp.analysis.flags 是"任意异常"的合集过滤器,先扫一遍再逐项展开,比手工翻页快得多。

# 非 GUI 环境:直接统计异常与 RTT
tshark -r cap.pcap -q -z io,stat,1,"COUNT(tcp.analysis.retransmission)tcp.analysis.retransmission"
tshark -r cap.pcap -Y "tcp.analysis.flags" -T fields -e frame.number -e ip.src -e tcp.len | head
tshark -r cap.pcap -Y "tcp.flags.reset==1" -T fields -e ip.src -e ip.dst -e tcp.srcport

RST 的方向最容易被误读:服务端发 RST,多半是"端口没监听"或"连接关闭后收到数据";客户端发 RST,常见于主动关闭或有状态防火墙超时。见 RST 就断定网络设备重置,是跳步。

6. 重传与乱序:长得像,指向完全不同

维度 重传 乱序
序号 与之前某帧同 seq 同长度 新 seq,但小于期望值
触发方 发送方超时或收 Dup ACK 多队列/多链路
指向 丢包、拥塞、零窗口 链路聚合、ECMP 不均
抓包点 接收端"重复到达"也可能是设备复制 发送端看不到乱序

顺序:先看抓包点(发送侧还是接收侧),再看 seq 是否重复,最后归因。发送侧的重传 = 对方没确认(丢包或对端慢);接收侧重复到达 = 上游复制或抓包点重复。

7. TIME_WAIT 与端口耗尽在抓包里长什么样

现象 抓包里的样子 旁证
大量短连接 同一对地址间高频 SYN/FIN,tcp.flags.fin 密集 ss -tan state time-wait | wc -l
主动关闭方堆积 本端先发 FIN,进入 TIME_WAIT 60 秒 ss -tan state time-wait '( sport = :80 )'
端口耗尽 抓不到任何 SYN,应用报 Cannot assign requested address cat /proc/sys/net/ipv4/ip_local_port_range
服务端端口耗尽 SYN 到达但无 SYN-ACK,或直接 RST ss -lnt 的 Recv-Q 持续非零

端口耗尽的陷阱:连接压根没建起来,pcap 里自然什么都没有。"抓不到包"此时是正确观测,不是抓包失败——去应用日志与 ss 里找 EADDRNOTAVAIL。

8. 延迟分解:把"慢"拆成四段

段 度量 典型量级
TCP 握手 SYN→SYN-ACK 的 tcp.time_delta 同机房亚毫秒,跨地域数十毫秒
TLS 握手 ClientHello→Finished TLS1.3 一个 RTT,1.2 两个
请求到首字节 请求末包→响应首包 取决于服务端处理
数据传输 首字节→末字节 受带宽与窗口限制
# 按流导出关键时间点:每个流的首包、末包、帧数
tshark -r cap.pcap -T fields -e tcp.stream -e frame.time_relative \
       -e tcp.time_delta -e ip.src -e tcp.len -Y "tcp" | head -20

tcp.time_delta 是"相对同一流上一帧"的差,不是全局相邻帧的差。看握手与 ACK 间隔很直观;混流 pcap 里按 frame 排序求差,会得出完全错误的结论。

9. HTTPS:能看到什么,看不到什么

层 可见 说明
TCP 完整 握手、重传、窗口
TLS 握手 明文的 ClientHello/ServerHello SNI(未用 ECH 时)、版本、套件、证书链
TLS 记录层 类型、长度、时间 可算握手耗时与记录节奏
应用数据 不可见 只能看长度与方向

所以 HTTPS 上的结论止于"握手多久、记录多大、有无重传"。要判断"接口 500 还是 200",抓包给不了——看应用日志或代理 access log。解密要会话密钥(SSLKEYLOGFILE),是另一套前提。

10. 文件太大怎么办

# 按包数切分,便于分机器并行分析
editcap -c 200000 big.pcap part.pcap

# 只留某条 TCP 流
tshark -r big.pcap -Y "tcp.stream==7" -w stream7.pcap

# 合并多点抓包(同一时钟源时才有意义)
mergecap -w merged.pcap a.pcap b.pcap

# 抓的时候就用环形缓冲,别等抓完再切
tcpdump -i eth0 -s 0 -C 200 -W 10 -w /tmp/ring.pcap

时间戳精度也要交代:默认微秒,--time-stamp-precision=nano 可到纳秒,需网卡与内核支持;它记的是内核收到包的时刻,不等于链路真实到达时刻,微秒以下的差异别当证据。

11. 容器与 K8s 里在哪抓

场景 做法 注意
单容器 docker exec -it <id> tcpdump -i eth0 镜像常没装 tcpdump
镜像无 tcpdump nsenter -t $(pidof app) -n tcpdump -i eth0 需宿主机 root
共享 netns docker run --net=container:<id> nicolaka/netshoot 同一份流量
K8s 单 Pod kubectl debug -it <pod> --image=nicolaka/netshoot 临时容器共享 netns
Service 层 抓 cni0 或 veth 宿主侧 是 DNAT 前还是后,要确认

veth 是一对管子:容器 eth0 与宿主机 vethXXX 各一端。两边同抓各得一份,合并前先确认是哪侧,否则会把单向流量误判成"双向重复发送"。

12. 排错

现象 原因 处理
回环服务抓不到 抓 eth0,流量走 lo 改 -i lo 或 -i any
校验和全错 TSO/GSO 卸载,抓到的是未算校验和的帧 忽略校验和,或禁用卸载
过滤后一条没有 BPF 优先级写错、VLAN、走了代理 先全抓,再逐步加条件
见重传就断定丢包 抓点在发送侧,也可能对端未确认 到接收侧再抓,比对 seq
应用慢归因网络 只看到"响应晚到" 分解三段,配合服务端日志
时间差为 0 或乱序 多接口同抓,时钟交错 单接口抓,或按 tcp.stream 分组

13. 边界

问题 该用什么 理由
应用返回 500 应用日志 / access log 抓包看不到 HTTP 语义
是否丢包 两端抓包 + 重传统计 单点无法区分丢在哪段
内核丢包 netstat -s、ethtool -S、ss -s pcap 看不到内核/驱动丢弃
谁占带宽 sFlow/NetFlow 抓包只适合短时窗口
长期趋势 指标系统 pcap 留不住

14. 自测

  1. "接口慢"要先拆成哪三个可判定的事实?
  2. 在宿主机 cni0 上只看到 ClusterIP,能得出"应用连错地址"的结论吗?
  3. tcpdump -i any 抓到的帧为什么没有以太网头?
  4. tcp.analysis.retransmission 与 tcp.analysis.out_of_order 分别指向什么问题?
  5. 端口耗尽时 pcap 里为什么可能什么都没有?该去哪里找证据?
  6. HTTPS 流量里,哪一部分是可读的,哪一部分注定不可读?
S02-J05 · 抓包分析实战:tcpdump 与 Wireshark 的判读方法
https://www.clxhxhhr.top/posts/6037/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。