两天的折腾,一条完整的决策链
需求很朴素:家里的台式机、随身的笔记本和手机,在不同城市、不同网络下能互相访问——远程开发、传文件、连家里的服务。装上 Tailscale 十分钟就”通”了,然后问题来了:ping 一下 200 多毫秒,SSH 打字一卡一卡,时不时还断流。
接下来两天,我把”多设备异地互联”这条技术栈从头到尾走了一遍:查公网 IP(申请不到)→ 研究打洞(打不通)→ 看官方中继(慢得没法用)→ 尝试自建中继(卡在域名和证书)→ 最后在淘宝买了个现成的国内中继节点,延迟从 200ms 掉到 40ms,十几块钱一个月,了事。
这篇文章把每一步”为什么会这样”讲清楚。如果你也在折腾异地组网,可以直接抄最后的决策表。
直觉:两个不让外人进门的小区
先把 NAT 穿透这件事讲成人话。
两台设备都躲在路由器后面,就像两个人各自住在只许出、不许进的封闭小区里:你可以随时出门找别人,但陌生人不能进小区找你。现在两个人想见面,有三种办法:
有一方住在临街的房子(公网 IP)——另一方直接上门,最简单,但现在临街房极其稀缺。
约在各自小区门口碰头(打洞)——两人同时出门,在门口”擦肩”的瞬间互相认识了,门卫以为对方是自己人放了进来。需要两边门卫的规则都比较宽松,时机也要掐准。
找个咖啡馆中转(中继)——双方都主动去同一家咖啡馆,让服务员传话。一定能见上,但咖啡馆离得远,传一句话要跑很远的路。
Tailscale 的策略就是按这个顺序来的:能直连就直连,能打洞就打洞,都不行就走中继——它管这个中继叫 DERP(Designated Encrypted Relay for Packets,指定加密包中继)。我那 200ms 的延迟,就是因为前两条路全堵死了,流量默默绕道了海外的”咖啡馆”。
为什么打洞在国内这么难
NAT 与 CGNAT
网络地址转换(Network Address Translation, NAT)是路由器把内网地址翻译成公网地址的机制。家里一层 NAT 不算完——国内家宽现在普遍还套着一层运营商级 NAT(Carrier-Grade NAT, CGNAT):你的”公网出口 IP”其实是运营商的,成千上万户共享,路由器 WAN 口拿到的是 100.64.x.x 这类地址。手机流量更是百分之百在 CGNAT 后面。
打洞(UDP Hole Punching)的原理是:双方先通过一个公网协调服务器(STUN,Session Traversal Utilities for NAT)各自探测出”我从外面看是什么地址和端口”,然后同时向对方的公网地址发包——各自的 NAT 看到出站流量,就给回程流量开了”洞”。
致命的对称 NAT
能不能打通,取决于 NAT 给出站连接分配端口的方式:
NAT 组合
打洞结果
锥形 × 锥形
基本都能通
锥形 × 对称
受限锥可通,端口受限锥很难
对称 × 对称
基本必败
锥形 NAT(Cone NAT)对同一个内网端口复用同一个公网端口——你从 STUN 探到的端口,跟你去连对方时用的端口是同一个,对方照着打就行。而对称 NAT(Symmetric NAT)对每个不同目的地都分配新端口:STUN 探到的端口,在你真正连对方时已经换掉了,对方照着旧端口打,永远打不中。
对称 NAT 下唯一的指望是暴力猜端口。猜中的概率有多低,可以算一下。NAT 从约 $N$ 个端口里随机分配,一方发 $k$ 个探测包,命中概率近似为:
\[P \approx 1 - \left(1 - \frac{1}{N}\right)^{k}\]
符号含义:$N$ 是可用端口空间大小(个),$k$ 是发出的探测包数量(个)。
代入数字:典型端口空间 $N = 64{,}512$(即 1024~65535),发 $k = 256$ 个探测包:
\(P \approx 1 - \left(1 - \frac{1}{64512}\right)^{256} \approx 0.4\%\)
就算两边配合做生日攻击式的双向猜测,实际工程里对称×对称的打洞成功率也低到不值得指望。运营商 CGNAT 大多正是对称型——这就是”打了两天洞一个没通”的原因,不是你配置错了。
打不通就中继:延迟账怎么算
打洞失败后,Tailscale 自动回落到 DERP 中继。所有流量变成:A → 中继服务器 → B。延迟从”两点之间直线”变成”两点经第三点折线”,往返时延(Round-Trip Time, RTT)近似为两段 RTT 之和:
\[RTT_{\text{中继}} \approx RTT_{A \leftrightarrow R} + RTT_{R \leftrightarrow B}\]
符号含义:$RTT_{A \leftrightarrow R}$ 是设备 A 到中继 R 的往返时延(ms),$RTT_{R \leftrightarrow B}$ 是中继 R 到设备 B 的往返时延(ms)。中继本身的转发处理在 1 ms 量级,可忽略。
代入数字:Tailscale 官方 DERP 节点全在海外,国内设备就近一般落到东京。杭州电信到东京 $RTT \approx 90\ \mathrm{ms}$,成都移动到东京 $RTT \approx 110\ \mathrm{ms}$:
\(RTT_{\text{中继}} \approx 90 + 110 = 200\ \mathrm{ms}\)
换成国内 BGP 机房的中继:两端到上海机房分别约 $15\ \mathrm{ms}$ 和 $30\ \mathrm{ms}$:
\(RTT_{\text{中继}} \approx 15 + 30 = 45\ \mathrm{ms}\)
同样是中继,换个位置,延迟差出 4 倍多。中继不可怕,远的中继才可怕。
比延迟更难受的是丢包
跨境链路真正折磨人的是丢包和抖动。WireGuard 本身是 UDP 不重传,丢包由上层的 TCP 或应用补——每丢一个包,就要多等至少一个 RTT。平均每个包需要的传输次数是:
\[E[n] = \frac{1}{1-p}\]
符号含义:$p$ 是单程丢包率(无量纲),$E[n]$ 是平均传输次数(次)。
代入数字:跨境高峰期丢包率 $p = 8\%$,$RTT = 200\ \mathrm{ms}$:
\(E[n] = \frac{1}{1-0.08} \approx 1.087\)
平均只多了 8.7% 的流量,看着不严重——但对交互式 SSH,意味着每 100 次按键有 8 次要卡 200ms 以上才显示出来。这就是”裸延迟 200ms 听着还行,用起来想砸键盘”的数学解释。体感由延迟分布的尾部决定,不由平均值决定。
进阶:DERP 到底是什么,自建卡在哪
DERP 的协议本质
DERP 是 Tailscale 自己设计的中继协议,跑在 HTTPS(TCP 443)上——伪装成普通网站流量,几乎任何防火墙都放行,这是它”一定能通”的底气。代价是 TCP 承载 UDP 流量,遇到丢包还有队头阻塞,进一步放大尾延迟。
关键的安全性质:节点间流量是 WireGuard 端到端加密(End-to-End Encryption, E2EE)的,DERP 只转发密文,看不到内容。中继方能看到的是元数据:哪些设备在通信、什么时候、流量多大。这是”敢用陌生人中继”的前提。
自建 derper 的完整账单
官方开源了中继服务端 derper,自建需要:
# file: 自建 derper 的最小部署
go install tailscale.com/cmd/derper@latest
derper --hostname=derp.example.com --certmode=letsencrypt
看着两行,实际要凑齐的东西不少:
一台国内有公网 IP 的 VPS(Virtual Private Server,虚拟专用服务器),轻量款每月 ¥20~50
一个域名,且 HTTPS 证书签发要求域名能正常解析——国内服务器还可能涉及备案
防火墙放行 TCP 443 与 STUN 的 UDP 3478
在 Tailscale 控制台的 ACL 里写入自定义 derpMap
别忘了 --verify-clients:不开的话你的中继是裸奔的,任何知道地址的陌生 Tailscale 用户都能蹭用你的带宽
我就卡在域名证书这一环:新域名解析生效慢、证书签发反复失败,折腾半天后意识到——我要的是”能用”,不是”自营”。
淘宝 DERP 的本质
淘宝上十几块钱一个月的”DERP 加速”,本质就是商家批量部署好的国内 derper 节点,卖你一段 derpMap 配置,粘进自己的 ACL 即可。它不碰你的密钥、不入你的虚拟网络,只是个转发密文的哑管道。
要留意的两件事:一是问清楚有没有开 verify-clients(没开意味着节点超卖、被蹭,高峰期带宽不稳);二是记住中继方看得到你的通信元数据,介意的话还是自建。
应用:方案决策表
两天踩坑浓缩成一张表:
方案
折腾量
月成本
典型 RTT
适合谁
运营商开公网 IPv4
打客服电话碰运气
0~10 元
10~40 ms
能开到的幸运儿
IPv6 直连
两端开 v6 + 放行防火墙
0
10~40 ms
两端都有 v6 的场景
打洞直连
0(Tailscale 自动)
0
10~40 ms
至少一端锥形 NAT
官方 DERP
0
0
150~300 ms,抖
临时凑合、低频使用
自建 DERP
VPS+域名+证书+部署
20~50 元
20~60 ms
要完全可控、介意元数据
购买 DERP
粘一段 ACL 配置
5~20 元
20~60 ms
只想要”能用”的大多数人
我的最终形态:保留 Tailscale 默认的直连尝试(哪天运气好打通了自动走直连),中继回落到购买的国内 DERP 节点。日常 RTT 稳定在几十毫秒,SSH 不再卡键,手机热点下也能秒连家里的机器。
回头看,这两天最大的收获不是”通了”,而是把直连、打洞、中继这三层逻辑和各自的失败模式亲手摸了一遍——下次再遇到”为什么这么慢”,第一反应不再是重启大法,而是 tailscale status 看一眼到底走的哪条路。
延伸阅读:Tailscale 官方博客 How NAT traversal works,把打洞讲得极透;以及 DERP 服务端源码 tailscale.com/cmd/derper。
术语表
术语
英文
含义
运营商级 NAT
Carrier-Grade NAT, CGNAT
运营商在用户侧之上再套的一层共享 NAT
锥形 NAT
Cone NAT
同一内网端口复用同一公网端口的 NAT,可打洞
指定加密包中继
Designated Encrypted Relay for Packets, DERP
Tailscale 的中继协议,跑在 HTTPS 上转发密文
端到端加密
End-to-End Encryption, E2EE
只有通信两端能解密,中间节点全程见不到明文
网络地址转换
Network Address Translation, NAT
内网地址与公网地址间的映射机制
往返时延
Round-Trip Time, RTT
数据包一来一回的总耗时
会话穿越工具
Session Traversal Utilities for NAT, STUN
帮设备探测自己公网地址和端口的协议
对称 NAT
Symmetric NAT
每个目的地分配不同公网端口的 NAT,打洞基本必败
UDP 打洞
UDP Hole Punching
双方同时向对方公网地址发包以建立直连的技术
虚拟专用服务器
Virtual Private Server, VPS
云上租用的独立服务器实例
—
WireGuard
现代轻量 VPN 隧道协议,Tailscale 的底层