MTU 与路径 — 只因为大小就失败的通信
一句话总结
TCP 只根据本机接口的 MTU 决定报文段大小,不知道路径中间是否有更窄的区段;若阻断告知这一情况的 ICMP,就会形成只有大数据包悄然消失的黑洞。
为什么需要它
有一种故障的症状非常独特:
- 健康检查端点正常响应。
- 列表查询等响应稍大时,请求就一直卡住,没有报错。
- ssh 可以连接,但在大目录执行
ls -la时画面冻结。 - 某些网站停在 TLS 握手阶段。
出现这种组合时,几乎可以直接判断为路径 MTU 问题。它通常发生在新增 VPN、隧道、覆盖网络或路径变化后。
工作原理
MTU 是接口一次可发送的最大 IP 数据包大小,以太网默认是 1500 字节。TCP 减去 20 字节 IPv4 首部和 20 字节 TCP 首部,把 1460 字节设为 MSS,并在握手的 SYN 包选项中告知对方。
关键在于:双方都只根据自己的接口 MTU 决定 MSS。 没人知道中间是否有更窄的链路。即使双方同意交换 1460 字节,中间若有 1420 字节的隧道也无法兑现。
因此小请求能成功:健康检查用一个报文段即可通过,只有超过阈值的数据包会消失,所以症状按大小分化。 TLS 握手卡住也一样,ClientHello 很小,而服务器证书链可达数千字节,很快就会产生满尺寸报文段。
TCP 原本通过**路径 MTU 发现(PMTUD)**解决此问题。Linux 在 TCP 包上设置 DF(禁止分片)位;中间路由器收到大于下一跳 MTU 的 DF 包时将其丢弃,并以 **ICMP 类型 3 代码 4(Fragmentation Needed)**告知下一跳 MTU。发送方缓存该值,缩小报文段后重发。
若阻断该 ICMP,发送方会不断发送同样大的包并不断被丢弃,重传也没有用。连接仍处于 ESTABLISHED,直到应用自己的超时才结束,这就是 PMTUD 黑洞。
“为了安全阻断全部 ICMP”本身会制造故障。可酌情阻断 echo request/reply,但类型 3 代码 4 必须放行。IPv6 更无选择,因为路由器不负责分片,ICMPv6 Packet Too Big(类型 2)是必需的。
记住封装开销有助于诊断:VXLAN 50 字节(有效 MTU 1450),WireGuard 60–80 字节(1440–1420),GRE 24 字节(1476),IPsec ESP 最多约 73 字节。
实际工作中的表现
Kubernetes 因覆盖网络经常遇到此问题。节点接口为 1500 时,若 VXLAN 接口和 Pod eth0 也设为 1500,就会形成黑洞;VXLAN 占用 50 字节,应该设为 1450。典型症状是 Pod 间小请求正常、大正文 POST 卡住,kubectl exec 正常,但日志很多的 Pod 执行 kubectl logs 时中途停住。
现实方案是在隧道入口进行 MSS 钳制:路由器直接调低经过的 SYN 包中的 MSS,不再依赖 ICMP。但它只适用于 TCP,QUIC 等 UDP 协议仍须自行调整大小。
后续测验将确认什么
确认看到“小请求正常,大响应卡住”时能立即想到 MTU,并能解释为何它会无错误地停住。