怎么读抓包 (概念)
一句话总结
抓包是判定“谁在说谎”的最终裁判。不过本实验环境没有抓包权限,所以先从概念上掌握读法,实验则用 ss 和 /proc/net 代替。
为什么需要这个
日志是应用的说法,ss 是内核的摘要。两者都不能直接告诉你“数据包实际上是怎么往来的”。因此在下面三种情况下,最终还是需要抓包。
- 双方日志各说各话时(客户端说发了,服务器说没收到)
- 需要查看重传、分片这类只发生在内核内部的事情时
- 需要确认中间设备(代理、LB、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 的 veth 抓包。
所以实际工作的顺序就固定为:先用 ss 和 curl -w 尽量缩小范围,只有当双方说法仍有分歧时才动用抓包。 抓包很强大,但代价高。
下一步要做的事
本课程的实验全部不使用抓包。取而代之的是,其中有一步要直接读取 /proc/net/tcp,确认内核持有的套接字状态——这是不抓包时最接近内核真相的方法。