NAT 打洞技术

NAT 打洞技术

主要介绍基于 UDP 协议的 P2P 打洞。

Intro

本文记录了一些关于 NAT 打洞技术的技术原理和本人之前不了解的细节。

定义

NAT打洞(Nat Traversal)技术是通过中间公网服务器的协助在通信双方的** NAT 网关上建立相关的映射表项**,使得双方发送的报文能直接穿透双方的 NAT 网关(防火墙),实现 P2P 直连。

P2P(点对点)技术

在介绍NAT打洞技术之前,让我们先介绍一下 P2P 技术。

P2P 又称对等互联网络技术,是无中心服务器、依靠用户群交换信息的互联网体系,与有中心服务器的中央网络系统不同,对等网络的每个用户端既是一个节点,也有服务器的功能。任何一个节点无法直接找到其他节点,必须依靠其用户群进行信息交互。
传统cs.png

p2p.png

P2P 的优势想必大家都知道:更快的下载速度,更节省成本,更安全的数据传输环境,去中心化所带来的可靠性等等,总之是前途一片光明的技术啊!(赞赏)

但是,在很多情况下,P2P 主机会位于 NAT 网关之后,也就是我们平常所说的:“中XX动没有给我的路由器/主机提供公网 IP”这在一定程度上破坏了端到端的网络通信,因为NAT 不允许外网主机主动访问内网主机,这不符合 P2P 的要求,所以也就衍生出了许多 P2P 穿越 NAT 的方案,本文要介绍的 NAT 打洞就是其中应用比较多的一种。
需要打洞

四种 NAT 类型与 UDP 打洞场景

在实际的网络应用情况下,打洞是基于UDP 协议而实现的,而且打洞双方位于不同的内网之中,如果你们已经在同一个内网里了为什么还要装模作样给空气打个洞啊喂,还可能是不同的 NAT 类型,在这里我们只讨论这种原教旨主义上的“打洞”(STUN),不讨论其他需要中继转发的情况。同时,由于应用场景不如UDP普遍,本文也不讨论基于TCP的打洞(事实上它和UDP打洞十分相似)。

四种 NAT 类型

我们用一个具体的情形来解释四种 NAT 类型:
假设有:

Client: 192. 168. 0. 3

Server: 1. 1. 1. 1

问题一:Client 的 100 端口与 Server 的 1111 端口在路由器上建立好映射关系后,如果 Client:100 又给另一台Server (2.2.2.2: 2222) 发送数据,路由器该怎么处理呢?

  1. 复用旧的映射关系 (8. 8. 8. 8: 800) ---> (192. 168. 0. 3: 100) 和 (2. 2. 2. 2: 2222) 通信, 这就是锥型 (Cone) NAT
  2. 创建新的映射关系如 (8. 8. 8. 8: 801) ---> (192. 168. 0. 3: 100) 和 (2. 2. 2. 2: 2222) 通信,这就是对称型 NAT

问题二:还是 Client (192. 168. 0. 3: 100) 和 Server (1. 1. 1. 1: 1111) 在路由器上建立好映射关系后,如果这个时候路由器 (8. 8. 8. 8) 在800 端口上收到从另外一台 Server (2. 2. 2. 2: 2222) 发来的数据,是不是应该转发给 (192. 168. 0. 3: 100) 呢?

  1. 无条件转发给 (192. 168. 0. 3: 100),这就是全锥型 (Full Cone) NAT
  2. 如果 (192. 168. 0. 3: 100) 之前给 (2. 2. 2. 2) 发送过数据,则转发, 这就是受限锥型 (Restricted Cone)
  3. 如果 (192. 168. 0. 3: 100) 之前给 (2. 2. 2. 2: 2222) 发送过数据,则转发, 这就是端口受限锥型 (Port Restricted Cone)
  4. 丢弃报文,拒绝转发,这就是对称型 NAT

至此,我们完全区分了四种NAT,从上面也描述也可以看出,对于安全性系数,  对称型 > 端口受限锥型 > 受限锥型 > 全锥型。

四种类型下的打洞

此时,节点 A 和 B 的 NAT 可能是任意一种 NAT 类型:

  • A 和 B 是锥型 NAT
  • A 和 B 分别是对称型和普通锥型(全锥型,限制型锥型)
  • A 和 B 分别是对称型和 Port-Restricted Cone NAT(端口限制型)
  • A 和 B 都是对称型

A 和 B 是锥型 NAT

以下流程假定已经完成了 NAT 类型探测,A/B 知道自己的 NAT 类型,以及通过 NAT 映射出去的端口 PA1/PB1

打洞策略:
全锥形.png

  1. A 向 server 发起与 B 的打洞请求,server 向 B 转发打洞请求,同时 A 向 PB1 直接发送探测包,那么 A 为 B 在 PA1 已经成功打洞,但是 A 的消息无法到达,因为 B 的 NAT 会将不明的地址(PA1) 丢弃。(注意:这里有可能不是丢弃,而是拒绝,稍后将讨论这种情况)
  2. B 收到从 server 转发过来的打洞请求后,向 PA1 直接发送探测包,这时 B 的 NAT 可以放行 PA1 的消息了,也就是 B 为 A 在 PB1 上完成了打洞。
  3. 至此,A 和 B 消息能够互通,打洞成功。

A 和 B 分别是对称型和普通锥形(全锥形,限制型锥形)

假设 A 是对称 NAT,B 是普通锥形:
第二种情况.jpg

打洞策略:

  1. A 向 server 发起与 B 的打洞请求,server 向 B 转发打洞请求,同时发送探测包PB1,这个探测包是从 PA2 发出的,不是 PA1(因为对称型)。也就是 A 在端口 PA2 为 PB1 完成打洞,同时 B 的 NAT 会丢弃来自不明地址 PA2 的包。(注意:这里有可能不是丢弃,而是拒绝
  2. B 收到从 Server 转发过来的打洞请求,向 PA1 发送初始探测包(一开始不知道 PA2),这个时候 B 已经为 A 在 PB1 打好洞,至此 PA2 的消息能够通过 PB1 到达 B。(注意:因为是普通锥形,不对端口做限制,所以从不同端口 PA2 过来的包能被 B 接受
  3. 经过步骤2,B 可以收到 PA2 的消息,同时结合 A 的 NAT 类型,重新改发探测包到 PA2,于是 A 在 PA2 能收到 PB1 的探测包,至此 A 和 B 消息可以互通,打洞成功。

A 和 B 分别是对称型和 Port-Restricted Cone NAT(端口限制型)

与前一种情况不同的是,PB1 只允许 PA1 通过,所以 PA2 过来的包会被 B 的 NAT 拒绝,导致打洞失败

A 和 B 都是对称型

这种情况比上一种的转发策略更为严格,A 和 B 探测得到的公网 Port 均会被修改,无法完成打洞。

对称型打洞的办法

根据一种叫做生日攻击的神奇理论,可以实现对成型和端口限制型的打洞,但是双方都是对称型还是无法实现打洞。

NAT 对陌生地址包的行为

在刚才两种打洞成功的情况中说到,NAT设备 对于陌生地址发来的包采用的是丢弃策略。但是如果不是丢弃,而是拒绝,也就是采用黑名单机制,当接收到陌生包后触发防火墙,把 { PA2, PB1 }加入 deny 列表。此时如果 B 往 A 发送包,发现自己的 deny 列表存在 PB1,就会重新选择 PB2 发送包,于是锥型 NAT 退化成对成型 NAT

要解决这个问题,可以采取以下方法:

设置有限TTL,避免“惊动“防火墙
  1. A 往 B 发包,设置 TTL 为 3,这个大小可以实现足够通过自己的外网 NAT,同时又会被某个中间运营商路由器丢弃,从而不会惊动 B 的防火墙模块,这就为 B 打好了洞。

  2. 对于 B 同理,也为 A 打好洞。

  3. 双方等待一段时间,如 2s。

  4. 再互相发探测包,不用设置 TTL。

  5. 打洞成功。

     具体的 TTL 值需要详细地探测。
    

UDP 在空闲状态下的超时问题

由于 UDP 转换协议提供的”洞“不是绝对可靠的,多数 NAT 设备内部都有一个 UDP 转换的空闲状态计时器,如果在一段时间内没有 UDP 数据通信,NAT 设备会关掉由”打洞“过程打出来的”洞“。

所以为了维持”洞“的工作,我们需要在想要维持工作的时间内向对方发送心跳包,或者在超时之前重新打一个洞,丢弃原来的洞。

总结

以上便是基于 UDP 协议的 NAT 打洞技术的实现过程,如果日后有基于 TCP 协议的 P2P 打洞技术的需求,我将会进一步学习并对本文进行补充。

参考:
https://www.cnblogs.com/xiugeng/p/12023620.html
https://www.rebootcat.com/2021/03/28/p2p_nat_traversal/

LICENSED UNDER CC BY-NC-SA 4.0
Comment