DNS 能解析,SSH 会话保持连接,然后一次大文件上传卡住了,而 ping 同一主机仍然有回应。这种分裂指向的是数据包大小,而不是坏掉的隧道。嵌套隧道会在两种隧道单独都不会遇到的问题尺寸上出现它。内层会话本身没问题;交给它的数据包已经装不下了。

路径 MTU 究竟去了哪里

每次封装都会在下一层轮到自己之前先花掉字节。一条以太网路径给外层数据包 1500 字节,外层隧道在内部任何东西被寻址之前就先取走自己那一份。一个凭感觉猜的额外开销数值,错的程度恰好就是关键的那部分。

按顺序列出各层:接入路径、外层传输、外层隧道、内层传输、内层隧道、应用。每个头部只算一次,并把 UDP 与隧道自己的头部和标签分开。

两层 WireGuard,只算一次

取一条 IPv4 接入路径,其中一层 WireGuard 隧道套在另一层里面。外层花掉 IPv4 的 20 字节、UDP 的 8 字节,以及 WireGuard 数据头加认证标签的 32 字节。再往里,内层隧道需要自己的 20 字节 IPv4 头和 8 字节 UDP 头,然后才是第二份 32 字节的 WireGuard 开销。剩下的空间容纳 IPv4 头、TCP 头和数据。外层若是 IPv6 路径,则要花 40 而不是 20,剩 1320。

LayerBytes
Path MTU1500
Outer IPv4 header20
Outer UDP header8
Outer WireGuard header and tag32
Inner transport IPv4 header20
Inner UDP header8
Inner WireGuard header and tag32
Innermost IPv4 header20
TCP header20
Payload that fits1340

算下来是 1340 字节。同样的规则下只套一层,TCP 载荷能剩 1400,也就是 1500 减去 20、8、32、20 和 20,而 60 字节的差值正好就是第二条隧道加上的内层封装。

把 1400 套用到嵌套路径上,内层隧道就少 60 字节,直到数据包大到需要它们才看得出来。填充和协议自带的分片选项又会改变数字,所以要相信这张表。

MTU、MSS 与分片作用在不同位置

接口 MTU 限制的是接口会发出什么,而 TCP MSS 钳制改变的是 TCP 对端愿意发送什么。 钳制能修好 TCP 会话 但对大的 UDP 数据报毫无用处,于是修复在一个应用上看起来成功,却悄悄漏掉了下一个。协议自带的分片选项是第三个杠杆,有它自己的失败方式。接口 MTU 是最常见的第一步,也是最粗的一步。

桥接与路由的隧道承载的帧不同,所以另一套部署里的数字很少能直接搬过来。路径 MTU 发现, RFC 8201中所描述的方法,只有在它所依赖的 ICMP 报文被放行时才有用。

找出不够的那一层

如果条件允许对比,就在嵌套之前先单独验证每条隧道。固定大小的探测能把这件事定下来: ping -M do -s 1472 -c 3 10.8.0.1 在该层内发送一个完整的 1500 字节 IPv4 数据包。加上第二层,再重复同样的探测,这样任何失败都归于某个已知的改动。

传输卡住时,一次只改一个有记录的参数,并记下失败时的尺寸。回程路径也要检查,因为嵌套路由递归足以把数据包大小问题模仿得很像,让人白花一下午。被过滤的 ICMP 回应会掩盖真正的上限,所以探测比推理更重要。

把预算写在下一个能找到它的地方

每层一行的表格比口口相传的经验活得更久。把它放在隧道示意图旁边,这样没人会在压力下重算一遍。

隧道越多,依赖越多,所以回滚方案值得占据预算旁边的一席之地。如果数字还是对不上,就去测量路径,而不是再猜一次。目标是你能为之辩护的数字,而不是能记住的数字。

相关文章