P2P网络NAT穿透原理(打洞方案)

1.关于NAT

NAT技术(Network Address Translation,网络地址转换)是一种把内部网络(简称为内网)私有IP地址转换为外部网络(简称为外网)公共IP地址的技术,它使得一定范围内的多台主机只利用一个公共IP地址连接到外网,可以在很大程度上缓解了公网IP地址紧缺的问题,同时也能防止外网攻击保障内网安全。如下图:

NAT主要的通过对数据包头的地址替换来完成内网计算机访问外网服务器的。当内部机器要访问外部网络时,NAT设备把内网机器的IP与端口号(网络层地址与传输层地址,如:192.168.1.100:666),转换成 NAT 的外部 IP与端口号(如:100.10.10.10:1666),发送给外网服务器;数据返回时,再把目的IP和端口端口号(100.10.10.10:1666)的数据包替换为内网机器的IP地址和端口号(192.168.1.100:666),发给内网机器。若通讯协议的内容中有IP地址的传递,如FTP协议,NAT在转换时还要把数据包内涉及协议地址交互的地方替换,否则协议就会出现地址混乱。在 NAT设备中维护了这个要替换地址的映射表,并根据内部计算机的通讯需求维护该表。外部网络来数据包能否进入内网,主要是看是否已经有可转换的映射表项,若没有就会丢弃。

下图的例子是内网Client A和Client B访问外网Server,Server回复Client A 和Client B请求,NAT转换IP过程中,通过映射不同端口号区分不用内网主机。

NAT的外网地址可以是一个IP,也可以是一个IP段,形成地址池。NAT 还可以把某个外网地址直接影射给内网的某个服务器,让外网的用户可以直接访问到这台服务器。NAT的工作隐藏了内网的机器,但允许内网主动打开到外网的通讯 "通道",也就是建立映射表项。

NAT的内外网通讯双方不是平等的,允许内网机器主动发起连接访问外网,当NAT没有对应的地址转换映射表项时,会建立一个映射表项,数据包经过NAT地址转换后发送给外网的服务器,连接建立以后可双向传送数据,但却禁止反方向的主动访问:在没有对应地址映射表项情况下,不允许外网主动访问内网主机,因为没有对应的地址映射表项用于转换地址,外网发送的数据包会被丢弃。而地址映射表项的建立往往是因为内网主动访问外网或NAT静态设置。

2.NAT类型

NAT分为两大类,基本NAT(Basic Network Address Translator)和NAPT(Network Address/Port Translator)。

基本NAT,它仅将内网主机的私有IP地址转换成公网IP地址,但并不将TCP/UDP端口信息进行转换,有动态与静态之区分。静态NAT:私有地址与公有地址进行一对一的映射,这种一对一映射无法缓解可用公有地址短缺的问题。动态NAT:私有地址与公有地址进行一对多的映射,先建立公有地址池,当私有地址向外通信时,会从公有地址池中选择非在用的公有地址进行映射,当通信结束时,释放映射关系,公有地址重新恢复到地址池中待用。

NAPT(Network Address/Port Translator),从名称上我们也可以看得出,NAPT不但会转换经过的数据包的IP地址,还会转换数据包的TCP/UDP端口。由于现在大部分都属于NAPT,故这里不详细讨论基础NAT,下文的NAT指NAPT。

NAT有4种类型:完全锥形NAT(Full Clone NAT)、限制性锥形NAT(Restricted Clone NAT)、端口限制性锥形NAT( Port Restricted Clone NAT)、对称式NAT(Symmetric NAT),前面3种是锥形NAT(Clone NAT)。

2.1.完全锥形NAT(Full Clone NAT)

完全锥形NAT(Full Clone NAT):所有从同一个内网主机的IP和端口发送出来的请求都会被映射到同一个NAT的外网IP和端口,且任何一个外网主机均可通过此映射表项发送数据包到对应的内网主机。外网主机可以主动连接内网主机,有点类似于静态NAT。

2.2.限制性锥形NAT(Restricted Clone NAT)

限制性锥形NAT(Restricted Clone NAT):所有从同一个内网主机的IP(标为Src_IP)和端口(标记Src_IP)发送出来的外网请求都会被映射到同一个NAT的外网IP和端口,但增加了一个限制:只有被内网主机(Src_IP:Src_IP)请求过的外网主机IP才能被使用(此外网主机可使用任意端口),否则外网发送的数据包会被丢弃。限制性锥形NAT也被称为IP限制性锥形NAT或地址限制性锥形NAT。

2.3.端口限制性锥形NAT( Port Restricted Clone NAT)

端口限制性锥形NAT( Port Restricted Clone NAT):所有从同一个内网主机的IP(标为Src_IP)和端口(标记Src_IP)发送出来的外网请求都会被映射到同一个NAT的外网IP和端口,但限制:只有被内网主机(Src_IP:Src_IP)请求过的外网主机IP和端口才能被使用,否则外网发送的数据包会被丢弃。即在IP受限锥形NAT基础上增加了端口的限制。

2.4.对称式NAT(Symmetric NAT)

对称式NAT(Symmetric NAT):所有从同一个内网主机的IP(标为Src_IP)和端口(标记Src_IP)发送到同一个外网主机(IP地址标为目的Des_IP,端口标记为Des_Port)的请求都会被映射到同一个NAT的外网IP和端口。即此映射表项只能被**外网主机(Des_IP:Des_Port)**所用。

看一个例子,假设有如下网络,Client使用666端口访问了Server1的1999端口、1988端口和Server2的1888端口。

根据NAT类型,可能产生如下映射表,↔符号左表示内网主机IP和端口,↔符号右表示NAT的外网IP和端口,@符号右表示限制条件:外网主机地址IP和端口。

实际上大部运营商提供的光猫上网服务都是锥形NAT的,而3G、4G网络、公共WiFi等因为安全因素都是对称式NAT。(我们通常在云上购买的服务器就可以理解为是完全锥型NAT)

3.NAT穿透

NAT技术虽然在一定程度上在解决IPv4地址、构建防火墙、保证网络安全方面都发挥了一定的作用,因不允许外网主机访问内网主机,却破坏了端到端的网络通信。为了让跨越NAT的主机之间有效的P2P通信,需要用到NAT穿透,或者叫做NAT打洞。

STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序,IETF RFC 3489)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。

3.1.检测NAT类型

第一步 对称式NAT、锥形NAT检测:

①Client首先使用相同的端口分别连接Server1和Server2,Server1获取到NAT映射的IP1:Port1,Server2获取到NAT映射的IP2:Port2;

②Server2把IP2:Port2发给Server1,Server1对比IP1:Port1和IP2:Port2,若不相等,NAT是对称式;否则就是锥形NAT。

第二步 限制锥形NAT、完全锥形NAT检测:

①Client首先向Server2发送消息Mesg,Server2获取到NAT映射的IP2:Port2和Mesg;

②Server2把IP2:Port2和Mesg发给Server1;

③Server1向IP2:Port2发送消息Mesg,Client如果收到Mesg,NAT完全锥形,否则就是限制性锥形。

第三步 IP限制锥形NAT、端口限制锥形NAT检测:

①Client首先向Server1发送消息Mesg,Server1使用端口Port_S1接收Mesg,并获取到NAT映射的IP1:Port1;

②Server1另取一闲置端口Port_Sx(Port_Sx≠Port_S1),Server1使用Port_Sx发送Mesg到IP1:Port1,若Client收到Mesg,NAT是IP限制锥形,否则是端口限制锥形。

3.2.NAT穿透原理与能力

NAT穿透原理如下图可以简要分为6步:

①Client1和Client2分别与Server连接,Server由此获取两者在NAT1和NAT2上的外网映射IP1:Port1和IP2:Port2;

②Server把IP1:Port1发给Client2并告之连接Client1;

③Client2发送请求IP1:Port1,NAT1接收到数据包由于没有对应的映射表项,NAT1把数据包丢弃,但在NAT2上留下IP1:Port1映射表项;

④Client2向Server发送消息:请求Server转告Client1连接Client2,Server发送IP2:Port2给Client1并告知连接IP2:Port2;

⑤Client1连接IP2:Port2;

⑥Client2接收到Client1连接,穿透成功;接收不到则穿透失败。

检测到需要穿越NAT的类型后,根据NAT类型特性,实施穿透策略。我们可以得出以下结论:

序号 NAT类型 穿越
1 完全锥形NAT 完全锥形NAT
2 完全锥形NAT IP限制性锥形NAT
3 完全锥形NAT 端口限制性锥形NAT
4 完全锥形NAT 对称式NAT
5 IP限制性锥形NAT IP限制性锥形NAT
6 IP限制性锥形NAT 端口限制性锥形NAT
7 IP限制性锥形NAT 对称式NAT
8 端口限制性锥形NAT 端口限制性锥形NAT
9 端口限制性锥形NAT 对称式NAT
10 对称式NAT 对称式NAT

3.3.两种特殊的P2P场景

3.3.1.TCP通信双方只有一方在NAT内网

这是所有P2P场景中最简单的,它使用一种被称为"反向链接技术"。

由于Client2拥有外网IP地址,所以Client1可以直接发起TCP连接到Client2,但如果Client2尝试发起TCP连接到Client1进行P2P通信则会失败。这时Client2不是直接向Client1发起TCP连接,而是通过Server给Client1发送一个请求:反过来请求Client1连接到Client2(即进行反向链接),Client1在收到从Server转发过来的请求后,会主动向Client2发起一个TCP的连接请求,这样在NAT设备上就会建立起映射表项,Client1和Client2之间能够正常TCP连接。

3.3.2.UDP通信双方在同一个NAT内网

假设有内网主机Client1和Client2,他们对应的NAT为NAT1和NAT2,有一个外网服务器Server,我们可以把Client1和Client2之间的UDP通信NAT穿透方案优化为:

①Client1和Client2向Server发送自己内网IP和端口,Server分别记为IP_C1:Port_C1和IP_C2:Port_C2;同时Server记下Client1、Client2实际与自己通信所使用的外网IP地址和端口号(即对应NAT映射表项外网IP地址和端口),分别记为IP1:Port1和IP2:Port2;

②Client1请求Server帮助建立与Client2的UDP连接;

③Server将Client1的{IP_C1:Port_C1, IP1:Port1}发给Client2,同时,Server将Client2的{IP_C2:Port_C2, IP2:Port2}也发给Client1;

④当Client2收到{IP_C1:Port_C1, IP1:Port1}后,开始向IP_C1:Port_C1和IP1:Port1分别发送UDP数据包,并且Client2会自动锁定第一个给出响应的IP地址和端口号。同理,当Client1收到{IP_C2:Port_C2, IP2:Port2}后,也会开始向IP_C2:Port_C2和IP2:Port2分别发送UDP数据包,并且自动锁定第一个得到Client2回应的IP地址和端口号。由于Client1与Client2互相向对方发送UDP数据包的操作是异步的,所以Client1和Client2发送数据包的时间先后并没有时序要求。

当Client1、Client2在同一个NAT内网时,NAT1=NAT2,如上图,正常情况下,Client1、Client2分别向对方内网IP端口发送的数据包因为不需要路由先到达,因而一般采用内网直联P2P通信。而Client1、Client2分别向对方外网IP端口发送的数据包能否收到取决于NAT是否支持不同端口之间的UDP数据包传输(即Hairpin转换特性)。

:Hairpin又被称为Hairpin NAT、Loopback NAT或Hairpin Translation,翻译"端口回流","回环NAT"。它能够让两台位于同一台NAT网关后面的主机,通过对方的外网地址和端口相互访问,NAT网关会根据一系列规则,将对内网主机发向其NAT外网IP地址的报文进行转换,并从内网接口发送给目标主机。

如上图, Client1和Client2进行P2P连接,最好的方式NAT1外网地址端口直联NAT2外网地址端口,但是实际上,Client1、Client2和Server都无法获取到NAT1、NAT2的外网地址和端口。Client1、Client2只能向对方在NAT3上映射的外网端口发送数据包,如果NAT3不支持Hairpin,那么Client1、Client2就不能进行P2P通信。

相关推荐
BingoGo9 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack9 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack3 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
DianSan_ERP4 天前
电商API接口全链路监控:构建坚不可摧的线上运维防线
大数据·运维·网络·人工智能·git·servlet
呉師傅4 天前
火狐浏览器报错配置文件缺失如何解决#操作技巧#
运维·网络·windows·电脑