公司通过 Tailscale 做 VPN。本地有两个网段:
172.31.162.0/24172.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 |
如果出口是 utun 或 tailscale0,说明流量被 Tailscale 接管了。
原因
操作系统选路会优先选择最长前缀匹配。
当路由表里同时存在:
1 | default -> 本地网关 |
访问 172.31.162.10 时,系统会选择 /24,而不是默认路由。
所以这里不是“让默认路由优先”,而是要增加一条比 Tailscale /24 更精确的本地路由。
解决方式
保留 Tailscale 的 subnet router,也保留客户端的 accept-routes,只在本地客户端上增加更长前缀的本地路由。
例如 Tailscale 下发的是:
1 | 172.31.162.0/24 -> Tailscale |
那就在本地客户端增加两条 /25 路由:
1 | 172.31.162.0/25 -> 本地网关 |
只在确认当前连接的是公司本地网络、且 172.31.165.254 可达时执行。Tailscale 官方也提醒,这类本地路由方案应只用于网络配置明确、固定的环境;移动网络场景要小心误路由。
临时加路由用 route:(这是临时方案,持久化需要 networksetup、LaunchDaemon 或公司统一下发。)
1 | sudo route -n add -net 172.31.162.0/25 172.31.165.254 |
这里的 172.31.165.254 是本地网关,按实际环境替换。
验证
执行:
1 | route -n get 172.31.162.10 |
期望结果是走本地网关,例如:
1 | gateway: 172.31.165.254 |
而不是 utun。
如果要删除:
1 | sudo route -n delete -net 172.31.162.0/25 |
小结
这个问题的关键不是关闭 Tailscale,而是利用最长前缀匹配:
Tailscale 继续负责公司 VPN;本地访问本地网段时,用更具体的本地路由让流量走本地网关。
更详细的原理和其他系统的处理方式,可以参考官方文档: