你挑了一台看起来更近的服务器,按下连接,浏览器在新隧道建立之前就停止了加载。图标跳到连接中状态,在所有标签页超时期间一直停在那里。这段停顿通常是 kill switch 在按设计工作:客户端还没有可用的隧道,流量因此被阻断。这段停顿能否接受,取决于客户端在那几秒里做了什么,也取决于它宣称解除阻断时是否真的解除。

客户端在两台服务器之间做了什么

切换端点不是一次开关,而是好几个步骤。客户端拆掉当前隧道,更新阻断规则,在隧道之外解析并连上新端点,然后安装新路由并解除阻断。你感到的那段空档,就在第一步和最后一步之间。

这里顺序比速度更重要。如果阻断规则先于新端点的例外生效,客户端就无法完成握手,会一直停在连接中直到超时。如果例外是旧服务器地址的副本,新的握手会被当作普通出站数据包丢弃。两种情况在浏览器里看起来完全一样,所以六秒后恢复的切换和永远不恢复的切换可能出自同一个原因。

新端点需要在阻断里开一个孔

要建立隧道,客户端必须通过物理接口向新服务器的地址和端口发送数据包,例如常见的 WireGuard 配置用 UDP 51820,OpenVPN 用 1194 和 443,具体以你自己的配置为准。这些流量不在即将创建的隧道之内,所以整机阻断会把它吞掉,除非存在匹配的放行规则。

基于主机名的配置还多一个条件:解析本身。如果客户端在阻断升起之前完成解析,可以用缓存地址,但一旦配置轮换地址或缓存条目过期,握手就会指向放行规则没有覆盖的地址。IPv6 让这一点变得明显:双栈端点可以同时发布 A 记录和 AAAA 记录,而只覆盖 v4 地址的放行规则会让客户端一直对着被阻断的 v6 地址重试。

选项名称和默认值随客户端和版本而不同,所以请阅读你所客户端有文档的列表,不要从截图里猜。OpenVPN Connect 的设置参考位于 openvpn.net/connect-docs/app-settings-windows,它就是一个客户端如何描述其阻断与分离隧道选项的例子。

Fail-closed 是一台状态机,不是一个开关

大多数客户端把阻断表达为防火墙策略,共三种状态:隧道已建立,流量走隧道且没有阻断;隧道已断开,阻断生效;以及切换中,阻断生效并临时放行端点。缺陷恰恰住在第三种状态里,因为它必须在几秒内变化两次,而没能退出这种状态看起来就完全像永久阻断。

机制因平台而异,故障也一样。Windows Filtering Platform 的筛选器有权重,所以压过你放行规则的阻断筛选器即使放行存在也照样丢包;而 被结束的客户端进程留下的筛选器 可能在应用消失之后仍让机器离线。macOS 客户端通常把 pf 规则写进 anchor,规则顺序和快速判定决定哪条规则生效。Linux 客户端通常管理带 drop 策略的 nftables 或 iptables 链,并对端点、隧道接口和回环给出显式放行。Android 的始终开启 VPN 配合无 VPN 时阻止连接是这组里最严格的,因为系统是对整台设备而不是按应用施加阻断。

WireGuard 需要单独说明,因为 AllowedIPs 是路由选择器,不是阻断器。它决定哪些目的地进入隧道,却不会阻止一个还没有隧道的数据包从 Wi-Fi 出去。在只有 WireGuard、没有防火墙规则的配置里,kill switch 实际上是缺少路由,握手被丢弃之后真数据包仍可能从物理接口出去。

去测量空档,而不是盯着图标

看数据包,不要看状态灯。在 Linux 上,一个终端对公网地址发起慢速 ping,另一个终端在物理接口上抓取隧道的传输端口:

ping -i 0.2 -c 100 1.1.1.1 | tail -5

sudo tcpdump -ni eth0 'udp port 51820'

两边都跑着的时候在客户端里切换服务器。有三件事重要:ping 停了多久、抓包是否显示握手到达新端点,以及流量是在客户端状态变成已连接之前还是之后恢复。如果 ping 空档先结束,要么阻断被提前解除,要么隧道在指示器承认之前就已经建立。如果空档结束得更晚,那指示器就过于乐观。

在 Windows 上,netsh wfp show state 会写出一份快照,你可以在其中查找客户端声称已断开时仍然存在的阻断筛选器。在 macOS 上,sudo pfctl -sr 会列出已加载的规则及其顺序。把任一份快照与客户端自己的日志对照;你 ping 出来的时间线会说明阻断是结束得晚,还是从未结束。

正常变暗与卡住的阻断

一定程度的中断正是这个功能的用意,所以先分诊再改任何设置:

What you seeUsually meansNext check
Two or three seconds of timeouts, then normal browsingExpected fail-closed gapNothing to fix
State says connected, traffic stays deadBlock filter or route left behindFirewall snapshot, client log
One app dead while others workTCP session bound to the old exit addressRestart the app
LAN devices unreachable after the switchLAN exception not reappliedAllow-LAN or split-tunnel setting
New server hangs, the old one reconnectsPermit covers only the old addressResolved address of the new host, including AAAA

如果你落在第二行或第五行,不要为了测试而关掉 kill switch,那会把路由缺陷变成泄漏。重启客户端的服务,或者在筛选器能在进程被杀后存活的环境里重启机器,然后看阻断是否消失。如果消失了,问题出在切换时的清理,而不是阻断本身。

下次切换之前,确认四件事:端点放行覆盖配置解析出的每一个地址族,客户端日志显示切换已经完成,你的 ping 测试量的是空档而不是图标,退出客户端能恢复正常联网。在切换期间守住、之后放开的 kill switch 是在尽职。提前放开、或者永远不放开的,值得去排查。

相关文章