NAT穿透(UDP/TCP 打洞)

Friend ship

2019年 IPv4 地址就已经枯竭了,所以网络运营商一方面开始推广 IPv6,另一方面开始大面积使用 NAT 技术来回收公网 IPv4 地址。而没有了公网 IPv4 地址,好多应用我们都无法直接使用了(比如自建 Web 服务器),只能使用 NAT 穿透技术来实现了。

背景知识

网络地址转换(英语:Network Address Translation,缩写:NAT;又称网络掩蔽、IP掩蔽)在计算机网络中是一种在IP数据包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术。这种技术被普遍使用在有多台主机但只通过一个公有IP地址访问因特网的私有网络中。它是一个方便且得到了广泛应用的技术。当然,NAT也让主机之间的通信变得复杂,导致了通信效率的降低。

  • NAT的优点:增加了内网的安全性,如隐藏真实IP、过滤非法数据包。
  • NAT的缺点:
  • NAT设备会对数据包进行编辑修改,降低了发送数据的效率;
  • 各种协议的应用各有不同,有的协议是无法通过NAT的,即双方PC无法直接建立链接。

NAT 类型

我们通常说的NAT1/NAT2/NAT3/NAT4,从 NAT1 至 NAT4,限制越来越来严格。

Full Cone NAT(完全圆锥型NAT)

这就是我们所说的NAT1,也是所有BT用户都希望的NAT。

  1. 一旦一个内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的包都经eAddr:ePort 向外发送。
  2. 任意外部主机都能通过 eAddr:ePort 来向 iAddr:iPort 发送数据,外部主机的源端口不受限。

Full Cone NAT 例子

Full Cone NAT

  1. 客户A 自 4000端口向服务器A 的 8000端口发送数据包Z
    数据包Z 分析:
  • IP与端口 192.168.0.2:4000
  • 目的IP与端口 6.6.6.6:8000
  1. 路由器第一次收到来自客户A 端口4000发来的数据包z
  • 路由器随机打开一个外部端口,如 5000,即 2.2.2.2:5000。
  • 将外部端口 2.2.2.2:5000 与内网客户A的4000端口进行映射。
  • 以后所有来自客户A 4000端口的数据包经路由器外部端口5000进行NAT转发。
  • 此时,所有发往路由器外部端口5000的数据包都会NAT转发至客户A。
  1. 路由器对数据包Z做源 IP 的 NAT 转换并转发。
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A的8000端口收到来自客户A的数据包Z
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A向客户A发送数据X端口可以是任意端口,这里用端口9000。
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 2.2.2.2:5000
  1. 路由器收到数据X,对其进行分析
  • 数据包X是发往外部端口 2.2.2.2:5000的
  • 外部端口 2.2.2.2:5000 已经与内网客户A的端口 192.168.0.2:4000 建立了映射
  1. 路由器对数据包X做目的IP的NAT转换并转发。
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 192.168.0.2:4000
  1. 客户A的4000端口收到来自服务器A的数据包X
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 192.168.0.2:4000

公网中的其它IP,如服务器B/服务器C等等,都可以通过路由器 2.2.2.2:5000 与客户A的端口 192.168.0.2:4000进行通信。

这就是为什么BT软件都喜欢用户处于NAT1环境下,因为这样其它BT用户可以轻易地与你所开放的端口进行链接。

Address-Restricted Cone NAT(地址受限圆锥型NAT)

这就是我们所说的NAT2,它在 NAT1 (Full Cone NAT) 的基础上,又增加了一条对外部主机IP的限制。

  1. 一旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的包都经由 eAddr:ePort 向外发送。
  2. 当 iAddr:iPort 已经向外部主机(hAddr:any)发送过数据后,hAddr:any 就能通过 eAddr:ePort 来向 iAddr:iPort 发送数据。any 指外部主机源端口不受限制。

Address-Restricted Cone NAT 例子

Address-Restricted Cone NAT

  1. 客户A自4000端口向服务器A的8000端口发送数据包Z
    数据包Z分析
  • IP与端口 192.168.0.2:4000
  • 目的IP与端口 6.6.6.6:8000
  1. 路由器第一次收到来自客户A端口4000发来的数据包。
  • 路由器随机打开一个外部端口,如 5000,即 2.2.2.2:5000。
  • 将外部端口 2.2.2.2:5000 与内网客户A的端口 192.168.0.2:4000 进行映射。
  • 将外部端口 2.2.2.2:5000 与目的IP 6.6.6.6 进行关联。
  • 所有来自客户A 4000端口的数据包都经路由器外部端口5000进行NAT转发。
  • 此时,只有目的IP(6.6.6.6)发往路由器外部端口 2.2.2.2:5000 的数据包才都会NAT转发至客户A 192.168.0.2:4000 上,而其它IP发往 2.2.2.2:5000 的数据包全部丢掉。
  1. 路由器对数据包Z做源IP的NAT转换。
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A的8000端口收到来自客户A的数据包Z
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A向客户A发送数据包X端口可以是任意端口,这里用端口9000。
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 2.2.2.2:5000
  1. 路由器收到数据包X,对其进行分析后
  • 数据包X 是发往外部端口 2.2.2.2:5000的
  • 外部端口 2.2.2.2:5000 已经与内网客户A的端口 192.168.0.2:4000建立了映射
  • 外部端口 2.2.2.2:5000 已经与IP 6.6.6.6 建立了联系。
  1. 路由器对数据包X做目的IP的NAT转换并进行转发。
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 192.168.0.2:4000
  1. 客户A的端口 192.168.0.2:4000 收到来自服务器A的数据包X
    数据包X分析
  • IP与端口 6.6.6.6:9000
  • 目的IP与端口 192.168.0.2:4000

公网中的其它IP,如服务器B/服务器C等等,发往路由器 2.2.2.2:5000 的数据包全部被路由器丢掉了,因为路由器的外部端口 2.2.2.2:5000 没有与它们的IP进行关联。

如果其它IP想与客户A进行通信怎么办?

假如服务器B想与客户A进行通信,那就需要重新走一次上面的 1-8 流程,即客户A首先向服务器B发出数据连接请求,然后服务器B才可以通过路由器与客户A进行通信。

NAT2下其它BT用户如何与我们进行数据交换?

以下为大致流程,具体实施细节可能有所不同。

  1. 需要一个中间人A,它可以是种子中心也可以是某个我们已经连接了的种子用户。
  2. 中间人A告诉我们,用户B 想和我们进行种子数据交换,用户B的IP地址 B.B.B.B:bPort
  3. 我们主动向 B.B.B.B:bPort 发去一个随机数据。
  • 此数据的目的是只为让路由器做映射
  • 此数据最终的命运会被丢掉。假如用户B处于NAT1或无NAT的情况下,那它收到此数据包会丢弃;如果用户B也处于NAT2以下,那么数据会被用户B的路由器B给丢掉,因为路由器B没有打开相应端口。
  1. 路由器收到数据后将外部端口(eAddr:ePort) 与IP B.B.B.B 进行关联并转发。
  2. 我们告诉中间人A,请求已发送。
  3. 中间人A 收到我们已发送的消息后,告诉用户B 可以与 eAddr:ePort 进行数据交换了。
  4. 用户B 收到消息,向 eAddr:ePort 发出交换种子数据的请求。
  5. 我们收到用户B的消息后,就可以进行种子数据交换了。

Port-Restricted Cone NAT(端口受限圆锥型NAT)

这就是NAT3,在 NAT2 (Address-Port Cone NAT) 的基础上,又增加了对外部主机端口的限制。

  1. 一旦一个内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的包都经由 eAddr:ePort 向外发送。
  2. 如果 iAddr:iPort 已经向外部主机(hAddr:hPort)发送了数据,那么 hAddr:hPort 就可以通过 eAddr:ePort 与iAddr:iPort 进行通信。

Port-restricted Cone NAT 例子

Port-Restricted Cone NAT

  1. 客户A自端口 192.168.0.2:4000 向服务器A的端口 6.6.6.6:8000 发送数据包Z
    数据包Z 分析
  • IP与端口 192.168.0.2:4000
  • 目的IP与端口 6.6.6.6:8000
  1. 路由器第一次收到来自客户A端口4000发来的数据包。
  • 随机打开一个外部端口,如 5000,即 2.2.2.2:5000。
  • 将外部端口 2.2.2.2:5000 与内网客户A的端口 192.168.0.2:4000 进行映射。
  • 将外部端口 2.2.2.2:5000 与目的IP与端口进行关联,这里为 6.6.6.6:8000。
  • 此后,所有来自客户A 4000端口的数据包都经路由器外部端口5000进行NAT转发。
  • 此后,路由器 2.2.2.2:5000 将来自 6.6.6.6:8000 的数据包转发至内网 192.168.0.2:4000,其它数据包全部丢弃。
  1. 路由器对数据包Z做源IP的NAT转换。
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A的8000端口收到来自客户A的数据包Z
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A向客户A发送数据包X端口必须指定端口 8000。
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 2.2.2.2:5000
  1. 路由器收到数据包X,对其进行分析后
  • 数据包X是发往外部端口 2.2.2.2:5000的
  • 外部端口 2.2.2.2:5000 已经与内网客户A的端口 192.168.0.2:4000建立了映射
  • 外部端口 2.2.2.2:5000 已经与源IP的端口 6.6.6.6:8000 建立了联系。
  1. 路由器对数据包X做目的IP的NAT转换并进行转发。
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 192.168.0.2:4000
  1. 客户A的端口 4000 收到来自服务器A的数据包X
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 192.168.0.2:4000

在这种情况下,服务器A 的非8000端口都无法与客户A进行通信,更别说其它IP(服务器B/服务器C)了。

在NAT3下其它BT用户如何与我们进行数据交换?

以下为大致流程,具体实施细节可能有所不同。

  1. 需要一个中间人A,它可以是种子中心也可以是某个我们已经连接了的种子用户。
  2. 中间人A告诉我们,用户B 想和我们进行种子数据交换,用户B的IP地址和端口为 B.B.B.B:bPort
  3. 我们主动向用户B B.B.B.B:bPort 发去一个随机数据包。
  • 这个数据包作用只是为让路由器进行端口映射
  • 此数据包不是被用户B的路由器丢弃就是被用户B自己丢弃。
  1. 路由器将外部端口 eAddr:ePort 与用户B B.B.B.B:bPort 进行关联。
  2. 之后我们告诉中间人A,数据已发送。
  3. 中间人A收到我们已发送的消息后,告诉用户B 可以和 eAddr:ePort 建立连接了。
  4. 用户B 收到消息,向 eAddr:ePort 发出交换种子数据的请求。
  5. 我们收到用户B的消息后,就可以进行种子数据交换了。

Symmetric NAT(对称NAT)

这就是我们经常说的NAT4,目前最为严格的NAT。
NAT3 (Port-Restricted Cone NAT)基础上,增加了路由器的外部端口与所关联的IP及端口的唯一性。

  • 来自同一内部主机 (iAddr:iPort) 的数据发送到同一外部主机 (hAddr:hPort),映射到同一外部地址(eAddr:ePort)。
    同一 iAddr:iPort 发到不同的 hAddr:hPort 的信息包,分别映射到不同的ePort
  • 只有 iAddr:iPort 已经向 hAddr:hPort 发送过数据包,hAddr:hPort 才能通过 eAddr:ePort 向 iAddr:iPort 发送数据。

Symmetric NAT 例子

Symmetric NAT

  1. 客户A自端口 192.168.0.2:4000 向服务器A的端口 6.6.6.6:8000 发送数据包Z
    数据包Z 分析
  • IP与端口 192.168.0.2:4000
  • 目的IP与端口 6.6.6.6:8000
  1. 路由器第一次收到来自客户A端口4000发来的数据包。
  • 随机打开一个外部端口,如 5000,即 2.2.2.2:5000。
  • 将外部端口 2.2.2.2:5000 与内网客户A的端口 192.168.0.2:4000 进行映射。
  • 将外部端口 2.2.2.2:5000 与目的IP与端口进行关联,这里为 6.6.6.6:8000。
  • 此后,外部端口 2.2.2.2:5000 接收来自目的IP 6.6.6.6:8000 的数据包。
  • 此后,外部端口 2.2.2.2:5000 转发内网 192.168.0.2:4000 发往 6.6.6.6:8000 的数据包。
  • 客户A端口4000发往其它IP或端口的数据,由路由器重新打开一个外部端口进行转发。
  1. 路由器对数据包Z做源IP的NAT转换。
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A的8000端口收到来自客户A的数据包Z
    数据包Z分析
  • IP与端口 2.2.2.2:5000
  • 目的IP与端口 6.6.6.6:8000
  1. 服务器A向客户A发送数据包X端口必须为端口 8000。
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 2.2.2.2:5000
  1. 路由器收到数据包X,对其进行分析后
  • 数据包X 是发往外部端口 2.2.2.2:5000的
  • 路由器的外部端口 2.2.2.2:5000 已经与内网客户A的端口 192.168.0.2:4000 建立了映射
  • 路由器的外部端口 2.2.2.2:5000 已经与源IP的端口 6.6.6.6:8000 建立了联系。
  1. 路由器对数据包X做目的IP的NAT转换并进行转发。
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 192.168.0.2:4000
  1. 客户A的端口 4000 收到来自服务器A的数据包X
    数据包X分析
  • IP与端口 6.6.6.6:8000
  • 目的IP与端口 192.168.0.2:4000
  1. 客户A 4000端口发往服务器A 9000端口的数据包Y,需要重新走一次1-8步骤,如上图的第二种情况。
NAT4下BT用户如何连接?
对方没有处于NAT4下

以下为大致流程,具体实施细节可能有所不同。

  1. 需要一个中间人A,它可以是种子中心也可以是某个我们已经连接了的种子用户。
  2. 中间人A告诉用户B,我们想和它进行种子数据交换,我们的的IP地址和端口为 eAddr:ePort
  3. 用户B (B.B.B.B:bPort) 向我们发送一个随机数据包。
  • 这个数据包的作用只是为让对方的路由器进行端口映射
  • 此数据包会被我们的路由器丢弃。
  1. 用户B告诉中间人A,数据已发送
  2. 中间人A告诉我们,可以与 B.B.B.B:bPort 进行数据交换。
  3. 我们向 B.B.B.B:bPort 发送数据。
  4. 路由器将 iAddr:iPort 和 B.B.B.B:bPort 与 eAddr:ePort 进行关联
  5. 用户B 收到我们的数据包,并进行回应。
双方都处于NAT4下

如果 BT 用户双方都处于 NAT4 下,无法进行 NAT 穿透建立直接链接,因为双方路由器打开的外部端口ePort全部无法确定。
如果双方确实需要通信,只能通过中间人进行转发。

NAT穿透

一、中间服务器转发

最可靠且最低效的点对点通信方法,莫过于将 p2p 网络通信看作一个C/S结构来通过服务器转发信息。
如下图,两个客户端A和B,均与服务器S初始化了一个TCP或UDP连接,服务器S 具有公网固定IP地址,两个客户端分布在不同的私网中,这样,他们各自的NAT代理服务器将不允许他们进行直连,则由Server S来将消息进行中转。

中间服务器转发

二、反向连接

这是第二种技术,但是只能在通信的两端只有一端处于NAT之后的情况下。
举例来说,客户端 A 处于NAT之后,而客户端B有一个公网IP地址,如下图所示.

反向连接

  1. 现在我们假设客户端B将会与客户端A初始化一个端对端连接会话.
  2. B 将首先试图连接A的一个地址,客户端A认为是它自己的IP有两个,一个是 10.0.0.1:1234,另一个是从服务器S观察到的 155.99.25.11:62000。然而不论B连接哪一个,都不会成功.
  • 第一种情况:试图连接 10.0.0.1 肯定会失败,因为10.0.0.1根本就不是公网IP地址;
  • 第二种情况:从B 传来的请求将能够到达 NAT A的端口62000,但NAT A会拒绝这个请求,因为此端口没有映射到内部的A。在所有的尝试都失败之后,客户端B 只能向服务器求助,这时服务器S 告诉 A 主动向 B 发起连接请求。这样客户端A将打开一个与客户端B通讯的连接,NAT A 允许这个来自内部的连接通过。因为客户端B没有位于NAT后,所以可以接收到来自 A 的连接请求。

这个方法的优势是:它也适合于任何NAT包括Symmetric NAT。
它的主要限制在于,只能有一端位于NAT之后.

三、UDP/TCP 打洞

这是第三种穿透方法,只要双方不是都位于NAT4 Symmetric NAT 后即可。

UDP/TCP 打洞

流程

因TCP需要握手三次才能建立链接,所以此方法一般使用UDP协议,但流程基本一致。

UDP 打洞的过程大致如此:

  1. 双方都通过 UDP 与服务器通讯后,网关默认就是做了一个外网 IP 和端口号 与你内网 IP 与端口号的映射,这个无需设置的,服务器也不需要知道客户的真正内网 IP。
  2. 用户 A 先通过服务器知道用户 B 的外网地址与端口。
  3. 用户 A 向用户 B 的外网地址与端口发送消息,在这次发送中,用户 B 的网关会拒收这条消息,因为它的映射中并没有这条规则。但是用户 A 的网关就会增加了一条允许规则,允许接收从 B 发送过来的消息。
  4. 服务器要求用户 B 发送一个消息到用户 A 的外网 IP 与端口号。
  5. 用户 B 发送一条消息,这时用户 A 就可以接收到 B 的消息,而且网关 B 也增加了允许规则。
  6. 之后,由于网关 A 与网关 B 都增加了允许规则,所以 A 与 B 都可以向对方的外网 IP 和端口号发送消息。

打洞工具

TCP 打洞工具,android 端和服务端,服务端放到内网,android 端手机使用移动网络 /或接入内网 wifi。


References:

  • csdn-nat
  • csdn-nate-2
  • wikipedia-nat
  • ietf-rfc-3489