隧道看起来要等服务器先发出点东西才通。从客户端发起时毫无反应,等几秒再试一次就成功了。常见诊断是 NAT 坏了,而这个常见诊断是错的。真正变的是:一方知道了另一方在哪。

问题在端点认知,不在可达性

WireGuard 的对等端在不知道往哪发之前不会发起握手。配置把它作为由地址和端口组成的端点提供,而运行状态记录的是它当前相信的那个端点。

wg show 会打印出这幅图的两侧,而输出里没有端点的对等端什么都发起不了,公钥和允许的前缀再正确也没用。改任何规则前,先把客户端和服务器的视角对一遍,因为两者常常对谁掌握什么意见不一致。

对等端是怎么学到端点的

WireGuard 故意保持沉默。当一个认证正确的包从对等端没预料到的地址到达时,实现会把这个对等端的端点更新为新的源地址,笔记本换网络时能漫游就是这个原因。这个行为解释了方向依赖:如果服务器先发、它的包到了客户端,客户端就学到服务器当前地址,也就能回应了。在那之前它没有地方可发。这套机制是认证漫游,而不是首次使用即信任的捷径,因为包必须在端点移动之前就是有效的。

wg 的工具文档描述了这次更新,如果你打算围绕它做设计,值得读一遍。

有状态转换会引入自己的方向

当 UDP 经过做转换的网关时,网关会为它看到的第一个包建立状态。RFC 4787 描述了这种行为,以及最终删除映射的超时。空闲映射会过期,来自远端的入站包随后在到达主机之前就被网关丢掉。早上还好好的隧道下午突然不应答,原因正是这个。

这是第二个独立的机制,产生同样的方向性症状。端点认知和转换状态都必须对,修好其中一个并不能修好另一个。

把方向配成你真正想要的样子

对必须主动发起连接的客户端,把服务器的端点写进客户端的对等端配置并留在那里。指望服务器先发,等于指望流量顺序上的偶然,而在安静的网络上它会变。配置应当写明预期的方向,而不是靠发现。

对位于转换之后、需要保持可被访问的对等端,在那一侧启用持久 keepalive。每 25 秒一次 keepalive 是通常的起始值,它的目的是让映射保持打开,而不是测试隧道。只选一侧承载它,因为两侧都开会把流量和日志噪音翻倍,却不会改善结果。

在触发前后对比状态

在失败的那一刻抓下两个视角,等服务器发过之后再抓一次。第一条命令显示端点与握手时间。第二条显示该端口是否存在转换表项。抓包显示到底是哪个方向真正过了线,路由检查则确认客户端没有把这份流量发去别的什么地方。

wg show
conntrack -L -p udp 2>/dev/null | grep 51820
tcpdump -ni any -c 20 'udp port 51820'
ip route get 198.51.100.7

把端点列和转换表项记在一起。没有端点的对等端和没有映射的网关是两种不同的故障。只有其中一种能靠 keepalive 修好。

相关文章