The silent page awaits the waking dawn.
网络位置 → 专用
↓
防火墙策略更加适合可信网络
↓
UDP 入站通信正常
↓
Tailscale 重新尝试 NAT 穿透
↓
P2P DirectTailscale 延迟异常
│
▼
tailscale ping
│
├── Direct → 正常
│
└── DERP
│
▼
tailscale netcheck
│
├── UDP = false
│ ↓
│ 检查网络 / 运营商
│
└── UDP = true
│
▼
检查 NAT 类型
│
▼
检查本机防火墙
│
▼
检查 Windows 网络位置
│
▼
Direct连接是否正常
↓
当前到底走 Direct 还是 DERP
↓
UDP 是否可用
↓
NAT 是否适合打洞
↓
操作系统防火墙是否拦截
↓
再考虑 DERP
最近折腾 Tailscale 的时候遇到了一个挺有意思的问题。
两台设备都已经加入了同一个 Tailnet,tailscale ping 也能正常通信,但是实际延迟却高得离谱。
正常情况下,两台设备之间如果能够建立 P2P 连接,延迟应该比较低。但我实际测试的时候,却发现:
这就有点不对劲了。
后来排查了一圈才发现,问题并不在 Tailscale 本身,而是 Windows 的网络配置导致 UDP 入站连接没有正常通过。
最终把 Windows 的网络位置调整好之后,再次测试:
从 400ms+ 降到 50ms 左右,基本可以确定两端已经成功建立了 P2P 直连。
这篇文章就记录一下这次排查过程,也顺便梳理一下 Tailscale 为什么有时候会从 Direct 退回 DERP。
一、先说一下问题:为什么 Tailscale 能连通,但速度却很慢?
Tailscale 底层使用的是 WireGuard。
理想情况下,两台设备之间应该直接通过 UDP 建立连接:
这种情况下数据不需要经过第三方服务器,延迟通常比较低
但现实网络环境往往没有这么理想。
很多设备都处于 NAT 后面,例如:
Tailscale 会尝试通过 NAT 穿透建立 P2P 连接。
如果最终没能打通,就会使用 DERP(Designated Encrypted Relay for Packets)作为中继:
这样虽然依然可以正常通信,但网络路径明显变长了。
尤其是当两台设备距离较远,而 Tailscale 最终选择的 DERP 节点又比较远时,延迟就可能明显增加。
所以遇到这种情况,我第一反应并不是马上去改 Tailscale 配置,而是先确认:
二、先别急着改配置,看看netcheck
Tailscale 提供了一个非常实用的诊断命令:
建议两台设备都执行一下。
我主要关注下面几个指标。
1. UDP
UDP:true
如果这里是 true,说明当前网络环境至少没有直接把 UDP 完全封死。
如果是:
UDP:false
那就不用继续折腾本机防火墙了,应该优先检查当前网络、运营商或者上级路由器。
2. MappingVariesByDestIP
另外一个比较关键的指标是:
MappingVariesByDestIP:false
简单理解一下:
如果这个值是 false,说明当前 NAT 映射相对稳定,NAT 穿透通常会更容易。
如果是:
MappingVariesByDestIP:true
则说明 NAT 行为更加严格,P2P 打洞的难度会明显增加。
当然,这个值并不能简单理解成:
网络环境还有很多其他因素,例如防火墙、端口映射、对称 NAT、运营商网络策略等。
但至少可以帮助我们判断:
当前网络到底有没有继续尝试 P2P 直连的价值。
我这次遇到的情况就是:
UDP: true
MappingVariesByDestIP:false
所以从网络条件来看,并不像是 NAT 本身的问题。
这时候就应该继续往本机防火墙和网络配置方向排查。
三、真正卡住我的地方:Windows 网络位置
我的其中一台设备是 Windows 笔记本。
Windows 对网络连接有一个比较重要的概念:网络位置。
一般会分成:
如果当前网络被识别成公用网络,Windows Defender 防火墙通常会采用更加严格的入站策略。
这在机场、酒店、公司网络、校园网以及一些公共 Wi-Fi 环境中尤其常见。
问题也就出在这里。
虽然 Tailscale 本身已经安装并正常运行,但是 Windows 对当前网络的安全策略比较严格,最终导致 UDP 入站连接没有按照预期通过。
于是 Linux 端虽然可以找到 Windows:
但真正尝试建立 P2P 连接的时候却失败了。
最后只能:
四、我的解决方法:修改 Windows 网络位置
这里我没有直接去写 Windows 防火墙规则,而是先把网络位置调整正确。
个人比较推荐通过本地组策略编辑器处理。
1. 打开本地组策略编辑器
按:
Win + R
打开运行窗口。
输入:
gpedit.msc
然后回车。
2. 找到网络列表管理器策略
在左侧依次展开:
不同 Windows 版本的中文翻译可能略有区别,也可能显示为:
网络列表经理策略
3. 找到对应的网络
在右侧找到当前网络对应的策略。
这里可能会看到类似:
Tailscale
或者:
识别的网络
未识别的网络
具体名称取决于 Windows 当前对网络的识别方式。
双击对应项目。
4. 修改网络位置
找到:
网络位置
然后进行如下设置:
位置类型:
专用
用户权限:
用户可以更改位置
最后点击:
应用 → 确定
五、为什么改完以后就能直连了?
这里其实很容易产生一个误解:
不是。
真正发生变化的是 Windows 对当前网络的安全策略。
原来的网络可能处于比较严格的公用网络环境:
调整之后:
所以这里的关键不是“专用网络能够打洞”,而是:
六、修改完以后,不要只看tailscale status
修改完成后,我建议直接使用:
tailscale ping <对方的 Tailscale IP>
测试。
例如:
第一次测试的时候,可以连续执行几次。
如果之前是:
而现在变成类似:
基本就可以确认:
P2P 连接已经建立。
这里最值得关注的是:
via xxx.xxx.xxx.xxx:xxxxx
而不是:
via DERP(...)
前者说明当前通信走的是对端公网地址和端口,也就是 Direct 连接。
七、再用 tailscale status 确认一下
可以继续执行:
tailscale status
观察对应节点的连接状态。
如果之前是:
relay
或者:
derp
现在变成:
direct
那么基本就可以收工了。
这时候再测试一下 SSH、RDP、文件传输等实际业务,通常会比之前经过 DERP 中继时明显流畅。
八、这里有一个容易误解的地方:Direct 不是永久状态
刚开始折腾 Tailscale 的时候,我也容易把 Direct 理解成:
实际上不是这样。
Tailscale 的连接状态是动态变化的。
设备之间没有持续通信的时候,连接状态可能发生变化;当再次产生实际流量时,Tailscale 会重新进行连接探测,并尝试建立更优的连接路径。
所以你可能会遇到这种情况:
刚测试:
Direct
过了一段时间:
DERP
重新通信:
Direct
这并不一定意味着配置又坏了。
因此排查的时候,最好不要只执行一次:
然后看到 DERP 就认为“打洞失败”。
更可靠的方法是:
连续测试几次,再观察连接路径和延迟。
九、如果这样还是无法 Direct,该怎么办?
如果修改 Windows 网络位置之后仍然无法直连,那就继续往下面几个方向排查。
1. 检查两端 UDP
分别执行:
重点看:
UDP
MappingVariesByDestIP
2. 检查 Windows 防火墙
如果网络位置已经正确,但仍然无法建立连接,可以进一步检查 Windows Defender 防火墙是否存在针对 Tailscale 的阻断规则。
尤其是:
这些都可能影响 UDP 入站。
3. 检查 Linux 防火墙
如果 Linux 服务器启用了 firewalld 或 UFW,也要确认没有把相关流量拦掉。
例如 firewalld 可以检查:
如果确实需要允许 Tailscale 接口,可以考虑将 tailscale0 加入可信区域:
如果使用 UFW:
不过这一步不要无脑执行。
如果本身没有开启相关防火墙,就没必要为了“教程完整”再增加规则。
十、如果一方是严格 NAT 呢?
如果你执行:
发现:
MappingVariesByDestIP: true
那情况就不一样了。
这种网络环境下,NAT 穿透本身就比较困难。
这时候即使你把 Windows 防火墙、Linux 防火墙全部检查一遍,也不一定能够建立稳定的 P2P 连接。
如果手里刚好有一台拥有公网 IP 的 Linux 服务器,那么可以考虑进一步搭建自己的 DERP 中继。
这样即使 P2P 最终失败:
至少可以把中继节点放在距离自己更近的位置,避免:
带来的高延迟。
这也是我后面比较想继续折腾的一个方向。
十一、最后总结一下这次排查
这次的问题其实没有想象中复杂。
整个排查思路可以总结成:
对我这次的情况来说,最关键的一步其实就是:
不要看到 DERP 就直接认为是 NAT 的问题。
先执行:
确认 UDP 和 NAT 环境,再判断到底应该继续排查网络,还是应该把注意力放到操作系统防火墙上。
最后 Windows 通过本地组策略把当前网络位置调整为「专用」之后,Tailscale 就成功从 DERP 切回了 Direct。
延迟从几百毫秒降到几十毫秒,体感上的差别还是非常明显的。
写在最后
Tailscale 好用的地方就在于,大多数时候你根本不需要关心 NAT、UDP、WireGuard 这些东西。
但一旦遇到:
稍微往底层看一层,通常很快就能找到答案。
对我来说,这次排查最大的收获也不是记住某一条命令,而是以后再遇到类似问题,可以按照:
这个顺序去排查。
如果后面继续把这台 Linux 服务器利用起来,我准备再折腾一下 自建国内 DERP 中继。这样即使某些网络环境实在没办法 P2P,也至少可以把中继路径掌握在自己手里。