LabHub
学习 学习路径 课程

Linux 网络诊断

小请求通得过,大响应卡住

在 LabHub 中继续学习

一句话总结

如果用 curl 能收到 header,却在正文处停住,十有八九是 MTU 黑洞。 能够建立连接,与大数据包能够通过,是两个不同的问题。

流程图: MTU 黑洞 · 悄悄丢弃了大数据包 · 两端的情况 · ICMP "Fragmentation Needed"

为什么需要了解这一点

这种症状会困扰人很久。

因为“能够连接”,所以不会怀疑防火墙;又因为“偶尔能用”,所以判断也不是 DNS。 真正原因是路径中的某个位置悄悄丢弃了大数据包

MTU 与 PMTUD

MTU(Maximum Transmission Unit)是一次能够发送的数据包最大尺寸。 以太网通常为 1500 字节。

问题出在路径中间存在 MTU 更小的区段时。

클라이언트(1500) → 라우터 → VPN 터널(1400) → 서버(1500)

TCP 建立连接时,双方会互相告知“我最多可以接收 1460 字节(MSS)”。 但这只是两端的情况,不代表中间区段的情况。

正常情况下,它会这样工作:1500 字节的数据包到达 1400 的区段时, 对应 router 会发送 ICMP "Fragmentation Needed",告知“数据包太大,请缩小到 1400”。 发送方缩小尺寸后重新发送。这个过程称为 Path MTU Discovery

阻止 ICMP 会形成黑洞

许多组织认为“ICMP 很危险”,于是在防火墙中将其全部阻止。随后就会发生以下过程。

  1. 大数据包到达 MTU 较小的区段
  2. router 将其丢弃并发送 ICMP
  3. ICMP 被防火墙阻止
  4. 发送方得不到任何消息,继续以相同尺寸重传
  5. 永远失败,也没有错误消息

这就是 PMTUD 黑洞。小数据包(handshake、header request)可以通过, 只有大数据包(正文)会消失,所以症状才如此令人困惑。

两端链路都是 1500 字节,只有中间的 VPN tunnel 是 1400 字节。小请求会直接通过狭窄区段,但 1500 字节的数据包会在入口处被悄悄丢弃,而用于告知该事实的 ICMP fragmentation needed 又被防火墙阻止。发送方得不到任何消息,只能不断以相同尺寸重传

诊断——无需特权的检查方法

通过 ping -M do -s 改变尺寸进行测量是教科书式方法,但 ping 需要 NET_RAW capability,在受限环境中无法使用。 好在还有其他方法。

1. 检查路径和 MTU

ip route get 8.8.8.8
# 8.8.8.8 via 10.0.0.1 dev eth0 src 10.0.0.5
ip -o link show          # 각 인터페이스의 mtu 값
cat /sys/class/net/eth0/mtu

2. 改变响应尺寸进行复现

curl -s -o /dev/null -w '%{size_download} %{time_total}\n' \
  'https://api.example.com/items?limit=1'
curl -s -o /dev/null -w '%{size_download} %{time_total}\n' \
  'https://api.example.com/items?limit=1000'

只有小响应成功,是一个强烈信号。

3. 分开检查 header 与正文

curl -I https://target/            # 헤더만 → 성공?
curl --max-time 5 https://target/  # 전체 → 멈춤?

4. 查看 socket 状态

ss -tin dst 10.0.3.20
# ... mss:1460 ... retrans:0/14 ...

retrans 持续增加,表示正在反复发送同一个数据包, 这是 MTU 黑洞的典型指纹。

应对方法

应对方式 方法 备注
允许 ICMP 至少在防火墙中开放 type 3 code 4 根本解决方案
降低 interface MTU ip link set eth0 mtu 1400 需要特权
MSS clamp 在 router/firewall 中让 MSS 与路径匹配 常见现场方案
缩小应用响应 pagination 只是绕过问题

现场最常使用的是 MSS clamp。如果 gateway 把 TCP handshake 的 MSS 值降低到符合路径 MTU,最初就不会生成大数据包。

同时检查路由

也有可能不是 MTU。如果 ip route get 指向的 interface 与预期不同, 问题就在路径本身。

ip route get 10.0.5.10
# dev eth1 이 나온다면 — eth0 로 갈 줄 알았는데 아니었다
ip rule show           # 정책 라우팅이 있는가
ip route show table all

开启 VPN 后,default route 整体发生变化的情况很常见。此时本应发往内部网络的 流量可能会进入 VPN,反之亦然。

实际工作中的表现

后续测验将检查什么

检查为何只有小请求成功的现象指向 MTU 黑洞,以及能否把 ss -tin 中 不断增加的重传与其他正常指标区分开。