ping 能通,大文件传输却卡住,而隧道还报告自己健康。有人降低接口 MTU,另一个人限制 MSS,偶尔问题会因无人记录的原因消失。两个数字描述的是不同对象,所以改错的那个会得到看起来像碰运气的修复。流程是:先确定层次,再做对应的调整,然后在新连接上测试。

两个数字,两个对象

MTU 属于链路或接口:它是不拆分即可交付的最大数据包。MSS 属于 TCP:它是一段报文能承载的最大 TCP 载荷量,并且不含 TCP 和 IP 首部。在运行 IPv4 的普通以太网链路上,1500 字节的 MTU 得到熟悉的 1460 字节 MSS。这两个数字描述不同层次,也由不同机制交换。把它们混为一谈很容易,因为两者都以字节计量,而且都在 1500 附近。

一个作为接口设置出现。另一个作为 SYN 报文中的选项出现,由永远看不到你接口的两端协商。

外层开销落在哪里

VPN 把内层数据包放进另一个包里,而外层包必须适应物理路径。封装首部、外层 IP 首部、传输层首部以及任何认证标签都会占用内层包无法使用的字节。让隧道接口停在 1500,外层包就会经常超出路径能承载的量。设置“不分片”位后,路由器会丢弃它,并应当返回 ICMP 错误;如果该错误被过滤,发送方永远不会知道。

这就是故障看起来有选择性的原因。小请求能完成,而任何填满报文的工作都会卡住,因为只有大包被丢弃。能通过 ping 的隧道并不是能承载数据的隧道。

限制 MSS,以及它适用的位置

MSS 钳制会在报文经过选定点时改写 SYN 中的值,使两端都选择更小的载荷,却都不知道原因。在 Linux 上它是 mangle 表的目标,由显式规则施加,而且必须 命中你预期方向的 SYN 包。把它限定在隧道接口和地址范围内,因为过宽的规则会改变从不进入隧道的流量的行为。

影响只针对 TCP。UDP 应用、QUIC 会话以及任何自行构建数据报的协议都不受它影响。

用固定路径来诊断

一次只保留一个变量。用你自己选择大小的数据包来测试,并设置“不分片”标志,让内核报告结果而不是把它藏起来。

记录故障从哪里开始:隧道内部、隧道接口,还是封装之后的物理路径。然后只改一个数字,重复同一个测试而不是换一个测试。已有连接会保留协商好的 MSS,所以策略改动只在新连接上可见,这也是为什么缓存页面在哪个方向都证明不了什么。如果某次大上传开始可用,你了解的是那条路径,而不是它上面的每一种协议。

每种改动修不了什么

降低接口 MTU 不会改变已经建立的会话的 MSS。限制 MSS 对 UDP 流没有帮助,也修不好 ICMP 错误只在单方向被过滤的路径。每种改动修好一类问题,其余部分原样保留。

写下你做了哪项调整、在哪里做、哪个测试显示了差异,否则下一位工程师会把两者都做上,并失去判断哪一个起了作用的能力。如果限制确实存在且长期有效,宁可选择能反映它的隧道 MTU,也不要选择掩盖它的钳制。如果必须钳制,就让规则范围明确并加上注释,因为未加说明的 mangle 规则默认会永久保留。

相关文章