状态显示隧道已建立,路由器自带的浏览器能打开网页,可它后面的每台设备仍然超时。这种组合把问题收窄到多数 VPN 教程从不提及的一层:需要让数据包穿过隧道的并不只有路由器这台主机。本机产生的流量和转发的流量,在同一台设备里走的是两条不同的路径。从客户端实测一次,区别就不再是理论。

从 LAN 主机复现故障

从出故障的设备开始,因为在路由器上做诊断回答的是另一个问题。确认客户端的默认网关就是这台路由器,记下它的源地址和子网,再看路由器隧道接口上究竟出去了什么。

如果客户端地址被转换,远端看到的是隧道地址,从远端看一切正常,客户端却依旧失败。如果没有做转换,远端必须知道通往客户端子网的返回路由,而许多网关默认并没有这条路由。无论哪种情况,路由器自身的成功都无关紧要,因为它的数据包在本机产生,从未经过转发路径。

本机流量与转发流量是两回事

Linux 和大多数路由器固件对本机产生的数据包与穿过的数据包区别对待。output 链看到的是前者,forward 链看到的是后者,而 针对路由器自身流量调好的策略,可能完全没有触及转发。

sysctl net.ipv4.ip_forward
ip -4 address show br-lan
ip route get 203.0.113.9
nft list ruleset | grep -A 6 'chain forward'

先确认转发到底有没有开启,然后读 forward 链而不是 output 链。放行加密外层连接的规则,不等于放行解密后数据包的转发,把两者混为一谈,出现的正是这个症状。隧道已建立,只证明前者。

回程路径同样必须存在

源地址转换决定需不需要回程路径。在隧道接口上做 masquerading,回包会回到隧道地址,由路由器再转换回来。不做转换,远端网关就需要一条通往客户端子网的明确路由,并且它自己的策略也要允许这个方向。

动手之前先记下部署采用两种设计中的哪一种,因为修法方向相反。加转换会掩盖缺失的回程路由,并破坏需要原始源地址的应用。不加转换而加回程路由,一旦远端防火墙丢弃陌生源地址就会静默失败。选定一种,写进文档,让另一端与之匹配。

客户端 DNS 属于同一轮检查。路由器的解析器能应答,而指向另一台服务器的客户端可能什么也得不到。

验证一条放行的流和一条拒绝的流

修正之后,从客户端发起新连接来测试,而不是复用路由器已有的会话。然后确认本不该走隧道的网段仍然没有走隧道。

curl -sS --max-time 8 https://api.ipify.org
ip route get 203.0.113.9
dig +short intranet.example.net

在远端核对实际观察到的源地址,因为这能说明真正在运行的是哪种设计。名称解析也要和地址一起验证,因为解析器的行为是同一台设备上的另一条路径。

把规则限定在需要隧道的客户端网段内,不要为了让某一项测试通过就关掉转发过滤。那样改修好了测试,却拆掉了隧道本该提供的边界。把源地址、地址转换的决定和回程路由写进同一条记录,并注明日期。

相关文章