小请求能用,大下载卡住,于是有人建议 MSS 钳制。这个建议经常对到值得按正确顺序一试,也经常错到盲用会白白浪费一下午。下面三项检查把真正的 TCP 尺寸问题与 UDP 问题、与坏掉的路径 MTU 探测、与一个永远看不到它该改写的那次握手的钳制区分开来。

先看故障的形状

尺寸故障有签名:连接建立,认证和小响应成功,然后一旦需要全尺寸分片,传输就无限期停住。能加载文字却永远加载不完图片的页面,是同一故障的缩小版。如果根本什么都连不上,问题在路径更早的环节,钳制帮不上忙。钳制修不好一次从未到达的握手。

这个签名之所以存在,是因为分片大小正是在握手中商定的。每一方都以 TCP 选项通告最大分片大小,发送方尊重对端的通告。如果路径扛不住那么大的分片,故障会在稍后、发送方终于尝试一个时出现。

读这个症状时要把传输层放在眼里。钳制是改写 TCP 选项,所以同一条隧道下卡住的语音或视频通话不是 MSS 问题,也不会因此改善。在碰 TCP 选项之前,先把传输层写下来。

给路径定价,再和隧道比一比

有两个数字很重要,而且常被混淆。路径 MTU 是路径在不分片情况下能承载的最大数据包,IPv4 通告的 MSS 就是这个值减去 IP 和 TCP 首部的 40 字节,IPv6 则减 60。隧道封装在内层数据包开始之前就吃掉了预算的一部分。

ping -M do -s 1400 198.51.100.20
tracepath 198.51.100.20

带 do-not-fragment 位的 ping 会告诉你该载荷大小的数据包能否存活;调整大小直到失败,你就凭经验找到了路径上限。tracepath 的输出报告沿途探测到的 MTU,能用时更快,中间跳过滤探测时则不太可靠。

现在减去隧道的开销。外层 IP 首部、UDP 首部和隧道专用首部都从同样的 1500 字节里出,所以典型以太网底层上可用的内层 MTU 比物理链路暗示的要低。如果算出的内层 MTU 低于对端通告的值,你找到的是真正的钳制候选,而不是猜测。如果算出的值已经高于对端通告,就停在这里,去别处找。

把钳制放在握手经过的地方

钳制的工作原理是在 SYN 和 SYN-ACK 数据包穿过网关时改写其中的 MSS 选项。如果规则挂在两个方向都不会经过的接口上,协商会原样完成,症状依旧,所以位置比数值更决定结果。

SymptomWhat it points toNext check
Handshake completes, large transfer stallsSegment size larger than the path can carryPath MTU against the tunnel's effective MTU
Nothing connects, small or largeRouting, firewall or keying failureNot MSS; look at the tunnel state
Only UDP applications stallDatagram size or a dropped ICMP errorPlatform-specific UDP handling, not clamping
Fixed after reconnect, returns laterClamp applied to one direction or one interfaceRule placement and the handshake path

方向是另一半。只钳一个方向只能修好一半流量,剩下的会以看似时断时续的方式失败,所以要应用到被转发流量的两个方向。隧道接口的经典 Linux 形式是一条匹配 SYN 数据包的 mangle 规则:

iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

注意 --clamp-mss-to-pmtu 实际做了什么:它从路由的 MTU 推导数值,而那并不总是隧道真实的载荷预算。在内外路由不一致的基于路由的隧道上,前一步算出的显式值比自动值更可预测。

用新连接验证,并让反馈活着

已建立的连接会保留它协商到的 MSS,所以在同一会话上测试证明不了什么。改动之后开一个新连接,直接读取协商值,而不是从一次碰巧完成的传输去推断。

ss -tin dst 198.51.100.20
iptables -t mangle -S | grep TCPMSS

第一条命令报告套接字的 MSS 及其拥塞状态,这是钳制生效的最快确认。第二条确认你以为加上的规则确实存在,而且就在它被放入的那张表里。

然后保护你没有修好的机制。路径 MTU 探测依赖 ICMP fragmentation-needed 报文到达发送方,所以丢弃它们的防火墙即使钳制正确,也会继续弄坏大传输。为隧道路径放行这些报文,或者接受钳制是在补偿你自己选择的策略。

最后,别把钳制当成调参旋钮。它限制分片大小,在根本不需要它的路径上会损失吞吐。三项检查之后再应用,它是有的放矢的修复;一上来就应用,它是掩盖原始故障、并给之后每次测试多加一个变量的改动。这笔交易值得有意为之,而不是默认去做。

记下你测到的路径 MTU、减去的隧道开销、添加的规则以及之后看到的 MSS。四个数字能让下一次事故变成查表,而不是调查。

相关文章