← 文档索引

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

Pangolin 客户端 · 断网保护设计依据与现状 · 2026-06-22(2026-06-30 更新:macOS L0→L3 已实现)

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)。同上
macOSL3✅ NE fail-closed(本轮 #1):includeAllNetworks+enforceRoutes(L2 OS 强制、扛崩溃)+ NEOnDemandRule 常开(L3)。随 killswitch 开关即时切换;手动断开先关 on-demand 防反弹。详见 §6.5。VpnChannel.swiftconfigureKillSwitch)、PacketTunnelProvider.swiftincludeAllNetworks()
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 现实判断与决策

6.5 macOS L0→L3 实现方案(本轮 #1)

macOS 走原生 NE System Extension(kUseNativeVpnMacOS=true),strict_route 那套对它不生效,必须用 NE 原语。L0→L3 的达成路径:

目标 LevelNE 机制效果
L2 OS 强制protocolConfiguration.includeAllNetworks=true + enforceRoutes=true全量流量入隧道、由 neagent 系统级强制,扛 app/内核崩溃(规则在 OS 不在 app 进程)
L3 常开+持久L2 + onDemandRules=[NEOnDemandRuleConnect()] + isOnDemandEnabled=true掉线/开机窗口也堵,OS 自动拉起,全程 fail-closed

改动(2 个原生文件,不动 Dart/服务端)

client/macos/Runner/VpnChannel.swift
  1. 新增缓存字段 killSwitchEnabled(默认 true,与 Dart AppSettings.killSwitch 默认对齐)。
  2. setKillSwitch 落地:存标志;若 manager 已存在 → 重新 applyKillSwitchConfig + saveToPreferences 即时生效。
  3. loadOrCreateManager 按标志设:includeAllNetworks / enforceRoutes=true / excludeLocalNetworks=true(放行 LAN 打印机等)/ onDemandRules + isOnDemandEnabled
  4. 关键 gotcha — stop():on-demand 常开时直接 stopVPNTunnel() 会被 OS 立刻拉回。手动断开须 isOnDemandEnabled=false + saveToPreferences stop;否则「断开」按钮失效。
  5. start()startVPNTunnel(options:) 附带 killSwitch 标志传给扩展。
client/macos/PacketTunnel/PacketTunnelProvider.swift
  1. startTunnel(options:) 读取 killSwitch 选项并缓存。
  2. libbox 回调 includeAllNetworks() 返回该缓存值(与 NE 层对齐,让 sing-box 不装绕行路由)。

无需新增 entitlement(packet-tunnel-provider 已有);includeAllNetworks/enforceRoutes/excludeLocalNetworks/on-demand 均 macOS 10.15/11+ 可用,Runner 部署目标满足。

验证(NE 难单测,靠真机手测)

  1. flutter build macos 通过、sysext 激活。
  2. killswitch 开 + 连接 → 正常上网;杀扩展进程或拔隧道 → 流量被阻断(无 IP/DNS 泄漏)= 跨过 L1→L2 质变线。
  3. on-demand:开机/掉线窗口隧道自动拉起;手动「断开」能真断、不反弹(验 stop() 的 on-demand 处理)。
  4. killswitch 关 → on-demand 取消、回普通连接。

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