Linux 上的按路由 MTU:三个尺寸控制,一条路由
接口 MTU、路由 MTU 与 MSS 钳制作用于不同层次。把预算挂到赢得查找的那条路由上,并让改动可回退。
一个 Linux 客户端上的三项设置听起来都像「把包改小」,但它们作用在不同层次。接口 MTU 限制设备发出的每一个包。路由 MTU 限制走该路由的包,这通常才是你为单个 VPN 目的地真正想要的。TCP MSS 钳制改变两端协商的内容,对 UDP 毫无作用。
三个看起来像同一个旋钮的控制
选择之前先把它们并排放好,因为选错那一行正是大多数不起作用的建议的来源:
| Control | Scope | What it cannot fix |
|---|---|---|
| Interface MTU | Every packet the device sends | Nothing, but it over-constrains unrelated traffic |
| Route MTU | Packets using that route | A send that the lookup never routes there |
| MSS clamp | TCP handshake on that path | UDP 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 看起来就像一天后才失败的修复。把这个值写进管理器的配置里,如果只是临时试验,先记录原始路由。
用一条新连接执行先前卡住的操作,然后确认目标私有路由仍然胜出、无关目的地没有变化。重连隧道后再读一次路由,因为重连后消失的值不算修复。
如果证据不支持这次改动,就通过它的管理器移除覆盖,并恢复原始路由属性,而不是删掉路由。然后把你在哪条路径上测了什么记下来,让下一个人能分清真正的改善和留在那里继续跑的试验。