一个流传的说法是,L2TP 在 Android 上只是 shell 脚本能启动的又一项:加条路由、调用旧服务,完事。这不可能,因为隧道要承载数据包,必须先让四个不同的层达成一致。IPsec 层保护路径,L2TP 承载会话,PPP 协商链路,而 Android 必须把结果注册成一个可路由的 VPN。我见过两次照抄命令的尝试,两次路由都在,却没有任何加密。

改一条路由究竟能做到什么

添加路由只决定数据包下一跳去哪里。它不协商安全关联,不打开 L2TP 会话,也不执行产生地址、对端和 DNS 设置的 PPP 交换。在手机上,可见的结果是流量走错路径,或者发出去就死掉,任何地方都没有隧道状态。

从旧版本抄来的服务调用同样如此。Android 内部的 VPN 管线并不是公开的自动化 API,另一个版本里的编号事务可能命中不同的操作,或者要求脚本没有的权限。携带密码的自动化不应依赖猜测的内部参数,因为失败要么是静默的 no-op,要么是留下一个半成品配置,没人清理。

四个归属者,脚本一个都不是

按顺序看。IPsec 建立并维护受保护的路径,认证对端。L2TP 在其上承载会话,这种分层正是标准把它们当作两套独立机制而非一个协议的原因。在此之内,PPP 协商客户端要使用的链路参数。随后 Android 掌握平台一侧:已注册的 VPN 接口、按应用的范围、always-on 状态,以及决定应用被杀后如何处理的生存周期。

脚本只能看到最后一层,而且通常是间接的。如果某个客户端暴露了有文档的控制接口,脚本可以请求它连接,之后还能观察接口状态。它无法替代下面三层,也无法让平台把未注册的接口当作 VPN。

为什么守护进程也不是捷径

在设备上启动一个有特权的 Linux 守护进程,看起来像缺的那块拼图,而且往往确实能把隧道拉起来。但它不会把这条隧道注册给 Android。接口存在于内核里,可平台的 VPN 选择、按应用路由、always-on 策略和生存周期都不知道它,守护进程自己的路由和 DNS 改动还会与网络管理器争抢同一批表项。

结果是:连接对守护进程的流量有效,却偏偏对你关心的应用无效;或者它撑到系统重新评估网络为止。两种结果都不是平台缺陷,而是拥有 socket 与拥有 VPN 设施之间的边界。

受支持的路径长什么样

如果组织能迁移到由 Android 自己提供的配置,平台路径才是诚实的做法。自 API 30 起,VpnManager 支持由应用来提供和控制平台 VPN 配置,并带有绑定到调用应用的授权流程。它不是 L2TP 转换器:它为 IKEv2 提供的配置模型不会转换现有的 L2TP 配置,换协议是迁移而不是改名。身份、路由、私有 DNS 与访问权限都必须有意识地搬过去。

如果服务必须留在 L2TP/IPsec 上,受支持的组合是运营商真正写明文档的客户端与系统版本,以及它已有的自动化契约。没有记录在案的无值守控制时,正确答案是受支持的手动连接,而不是一个在某次构建上碰巧能跑的脆弱特权脚本。

正确的自动化做什么

有了文档化的控制,自动化的职责很小也很站得住脚:请求指定的连接、等待就绪、在认证或配置失败时停止、执行预定工作负载,并把清理留给拥有该连接的组件。凭据留在懂得如何存储和轮换它的客户端里。

写第一行之前,先记下每一层归哪个组件所有,并据此核对结果:

  • IPsec 与 PPP 由客户端处理,而不是脚本的路由。
  • 连接会出现在平台的 VPN 状态里,因此按应用范围和 always-on 对它生效。
  • 任何内部服务调用、事务编号或猜测的参数都不携带凭据。
  • 任何一步失败都不会留下注册到一半的配置。

相关文章