公司通过 Tailscale 做 VPN。本地有两个网段:

  • 172.31.162.0/24
  • 172.31.164.0/23

本地的 subnet router 会把这两个网段上报到 Tailscale,外地同事依赖它访问本地网络,所以不能删除 subnet router。

同时,本地客户端也需要通过 Tailscale 访问外地办公网,所以不能关闭 accept-routes

问题

本地 172.31.164.0/23 访问 172.31.162.0/24 时,本来应该走本地网关:

1
172.31.164.x -> 本地网关 -> 172.31.162.x

但实际流量走进了 Tailscale。

可以用下面的命令确认:

1
route -n get 172.31.162.10

如果出口是 utuntailscale0,说明流量被 Tailscale 接管了。

原因

操作系统选路会优先选择最长前缀匹配。

当路由表里同时存在:

1
2
default              -> 本地网关
172.31.162.0/24 -> Tailscale

访问 172.31.162.10 时,系统会选择 /24,而不是默认路由。
所以这里不是“让默认路由优先”,而是要增加一条比 Tailscale /24 更精确的本地路由。

解决方式

保留 Tailscale 的 subnet router,也保留客户端的 accept-routes,只在本地客户端上增加更长前缀的本地路由。

例如 Tailscale 下发的是:

1
172.31.162.0/24 -> Tailscale

那就在本地客户端增加两条 /25 路由:

1
2
172.31.162.0/25   -> 本地网关
172.31.162.128/25 -> 本地网关

只在确认当前连接的是公司本地网络、且 172.31.165.254 可达时执行。Tailscale 官方也提醒,这类本地路由方案应只用于网络配置明确、固定的环境;移动网络场景要小心误路由。

临时加路由用 route:(这是临时方案,持久化需要 networksetup、LaunchDaemon 或公司统一下发。

1
2
sudo route -n add -net 172.31.162.0/25 172.31.165.254
sudo route -n add -net 172.31.162.128/25 172.31.165.254

这里的 172.31.165.254 是本地网关,按实际环境替换。

验证

执行:

1
route -n get 172.31.162.10

期望结果是走本地网关,例如:

1
2
gateway: 172.31.165.254
interface: en0

而不是 utun

如果要删除:

1
2
sudo route -n delete -net 172.31.162.0/25
sudo route -n delete -net 172.31.162.128/25

小结

这个问题的关键不是关闭 Tailscale,而是利用最长前缀匹配:

Tailscale 继续负责公司 VPN;本地访问本地网段时,用更具体的本地路由让流量走本地网关。

更详细的原理和其他系统的处理方式,可以参考官方文档: