怎么读抓包 (概念)
一句话总结
抓包是判断“到底谁在说谎”的终极裁判。不过,本实验环境没有抓包权限,因此先从概念上掌握如何阅读抓包结果,实际操作则用 ss 和 /proc/net 代替。
为什么需要抓包
日志是应用程序的一面之词,而 ss 是内核提供的摘要。两者都不能直接呈现“数据包实际是怎样往返的”。因此,在下面三种情况下,最终还是需要抓包。
- 两端日志各执一词时(客户端称已经发送,服务器却称没有收到)
- 需要观察重传、分片等只发生在内核中的事件时
- 需要确认中间设备(代理、负载均衡器、NAT)是否改写了某些内容时
它是如何工作的
tcpdump 真正必需的选项屈指可数。
tcpdump -nn -i any 'host 10.0.3.11 and tcp port 8080'
tcpdump -nn -i any 'icmp[icmptype] == 3 and icmp[icmpcode] == 4'
tcpdump -nn -i any -w /tmp/cap.pcap -c 2000
-nn表示不解析主机名和端口名,从而避免在抓包过程中又发起 DNS 查询。-i any表示监听所有接口,在容器环境中尤其有用。- 使用
-w保存为文件,再用 Wireshark 打开分析。直接从屏幕输出中只能看出大致流向。
阅读时最关键的是理解 TCP 标志的表示法。
| 表示 | 含义 | 此时发生的事情 |
|---|---|---|
[S] |
SYN | 尝试建立连接 |
[S.] |
SYN+ACK | 对端接受连接 |
[.] |
ACK | 确认数据 |
[P.] |
PSH+ACK | 传输数据 |
[F.] |
FIN+ACK | 开始正常关闭 |
[R] / [R.] |
RST | 拒绝连接或强制关闭 |
只要掌握这张表,就能一眼区分两类故障。
- refused:发出一个
[S],收到一个[R.]后就结束。总共只有两行。 - timeout:同一序列号的
[S]按 1 秒、2 秒、4 秒的间隔反复出现,完全没有任何响应行。
诊断 MTU 黑洞时,要捕获 ICMP 类型 3、代码 4(Fragmentation needed)。这也解释了为什么“为了安全而封锁全部 ICMP”的策略本身会成为故障原因。最多只能考虑阻止 echo request/reply,类型 3、代码 4 必须放行。在 IPv6 中,Packet Too Big(类型 2)承担同样的职责,而且此时甚至没有选择余地。
实际工作中会遇到的情况
在容器中抓包并不容易。 像本实验环境这样移除 capability 后,tcpdump 就无法打开套接字。在 Kubernetes 中,标准的绕行方法是使用临时容器(kubectl debug),将诊断 Pod 接入同一个网络命名空间。如果能够登录节点,也可以在节点上指定 Pod 的 veth 进行抓包。
因此,实际工作中的处理顺序逐渐固定下来:先用 ss 和 curl -w 尽可能缩小范围,只有双方的说法依然矛盾时才开始抓包。 抓包非常强大,但成本也很高。
接下来要做什么
本课程的所有实验都无需抓包。作为替代,其中包含直接读取 /proc/net/tcp、确认内核所保存套接字状态的步骤——这是不抓包时最接近内核真相的方法。