WireGuard 主机前缀:为什么只有一个地址能通
接口上的 /32 就是合法的主机前缀,只路由这一个地址。补上 AllowedIPs 隐含的路由,再检查回程路径。
隧道是通的,handshake 每两分钟发生一次,而对端恰好只有一个地址能应答。到对端隧道地址的 ping 通,它后面的东西一概不通。有人读了接口配置,看到主机前缀,就断定掩码写错了。把它放宽到 /24 后 ping 暂时好了,随后又弄坏别的东西。前缀从来不是原因,而缺失的路由并不是掩码所管的那条。
接口前缀实际创建了什么
在 Linux 上,给接口分配地址会为该前缀创建一条直连路由。分配主机前缀,直连路由就只覆盖一个地址;分配 /24,它就覆盖 256 个地址,而每一个都被视为经该链路可达。
ip -4 addr show dev wg0
ip -4 route show dev wg0
ip route get 10.80.7.1
对点对点隧道来说,主机前缀是完全正常的选择,因为对面只有一个 peer。第三条命令才是回答用户问题的:它说明内核会为该目的地使用哪个接口,而若没有任何路由匹配,它就什么有用的信息都不显示。
这就是症状的全部解释。某个地址能应答,是因为存在它的直连路由,或者因为 peer 的允许列表恰好包含了它。其余的一切都没有路由,在 WireGuard 参与之前就被丢掉了。隧道自始至终是健康的,所以 handshake 计数看起来依旧让人放心。
AllowedIPs 是一个名字下的两条规则
WireGuard 的按 peer 允许列表同时做两件事,而配置语法让它们看起来像一件。入方向,它授权 peer 可以出示哪些源地址;出方向,它通过找最具体的匹配来决定数据包发给哪个 peer。
这两件事都不是路由。内核仍需在 WireGuard 看到数据包之前判定它属于隧道接口,而这个判定来自路由表。带宽松允许列表的 peer 条目 本身并不会安装路由。
这就是为什么配置看起来完整,行为却不对称。客户端能发向对端,是因为存在路由;回应却到不了,是因为对端的允许列表不授权客户端的源,或者对端没有回程路由。两个方向是两个独立的决定,失败方式也各自独立。写下你在测哪一个,就完成了诊断的一半。
路由从哪里来
用 wg-quick 时,路由会根据每个 peer 的允许列表自动生成,除非你设置了 Table = off 而自己接管。这是该工具文档化的行为,也是一份没有显式路由指令的配置仍然能工作的原因。
sudo wg show wg0 allowed-ips
ip route show | grep wg0
sudo wg-quick strip wg0
把允许列表与实际存在的路由对比。某个 peer 允许了整个子网却只生成了主机路由,说明辅助程序从未运行,或者你是用 wg 和 ip 直接管理接口,那样就没有任何东西替你生成路由。
手动管理时,路由只有一行,形状是明确的:目的地经该接口,没有网关,因为链路是点对点的。加上它并不是对坏前缀的绕过,而是一份决定自己管理路由的设计所缺的另一半。
加宽前缀并不能补上 peer 条目
诱人的改法是把地址改成 /24,让直连路由覆盖服务器的局域网。它看起来有效,而看起来有效的原因是直连路由现在匹配了,并非 WireGuard 学到了什么新东西。
你同时还把整个网段声明为可经该链路直达,其中包括属于其他 peer 的地址,或其实位于服务器背后的局域网地址。到这些地址的流量现在会进入隧道,能否从另一端出来,取决于服务器自己的路由和允许列表。
加密一侧在任何情况下都不受影响。WireGuard 仍然需要允许列表里覆盖目的地的条目来选定 peer,对端也仍然需要覆盖你源地址的条目来接受它。更宽的接口前缀这两者都给不了。
改动之前先确认两个方向
两端分开处理,写下各自说了什么。客户端是路由与 peer 条目;服务器是镜像,再加上它自己的回程路径:
ip route get 192.168.70.20
sudo wg show wg0 allowed-ips
sudo wg show wg0 latest-handshakes
sudo tcpdump -ni wg0 -c 10
抓包才是解决方向争议的东西。如果出向数据包出现在隧道接口上而服务器没有记录,就是远端在拒绝,通常因为你的源不在它的允许列表里。如果完全没有出现,就是路由缺失或指向了别处。
然后修较窄的那一端。服务器侧为你的客户端地址加一条允许列表条目、客户端侧为目的子网加一条条目,这是常规形状,而且不动接口前缀。只有当地址规划确实想在该链路上共享子网时才放宽前缀,并且把它当作规划变更,而不是调试步骤。