Mac Tailscale + QuantumultX 实现全局代理和内网穿透

前段时间折腾了在 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 创建的路由:

image-20260211220202354

可以看到,所有去往 100.64.0.0/10 这个网段的流量都会走 utun8。

排查问题:excluded_routes 的坑

到这里一切看起来都很完美,是吧?但实际上根本没用,哈哈。我在排查的时候注意到:

image-20260211220845559

嗯?路由表里多出了一条规则——去往 100.64.0.0/10 的数据不走 utun8,而是直接发往局域网网关了。其实本来我以为需要配合QuantumultX,于是在它的配置文件里面翻来翻去,终于找到了这行:

excluded_routes=100.64.0.0/10,127.0.0.0/8, 192.168.0.0/16

100.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 地址段)。