TAP 还是 TUN?Android 上的 OpenVPN 客户端与桥接服务器
TAP 承载以太网帧,TUN 承载 IP 包,而 Android 应用只能拿到后者。先把两者比清楚,再去改服务器一侧。
一个在笔记本上能用的配置,在手机上却失败。显然的做法是改客户端配置里的一个词。这个词不会有用,因为两端说的不是同一类流量。
TAP 承载以太网帧。TUN 承载 IP 包。Android 的 VPN 应用拿到的是 IP 接口,所以不管配置怎么写,两种安排里只有一种可能。
比较很短,但它决定了你是在手机上修好,还是必须改服务端。
两种隧道,载荷不同
TAP 接口的行为像一块以太网网卡。它承载帧,也就是说 ARP、广播,以及 LAN 在 IP 之下承载的一切。正因如此才能做桥接:隧道扩展了一个广播域,客户端就像插在交换机上一样。
TUN 接口承载发给它的 IP 包。没有需要协商的链路层,没有 ARP,也没有广播。什么进入隧道由路由决定,服务端配置用的是子网而不是网桥。
两端必须对做的是哪一种达成一致。只在一侧把 dev tap 改成 dev tun 并不会转换服务;结果是客户端期待一条路由型隧道,而服务器仍在推帧,握手可能成功,可用的流量却一点也过不去。
Android 实际交给应用的是什么
平台的 VpnService API 给应用一个用于 IP 包的虚拟接口,用地址、路由和 MTU 配置。这个 API 里没有以太网网卡,应用也没法凭空造一块出来。
所以 OpenVPN Connect 文档里写的是 Android 支持路由型 TUN 连接,而不是 TAP 桥接。Android 常见问题就是客户端一侧对这条边界的说明,它与平台 API 一致。
带 root 权限的网络工具、定制固件,或者实现了显式转换层的产品,属于另外的安排。常见的情况才是该围绕它做规划的:一个能导入你的文件、以普通用户身份运行的应用,只有 IP 接口,下面什么都没有。
硬要试的话,会看到什么
连接通常会建立。这正是失败让人困惑的地方:客户端报告已连接,服务器日志里出现了一个对端,可没有任何应用能用。
发给手机分配地址的流量到不了,因为服务器把帧桥接到一个手机从来没有过的 MAC 地址。依赖广播的发现一直是空的。如果 LAN 依赖跨网桥的 DHCP,手机就无法用这种方式拿到租约,而在桥接子网里手工指定地址,也仍然造不出缺失的那块接口。
有些客户端更早就失败了,在导入配置时看到它无法实现的设备类型。两种表现意思一样,而加路由、改 DNS、拓宽隧道地址段,都修不好其中任何一种。
服务端必须改什么
这件事归运行 VPN 服务的人管,而且通常是加一套,而不是迁移。再跑一个 OpenVPN 实例,使用路由型设备、自己的客户端子网,以及会分配地址并下发手机所需路由的服务端配置。原有的桥接实例继续为真正需要以太网的设备服务。
有两个细节让它在实践中真正可用。LAN 需要一条指向 VPN 主机的、通往新客户端子网的回程路由,否则回包永远回不来。隧道主机上的防火墙必须在隧道接口和 LAN 之间转发,而这正是从主机自身测试成功时最常被漏掉的一步。
如果运营方加不了服务,替代方案是让一台能加入网桥的设备代劳,也就是笔记本或路由器替手机出面。那是一条有自己的管理成本的绕行做法,不是 Android 的配置。
从应用出发决定,而不是从隧道出发
先问手机到底要够到什么,再把它映射出来。按名字或地址访问的文件共享或内网站点属于 IP 需求,只要 DNS 能解析名字、路由存在,路由型隧道就能满足。遍历本地广播域的发现工具不属于 IP 需求,补多少路由都填不满。
| Requirement | Tunnel that satisfies it |
|---|---|
| File server or intranet by hostname | Routed TUN, with working DNS |
| Broadcast or ARP-based discovery | Ethernet bridging, so not an ordinary Android client |
| Multicast delivery | Bridging or an explicit multicast relay |
| One application to one address | Routed TUN, narrowest possible route |
那张表同时也是要发出去的请求。说清是哪个应用、用的是什么地址或名字、是否依赖发现。运营方能照着这段描述动手,而面对一台连不上的手机,谁都动不了。
最后,把 TAP 服务留给真正需要它的设备,而不是让每个客户端都去模仿一块 LAN 网卡。两个目的清楚的服务,比一个对所有人都只做一半的服务更好维护。