← 文档索引

KillSwitch 设计与跨平台能力矩阵(知识库)

Pangolin 客户端 · 断网保护设计依据与现状 · 2026-06-22

KillSwitch 不是「一个开关」,而是分层能力——理想态、平台天花板、当前实现是三件不同的事。本文沉淀其设计依据与现状。

1. 本质:fail-closed

KillSwitch 的本质是 fail-closed

隧道不处于活动状态时,禁止任何流量走明文/默认路径出去。

目标是堵住 VPN 掉线瞬间的 IP / DNS 泄漏。没有 KillSwitch 时,隧道一掉,流量静默回落到运营商默认路由,用户真实 IP 与 DNS 查询直接暴露——对科学上网场景是致命泄漏。

2. 理想设计的 5 条属性

  1. 默认关闭(fail-closed):隧道一旦不可用,流量是「被丢弃」而非「放行」。
  2. 扛得住进程死亡 ← 最难。app/内核进程崩溃或被系统杀掉后,封锁依然生效。这要求封锁由 OS 内核/框架强制,而不是靠 app 进程内的路由表。
  3. 覆盖所有「缺口窗口」:开机后还没连上的窗口、掉线重连的窗口、进程崩溃的窗口——三个都要堵。
  4. DNS 防泄漏:DNS 查询也强制走隧道或被一并阻断。
  5. 可控范围 + 诚实 UI:可选放行 LAN(局域网打印机等)、可选分应用;并如实告诉用户当前到底保护到了哪一层,不夸大。

3. 能力分级(统一标尺)

Level机制能堵什么堵不住什么
L0 无掉线即走明文全泄漏
L1 内核路由绑定(软)路由绑死隧道接口(如 sing-box strict_route进程活着、隧道未建/重连时不泄漏进程一死、接口被拆 → 路由恢复 → 泄漏
L2 OS 防火墙强制(硬)OS 级包过滤规则(NE 框架 / WFP / nftables)阻断非隧道流量进程死了也照堵(规则在 OS 内核,不在 app)开机到规则生效前的窗口
L3 常开 + 开机持久L2 + OS 从开机起自动拉起 VPN 并强制阻断开机窗口也堵,全程 fail-closed(基本无死角)

理想态 = L3。

关键认知:L1 与 L2 之间有一条质变线——L1 是「app 进程内的约束」,L2/L3 是「OS 框架的约束」。只有跨过这条线才算「真 KillSwitch」。目前 Pangolin 各平台用的 strict_route 都还停在 L1。

4. 各平台能力天花板

平台机制天花板说明
macOSNetworkExtension System ExtensionL3includeAllNetworks=true 全量入隧道 + NEOnDemandRule 常开 + enforceRoutes。NE 守护进程(neagent)在系统级强制,扛 app 崩溃。Apple 平台原生支持最完整。
iOSNetworkExtensionL3同 NE 原语,甚至更干净;on-demand「按需常开」。⚠️ 目前无 iOS 客户端
AndroidVpnServiceL3,但有条件app 内只能到 L1strict_route);真正的 L2/L3 = 系统设置「始终开启 VPN + 无 VPN 时阻止连接」,OS 级强制、扛崩溃、开机生效。但 app 不能编程开启,只能深链引导用户手动开(或 Device Owner/MDM 下发)。
Windows子进程 sing-box + wintunL2(需开发)现仅 strict_route(L1)。要到 L2 须让 app 装 WFP(Windows Filtering Platform)过滤器阻断非隧道流量;做成系统服务持久化才能扛崩溃。
Linux(非主目标)子进程 + tunL2(需开发)L1 现成;L2 靠 nftables/iptables killswitch 链。

一句话:Apple 两端原生能直达 L3;Android 能到 L3 但要用户手动配合;Windows/Linux 要自己写 OS 防火墙规则才能上 L2。

5. 各平台当前实际实现(代码事实)

平台当前 Level真实状态代码位置
WindowsL1✅ 改 strict_route + 子进程重载 + 退避重连。进程被硬杀仍泄漏。desktop_vpn_bridge.dart:305applyKillSwitchToConfig)、:178
LinuxL1同 Windows(共用 DesktopVpnBridge)。同上
macOSL0❌ stub:setKillSwitch 直接 result(nil)includeAllNetworks()→false。天花板 L3,实际啥都没做。VpnChannel.swift:69PacketTunnelProvider.swift:224
AndroidL0❌ stub:setKillSwitch 只打日志(TODO 11G)。MainActivity.ktsetKillSwitch 分支)
iOS无客户端。

注:macOS 自 kUseNativeVpnMacOS=truevpn_bridge_provider.dart:19)起走原生 System Extension,不再走 DesktopVpnBridge。所以 macOS 的 strict_route(Windows 路线)对它不生效,必须走 NE 原生路线。

6. Pangolin 现实判断与决策

7. 关联待办

KillSwitch 相关待办见项目 todo//todo list),主要三条:

8. 参考实现位置索引

主题文件
桥接契约(setKillSwitch 方法签名)client/lib/bridge/vpn_bridge.dart
平台分派(哪个平台走哪个 bridge)client/lib/bridge/vpn_bridge_provider.dart
Windows/Linux 实现(strict_route)client/lib/bridge/desktop_vpn_bridge.dart
macOS 原生通道(stub)client/macos/Runner/VpnChannel.swift
macOS 隧道 Providerclient/macos/PacketTunnel/PacketTunnelProvider.swift
Android 通道(stub)client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/MainActivity.kt
Android VPN 服务client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt