客户端显示已连接,隧道地址能应答 ping,办公 LAN 上却没有任何主机响应。几乎每次客户端升级后我都会遇到这个现象,而第一反应是去开防火墙,这通常是错的方向:隧道本身没问题,断点在于数据包必须经过的三跳中的某一跳。按数据包实际经过的顺序走一遍:先看客户端路由,再看服务器的转发,最后看回程,几分钟内就能找到故障的归属。

第一步:观察客户端的路由判断

在动服务器之前,先证明客户端确实把数据包送进了隧道。不要通读整张表,只针对某台具体的 LAN 主机问内核会走哪个接口:

ip route get 192.168.50.10

如果答案指向你的物理网卡和办公网关,那么服务器侧怎么改都没用。OpenVPN 只有在服务器下发路由时才会给出路由,所以去服务器配置里找 push "route 192.168.50.0 255.255.255.0",然后重新连接客户端。多数客户端只在建立连接时应用下发的路由,重新加载配置文件时并不会应用。

在 Windows 上,同一个问题对应另一条命令。Find-NetRoute -RemoteIPAddress 192.168.50.10 会打印协议栈会选择的接口和下一跳,route print -4 则显示下发的网段到底存不存在。

第二步:确认服务器愿意转发这个数据包

如果客户端路由正确,数据包就到了服务器,而服务器必须同意转发它。内核开关是第一道门,sysctl net.ipv4.ip_forward 必须返回 1。用 echo 1 > /proc/sys/net/ipv4/ip_forward 设置只在下次重启前有效,这正是几周后同样的抱怨又回来的原因。

把它写到一个能熬过重启的地方:

echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-openvpn-forward.conf
sysctl --system

第二道门是过滤表。装了 Docker 的主机常常把 FORWARD 策略设成 DROP,隧道也跟着继承这个决定。iptables -L FORWARD -v -n 会同时显示策略和包计数器;如果策略是 DROP,转发就需要为隧道到 LAN 这个方向显式放行一次,再为回程放行一次。

第三步:把数据包跟到 LAN 上,并寻找回程

同时盯住服务器的两侧。在隧道的网卡和 LAN 网卡上抓包,过滤到单个客户端和单台主机,保证输出仍然可读:

tcpdump -ni tun0 host 192.168.50.10
tcpdump -ni eth0 host 192.168.50.10

请求出现在 tun0 上,又以 10.8.0.x 为源出现在 eth0 上,随后归于沉默,说明包到了,回程却去了别处。LAN 主机把回程交给默认网关,而那个网关没有到 VPN 地址池的路由,回程就死在了 LAN 路由器上。从服务器去 ping LAN 主机并不能证明这条路径,因为那份流量根本不过隧道。

第四步:在地址转换和回程路由之间二选一

两种修法都能堵住这个缺口,而这个选择的影响不止于 ping 通不通。源地址转换让服务器成为表面上的发送方:

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

把规则限定在地址池和 LAN 网卡上,然后持久化,否则下一次防火墙重载会悄悄抹掉你的修复。代价是身份:在 LAN 日志和访问规则里,每个客户端都长得像 VPN 服务器,而且per-client policy becomes guesswork。

在 LAN 路由器上加一条指向 VPN 服务器的 10.8.0.0/24 静态路由,客户端地址就仍然可见,当那台路由器是你的时候,这是更好的长期答案。不少站点会并行一段时间:先用 NAT 应急,等防火墙策略写好了再上路由。如果服务器不是地址池而是站点到站点的对端,服务器侧同样需要为远端网段配 iroute,否则对端路由存在,客户端流量却永远不会关联到它。

第五步:从客户端验证,并记录改了什么

收尾要在客户端做,而不是在服务器上。连上 LAN 主机上的真实服务,再用 iptables -t nat -L POSTROUTING -v -n 确认转换计数器动了,因为零包的规则等于从未匹配过的规则。当你需要证明回程方向被跟踪时,conntrack -L -s 10.8.0.5 会显示流及其状态。

在服务器配置里留一条备注:哪个网段、哪个网卡、哪条规则,以及 LAN 路由器是否带回了程路由。下次客户端升级把下发的路由冲掉时,这条备注能把一场救火变成一个单行修复。

相关文章