VPN 隧道始终连不上:数据包究竟停在哪一步
隧道始终无法建立时,两端都只留下空白日志。查明 GRE、ESP 还是 UDP 4500 被丢弃,并在改动服务器之前识别运营商 NAT。
客户端不停重试,日志在握手完成前就中断,服务器端则什么都没有记录。这种模式把注意力从凭据转向链路:两端之间存在某种机制,拒绝承载隧道所需的协议。在改动任何一侧配置之前,先确定数据包停在哪一步,因为被过滤的协议和错误的密码在多数客户端里给出的是同一条友好提示。测试按成本排序,最便宜的放在最前面。
证明流量停在哪一步
同时在客户端的出站接口和服务器上抓包。对比发出的与收到的内容,并记下差异出现在哪个方向。
服务器侧什么都没有,说明路径或第一层 NAT 丢弃了流量;有到达却没有回应,则指向服务器上的防火墙或不存在的回程路由。经常终结隧道的路由器很少留下有用的日志,因此抓包就成了记录。记下目标端口和协议号,因为这两种故障在客户端对话框里看起来完全一样。
缺少协议 47,PPTP 直接失效
PPTP 用 TCP 1723 承载控制,数据放在 GRE 里,而 GRE 是 IP 协议 47,并不是端口号。只转发 1723 会得到经典结果:连接通过认证后陷入静默,因为承载载荷的协议没有任何放行规则。客户端无法把它和密码被拒区分开,所以抓包必须发生在改配置之前。
透传与入站转发方向相反,而固件常常对两者用同一个标签,足以把所有人绕晕。在 Linux 客户端上,本地防火墙可能在路由器介入之前很久就丢弃 GRE,因此协议 47 需要自己的规则,并且必须加载 conntrack 辅助模块。Ubuntu 的 ufw 把这些规则放在 before 文件里,因为普通的 allow 规则无法表达协议号。之后请重载防火墙,而不是重启机器。
# /etc/ufw/before.rules, inside *filter
-A ufw-before-input -p gre -j ACCEPT
sudo ufw allow 1723/tcp
sudo modprobe nf_conntrack_pptp
sudo conntrack -L | grep gre两层 NAT 背后的 L2TP 与 IPsec
L2TP 放在 IPsec 里,需要 UDP 500 做密钥交换、检测到 NAT 后使用 UDP 4500,以及作为协议 50 的 ESP。三者各自独立过滤,所以放行其中一个的规则并不能证明另外两个也通。两台路由器串联会让每个数据包被转换两次,两端对协商结果的认知也不再一致。
把外层调制解调器改为桥接,或关闭内层路由器的 NAT,让公网地址到达终结隧道的那台设备。两者都不可行时,隧道必须从双重 NAT 边界内部发起,而不是在那里被终结。
运营商网络与地址共享
WAN 地址落在 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 或 100.64.0.0/10 之内,说明路由器没有公网地址,任何来自互联网的入站转发都到不了它。运营商级 NAT 还会过滤它不预期的协议,GRE 在移动链路上经常成为牺牲品。要比较的是路由器自身的 WAN 地址,而不是查询网站为它后面的客户端给出的地址。
把同一台路由器接到有线线路上,一次测试就能把运营商侧的问题和配置错误分开。如果运营商拒绝该协议,剩下的选择是改走 443 上的其他传输,或从内部发起隧道。
关闭端口之前先读监听列表
吓人的端口扫描往往只是出厂即启用的功能集合。清点监听者,把每个套接字对应到一项功能,然后关闭该功能而不是封锁端口,这样两种视图才不会逐渐脱节。
ss -lntup
systemctl list-units --type=service --state=running | grep -i vpn
nc -vz 203.0.113.10 1723
nc -vzu 203.0.113.10 4500SoftEther 的默认值就是个好例子:TCP 443、992 和 5555,而 L2TP/IPsec 与 DNS 隧道功能还会添加自己的套接字。关掉这些功能是把监听者移除,而不是藏起来;从外部主机探测,才能知道运营商到底放不放行该端口。本地能应答的监听者仍可能在上游被关闭,所以要把路由器自身的 WAN 地址与外部服务报告的结果对照。