一个 Linux 客户端上的三项设置听起来都像「把包改小」,但它们作用在不同层次。接口 MTU 限制设备发出的每一个包。路由 MTU 限制走该路由的包,这通常才是你为单个 VPN 目的地真正想要的。TCP MSS 钳制改变两端协商的内容,对 UDP 毫无作用。

三个看起来像同一个旋钮的控制

选择之前先把它们并排放好,因为选错那一行正是大多数不起作用的建议的来源:

ControlScopeWhat it cannot fix
Interface MTUEvery packet the device sendsNothing, but it over-constrains unrelated traffic
Route MTUPackets using that routeA send that the lookup never routes there
MSS clampTCP handshake on that pathUDP payload sizes and ICMP

把整台机器的以太网 MTU 调低,是这个修法的粗暴版本。它有效,但也会惩罚主机上其他所有应用,包括那些路径本来没问题的。按路由设值能表达同样的意图,而不带这份附带代价。

这个比较只对主机自身产生的流量成立。为另一个客户端转发包,涉及它自己的路径和隧道的外层预算,而本机上的路由度量跨不过那条边界。

把预算挂到赢得查找的那条路由上

一条示例性 IPv4 路由记录如下:

ip -4 route get 10.60.0.20
ip -4 route add 10.60.0.0/16 dev tun-office src 10.90.0.2 mtu 1380

把这条记录读作意图的描述,而不是照抄的值。前缀、设备和首选源必须与真实配置一致,并且 这个数字要来自关于封装路径的证据,而不是来自某篇博客文章。路由 MTU 限制的是内层 IP 包,这与 1380 字节的 TCP 载荷或 1380 字节的加密外层包并不是一回事。

在添加任何东西之前,先看查找实际在哪里解析。带策略规则的主机可能通过 main 之外的表来路由某个目的地,而只存在于 main 里的度量对该请求不可见。请把 ip -4 route show table all 和 ip route get 的答案做比较,规则用到时还要带上 mark 或命名空间上下文。如果两者不一致,需要改度量的是那张真正应答的表,而不是看起来更整齐的那张。

mtu 不是 mtu lock

默认情况下内核仍可依据 ICMP 反馈更新路径 MTU,路径 MTU 发现正是为此。加上 lock 就会停止这一行为。这是另一个决定,而不是保存同一个数的更强方式;对 IPv4 它还会改变「不分片」位的既有文档行为。

当路径已经测过、并且希望无论来什么都保持这个值时,锁定是合理的。当网络会发来合法的尺寸反馈时,它是糟糕的默认值,因为内核无法再调整,故障会转移到别的症状上。

如果路径某处过滤了 ICMP,任何路由设置都修不好。修的地方在过滤器,而更小的 MTU 只是权宜之计,会把真正的故障掩盖到下一次路径变化。

隧道软件会加上自家的尺寸旋钮

OpenVPN的 mssfix 会针对其文档记录的 UDP 隧道场景调整 TCP 段大小。它作用在 TCP 会话上,所以不是操作系统路由 MTU 的另一种写法,也帮不了忽略协商的 UDP 服务。

不要在同一个变更窗口里叠加路由 MTU、接口 MTU 改动和 MSS 钳制。如果问题消失了,你不会知道是哪一个起了作用,而保留下来的那些会年复一年限制流量,理由却没人记得。

让它可回退,然后再验证

路由的真正归属决定改动能否扛过重连。动态安装路由的 VPN 客户端通常会在没有你的度量值的情况下重建它,于是手动 ip route add 看起来就像一天后才失败的修复。把这个值写进管理器的配置里,如果只是临时试验,先记录原始路由。

用一条新连接执行先前卡住的操作,然后确认目标私有路由仍然胜出、无关目的地没有变化。重连隧道后再读一次路由,因为重连后消失的值不算修复。

如果证据不支持这次改动,就通过它的管理器移除覆盖,并恢复原始路由属性,而不是删掉路由。然后把你在哪条路径上测了什么记下来,让下一个人能分清真正的改善和留在那里继续跑的试验。

相关文章