路由器报告的 WAN 地址以 100.72 开头,动态 DNS 名称解析到另一个公网地址,而这台路由器上的 VPN 服务器从未完成来自外部的握手。端口转发已配置,服务正在运行,从 LAN 发起的测试仍然成功。路由器日志里看不到任何错误。这种组合就是运营商级 NAT,没有任何防火墙规则能在 ISP 持有的地址上打开端口。

确认共享地址只需两分钟,却能避免一周的配置折腾。

指向运营商 NAT 的迹象

把路由器状态页上的 WAN 地址与外部查询报告的地址作对比。WAN 地址落在 100.64.0.0/10 内是常见迹象,私有网段 10/8、172.16/12 和 192.168/16 也说明同样的问题。如果 traceroute 反复显示同一个公网跳点,或者较早的跳点以私有地址应答,说明路由器和互联网之间还有一层转换。有些 ISP 只在客户门户里列出分配的地址,所以要把该值连同 traceroute 一起导出。

更换协议或端口没有用,因为问题在于入站可达性,而不是端口被关闭。

为什么端口转发和动态 DNS 都修不好

端口转发规则映射的是路由器 WAN 接口上的端口,但入站流量根本到不了这个接口:ISP 先做转换,只按自己的表转发允许的部分。把共享地址发布到动态 DNS,只会把客户端指向许多人共用的 ISP 网关,所以域名能解析,握手仍然失败。hairpin 测试能成功,是因为路由器从内部应答自己的规则,UPnP 也有完全相同的盲区。

重新配置客户端也没用,因为故障在任何客户端设置被读取之前就已经发生。

用带显式路由的 VPS 做中继

在前面放一个公网地址:租一台能承载流量的最小 VPS,然后让家里的路由器向外拨号连它。家庭网络发出的 UDP 能穿过运营商 NAT,于是隧道从内部建立,VPS 成为可到达的端点。家庭一侧变成拥有固定隧道地址的客户端,而不是服务器。

在 VPS 上添加针对该客户端的目录项,让服务器知道哪个对端拥有家庭子网,并添加匹配的内核路由。

# /etc/openvpn/server/ccd/home-site.conf
ifconfig-push 10.8.0.10 255.255.255.0
iroute 192.168.50.0 255.255.255.0

# server.conf
client-config-dir /etc/openvpn/server/ccd
route 192.168.50.0 255.255.255.0
push "route 10.8.0.0 255.255.255.0"

iroute 行告诉 OpenVPN 哪个客户端接收家庭子网的流量;route 行把该子网放进 VPS 的内核路由表,数据包才可能进入隧道。对于要发布的服务,再添加一条目标 NAT 规则,把公网端口转发到 10.8.0.10,并启用 net.ipv4.ip_forward。

从外部测试路径

逐跳验证公网一侧,因为隧道成功并不证明服务可达。VPS 上的监听状态和抓包能区分“什么都没到”和“到了又被丢弃”。

ss -lunp | grep 1194
tcpdump -ni any udp port 1194
nft list ruleset | grep -A4 dnat

# from a phone on mobile data, not the home Wi-Fi
nc -vz -u 203.0.113.10 1194

如果抓包显示手机发来的数据包却没有回应,下一步要查服务器配置或防火墙。如果抓包什么都没有,客户端仍在解析或发往错误地址。

值得先比价的替代方案

向 ISP 申请独立 IPv4 地址是最短路径,通常只需少量月费。当 VPN、客户端网络和 DNS 记录都支持时,原生 IPv6 能消除两端的转换。在家庭网络内放一台子网路由器做 mesh 覆盖,也能实现同样的中继,代价是要依赖协调服务。

如果需求是稳定的入站访问、每条规则都要掌握在自己手里,就选中继。如果预算允许、目标只是不再操心这件事,就选 ISP 地址。

相关文章