记一次 ZeroTier + Tailscale 双开导致内网穿透失败的排查
记一次 ZeroTier + Tailscale 双开导致内网穿透失败的排查
背景
我在家里的 NAS 上同时跑了两个内网穿透工具:ZeroTier 和 Tailscale。平时用 ZeroTier 访问家里局域网的服务,一切正常。
直到有一天,我在南京的台式机(Windows)上通过 ZeroTier 访问马鞍山家里的 NAS,发现:ping 得通,但网页打不开。
折腾了一晚上,最后发现原因出乎意料。记录一下排查过程,也顺便介绍一下这两个工具。
先认识一下两位选手
ZeroTier
ZeroTier 是一个 P2P 虚拟组网工具。简单说,它能把世界上任意地方的设备组成一个虚拟局域网,每台设备拿到一个虚拟 IP(比如 192.168.80.x),然后就像在同一个局域网里一样互相访问。
工作原理:
- 每个节点安装 ZeroTier 客户端,加入同一个 Network ID
- 节点之间尝试 UDP 打洞(Hole Punching),建立 P2P 直连
- 打洞失败的话,通过 PLANET 服务器(ZeroTier 官方的根服务器)中继转发
- 你也可以自建 Moon 节点,作为更近的中继/打洞辅助
优点:
- 免费版支持 25 台设备
- 配置简单,装上就能用
- 支持自建 Moon,国内访问更快
缺点:
- NAT 打洞成功率取决于网络环境
- 国内连接官方 PLANET 服务器延迟较高
Tailscale
Tailscale 是另一个 P2P 虚拟组网工具,基于 WireGuard 协议。原理类似,但实现方式不同。
工作原理:
- 基于 WireGuard 加密隧道
- 通过 Tailscale 的 协调服务器 交换密钥和路由信息
- 节点之间尝试直连(UDP 打洞)
- 直连失败时,通过 DERP 中继服务器转发(全球有几十个节点)
优点:
- 基于 WireGuard,安全性高
- 配置极其简单
- DERP 中继全球分布,延迟尚可
缺点:
- 免费版只有 100 台设备(个人够用)
- DERP 中继带宽有限
- 主要面向海外用户,国内 DERP 延迟较高
两者的核心区别
| 特性 | ZeroTier | Tailscale |
|---|---|---|
| 协议 | 自有协议 (VL1/VL2) | WireGuard |
| 中继 | PLANET 服务器 / 自建 Moon | DERP 服务器 |
| 虚拟 IP | 自定义网段(如 192.168.80.x) | 固定 100.x.x.x 段 |
| 组网方式 | 加入同一 Network ID | 登录同一账号 |
| NAT 打洞 | UDP 打洞 | UDP 打洞 + DERP |
| 开源 | 是 | 部分开源 |
什么是 MTU?
在讲排查过程之前,必须先解释一个关键概念:MTU。
MTU 是什么
MTU(Maximum Transmission Unit,最大传输单元)是指一条网络链路上能传输的单个数据包的最大字节数。
打个比方:网络传输就像用卡车运货。
- MTU 就是卡车的载重量
- 你要运 10 吨货,卡车载重 5 吨,就需要跑两趟
- 如果卡车司机不知道路有限高(MTU 被限制),硬要装 10 吨,车就翻了(数据包被丢弃)
常见的 MTU 值
- 普通以太网:MTU = 1500 字节(最常见)
- PPPoE 拨号:MTU = 1492 字节(比以太网少了 8 字节 PPPoE 头)
- ZeroTier 虚拟网卡:MTU = 2800 字节(ZeroTier 默认设的,因为它觉得虚拟网络不需要限制)
- Tailscale:MTU = 1280 字节(保守设置,兼容性好)
MTU 不匹配会怎样?
当网络路径上某一段的 MTU 比你的数据包还小时:
- 正常情况:中间的路由器会把大包切成小包(分片,Fragmentation),到了目的地再组装
- DF 位设置了:现代操作系统默认设置 "Don't Fragment"(不分片)标志。路由器收到大包时,不会分片,而是丢弃,同时返回一个 ICMP 消息说"包太大了"
- ICMP 被防火墙拦截:很多防火墙会拦截 ICMP 消息。发送端永远收不到"包太大"的通知,继续发大包,全部被丢弃
这就是 PMTUD(Path MTU Discovery)失败 的经典场景。
表现就是:小包能通(比如 ping),大包不行(比如网页)。
排查过程
第一步:确认现象
在南京的台式机(ZeroTier 分配的 IP 是 192.168.80.4,Windows 系统)上:
C:\> ping 192.168.80.80
Reply from 192.168.80.80: bytes=32 time=429ms TTL=128
ping 通了,但浏览器打开 http://192.168.80.80 一直转圈,最终超时。
第二步:在 NAS 上检查服务状态
SSH 登录马鞍山的 NAS(192.168.80.80),检查 Web 服务是否正常:
bash
# Nginx 确实在监听 80 和 443 端口
ss -tlnp | grep -E ':(80|443)\s'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",...))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",...))
服务没问题。在 NAS 上本地访问也没问题:
bash
curl -sI http://192.168.80.80
HTTP/1.1 302 Moved Temporarily # 正常响应
说明问题出在网络传输层面,不是 Web 服务本身。
第三步:检查防火墙
bash
# iptables INPUT 链
iptables -L INPUT -n
Chain INPUT (policy ACCEPT)
# 没有拦截规则
防火墙也是放行的。
第四步:测试不同大小的数据包
这一步是关键突破。我用 ping 的 -s 参数指定不同的载荷大小:
bash
# 从马鞍山 NAS (192.168.80.80) ping 南京台式机 (192.168.80.4)
# 56 字节 - 默认大小
ping -c 1 192.168.80.4
# ✅ 通了,429ms
# 1000 字节
ping -c 1 -M do -s 1000 192.168.80.4
# ✅ 通了,385ms
# 1100 字节
ping -c 1 -M do -s 1100 192.168.80.4
# ✅ 通了
# 1200 字节
ping -c 1 -M do -s 1200 192.168.80.4
# ❌ 100% 丢包!
# 1400 字节
ping -c 1 -M do -s 1400 192.168.80.4
# ❌ 100% 丢包!
-M do 表示设置 DF(Don't Fragment)位,禁止分片。
结论:这条路径的 MTU 上限在 1100~1200 字节之间。
而 ZeroTier 虚拟网卡的 MTU 设的是 2800 字节:
bash
ip link show ztuze3h6cc
6: ztuze3h6cc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2800 ...
HTTP 的 TCP 分段通常是 1460 字节,远超 1100 字节的路径限制。数据包在路上被丢了,TCP 握手能成功(因为 SYN 包很小),但传数据时大包过不去。
浏览器表现为:连接建立了,但一直在等待数据,最终超时。
第五步:找到真正的连接路径
到这里我知道是 MTU 的问题了,但还有一个疑问:为什么路径 MTU 这么低?
查看 ZeroTier 的节点连接状态:
bash
zerotier-cli peers
<ztaddr> <role> <lat> <link> <path>
08702da864 MOON 13ms DIRECT 124.222.237.219/42988
e8387325e3 LEAF 782ms DIRECT fd7a:115c:a1e0::283b:ba3e/9993
公司电脑(e8387325e3)显示延迟 782ms,路径是 fd7a:115c:a1e0::283b:ba3e。
看到这个 IP 地址我愣了一下。fd7a:115c:a1e0::/48 这个前缀很眼熟——这不是 Tailscale 的私有 IPv6 地址段 吗?
再看公司电脑上的网络配置:
未知适配器 Tailscale:
IPv6 地址 . . . . . . . . . . . . : fd7a:115c:a1e0::283b:ba3e
IPv4 地址 . . . . . . . . . . . . : 100.126.186.62
破案了。 公司电脑上同时跑了 Tailscale,ZeroTier 把 Tailscale 的地址当成了可用的网络路径。
实际的数据传输路径是这样的:
公司电脑 (192.168.80.4)
│
├→ ZeroTier 封装 (UDP 9993)
│
├→ Tailscale 虚拟网卡 (fd7a:115c:a1e0::283b:ba3e)
│
├→ Tailscale DERP 中继 (旧金山 sfo 节点, ~400ms)
│
└→ 到达 NAS (192.168.80.80)
双重隧道套娃! ZeroTier 隧道套在 Tailscale DERP 中继里面。
所以:
- 延迟高达 782ms(Tailscale DERP 旧金山中继 400ms + ZeroTier 额外开销)
- 路径 MTU 被 Tailscale 限制(Tailscale 的虚拟网卡 MTU 只有 1280 字节,再套上 ZeroTier 封装,实际可用更小)
- HTTP 大包过不去
第六步:为什么以前没有这个问题?
在没有自建 Moon 之前,公司电脑和 NAS 之间的连接是这样的:
公司电脑 → PLANET 服务器(公网中继) → NAS
走的是 ZeroTier 官方 PLANET 服务器中继。延迟高一点(~200ms),但路径 MTU 是正常的 1500 字节,HTTP 能正常工作。
自建 Moon 之后,ZeroTier 优先选择直连(DIRECT)而不是中继(RELAY)。它发现了 Tailscale 这条"直连"路径,就走这边了。虽然延迟更高、MTU 更低,但 ZeroTier 的逻辑是 DIRECT > RELAY,所以它觉得这是更好的路径。
再看一个对比:
我后来在公司的 Mac 笔记本上做了对比测试,Tailscale 和 ZeroTier 同时开着,完全没问题。原因是公司网络环境的 NAT 类型友好,ZeroTier 能成功打洞直连,不需要走 Tailscale。
而南京的台式机没有公网 IPv4,NAT 类型是对称型(Symmetric NAT),打洞成功率低。ZeroTier 打洞失败后,发现了 Tailscale 的地址,就走这条路了。
解决方案
临时方案:调整 MTU
在 NAS 上降低 ZeroTier 虚拟网卡的 MTU:
bash
# 临时生效
ip link set ztuze3h6cc mtu 1100
但这只是治标,而且需要两端都改。公司电脑那边是 Windows,改 MTU 比较麻烦。
治本方案
核心思路:让公司电脑的 ZeroTier 能成功直连 NAS,不需要走 Tailscale。
NAS 有公网 IP(124.112.181.254),关键在于公司电脑那边的 NAT 打洞能不能成功。
方案 1:公司电脑的路由器开启 UPnP
UPnP 允许内网设备自动在路由器上创建端口映射。ZeroTier 支持 UPnP/NAT-PMP,开启后打洞成功率会大幅提升。
方案 2:手动端口转发
在公司路由器上设置端口转发,把 UDP 9993 转发到公司电脑的内网 IP。
方案 3:临时关闭 Tailscale 测试
在公司电脑上运行 tailscale down,然后观察 ZeroTier 能不能通过 PLANET 自动建立连接:
powershell
tailscale down
# 等几分钟,然后检查
zerotier-cli peers
如果 ZeroTier 能找到其他路径(比如通过 Moon 中继或直接打洞),说明问题就是 Tailscale 干扰的。
总结
| 问题 | 原因 |
|---|---|
| ping 通但网页打不开 | 路径 MTU 不匹配,大包被丢弃 |
| 路径 MTU 为什么低 | ZeroTier 隧道套在 Tailscale DERP 中继里面 |
| 为什么走 Tailscale | 公司电脑没有公网 IP,NAT 打洞失败,Tailscale 是唯一可用的"直连"路径 |
| 为什么以前没问题 | 没有 Moon 时走 PLANET 中继,MTU 正常 |
教训: 两个穿透工具同时运行时,要注意它们之间可能互相"抢路"。如果其中一个提供了"直连"路径(即使这个路径质量很差),另一个可能会优先选择它而不是走正常的中继。
排查环境:NAS (Debian, ZeroTier 1.14.2) + 公司电脑 (Windows 11, ZeroTier 1.16.0, Tailscale)