前段时间折腾了在 iOS 上用小火箭 + WireGuard 实现全局代理和内网穿透。由于 iOS 的限制,只能这么干——WireGuard 协议本身并不是 P2P 的,所以 iOS 上的方案通信时必须从中继服务器兜一圈,会稍微增加一些延迟。但 macOS 上没有这个限制,我没有选择原生 WireGuard,而是用了 Tailscale 这个免费工具。它基于 WireGuard 协议,做了自动打洞,打洞失败时会自动走中继节点,拿来做内网穿透再合适不过了。
Tailscale 可以直接在 App Store 安装,也可以用 Homebrew 安装;QuantumultX 可以在 App Store 购买(仅限美区商店)。装好之后直接配置就行了,下面主要记录一下我踩坑的地方。
网络数据包的流转顺序
正常安装之后,大致的网络结构是这样的:QuantumultX 接管了系统网络,Tailscale 会创建一个虚拟隧道网卡(比如 utun8)。首先我们要明确,macOS 发出一个网络包的时候,流转顺序是什么样的:
┌────────────────────┐
│ 浏览器 / App │
└─────────┬──────────┘
│
socket()
│
┌─────────▼──────────┐
│ macOS 网络栈 │
└─────────┬──────────┘
│
查 路 由 表
│
┌─────────────────┼──────────────────┐
│ │
访问公网 (Google) 访问 Tailscale IP
│ │
default → utun8 100.64/10 → utun3
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ QuantumultX │ │ Tailscale │
│ PacketTunnel │ │ PacketTunnel │
└──────┬───────┘ └──────┬───────┘
│ │
规则引擎判断 WireGuard 封装
│ │
直连 / 代理 │
│ │
▼ ▼
en0 物理网卡 en0 物理网卡
│ │
Internet 对端节点配置 Tailscale 路由
这里其实啥都不用配置,我们就安装好 Tailscale,登录账号,连接上一个节点就行了。
Tailscale 在创建虚拟隧道网卡的时候,会往路由表中注入路由规则。我们可以用 netstat -nr | grep 100.64 来查看 Tailscale 创建的路由:
可以看到,所有去往 100.64.0.0/10 这个网段的流量都会走 utun8。
排查问题:excluded_routes 的坑
到这里一切看起来都很完美,是吧?但实际上根本没用,哈哈。我在排查的时候注意到:
嗯?路由表里多出了一条规则——去往 100.64.0.0/10 的数据不走 utun8,而是直接发往局域网网关了。其实本来我以为需要配合QuantumultX,于是在它的配置文件里面翻来翻去,终于找到了这行:
excluded_routes=100.64.0.0/10,127.0.0.0/8, 192.168.0.0/16100.64.0.0/10 赫然在 excluded_routes 里面!突然就明白了,excluded_routes 的实现原理就是往路由表中插入一条更高优先级的规则,让匹配的流量直接走物理网关,从而绕过隧道。如果真要让数据都走 utun8,就不能保留这一条。
于是删掉 100.64.0.0/10,再用上面那个命令检查,果然没有两条冲突的路由规则了。重新测试 Tailscale,连接成功!幸好我这里的网络不是对称型 NAT,成功打洞,两台机器直接 P2P 通信,延迟非常低。
事后想了想,为什么会有这个奇怪的 excluded_routes 呢?其实我的配置文件是 copy 别人的,官方仓库里的示例也带着这个配置,可能就这么一直流传下来了吧。这次踩坑也暴露了自己对 macOS 网络流转顺序的理解不够深入,啊哈哈,搞来搞去,其实和QuantumultX 没啥关系。
注意:
100.64.0.0/10是 Tailscale 使用的固定网段(CGNAT 地址段)。