为了缓解 IPv4 地址不足的问题,在子网(subnetwork)和超网(CIDR)之后,臭名昭著的 NAT 诞生了。它是作为一个对 IPv4 地址不足问题的临时补救方案,但对当下互联网产生了深刻的负面影响。

其中一个影响在于,当我们想要实现 P2P 通信时,不得不使用一些手段来绕过 NAT 的限制。它使得 P2P 的实现变得复杂困难。

NAT (Network Address Translation)

NAT 可以让多台主机共享同一个公网 IP 地址,主要通过 NAT 转换表来实现。

在这片文章中,我们将能够在互联网上路由到的地址成为全局地址,它与公共地址、公开地址是同一个概念;将不能在互联网上路由到的地址成为专用地址,它与私有地址、内网地址是同一个概念。

全局地址和专用地址

在 RFC1518 中对 IP 地址的全局地址和专用地址做了规定。IPv4 为内部网络预留了 3 组专用地址,第一组是 A 类地址的一个地址块(10.0.0.0 ~ 10.255.255.255);第二组是 B 类地址的 16 个地址块(127.16 ~ 127.31);第三组是 C 类地址的 256 个地址块(192.168.0 ~ 192.168.255)。这些专用地址已经不在公网中使用,网络设备可以很快识别出它们是一个专用地址。试图在公网上访问专用地址的行为都将失败。

NAT 原理

NAT 可以让多个专用地址共享一个全局地址。通过一张 NAT 转换表,将专用地址映射为全局地址。

假设内网的一台主机的专用地址为 192.168.0.1,它希望访问公网的服务器 135.2.1.1,假设该内网主机使用的端口为 3342,公网服务器的端口为 80,那么将产生一个源地址为 192.168.0.1,端口号为 3342,目的地地址为 135.2.1.1,端口号为 80 的分组,记为 S=192.168.0.1,3342,D=135.2.1.1,80。在该分组到达执行 NAT 功能的路由器时,会将分组的源地址从专用地址转换为可在公网上路由的全局地址,并得到一个新端口。假设公网地址为 202.0.1.1,转换后产生另一个分组,S=202.0.1.1,5001,D=135.2.1.1,80。

当内网主机访问请求到达服务器后,在服务器看来,就是地址为 202.0.1.1,端口 5001,的主机在访问它,当服务器处理完请求后,会将响应发送回地址为 202.0.1.1,端口为 5001 的主机。

但地址 202.0.1.1 并不是发送请求的内部主机地址,而是经过 NAT 转换的地址。所以响应会被地址为 202.0.1.1 的路由接收,并发现有一条目的地为 135.2.1.1:80 的 NAT 转换表记录,根据记录将响应转发给内部主机 192.168.0.1。

根据上面的流程,多个内部地址可以被 NAT 映射为 IP 相同,端口不同的全局地址。

如:192.168.0.1:3344 映射为 202.0.1.1:5001,192.168.0.2:3344 映射为 202.1.1:5002。

NAT 的缺点

NAT 有效地缓解了 IPv4 地址不足的问题,但它也带来一些问题。

  1. NAT 违反了 IP 地址的设计原则:每个 IP 均标识一个网络或一台主机。NAT 使多名主机有相同的地址。
  2. NAT 使 IP 协议从无连接变成有连接。在 NAT 的情况下,如果路由出现故障,也不会影响 TCP 的执行,因为TCP 的超时重传。存在 NAT 时,NAT 维护的 NAT 转换表,网络可能变得脆弱。
  3. NAT 违反了网络基本分层结构模型。它修改了其他分层的头部,破坏了各层独立原则。
  4. 有些应用将 IP 地址插入到正文中,如 FTP 和 VOIP 协议 H.323。如果 NAT 与这类协议一起工作,则需要做一定的修正。
  5. NAT 使 P2P 应用的实现出现困难。隐藏在 NAT 后面的主机无法被直接访问,只能用一些手段来绕过 NAT 的限制。
  6. NAT 存在对高层协议和安全性的影响问题。
  7. NAT 推迟了 IPv6 的普及进程。

NAT 的类型

NAT 的存在使得 P2P 通信出现困难,但也不是没有办法。要在 NAT 下进行 P2P 通信,首先要了解所有 NAT 的类型。

NAT 的类型决定了要进行通信的双方能否 P2P 通信。总的来说,NAT 的类型为以下 4 种:全锥型(Full Cone),受限锥型(Restricted Cone)- 也叫 IP 受限锥型,端口受限锥型(Port Restricted Cone),对称型(Symmetric)。

具体表现为映射的规则和来自公网数据的筛选规则不同。

全锥型

同一个内网主机和端口访问不同的服务器都使用同一个映射。所有发送给映射后的公网地址的数据都无条件转发给内网主机。

例:内网主机 192.168.0.1 和端口 3344,往服务器 135.2.1.1:80 发送数据,在 NAT 表上建立了映射,将 192.168.0.1:3344 转换为公网地址和端口 202.0.0.1:5566。然后同一个内部主机和端口 192.168.0.1:3344 访问了另一个服务器 136.3.2.2:443,按照全锥型 NAT 的规则,映射为同一个 公网地址和端口 202.0.0.1:5566。

在建立 192.168.0.1:3344 到 202.0.0.1:5566 的映射后,公网上其他主机给公网地址和端口 202.0.0.1:5566 发送的数据都会转发到专用地址和端口 192.168.0.1:3344 的主机上。

受限锥型

映射规则和全锥型相同,相同的内部地址和端口访问不同的服务器会映射为同样的公网地址和端口。

不同的是,如果公网上别的主机发送数据到 NAT 映射后的公网地址,仅限在内部主机向给服务器的地址发送过数据的情况下,才会转发给内网主机。

例:NAT 映射为 192.168.0.1:3344 到 202.0.1.1:5566,此时公网有服务器 136.3.2.2:443 向 202.0.1.1:5566 发送数据,那么只有在内部主机和端口 192.168.0.1:3344 想服务器地址 136.3.2.2(端口不限)发送过数据,才会被转发给内部主机和端口 192.168.0.1:3344。

端口受限锥型

映射规则和受限锥型相同,不同的是公网其他主机发送过来的数据,仅限在内部主机向该公网地址和端口发送过数据的情况下才会转发。

例:映射为 192.168.0.1:3344 到 202.0.1.1:5566,此时服务器 136.3.2.2:443 向 202.0.1.1:5566 发送数据,那么只有在内部主机和端口 192.168.0.1:3344 向 服务器地址和端口 136.3.2.2:443 发送过数据,才会被转发。

对称型

同一个专用地址和端口访问不同的地址和端口会被映射为不同的公网地址和端口。公网的主机只有在内部主机向该公网主机发送过数据才会转发。

例:内部主机和端口 192.168.0.1:3344 访问 136.3.2.2:443,会将 192.168.0.1:3344 映射为 202.0.1.1:5566。同一个内部主机和端口访问 136.4.3.3:80 会将 192.168.0.1:3344 映射为 202.0.1.1:5577.。

这几种类型的 NAT,全锥型最宽松,其次是受限锥型,然后是端口限制锥型,对称型最严格。

NAT 下 P2P 通信原理

如何利用不同的 NAT 类型来绕过 NAT 的限制进行 P2P 通信呢。

我们可以简单分析下通信双方在各种 NAT 下的表现。

名词解释:在这一节中,我们假设有两个隐藏在 NAT 后的客户端 A 和 B,尝试进行 P2P 通信,当我们说它们到对方的公网地址时,如果没有特别说明,就是指通过 STUN 服务器获取到对方经过 NAT 映射后的公网地址。这种情况下,A 的公网地址为 A1,B 的公网地址为 B1

此外还会提到 A 的另一个公网地址 A2,B 的另一个公网地址 B2,它们的获取方式将在提到它们时说明。

全锥型-全锥型

全锥型 NAT 的特性,同一个内部地址映射为同一个公网地址,且其他公网主机发送的数据都会转发给内部主机,那么只要知道对方映射后的公网地址就可以通过给该公网地址发送数据来通信。

全锥型-受限锥型

当受限锥型想要接收对方发送的数据,由于受到受限锥型的特性,对方就算知道了受限锥型的映射公网地址,在受限锥型没主动给对面的地址(端口不限)发送过数据的情况下是不会接收对方的数据的。所以受限锥型必须先主动发送数据给对面的地址。

全锥型-端口受限锥型

端口受限锥型和受限锥型差不多,必须先主动发送数据,但更严格一点,必须先主动向要通信的地址和端口发送数据。

全锥型-对称型

和对称型的通信会复杂一点,原因在于对称型对不同主机和端口发送数据会建立新的映射。假设全锥型的客户端 A 试图和对称型的客户端 B 进行 P2P 通信,会发现 A 无法向 B1 发送数据,因为对称型必须先主动向对面发送数据后才能接收对方的数据。但是在 B 向 A1 发送数据后映射为一个不同的公网地址 B2,所以无论如何 A 都不能向 B1 发送数据。

好在 B 向 A1 发送数据建立了新的映射后,A 可以获取到 B 的新公网地址 B2,然后双方进行通信。

受限锥型-对称型

双方知道对方的公网地址和端口,假设 A 是受限锥型,B 是对称型。如同我们上面所讲过的 B 先向 A 发送数据,以让 A 获取到 B 的新公网地址 B2,但是 A 受限锥型,A 也必须主动先向 B1 发送数据才能接收 B 发送过来的数据,但 A 不接受 B 的数据就不能知道 B 的新公网地址 B2,双方都必须先主动发送数据。

要解决这种竞争情况,必须让 A 向 B1 发送数据,让 B 向 A1 发送数据,且两者在同时进行。也就是双方都在在对方向自己发送了数据且自己还没有接收到对方的数据前,向对方发送数据。

要让这种方式成功非常依赖双方的时效性,其中一方没能及时发送数据都将使连接的建立失败。

而且与对称型建立 P2P 连接的一个前提是另一方不能是端口受限锥型或对称型。让我们来看端口受限锥型和对称型是怎么样的。

端口受限锥型-对称型

当我们想要用和受限锥型与对称型建立连接的方式,让端口受限锥型和对称型建立连接时,会发现端口受限锥型无法接收到对称型的数据。

这是由于端口受限锥型的特性,它必须先给一个地址和端口发送数据后才能接收该地址和端口发送给他的数据。由于是地址加端口的限制,我们不能通过向地址 B1 发送数据来接收 B2 的数据,无法接收 B2 发送过来的数据,就无法获取 B2 地址,也就是无法建立连接。

回看受限锥型和对称型建立连接的过程,会发现它可以向 B1 发送数据来接收 B2 的数据。

所以,受限锥型和对称型无法建立连接。

同样的,对称型和对称型无法建立连接,因为 A2 无法接收 B2 的数据,反之亦然。

还有一些没有讲到的情况,比如受限锥型和端口受限锥型的通信,也是和受限锥型和对称型通信差不多的道理。需要处理竞争情况。

从上面几个例子能够看到 NAT 对 P2P 通信的影响,它极大增加了 P2P 通信的实现难度,光是建立连接都要经过一系列复杂的流程。且在端口受限锥型-对称型和,对称型-对称型这两种情况下无法建立 P2P 连接。

总结

在这篇文章中我们介绍了 NAT 的用处和原理,它是为了缓解 IPv4 地址不足而提出的,也讨论了它对当下网络的影响,带来了一系列的问题。最后我们详细讨论了 NAT 是如何影响 P2P 通信的,并用几个例子演示如何绕过 NAT 尽可能地进行 P2P 连接。

总的来说,NAT 是一个有用处的协议,它的确在一定程度缓解了 IPv4 地址不足的问题,但也是一个非常失败的协议,它破坏了原本的网络协议的结构,使网络变得更复杂和脆弱。NAT 的普及不仅没有解决 IPv4 地址不足的问题,同时也延缓了 IPv6 的普及。

NAT 的缺点,远大于它的优点,是非常失败的。