从 OpenVPN 无法访问子网到解决:一次 OpenWrt 路由排障记

✅ 先说答案:解决办法

  • 去掉错误的静态路由:在 OpenWrt 的“静态路由”页面发现有一条 192.168.3.0/24 指向网关 192.168.3.254 的路由(图 3)。这条规则会导致内核把去往 192.168.3.1 的包送到 WAN 口(pppoe-wan),从而无法通过 LAN2 到达目标。删除该路由,并重启网口后,内核自动生成的直连路由恢复正常。
  • 恢复正确路由:确认 ip route get 192.168.3.1 输出为 dev eth2 src 192.168.3.254 即可。此时 OpenWrt 本机可以正常 ping 通 192.168.3.1,VPN 客户端也能访问。
  • 若上级路由不知道 VPN 网络,则需要在上级路由(192.168.3.1)添加 回程路由(10.8.0.0/24 和 10.8.1.0/24 指向 192.168.3.254)。如果无法修改上级路由,可在 OpenWrt 的 VPN 区域开启 SNAT/MASQUERADE,让 VPN 客户端访问 LAN2 时使用 192.168.3.254 作为源地址,从而绕过回程路由。SNAT 会在数据包离开内部网络时把源 IP 转换为网关的 IP,使外部主机看到的是转换后的地址.

下文按照时间顺序记录从发现问题、排查过程到最终解决的全过程。

📌 背景与网络拓扑

  • OpenWrt 作为旁路由,LAN2 接口配置为 192.168.3.254/24,与主路由器(192.168.3.1)在同一网段,通过 eth2 物理接口连接。
  • OpenVPN 服务器运行在 OpenWrt 上,通过 tun0/tun1 建立 10.8.0.0/24 与 10.8.1.0/24 两个虚拟子网。
  • OpenVPN 的配置中已经正确 push "route 192.168.3.0 255.255.255.0"(图 1),客户端获得了访问 LAN2 网段的路由。
  • 防火墙中定义了 VPN 区域,并允许与 lan 区域互访,理论上 VPN 客户端可访问 192.168.3.0/24。

OpenVPN 服务器路由推送配置

图 1 OpenVPN 服务器通过 push route 推送 192.168.3.0/24 到客户端

🔍 问题发现

VPN 客户端连接成功后,测试结果如下:

  • ping 192.168.3.254 —— 可以通信,说明隧道连通、OpenWrt 可达。
  • ping 192.168.3.1 —— 完全丢包。同时在 OpenWrt 上直接 ping 192.168.3.1 也不通,初步排除回程路由问题,怀疑链路或本机路由。

🛠️ 排查过程(按时间顺序)

1. 确认配置无误

  • 路由下发:打开 VPN 服务器配置,确认有 push "route 192.168.3.0 255.255.255.0"(图 1)。在客户端路由表中也看到了 192.168.3.0/24 走隧道,因此排除“未下发路由”。
  • 防火墙:检查防火墙区域,VPN zone 已经允许转发到 lan 区域;lan 区域包含 LAN2 接口,因此防火墙不会阻止此流量。
  • 二层连通性:使用 arping -I eth2 192.168.3.1,OpenWrt 能收到 192.168.3.1 的 ARP 回复,证明物理链路和交换网络正常。

防火墙区域配置:VPN 与 LAN 互通

图 2 防火墙中 VPN 区域允许与 lan 区域互访,且 LAN2 接口在 lan 区域

2. 发现内核路由错误

为了进一步定位原因,在 OpenWrt 上执行 ip route get 192.168.3.1,结果却是:

192.168.3.1 via 10.100.0.1 dev pppoe-wan src 10.100.18.64

也就是说,内核认为到 192.168.3.1 需要通过 WAN 口的上级网关,而不是直接走 LAN2 接口。这解释了为什么 ping 不通,因为包被送到外网去了。继续检查“静态路由”页面,发现一条异常的路由:

错误的静态路由配置

图 3 静态路由配置中把目的网段 192.168.3.0/24 的网关设成了本机 192.168.3.254,导致内核选路异常

这条路由的意思是 “到 192.168.3.0/24 要通过 192.168.3.254”,而 192.168.3.254 又是本机地址。由于优先级比自动生成的直连路由低,它没有立即生效,但在某些情况下会干扰内核的路由缓存,导致内核改走 WAN。这就是能 ping 通 192.168.3.254 却 ping 不通 192.168.3.1 的原因。

3. 临时修复验证

为了验证这一假设,先在命令行手动添加一条正确的直连路由:

ip route replace 192.168.3.0/24 dev eth2 scope link src 192.168.3.254

再执行 ip route get 192.168.3.1 得到:

192.168.3.1 dev eth2 src 192.168.3.254

这时 OpenWrt 本机 ping 192.168.3.1 就成功了,证明问题确实在路由表。随后删除上述错误的静态路由并重启网络服务,内核自动生成的直连路由(192.168.3.0/24 dev eth2) 恢复,问题解决。

4. VPN 客户端回程问题

经过上述操作,OpenWrt 本机与 192.168.3.1 可以互通。但 VPN 客户端 ping 192.168.3.1 时还是没有回应。使用 tcpdump -ni eth2 抓包发现,192.168.3.1 不停发送 who-has 10.8.0.2 的 ARP 请求。这说明它不知道 10.8.0.0/24 应该回到 192.168.3.254,而是误以为 10.8.0.2 就在本地以太网。

这就是经典的 回程路由缺失:上级路由(192.168.3.1)没有到 VPN 子网的路由,导致它无法把返回的流量交给 OpenWrt。

为了让 VPN 客户端能访问 192.168.3.0/24,有两个办法:

  1. 在 192.168.3.1 上配置静态路由

    • 10.8.0.0/24 via 192.168.3.254
    • 10.8.1.0/24 via 192.168.3.254

    这样 192.168.3.1 就会把回包交给 OpenWrt,再通过 VPN 隧道转发给客户端。

  2. 在 OpenWrt 上开启 SNAT/MASQUERADE

    如果无法修改上级路由,可在防火墙的 VPN 区域开启 IP 动态伪装(MASQUERADE)。当 VPN 客户端访问 192.168.3.0/24 时,OpenWrt 会把数据包的源地址从 10.8.0.2 修改为 192.168.3.254,让 192.168.3.1 直接回复本机。SNAT 的定义是:当内部主机访问外部网络时,NAT 设备会将数据包的源 IP 地址从私有地址转换为公共地址,使外部主机看到的是转换后的地址【23302085190512†L146-L151】。由于回包只需要回到网关,不用知道真实源地址,SNAT 是一种“权宜之计”。

在我的环境中,上级路由并未禁止回程,于是在删除错误的静态路由后,OpenWrt 和上级路由之间的默认路由就能互通,因此不需要额外 NAT,VPN 客户端访问 192.168.3.1 也恢复正常。

🧾 总结与经验教训

  1. 不要在同一子网配置“指向自己”的静态路由。OpenWrt 已经为直连网段自动生成路由,额外添加错误的静态路由可能干扰内核路由选择,导致包走到意想不到的接口。
  2. 排查顺序很重要
    • 先验证 OpenVPN 是否正确推送路由和防火墙是否放行;
    • 再用 arping 和抓包确认二层链路;
    • 最关键是用 ip route get 检查内核实际走哪条路径。
  3. 回程路由与 SNAT:如果上级路由不知道 VPN 子网的存在,就需要在上级路由添加回程路由;不能修改时,可以在 OpenWrt 对 VPN→LAN2 的流量做 SNAT,让源地址变成网关地址,利用 NAT 的源地址转换功能【23302085190512†L146-L151】。
  4. 工具与观察tcpdumpip route get 等工具在排查网络问题时非常有用,可以直观了解包的流向和路由决策。

这次排障经历虽然过程曲折,但充分说明了路由配置的重要性。通过系统的检查和逐步定位,最终找到了症结所在,也顺便复习了一遍 NAT 和回程路由的概念。