一条通往 VPN 网络的路由携带两个容易混淆的地址:数据包要前往的目的前缀,以及把它推进一步的下一跳。在 LAN 主机上,这个下一跳必须是同一链路上真正可达的路由器。远端 VPN 地址不是下一跳,无论它与心中的目的地址多么吻合。

本手册覆盖常见情形:一台位于 192.168.20.0/24 的笔记本电脑、一个 LAN 地址为 192.168.20.5 的 VPN 网关,以及该网关后面的远端网络 10.70.0.0/24。以下所有命令都假定你在笔记本上,除非另有说明。

两个地址,一条路由

预期的条目是 10.70.0.0/24 via 192.168.20.5。前缀说明数据包去哪里,via 地址说明谁把它带过去。内核拒绝安装网关无法通过你指定的接口到达的路由,而这种拒绝正是要点:需要隧道才能可达的下一跳,不能成为建立隧道的下一跳。

所以第一项检查不是“网关是否在运行”,而是“网关是否在这条链路上”。笔记本自己的子网、它的 ARP 或邻居条目、以及路由中的接口必须彼此一致。只存在于 10.70.0.0/24 内部的地址可以作为第二条路由的目的,但永远不能是第一条路由的 via。

下一跳让网关承担什么

在笔记本上安装路由只决定了第一次交接。之后网关需要自己的路径:开启转发、为 10.70.0.0/24 建一条隧道路由,以及任何规定谁可以使用它的访问策略。在 Linux 上就是 net.ipv4.ip_forward=1 加一条隧道路由;它前面的防火墙需要一条允许转发流量的规则。

回程方向是人们容易忘记的部分。远端必须有回到 192.168.20.0/24 的路由,或者设计必须把笔记本的源地址转换成远端认识的形式。从网关能 ping 通只证明网关能到达远端网络;它完全不能说明笔记本的数据包能否活着回来。

从发送方主机验证查找

询问内核它会选择哪条下一跳,并读出回答中的接口:

ip route get 10.70.0.10
ip route show 10.70.0.0/24
ip neigh show 192.168.20.5

如果查找报告的是网关和 LAN 接口,第一段就按预期配置好了。如果报告的是隧道设备,表中更靠前的某条更具体,正在胜出。如果路由根本装不上,内核是在告诉你网关不可达,这条信息比任何抓包都更有用。

然后从正确的位置重做测试。在网关上发出的请求使用网关自己的源地址,这可能掩盖缺失的回程路由,或掩盖只允许特定源的策略。ip-route 手册把目的、网关和 onlink 覆盖区分开,使用覆盖之前值得先弄懂它。

不要用 onlink 伪造可达性

onlink 让内核跳过网关可达性检查并照样发送。它不会创建路径,不会为不存在的地址解析 ARP,也不会让远端路由器在你的 Ethernet 网段上应答。对于能应答但不在直连子网里的网关它是正当的;用来掩盖你没解释清楚的错误时,它就是陷阱。

如果路由只有加上 onlink 才能装上,下一个问题就是网关为什么不在你以为的位置。错误的 VLAN、错误接口上的第二个地址、或最后一个八位组里的笔误都会造成这种情况,而它们每一个在流量开始流动后也会破坏回程路径。

点对点隧道不是 Ethernet

隧道设备不做 ARP,通常也没有像样的邻居子网。它的路由常常形如 10.70.0.0/24 dev tun0,完全不带 via,因为对端是唯一的另一端。把 LAN 风格的 via 复制进那张表,会产生内核可能拒绝的路由,或者更糟,产生指向对端从不应答的地址的路由。

在手册里把两种模型分开:LAN 路由有可达的网关,隧道路由有接口和对端。WireGuard、OpenVPN 和 IPsec 呈现的形态略有不同,所以记录你实际用过的形态,而不是从一台主机上泛化。

添加、确认、回滚

整个改动是三条命令加一次检查,并且应当按这个顺序写下来:

sudo ip route add 10.70.0.0/24 via 192.168.20.5
ip route get 10.70.0.10
ping -c 3 10.70.0.10
sudo ip route del 10.70.0.0/24 via 192.168.20.5

确认路径,确认一个目的,然后备好删除行,以防改动以你未预料到的方式让主机不可达。如果路由必须经受重启,就把它放进主机的网络配置而不是 shell 配置文件,并在依赖它之前测试重启。只存在于添加它的那个 shell 里的路由,是你将要调试两次的路由。

相关文章