小 ping 整天穿过隧道,SSH 横幅到了,然后文件传输一开始会话就卡死。这个模式指向一个算错层次的字节预算:外层隧道只按自己的开销定尺寸,没人把内层那份减掉。嵌套的 IPIP 包对 WireGuard 来说只是又一个 IP 包,正因如此它才会吃掉你从未预留的字节。修正就是算术,三个命令加一张表就够了。

暴露问题的症状

分片问题总是以尺寸现身,从不出现在地址上。对方的 56 字节 ping 一小时都有回应,到 1400 字节却突然沉默,而隧道仍在、路由未变。握手过得去,第一个完整 TCP 窗口过不去,传输在看起来健康几秒后就停住。

先测天花板再动手,没测过的天花板你会追两次。用 do-not-fragment 位发一个低于可疑尺寸的探测包,ping -M do -s 1000 10.9.0.2,再在一个已知过大的尺寸上重复。如果目的地不变而翻转只随载荷尺寸变化,那说明路由在正常工作,是预算里少算了一个包头。

每个包头只算一次,而且算在正确的层

从路径上最小的 MTU 起算,而不是从隧道接口起算,因为那堵墙在两个端点之上。WireGuard 在 IPv4 外层传输上花 60 字节:IP 20、UDP 8、自身包头 32;外层换成 IPv6 则是 80 字节。1420 字节的 wg0 在 1500 字节路径真正能送达的 1440 之下留了 20 字节余量,所以从 wiki 抄来的那个数字放在这里仍然成立。把整条栈画进图里,因为下一个改 MTU 的人否则会随手挑一个新的整数,然后继承你的 bug。

LayerBytesRemaining
Path MTU on the wired linknone yet1500
WireGuard outer: IP, UDP, WG header601440
Slack kept for safety201420 (wg0)
IPIP header inside the tunnel201400 (ipip0)
Original IPv4 and TCP headers401360 payload

第五行才是预算出错的地方,因为嵌套接口承载的 1400 字节里已经包含了原包自己的 40 字节包头。真正到达读文件进程的只有 1360 字节。把 1400 当成应用载荷——任何缺那一列的图都在暗示你这么做——内层设备就会在没动过的墙前面多出 40 字节的慷慨。

两个 MTU 分别设置

承载方和嵌套设备是两个独立对象,所以各自需要自己的值。按这个顺序应用,因为嵌套设备不会从承载方继承任何东西:

ip link set wg0 mtu 1420
ip tunnel add ipip0 mode ipip remote 203.0.113.9 local 198.51.100.4
ip link set ipip0 mtu 1400

值得记住的规则:内层 MTU = 外层 MTU - 20(IPIP 隧道承载的是 IPv4 包时),一旦内层包任一侧是 IPv6 就变成 - 40,因为嵌套包头从 20 字节长到 40 字节。这个减法里不该有别的东西。你选 1420 时 WireGuard 的开销已经付过了,再收一遍只会得到一个完美工作、却始终很慢的隧道——只要没人去测它。两台设备都挂整数 1400 的部署,正是这个错误的指纹。

嵌套包任一侧出现 IPv6 都会让上面的数字全部作废,所以要重算,而不是从相似站点抄一个数。IPv6 一旦出现在外层路径或原始数据报里,被减掉的包头就是每个 40 字节,载荷数字也正好下降这么多。

你大概不需要的那条防火墙规则

两个 WireGuard 对端之间,线上只有 UDP,所以为协议 4(RFC 2003 分配给 IPv4-in-IPv4 封装的那个号)开一条入站规则,既不能建立嵌套路径,也不能维持它。IPIP 包头活在加密载荷内部,任何边界防火墙都看不到。反正开协议 4 除了多一条被允许进入主机的路,什么也买不到。

提到原包源和目的的策略属于解封装之后,落在隧道接口和路由表上。放在那里,边界规则就可以保持关闭。

WireGuard 实际验证的是什么

AllowedIPs 判定的是 WireGuard 承载的包的源,而它承载的是 IPIP 封装后的包。被允许的 IPIP 源即使内部包头写的是你并不想路由的主机,也会被接受。因此原始源并不是对端替你检查的东西,那对内部地址需要在解封装之后有自己的执行。

包装层提供的是路由结构,不是第二层密码学。把 IPIP 说成 WireGuard 内部的额外保护,描述的是另一种设计。

用梯子验证,而不是一个 ping

试探跨过你算出边界的尺寸,然后确认内核对两台设备的看法。1360 字节载荷加 40 字节包头正好 1400,下面第一个回显过得去,第二个就坐在天花板外一个字节:

ping -M do -s 1372 10.9.0.2
ping -M do -s 1373 10.9.0.2
wg show wg0
ip -d link show ipip0

天花板下干净通过、在上方第一个尺寸就失败,说明算术从头到尾成立。然后把最初卡住的那次传输再跑一遍,因为合成探测只证明预算,对发送方自己的写入尺寸一无所知。如果嵌套路径在你证明能过的尺寸上仍然断,先检查远端解封装和路由,再考虑从 MTU 上再削十字节。

相关文章