2719 字
约 9 分钟
13
S02-A27 · 分片与 PMTUD:为什么"改小 MTU"能修好一个连不上的接口

换了一条专线,接口 90 % 的请求正常,少数请求卡住直到超时。抓包看得很清楚:客户端把 1,4xx 字节的报文发了,服务端应"给我回个 ICMP 说太大",但这条 ICMP 从来没到——中间有一个设备的 ACL 把 ICMP 全丢了。双方于是各自重传,卡在那个永远送不到的报文上。

这类故障有个固定形状:小请求全好、大请求必坏、且坏的方式是"超时"不是"报错"。它不属于应用问题,也不属于带宽问题,是"链路允许的报文上限"与"两端默认假设"对不上,而发现并协商这个上限的机制又被静默地阻断了。

这篇拆三层:分片发生在哪、PMTUD 靠什么商、以及为什么最后大家都改用"直接钳小 MSS"。


1. 分片发生在 IP 层,代价由谁付

以太网默认 MTU 1500,减去 20 字节 IP 头与 20 字节 TCP 头,可得 MSS 1460。超过这个数的 IP 报文必须分片;而分片只在 IPv4 允许,IPv6 明确禁止中间设备分片。

层 上限 谁负责遵守 超了会怎样
链路 MTU 1500(以太网) 下一跳接口 丢弃或分片
IP 报文 受 MTU 约束 发送端 IPv4 可分片,IPv6 直接丢并回 ICMP
TCP MSS MTU − 40 两端协商 由 MSS 保证不分片
应用报文 受 MSS 与缓冲约束 应用 粘包、拆包
隧道封装 MTU − 封装头 隧道端点 需要更小的 MSS

第三行是真正省钱的一层:只要 MSS 协商正确,IP 层根本不需要分片。所以"分片"这件事,本质是"MSS 没协商对时的兜底",而兜底一失败,就变成一包都送不过去。

// 同一条 1500 MTU 链路上,一次 3,000 字节的 HTTP 体
不分片(MSS 1460):1500 + 1500 + 80      三段,正常
分片(MSS 1500):  1500 + 1480 + 20      末片只有 20 字节载荷
// 后者危险:任何一片丢失,整个 IP 报文都要重传(TCP 也救不了)

2. PMTUD:靠 ICMP 商量出来的上限

路径 MTU 发现(PMTUD)的流程只有三步:发送端把 DF(Don't Fragment)置位,中间设备发现报文超过自己的 MTU 就回一条 ICMP Fragmentation Needed(IPv6 是 Packet Too Big),发送端据此调小 MTU 重发。

环节 期望行为 现实里的失效方式
发送端 置 DF 位,收到 ICMP 后降 MTU 内核未开 PMTUD,或忽略 ICMP
中间设备 超限时回 ICMP ACL 丢 ICMP、限速、只放 echo
隧道端点 正确计算并回填 MTU 封装后未扣头,仍按 1500 转发
负载均衡 透传 ICMP 或代为生成 不回 ICMP,静默丢弃

四个环节任一断开,就是 PMTUD 黑洞:发送端永远不知道上限在哪,永远重发同一个过大的报文,用户看到的是"页面卡住然后超时"。

// 三步确认是不是黑洞:DF 置位、逐步加大、看在哪一档断
$ ping -M do -s 1472 -c 2 <target>      # 1472 + 28 = 1500,通 → MTU 至少 1500
$ ping -M do -s 1400 -c 2 <target>      # 通
$ ping -M do -s 1472 -c 2 <tunnel_hop>  # 断,而 1400 通 → 黑洞在 tunnel_hop
// IPv6 下没有 -M do(禁止分片),直接看 Packet Too Big 是否回来
$ ping6 -s 1452 -c 2 <target>

3. 隧道/VPN 为什么必然踩这个坑

任何封装都会吃掉 MTU:IPsec 约 50–70 字节、GRE 24、VXLAN 50、WireGuard 60。1500 的链路,套一层 VXLAN 就只剩 1450,再套 IPsec 只剩 1380 左右。而两端如果不重新协商 MSS,仍然按 1460 发,超出的部分全靠 PMTUD 救——偏偏隧道端点的 ICMP 生成经常没配。

封装 典型额外开销 建议内层 MTU 常见症状
GRE 24 字节 1476 大响应卡住
VXLAN 50 字节 1450 容器间大包超时
IPsec(ESP) 50–73 字节 1400–1420 只有大请求坏
WireGuard 60 字节 1420 SSH 卡在登录后
双层隧道 100+ 字节 1350 以下 几乎不可用

第三列就是"改小 MTU 能修好"的全部秘密:不是让链路变宽,是让两端从一开始就不发过大的包,把 PMTUD 这条不可靠的协商路径整个绕开。


4. MSS clamping:把"协商"变成"强制"

生产上最稳的做法不在终端改 MTU(改不全:容器、LB、sidecar 各有各的),而在路径上的第一个可配置点做 MSS clamping:把 SYN 里的 MSS 选项改写成你想要的值。

位置 能否 clamp 说明
客户端主机 能(路由 MTU) 只能管自己,管不了服务端
服务端主机 能 同上
隧道端点(GRE/VXLAN) 能,首选 一次配置覆盖整条隧道
云 LB / 网关 多数能 需确认是否改写 TCP 选项
CDN / 反代 部分能 通常在回源侧生效
// 隧道端点做 MSS clamping(iptables),把过大 MSS 改写为 1360
$ iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --clamp-mss-to-pmtu
// 或显式指定,避免依赖 PMTU 探测结果
$ iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --set-mss 1360
// nftables 等价写法
$ nft add rule inet filter forward tcp flags syn tcp option maxseg size set 1360

--clamp-mss-to-pmtu 看起来更聪明,但它依赖内核对路由 MTU 的认知——在容器与 overlay 网络里这个值经常是错的。显式 --set-mss 更稳,代价是你要自己算出安全值。


5. 什么现象指向 MTU,什么现象不指向

这是本篇最实用的一张表。MTU 问题的形状太特殊,认错方向会浪费一整天。

现象 指向 MTU? 理由
小请求正常,大响应超时 是 典型的单向超限
TLS 握手成功、首个大数据包卡住 是 证书链大包被丢
所有请求都慢但都成功 否 更可能是拥塞或带宽
连接直接 RST 否 端口/策略/重置,不是分片
curl 小文件正常、大文件 0 字节 是 典型 PMTUD 黑洞
DNS 解析偶尔超时 可能 大响应 + EDNS 缓冲
SSH 能登录、ls 卡住 是 登录包小、目录列表大
// 三问排除法,比翻参数快
1 报文大小是唯一变量?  否 → 查拥塞与带宽,结束
2 只有单向坏?          是 → 只 clamp 出口方向,别两端各改一半
3 有隧道 / overlay?    是 → 先改隧道端点 MTU,其次 clamp MSS
// 三问都不成立 → 看 ICMP 是否被丢,而不是继续改 MTU

第六行值得单独说:DNS 的 UDP 响应超过 512 字节会走截断/EDNS,链路把大 UDP 丢了,就表现为"偶尔解析失败"。这类问题在 VPN 里非常常见,而它看起来完全不像网络层问题。


6. 排错:五种"以为是带宽"的失败

症状 八成原因 处理方向
大 POST 超时、小 GET 正常 PMTUD 黑洞 隧道端点 clamp MSS
改 MTU 后别的服务坏了 只改了一端/一处 在路径公共点统一改
容器里通、宿主不通 overlay MTU 未扣封装头 调 CNI 的 mtu 参数
偶发超时、重传率高 分片末片丢失 消除分片(clamp MSS)
IPv6 全不通、IPv4 正常 中间设备不回 Packet Too Big 降 announced MTU 或修 ICMP
// 容器与 overlay 里最常见的一处:CNI 的 mtu 没扣封装头
$ ip link show vxlan0 | head -2
$ docker network inspect bridge -f '{{.Options}}'
$ kubectl get cm -n kube-system kube-flannel-cfg -o yaml | grep -i mtu
// VXLAN 在 1500 链路上应配 1450;配成 1500 就是 100% 黑洞

第二行是最常见的二次事故:只把客户端 MTU 调到 1400,服务端仍按 1460 发,于是反方向仍然黑洞。MTU 是双向的,改一半等于没改。


7. 边界:什么情况不该动 MTU

场景 为什么不该动 该做什么
链路本身丢包严重 问题是丢包,不是上限 查重传与拥塞窗口
单条流慢、多条流正常 是拥塞控制,不是分片 查 RTT 与 BDP
应用侧报文本来就很碎 分片不是瓶颈 查应用层缓冲
云厂商托管 LB 后 改不了路径中间点 用其 MTU 建议值
只有 UDP 大包失败 常见于 EDNS/QUIC 单独看 UDP 的处理链

判断口径一句话:当"报文大小"是唯一能区分成功与失败的变量时,才轮到 MTU;如果缩小报文也不改善,那从来不是分片问题。这条判据能省掉大量无用功——因为它把"大包失败"与"大包恰好赶上拥塞"区分开了,而后者才是多数"看起来像 MTU"的现场。


8. 自测

  1. MSS 与 MTU 的数值关系是什么?为什么"MSS 协商正确时 IP 层不需要分片"?
  2. PMTUD 的三步流程里,哪一步被 ACL 丢弃会造成黑洞?表现为什么现象?
  3. 为什么隧道必然会踩这个坑?举两个封装的开销数字。
  4. MSS clamping 放在哪一层最有效?--clamp-mss-to-pmtu 为什么不如显式值稳?
  5. "DNS 偶尔解析失败"与 MTU 的关系是什么?
  6. 什么判据能让你快速排除 MTU 方向?

9. 一句话总结

MTU 故障的形状是"小包全好、大包必坏、坏法是超时",根因是两端的默认假设(1500/1460)与链路上真实上限不一致,而发现不一致的机制(PMTUD 的 ICMP)又被静默丢弃。修法不是把链路变宽,而是在路径的公共点强制钳小 MSS,让过大的报文根本不被发出。判据很朴素:报文大小是唯一能区分成败的变量时才是 MTU,否则去查拥塞。

S02-A27 · 分片与 PMTUD:为什么"改小 MTU"能修好一个连不上的接口
https://www.clxhxhhr.top/posts/6726/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。