切换服务器时 kill switch 为什么会中断流量
为什么切换服务器期间 kill switch 会阻断流量、客户端在那几秒里做了什么,以及如何区分正常空档和卡住的阻断。
你挑了一台看起来更近的服务器,按下连接,浏览器在新隧道建立之前就停止了加载。图标跳到连接中状态,在所有标签页超时期间一直停在那里。这段停顿通常是 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 see | Usually means | Next check |
|---|---|---|
| Two or three seconds of timeouts, then normal browsing | Expected fail-closed gap | Nothing to fix |
| State says connected, traffic stays dead | Block filter or route left behind | Firewall snapshot, client log |
| One app dead while others work | TCP session bound to the old exit address | Restart the app |
| LAN devices unreachable after the switch | LAN exception not reapplied | Allow-LAN or split-tunnel setting |
| New server hangs, the old one reconnects | Permit covers only the old address | Resolved address of the new host, including AAAA |
如果你落在第二行或第五行,不要为了测试而关掉 kill switch,那会把路由缺陷变成泄漏。重启客户端的服务,或者在筛选器能在进程被杀后存活的环境里重启机器,然后看阻断是否消失。如果消失了,问题出在切换时的清理,而不是阻断本身。
下次切换之前,确认四件事:端点放行覆盖配置解析出的每一个地址族,客户端日志显示切换已经完成,你的 ping 测试量的是空档而不是图标,退出客户端能恢复正常联网。在切换期间守住、之后放开的 kill switch 是在尽职。提前放开、或者永远不放开的,值得去排查。