握手还在,地址没了:WireGuard 的三种状态
新的握手只能证明对端应答过,不能证明接口仍持有地址,也不能证明下一个包会走预期的上行链路。
WireGuard 可以同时保留一次新鲜的握手而没有地址,这种状态自洽而非矛盾。握手是对端事件,地址属于接口,路由决定包从哪条上行链路离开。三个层次、三个归属,任何一个都可能坏掉,而另外两个看起来完全正常。
把它们当成一个状态来读,正是两分钟的排查变成一整个下午反复重启服务的原因。
握手是对端事件,不是地址事件
WireGuard 的握手只能证明已配置的对端用有效密钥应答过,并且会话密钥是最新的。它对接口上的本地地址只字未提,也不说明是否有路由指向那里。地址由辅助工具或网络管理器分配,而协议配置只负责命名对端和密钥。混淆这两件事,就是健康握手被误读成可用隧道的原因。
正因如此,wg show 可以在一个连一个包都无法从应用接收的接口上报告几秒前的握手。
重新创建接口的管理器可以在不碰对端配置的情况下丢掉地址,而由于密钥从未改变,下一次握手会立刻成功。关于握手成功的日志行是准确的,却与这次故障毫无关系。
| Observation | Which layer it belongs to | Wrong conclusion it invites |
|---|---|---|
| Handshake a few seconds old | Peer and keys | The tunnel is usable for application traffic |
Address present on wg0 | Interface configuration | Traffic actually egresses through it |
Route points at wg0 | Forwarding table | The 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 手册 明确说明其中哪些由辅助工具设置、哪些属于协议本身。