VPN 网关上的 MSS 钳制:能修什么,不能修什么
钳制会改写 TCP 握手里的 MSS,让分片塞得进隧道。它从不修复 UDP,也不修复坏掉的路径 MTU 探测。三项检查,按顺序来。
小请求能用,大下载卡住,于是有人建议 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 选项。如果规则挂在两个方向都不会经过的接口上,协商会原样完成,症状依旧,所以位置比数值更决定结果。
| Symptom | What it points to | Next check |
|---|---|---|
| Handshake completes, large transfer stalls | Segment size larger than the path can carry | Path MTU against the tunnel's effective MTU |
| Nothing connects, small or large | Routing, firewall or keying failure | Not MSS; look at the tunnel state |
| Only UDP applications stall | Datagram size or a dropped ICMP error | Platform-specific UDP handling, not clamping |
| Fixed after reconnect, returns later | Clamp applied to one direction or one interface | Rule 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。四个数字能让下一次事故变成查表,而不是调查。