WireGuard 可以同时保留一次新鲜的握手而没有地址,这种状态自洽而非矛盾。握手是对端事件,地址属于接口,路由决定包从哪条上行链路离开。三个层次、三个归属,任何一个都可能坏掉,而另外两个看起来完全正常。

把它们当成一个状态来读,正是两分钟的排查变成一整个下午反复重启服务的原因。

握手是对端事件,不是地址事件

WireGuard 的握手只能证明已配置的对端用有效密钥应答过,并且会话密钥是最新的。它对接口上的本地地址只字未提,也不说明是否有路由指向那里。地址由辅助工具或网络管理器分配,而协议配置只负责命名对端和密钥。混淆这两件事,就是健康握手被误读成可用隧道的原因。

正因如此,wg show 可以在一个连一个包都无法从应用接收的接口上报告几秒前的握手。

重新创建接口的管理器可以在不碰对端配置的情况下丢掉地址,而由于密钥从未改变,下一次握手会立刻成功。关于握手成功的日志行是准确的,却与这次故障毫无关系。

ObservationWhich layer it belongs toWrong conclusion it invites
Handshake a few seconds oldPeer and keysThe tunnel is usable for application traffic
Address present on wg0Interface configurationTraffic actually egresses through it
Route points at wg0Forwarding tableThe outer packet uses the intended uplink

接口地址有自己的所有者

隧道设备上的地址可能来自辅助工具的 Address 行,来自在接口上运行的 DHCP 客户端,也可能来自连接时创建它的管理守护进程。无论来自哪里,该组件才是地址的所有者,它会按自己的逻辑决定是否恢复地址。只读地址而不指名其所有者,就无法解释它为何消失。

同一个接口上有两个所有者是经典故障。在同一设备上同时启动辅助工具和桌面网络管理器,会产生互相删除对方刚配置内容的竞争,而端点在此期间一直在应答。

只选一个所有者,让另一个工具只做观察而不做管理。这个决定值得写下来,因为症状会在几周后出现,看起来像随机的地址丢失。

选上行链路的是路由,不是接口

指向 wg0 的内部路由决定 流量进入隧道。另一个决定负责把加密后的外层包送往对端,在有多个上行链路的主机上,这个决定来自转发表和策略规则。一次 Wi-Fi 漫游就能改变外层路径,而完全不碰内层地址。

所以,可用隧道和不可达对端是相容的状态,正确的对端和断裂的内层路由同样相容。三个观测必须在同一时刻一起取,否则比较毫无意义。

一次把三种状态对上

三条只读命令按最快隔离故障的顺序回答三个问题。执行它们之间不要改动任何东西。

ip address show dev wg0
ip route get 10.80.0.1
wg show wg0 latest-handshakes transfer

第一条显示接口到底有没有地址,第二条显示某个目的地此刻会走哪个设备。第三条报告最后一次握手的时间和字节计数,也是三者中唯一描述对端而不是本机的一条。

除了时间戳,也要读计数器。握手不断刷新而传输始终持平,说明对端在应答但没有有用的东西通过,这指向路由或 ACL,而不是密钥。有地址时时间戳却停止前进,则指向更上游的位置。两种读法来自同一行输出,如果只确认握手存在,两者都看不出来。

修正真正出错的那个具体状态,然后重测应用而不是隧道。新鲜的握手是关于对端的证据,而关于对端的证据并不能证明浏览器发出的下一个请求能找到地址、路由和可用的上行链路。wg-quick 手册 明确说明其中哪些由辅助工具设置、哪些属于协议本身。

相关文章