夜雨聆风学习资料网

ARTICLE · 1239727

互联网中心化与NAT原罪:为啥你的电脑不能当服务器?

互联网中心化与NAT原罪:为啥你的电脑不能当服务器?

你让朋友发一个 25MB 的文件给你。

他说微信传不了,你让他开个 FTP,他反问你:FTP 是什么?

这个场景对很多人来说只是日常,但仔细想想其实非常离谱:互联网已经发明几十年了,两台电脑之间直接传个文件,居然还是个难题,更离谱的是,今天如果你问一个“懂电脑”的人,他会告诉你:你家里那台电脑当然不能让别人直接连,那叫服务器,服务器是要买云服务的。

我可以非常明确地告诉大家:这个认知是错的,但错得非常有来由。

互联网在 1970 年代被设计出来的时候,从来没有“客户端”和“服务器”这种等级区别,每一台主机都是一个完整的节点,有全局唯一的地址,既可以是服务的消费者,也可以是服务的提供者,1980 年代的 UNIX 工作站,装个 sendmail 就是邮件服务器,装个 ftpd 就是文件服务器,没有云,也不需要云,这就是互联网的原始设计:端到端,对等,任何人都能向任何人发起连接。

那么问题来了:为什么今天你的电脑,只能主动找别人,不能让别人找你?

答案藏在 1994 年的一份 RFC 里。

一、地址不够了,NAT 上场

1994 年,IETF 发布了 RFC 1631,正式提出了 NAT(Network Address Translation,网络地址转换)。

这份文档的摘要写得很清楚:IP 互联网面前有两个最紧迫的问题:地址枯竭和路由扩展,长期方案是下一代互联网协议,也就是后来的 IPv6;短期方案是 CIDR;而 RFC 1631 自己,提出了第二个短期方案,就是 NAT。

很多人不知道的是,NAT 最初的定位只是一个临时过渡,用来给 IPv6 争取时间,后来发生的事大家都知道了:临时方案活成了永久方案,IPv6 反而成了那个“永远在明年”的东西。

但先不急着骂人,我们先把 NAT 到底干了什么搞清楚。

NAT 最早被设想成纯 IP 地址翻译,但今天你家路由器上跑的那个东西,学名其实叫 NAPT(Network Address and Port Translation,网络地址端口转换),由后来的 RFC 3022 标准化,它同时改 IP 地址和端口号,你访问一个网站时,数据包是这么走的:

你的电脑发出去一个包:

源 IP
源端口
目的 IP
目的端口
192.168.1.100
50413
93.184.216.34
80

路由器收到之后,把它改成:

源 IP
源端口
目的 IP
目的端口
203.0.113.7
60612
93.184.216.34
80

1)服务器回包,发给 203.0.113.7:60612。

2)路由器查自己的 NAT 映射表,把目的地址和端口改回 192.168.1.100:50413,然后丢给内网。

如果你仔细想过这个流程,问题就来了:

外面有台服务器想主动找你,怎么办?

它往 203.0.113.7:60612 发一个包,你的路由器翻遍映射表,发现根本没有这一条。

为什么没有?因为 NAT 的映射表是出站流量触发建立的,内部设备不先发,映射就不存在;映射不存在,外部设备就是瞎子,这是 NAT 的固有属性。

顺便说一个硬核细节:NAT 映射不是永久的,TCP 映射在连接关闭后很快过期,UDP 映射通常只有几十秒到几分钟,这意味着即使你成功建立过一条映射,只要你安静一会儿,外面的路就又断了,这就是为什么大量 P2P 应用必须周期性发 keepalive 包,真不是它们想费电,是它们在和 NAT 的计时器赛跑。

二、端口转发:最直接,也最没用

聪明的人可能已经想到了:那我直接告诉路由器“凡是来 60612 端口的包,都给我转给 192.168.1.100”不就行了?

这就是端口转发,它确实是 NAT 最广为人知的补丁,但问题也很明显。

第一,一个公网 IP 的一个端口,一次只能映射给一台设备,宿舍四个人,都想开 Minecraft 服务器,默认端口 25565 只有一个,你想开第二个,就得换端口,让朋友记一个“25566”这种没人记得住的数字。

第二,端口转发的前提是你能控制那台做 NAT 的设备,但这个前提正在消失,因为运营商也缺地址。

运营商级的 NAT,叫 CGNAT(Carrier-Grade NAT),RFC 6598 专门划了一段 100.64.0.0/10 给运营商级 NAT 当内网地址用。换句话说,你家的路由器从运营商那里拿到的“公网 IP”,很可能根本不是一个真公网 IP,而是 100.64.0.0/10 里的一个运营商内网地址,你的路由器再做一层 NAT,于是你的包要经过两层翻译才能出网。

这时候你想配置端口转发?对不起,翻译表根本不在你手里,你连“我的包出网时长什么样”都不知道,更别提 CGNAT 为了满足监管和溯源要求,可能还要记录海量映射日志——地址是省了,成本转嫁给了运营商,最终转嫁给你。

三、UPnP:让软件自己敲门

端口转发太麻烦,于是有人发明了 UPnP,以及它的现代亲戚 NAT-PMP 和 PCP。

想法很朴素:既然用户不会配路由器,那让程序自己去配,你的 BT 客户端启动时,直接跟路由器说:“帮我开一个 51413 端口的映射,期限两小时,”

这个方案技术上没问题,但现实里经常用不了,因为很多路由器默认关闭 UPnP,原因被包装成“安全”,早期某些 UPnP 实现确实有漏洞,这是事实;但更普遍的理由,是一个根深蒂固的误解:

NAT 被当成防火墙了。

这里我要非常明确地说一句:NAT 不是防火墙,它只是一个因为地址不够而发明的翻译机制,它“挡住”入站连接,不是因为它在检查什么恶意流量,而是因为它根本不知道该把包转给谁,它带来的隐蔽感,是副作用,不是设计。

但就是这个副作用,让整整一代人形成了“别人能连到你的电脑 = 危险”的直觉,这个直觉后来成了 IPv6 推广最大的障碍之一,我们先按下不表。

四、STUN、TURN、ICE:互联网史上最复杂的绕路工程

如果端口转发没戏,UPnP 也没戏,还有什么办法?

有,人类为了解决“两台都在 NAT 后面的设备如何直连”这个问题,发明了一整套协议,复杂到可以写好几本教材。

先讲 STUN。

STUN 的思路很优雅:既然路由器不配合,那我不求路由器,我求公网上的一台服务器,我发个包给 STUN 服务器,然后问它:“我的包到你这里的时候,长什么样?”STUN 服务器回答:“你看起来是 203.0.113.7:60612,”

拿到这个地址之后,你把它告诉对方,对方直接打过来,如果顺利,两边都向对方发几个包,把各自 NAT 上的映射打开,连接就通了,这就是打洞(hole punching)。

但这里有一个关键的硬核细节:NAT 分型。

老的 RFC 3489 把 NAT 分成四类:Full Cone、Restricted Cone、Port Restricted Cone、Symmetric,后来 RFC 5780 把它修正成两个维度:映射行为(Mapping Behavior)和过滤行为(Filtering Behavior)。

  • Endpoint-Independent Mapping:同一个内网 IP:端口,无论目的地址是谁,映射出来的公网 IP:端口都一样,这就是所谓“锥型”。

  • Address-Dependent Mapping:目的 IP 不同,映射出来的公网端口就不同。

  • Address and Port-Dependent Mapping:目的 IP 和端口都不同,映射也不同,这就是所谓“对称型 NAT”。

STUN 打洞能不能成,关键就在映射行为,只有 Endpoint-Independent Mapping,STUN 问到的地址才对别人也有效,如果是 Address and Port-Dependent Mapping,你在 STUN 服务器眼里是 60612,在 peer 眼里又是 60613,问到的答案根本没用。

而对称型 NAT 恰恰大量出现在 CGNAT、校园网、企业网这些最需要打洞的地方,这就是这个世界的恶意:有公网 IP 的人不需要打洞,最需要打洞的人偏偏打不通。

打洞失败怎么办?上 TURN。

TURN 用一句话就能说清楚:放弃治疗,两台设备都主动连一台公网中继服务器,所有流量全部绕中继转发,这招几乎 100% 能通,因为两边都是主动出站连接,NAT 拦不住,但代价是:本来点对点 10ms 的延迟变成 30ms,本来免费的流量变成烧中继服务器的带宽钱,谁的带宽?要么是你的,要么是服务商的,反正不是免费的。

最后是 ICE,ICE 不是一个新协议,它是一套妥协框架:把直连、STUN 打洞、TURN 中继全部列为候选地址,排好优先级,一个个做连通性检查,能直连就直连,不能就绕,WebRTC 用的就是这一套。

所以每次你的视频会议卡顿、每次网页版语音通话出现回声,背后都可能有一个悲伤的故事:你和对方都在对称 NAT 后面,STUN 失败,TURN 兜底,你们的数据包正绕着一台本不该存在的服务器在跑长途。

我们用一个极其复杂的协议栈,模拟了一个本来应该非常简单的东西:两台电脑直接说话。

五、IPv6:长期方案为什么没救场

NAT 的所有荒诞,原本都应该由 IPv6 来终结。

IPv6 地址有 128 位,总地址数是 2^128,大约 3.4×10^38 个,这个数字大到什么程度?把地球上所有沙滩和沙漠里的沙子全部编上号,都还绰绰有余,在 IPv6 的世界里,每台设备都可以有全局唯一地址,NAT 根本没有存在的必要。

但现实是,IPv6 的采用率在爬了很多年之后,依然没有形成碾压,更荒诞的是,很多已经部署 IPv6 的网络,依然在用 NAT 的思维管理 IPv6。

有的管理员给 IPv6 也做 NAT66,把 fc00::/7 的 Unique Local Address 当私网地址用,再翻译成公网 IPv6,翻译一个本来不需要翻译的东西,这已经是路径依赖了。

更多的网络,哪怕分了公网 IPv6 地址,防火墙也默认拒绝一切入站连接,为什么?因为大家已经被 NAT 训练了三十年,形成了肌肉记忆:入站连接 = 攻击,你终于有了自己的地址,但全网都习惯性地把你的门焊死。

这就好比你家漏了三十年雨,全楼的人都学会了在屋里打伞,后来屋顶修好了,但没人愿意把伞放下,因为“在屋里不打伞不安全”已经成了常识。

六、中心化不是商业阴谋,是技术债

现在我们可以回答开头那个问题了:为什么你的电脑永远只能当客户端?

因为 NAT 把你的地址藏起来了,你没有公网地址,所以别人找不到你;别人找不到你,所以你必须先找别人;所有人都必须先找别人,所以必须有一个“别人”在那里等着所有人来连接。

那个“别人”,就是服务器,那个“别人的集合”,就是云。

这就是互联网中心化的技术根源,中心化不是某个公司的商业阴谋,它是 NAT 三十年积累下来的技术债。

这笔债的账单很长:

  • 你想自己跑一个邮件服务器?多数云厂商和运营商把住宅 IP 段整段拉黑,因为垃圾邮件发送者早就用 NAT 后面的肉鸡把住宅 IP 的信誉打穿了。

  • 你想用 BT 下载?为什么那么多 peer 连不上你?因为你在 NAT 后面没有可连接性,只能靠别人有公网 IP 的节点救你。

  • 你想从公司连回家里 NAS 拿文件?你要么配端口转发,要么依赖厂商的中继服务器,而后者意味着你的文件从别人的机器上绕了一圈。

  • 你想做视频会议?WebRTC 打洞失败的代价,就是所有人都得为 TURN 中继的带宽买单。

  • 你想跑一个游戏服务器?先看看你的宽带是不是 CGNAT,再看看你有没有本事让运营商给你真公网 IP。

最讽刺的地方来了:NAT 本来是为了节省地址而发明的省钱方案,结果它催生了一个庞大的云计算产业,地址是省下来了,但你本来可以跑在家里旧电脑上的服务,现在只能去租一台 VPS。

NAT 把“拥有一台服务器”从每个互联网用户的默认权利,变成了需要额外付费的稀缺资源。

七、所以,谁该为互联网的封闭负责?

你可以怪云厂商,可以怪运营商,可以怪防火墙厂商,甚至可以怪那个把“你的 IP 被别人知道了”拍成恐怖片的安全文化。

但真正最早按下开关的,是 1994 年那个为了解决地址短缺而提出的临时方案。

NAT 当然不是互联网中心化的唯一原因,但它是最早的那个,它改变了三件事:

  • 第一,它改变了拓扑,互联网从对等网络变成了客户端-服务器星型结构,因为大部分节点失去了被寻址的能力。

  • 第二,它改变了心智,人们开始觉得“我的设备只配主动找云,不配被别人找”是自然规律,甚至把这种残缺当成安全。

  • 第三,它改变了经济,当可连接性变成稀缺资源,谁拥有公网地址和服务器,谁就能收税。

所以,当你下一次打开网盘,看着几十 KB/s 的上传速度想骂产品经理的时候,不妨想一想:真正该背锅的,可能不是产品经理,也不是云厂商,而是那个本该被 IPv6 送进历史博物馆、却一直赖着没走的 NAT。

中心化不是互联网的原设计,它是 NAT 的原罪。

作者丨最后的绅士
来源丨网址:https://zhuanlan.zhihu.com/p/2077783800315172271?share_code=vfPkpUjp2Vp1&utm_psn=2078496680681418931
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

相关学习资料