接口出现在你期望的命名空间里,应用也绑定了它,可隧道流量始终不出现。这是 Linux 内核中的 WireGuard 设备在一个命名空间创建、然后被移动到另一个命名空间时的典型征兆:接口搬走了,它加密用的 UDP 套接字却没有。两者都必须位于正确的位置,内部流量才有意义。一旦知道要看两个上下文而不是一个,排查就会很快。

症状:命名空间正确,却没有数据包

在命名空间内部,隧道看起来正常。设备存在,地址已分配,对端配置也在。可到了外面,预期的 UDP 交互始终没有出现在你观察的接口上,另一端则报告没有发生握手。

原因在于应用的内部数据包到达的是被移动过的设备,而该设备试图通过属于另一个网络上下文的套接字把它们发出去。它所使用的底层路由,以及适用于它的每一条防火墙规则,都属于创建设备时所在的命名空间。

这既不是移动过程中的缺陷,也不是对端配置错误。内核实现就是这样工作的,WireGuard 的命名空间文档也直接说明了创建规则。

套接字为什么有出生地

WireGuard 设备为自己的加密传输拥有一个 UDP 套接字,这个套接字在创建设备的命名空间中被打开。用链路命令移动接口会搬走可见的设备,却把传输留在原处。于是两个对象就归不同的所有者。

后果很容易被忽略,因为两半在不同地方检查。内部路由在应用运行所在的命名空间里决定,外部路由则在套接字所在之处决定。只检查其中一半会得到很有把握却错误的结论。两半都检查通常一趟就能收尾。

在你要套接字的地方创建它

办法是在目标命名空间内部创建设备,而不是在外面创建再移动进去。假设已有一个获批的命名空间,且名字尚未被占用:

ip netns exec vpn-apps ip link add wg-demo type wireguard
ip netns exec vpn-apps wg show

现在接口和它的套接字都属于这个上下文,因此底层路由和适用的过滤规则都是在那里配置的。如果设计要求明确的一侧位于别处,请先把底层接口放进命名空间,再创建隧道。顺序属于设计的一部分,而不是实现细节。

把它们当作网络管理命令,而不是照抄的配方。它们创建了设备并显示状态;密钥、对端、地址和路由仍然必须来自获批的配置,示例中也不应替换掉现有的生产接口。

同时读通道的两侧

设备就位后,分别验证两个判断。先从应用上下文中做内部查找,再从持有套接字的上下文中检查外部路径:

ip netns exec vpn-apps ip route get 10.30.0.9
ip netns exec vpn-apps wg show
tcpdump -ni any udp port 51820

如果内部查找经由隧道解析成功,外部抓包也显示加密交互,那么两半是一致的。如果抓包为空,说明套接字仍在别处,或者它所在命名空间的底层路由到不了终点。

还有一项检查应写入记录:重启之后设备是如何创建的。用不同创建上下文重建接口的管理程序会改变套接字的出生地。一次成功的手工移动,并不能证明受管理路径也如此。

相关文章