P2P 为 Peer To Peer,顾名思义是对等的端对端通讯而不需要借助第三者。
端对端的能充分的利用两端的网络资源,减少对公共服务器的依赖。但在 IPv4 短缺的今天,有很多设备是在一个内网里向外传输信息的,基本上做不到每台设备拥有独立IP。而普通情况下,在内网的设备很难被主动访问,因此很难直接通讯。P2P的直接通讯需要借助在网关设备上的 “打洞” 来进行。
要解释 “打洞” 就需要从NAT技术讲起。
NAT 介绍
上网搜时,可能看到 NAT 是为了解决 “IPv4地址短缺问题” ,而其实更本质上来说,这是一个路由拥有自洽内网系统的一个必要功能——对于外界来说,传输层IP包的沟通对象是 NAT 网关设备,再由网关转发给对应的子网设备来通讯,这样就能很好的让子网自洽而不用顾及外部的网络环境。
简而言之,NAT 的工作原理是一个 IP、Port 的转换过程,NAT = Network Address Translation。显然它的本质问题,是如何建立并维护起【NAT设备的外网地址 <—> 内网设备地址】的映射关系。
转换关系层级
NAT 转换关系存在许多种策略,它们是一个非此即彼的存在,亦即只会同时生效一种策略。这些转换策略分两个层级。
IP 层的转换
最底层的是只在 IP 层转换。即 NAT网关有多个外网 IP 地址的情况下,将外网 IP 地址跟内网的某用网设备的内网 IP 对应起来。外部对某个外网IP的访问即对应到内网具体的某IP上。
对应关系可以分两种:一种是手动下的静态对应,叫静态NAT(Static NAT);另一种是在外网IP池子里自动动态分配,叫池NAT(Pooled NAT)。
现代的用网设备会远远超出某个 NAT 网关能拥有外网IP,除非有特别的应用场景,否则IP这一层的转换显然不具广泛的适用性,所以不重点讨论。
Port 层的转换
在 Port 层的转换又叫 NAPT,Network Address Port Translation。在这一层的转换已经在 NAT 设备上重用到端口级,亦即一个端口可以转换多条对外的链接。
端口的概念需要关联到具体的运输层协议,比如 UDP、TCP 等,不同的协议需要依赖于 NAT 设备的支持。
在 NAPT 中又根据不同的工作模式,细分了两种不同的类型——对称型(Symmetric NAT)以及非对称性(也叫锥形,Cone NAT)。
同时锥形 NAT 下还会根据对当前端口重用的限制等级分为三种不同的 NAT,完全锥形 NAT(Full cone NAT)、受限NAT(Restricted Cone NAT)和端口受限 NAT(Port restricted cone NAT)。
NAPT 是我们现代网络最常用的 NAT 模式,NAT 设备对 UDP 与 TCP 都有比较好的支持,原理和过程几乎共通。下面展开。
NAPT 的两种大模式
NAPT 按照端口的重用方式,分为两大类——对称型(Symmetric NAT)以及非对称型(也叫锥形,Cone NAT)。
这两类的分类方式的不同,通俗来讲来讲就是 NAT 的同一个内侧设备,对外的不同链接(这个不同包括 IP 与 Port),要不要在NAT设备的外侧新起端口来对应。
如果需要新起端口来对应,意味着每一个新的连接,在外网部分看来,目的地以及源都是全新的。亦即在 NAT 设备的外网侧存在严格的 SourceIP:SourcePort <—> TargetIP:TargetPort 的唯一性,这也是 对称型 名字的由来。反之则是 锥形。
一、锥形 NAT(Cone NAT)
锥形的命名其实很形象,指的是 NAT设备内网侧的设备一旦启用某端口进行通讯后,NAT 设备只需要固定开放一个外网侧端口与之对应即可。往后内网设备从这个端口往外的通讯,都固定由外侧的对应的端口转换。
这样外网侧看起来就是一个从NAT设备外网端口到外网设备的放射形的结构,因此叫之为锥形。
锥形 NAT 也有被称为一对一NAT(one-to-one NAT)。这里的一对一指的是 NAT 设备内网侧设备的 IP:Port 跟其外网侧的 IP:Port 的转换关系。
出于安全性跟便捷性的平衡考量,这种锥形 NAT 在 NAT 设备的外网侧有三种不同的策略。其严格程度从低到高分别为:全锥型(Full Cone)、受限锥型(Restricted Cone)、端口受限锥型(Port Restricted Cone)。简单来说就是完全不限制、限制外网目标IP、限制外网目标IP+外网目标Port。
1、全锥型(Full Cone)
如图1示,内网设备的端口 2000,只要跟 NAT 设备的外网侧对应好后(图里没画出来),所有的外网机器、不限制IP、不限制Port,都可以往这里丢信息并被 NAT 设备转发进来。这种模式下,内网设备的某个端口的通讯等于完全暴露在外网中。
2、受限锥型(Restricted Cone)
在受限锥形模式下,只有内网设备曾经通过某个 NAT 设备外侧端口主动通讯过的外网服务器,才能反过来向内网设备发包。
如图2中,内网设备用 2000 端口通过 NAT 设备的某个端口向 s1 的 3000 端口发送数据,因此 s1 的 2000 端口跟 3000 端口(以及其他任意端口)都可以向内网设备的 2000 端口通讯。
3、端口受限锥型(Port Restricted Cone)
端口受限锥型比受限锥型对端口也进行了限制,不只是内网设备的主动通讯过的外网IP需要一致,外网服务器与 NAT 设备通讯时的端口也要求是内网设备在主动通讯时的目标端口。
在图3中,s2 的所有端口、s1 的 3000 端口都会被 NAT 设备拒绝。仅有 s1 的 2000 端口是内网设备主动通讯过的对象,报文得以放行。
二、对称型 NAT(Symmetric NAT)
对称型的 NAT 放弃了 NAT 设备与内网设备端口一对一联系的设定。在对 IP、端口都有限制的前提下,就算是内网设备用同一个端口对不同的目标发起通讯,NAT 设备也会在自己的外网侧开辟新的端口来与之对应。
对称型的 NAT 是最为严格跟安全的策略。同时也是最为死板的。
如上图,即使内网设备用的都是 2000 端口发起对 s1 的通讯,但在 s1 看来,自己的 2000 端口跟 3000 端口,需要通讯的是 NAT 设备上两个不同的端口。
这种策略无疑是比锥形的三种策略都要严格得多,它区分开了每一个独特的链接,像一些超时、流控等策略就可以具体到链接去做了。
NAT 打洞
打洞思路
像这种被 NAT 设备隐藏的设备要通讯,无疑需要一个公共服务器来协助。
注意,由于对称型 NAT 每次发起都会被映射到 NAT 设备的新端口,而不是登录时通知到打洞服务器的端口,同时新端口无法统一预测,所以对称型的 NAT 是难以打洞的。只有其中一方是不对端口做限制的策略,而来做被握手的一方才可能可以。
不同策略的组合打洞可能性如下:
Peer | Peer | 能否打洞 |
---|---|---|
全锥型 | 全锥型 | ✓ |
全锥型 | 受限锥型 | ✓ |
全锥型 | 端口受限锥型 | ✓ |
全锥型 | 对称型 | ✓ |
受限锥型 | 受限锥型 | ✓ |
受限锥型 | 端口受限锥型 | ✓ |
受限锥型 | 对称型 | ✓ |
端口受限锥型 | 端口受限锥型 | ✓ |
端口受限锥型 | 对称型 | ✘ |
对称型 | 对称型 | ✘ |
如果两方处于能打洞的 NAT 后方,假设两台需要通讯的设备为 A、B,公共服务器 S,这个打洞过程大概如下:
1)设备A 通过私有协议,携带自己的内网信息、外网信息(此信息可以被 S 自动获知)登录 S。登录后 S 可以对 A 进行测试性发送数据,根据包拒绝与否、端口变化等,来确定设备A 当前的 NAT 类型。此时 S 就有了设备 A 的记录;
2)设备B 重复 1 中的过程,登录 S;
3)设备A 向 S 请求,想与 B 发起直接通讯;
4)S 开始同时向 A、B 发送对方的信息,做打洞准备;
5)A 拿到 B 的外网信息后,对 B 发起了一次通讯,此时 NAT 设备将建立起一个 A 对 B 的口子(session);
6)类似 5,B 同样建立好与通讯 A 的口子;
7)A 或者 B 收到对方的包后,根据上层协议是 TCP 或者是 UDP,A 跟 B 各自做好准备。TCP 会相对复杂一些,因为 A、B 会同时发起一个握手请求,这里面有很多异步的问题。理论上来说只保留一个最先成功的链路即可,实操上会有些许的差异;
8)以上过程可以加入重试来容错。
三种不同的情况
这个过程可以分三种不同的情况来思考其是否成立。
- 在同一个 NAT 设备下面
- 双方在不同的 NAT 设备下面
- 双方在多层 NAT 设备下面
其他
还有其他的细节(比如通讯协议的差异考虑),可具体参考这里的论文:Peer-to-Peer Communication Across Network Address Translators
NAT 一些成熟的打洞方案
上面说了一些简单的理论,下面介绍一下市面已有的一些打洞方案。也许一些方案已经在家里的路由器上集成了(比如UPnP)。感兴趣可以深入了解。
UPnP
UPnP 是一套由 UPnP 论坛颁布的计算机网络协议,官网地址。
UPnP 架构允许建立由个人电脑、联网设备和各种无线设备组成的 P2P 网络。当一个新的主机需要连接时,一个 UPnP 设备(比如我家的路由)可以自动配置一个网络地址,宣布其在网络上的子网,并交换设备和服务的描述。
目前,许多互联网网关供应商,如 D-Link、英特尔、Buffalo 科技和 Arescom 都在提供具有 UPnP 功能的设备。
UPnP 中的 NAT 打洞行为被称为互联网网关设备(IGD)协议。然而 UPnP 的一个缺点是,它要求网络中的所有设备都支持UPnP。即使一个设备不符合UPnP标准,我们也不能实现 P2P 通信。
*而且这玩意儿很容易导致家用网络上有bug,比如游戏连不上服务器等等。一般我都是关了它了事。
STUN
这个就没啥好说的了,基本上就是刚刚的思路的企业化实现。
STUN 一样有上面提到的缺点。对称型的 NAT 很难打通。
Teredo
微软提出的技术。比较有识别点的地方在于,它是 IPv6 时代的产物。
总结
NAT 就介绍完了。作为折腾佬,也许在 IPv6 时代,能保障到自己内网所有的设备都能有一个全局 IP 的时候,也许 NAT 打洞的需求就过去了。
但目前来说 IPv4 还仍然是一个主流的协议(特别是国内)。
了解 NAT 的过程也很有意思, 中间穿插了很多网络相关的知识可供想象。