文章目录
- NAT概念
- # 背景
- # 内网划分
- # NAT概念
- # NAT工作流程
- # NAT特性
- # NAT的问题、方案
- 问题一: 资源回收问题
- 问题二: 流量跟踪问题
- 问题三: 无法理解多会话的关联性、无法接收公网侧的请求
- 问题四: 对包头敏感的协议需要特殊处理
- NAT类型
- # NAT1 ── Full Cone NAT(全锥型NAT)
- Wireshark抓包分析
- # NAT2 ── Address Restricted Cone NAT(地址限制锥型NAT)
- Wireshark抓包分析
- # NAT3 ── Port Restricted Cone NAT(端口限制锥型NAT)
- Wireshark抓包分析
- # NAT4 ── Symmetric NAT(对称型NAT)
- Wireshark抓包分析
- NAT类型 - 检测方法(原理)
- NAT类型 - 安全性
- P2P概念
- # P2P与NAT兼容问题
- 基于UDP协议的P2P打洞技术
- ## 典型P2P场景1: 两客户端中只有一方位于NAT设备之后
- ## 典型P2P情景2: 两客户端位于同一NAT设备后面(即相同内网中)
- ## 典型P2P情景3: 两客户端位于不同的NAT设备后面(分属不同的内网)
- ## 典型P2P情景4: 两客户端位于两层(或多层)NAT设备之后(分属不同的内网)
- ## NAT类型 - 穿透性
- 基于TCP协议的P2P打洞技术详细
- STUN
NAT概念
# 背景
IANA(Internet Assigned Numbers Authority,互联网号码分配机构)批量向大中型企业分配A类(0xxxxx…)、B类地址(10xxxx…),导致IPv4(Internet Protocol Version 4)消耗非常快,而且很多地址闲置在这些大中型企业中。再加上上网设备的普及,2011年4月15日开始,IPv4面临地址耗光的问题。
# 内网划分
为了解决IPv4地址耗光的问题,RFC1918规定了三个保留地址段落:
- 10.0.0.0 ~ 10.255.255.255;
- 172.16.0.0 ~ 172.31.255.255;
- 192.168.0.0 ~ 192.168.255.255;
这三个范围分别处于A,B,C类的地址段,不向特定的用户分配,被IANA作为私有地址保留。
这些地址可以在任何组织或企业内部使用,和其他Internet地址的区别就是,仅能在内部使用,不能作为全球路由地址。
💡这就是说,出了组织的管理范围这些地址就不再有意义,无论是作为源地址,还是目的地址。对于一个封闭的组织,如果其网络不连接到Internet,就可以使用这些地址而不用向IANA提出申请,而在内部的路由管理和报文传递方式与其他网络没有差异。
# NAT概念
基于上面提到的内网划分规则,行业中形成一种共识 ── NAT(Network Address Translation,网络地址转换)
通过NAT,大量不同的终端(内网设备)可以同时使用同一个公网地址给其他公网上的设备发送请求。同时,内网设备可以给外网设备发送请求,和收到外网设备传回的响应。
e.g.
NAT设备的一个公网地址和端口可以同时映射到内部几个服务器上以实现负载分担,比如一个对外提供WEB服务器的站点可能是有成百上千个服务器在提供HTTP服务,但是对外却表现为一个或少数几个IP地址。
# NAT工作流程
- 发送
对于有Internet访问需求而内部又使用私有地址的网络,就要在组织的出口位置部署NAT网关,在报文离开私网进入Internet时,将源IP替换为公网地址,通常是出口设备的接口地址。 - 接收
一个对外的访问请求在到达目标以后,表现为由本组织出口设备发起,因此被请求的服务端可将响应由Internet发回出口网关。出口网关再将目的地址替换为私网的源主机地址,发回内部。
# NAT特性
上面的描述概括了NAT处理报文的几个关键特点:
- 网络被分为私网和公网两个部分,NAT网关设置在私网到公网的路由出口位置,双向流量必须都要经过NAT网关;
- 网络访问只能先由私网侧发起,公网无法主动访问私网主机; (重点)
- NAT网关在两个访问方向上完成两次地址的转换或翻译,出方向做源信息替换,入方向做目的信息替换;
NAT网关的存在对通信双方是保持透明的(看不到、无法知道数据包是否经过NAT的);- NAT网关为了实现双向翻译的功能,需要维护一张关联表,把会话的信息保存下来。
其中第二个特点是重点,它打破了IP协议架构中所有节点在通讯中的对等地位,这是NAT最大的弊端,为对等通讯带来了诸多问题,当然相应的克服手段也应运而生(下面介绍)。
# NAT的问题、方案
NAT在解决IPv4地址短缺问题上,并非没有副作用,其实存在很多问题。
问题一: 资源回收问题
因为一个会话建立后会在NAT设备上建立一个关联表,在会话静默的这段时间,NAT网关会进行老化操作。这是任何一个NAT网关必须做的事情,因为IP和端口资源有限,通信的需求无限,所以必须在会话结束后回收资源。
通过定时器老化回收
- TCP
通常TCP会话通过协商的方式主动关闭连接,NAT网关可以跟踪这些报文,但总是存在例外的情况,要依赖自己的定时器去回收资源。 - UDP
而基于UDP的通信协议很难确定何时通信结束,所以NAT网关主要依赖超时机制回收外部端口。
感知的连接中断问题
通过定时器老化回收会带来一个问题,如果应用需要维持连接的时间大于NAT网关的设置,通信就会意外中断。因为网关回收相关转换表资源以后,新的数据到达时就找不到相关的转换信息,必须建立新的连接。当这个新数据是由公网侧向私网侧发送时,就会发生无法触发新连接建立,也不能通知到私网侧的主机去重建连接的情况。这时候通信就会中断,不能自动恢复。即使新数据是从私网侧发向公网侧,因为重建的会话表往往使用不同于之前的公网IP和端口地址,公网侧主机也无法对应到之前的通信上,导致用户可感知的连接中断。
保活的机制
NAT网关要把回收空闲连接的时间设置到不发生持续的资源流失,又维持大部分连接不被意外中断,是一件比较有难度的事情。在NAT已经普及化的时代,很多应用协议的设计者已经考虑到了这种情况,所以一般会设置一个连接保活的机制,即在一段时间没有数据需要发送时,主动发送一个NAT能感知到而又没有实际数据的保活消息,这么做的主要目的就是重置NAT的会话定时器。
问题二: 流量跟踪问题
NAT在实现上将多个内部主机发出的连接复用到一个IP上,这就使依赖IP进行主机跟踪的机制都失效了。如网络管理中需要的基于网络流量分析的应用无法跟踪到终端用户与流量的具体行为的关系。基于用户行为的日志分析也变得困难,因为一个IP被很多用户共享,如果存在恶意的用户行为,很难定位到发起连接的那个主机。
即便有一些机制提供了在NAT网关上进行连接跟踪的方法,但是把这种变换关系接续起来也困难重重。基于IP的用户授权不再可靠,因为拥有一个IP的不等于一个用户或主机。一个服务器也不能简单把同一IP的访问视作同一主机发起的,不能进行关联。有些服务器设置有连接限制,同一时刻只接纳来自一个IP的有限访问(有时是仅一个访问),这会造成不同用户之间的服务抢占和排队。有时服务器端这样做是出于DOS攻击防护的考虑,因为一个用户正常情况下不应该建立大量的连接请求,过度使用服务资源被理解为攻击行为。但是这在NAT存在时不能简单按照连接数判断。总之,因为NAT隐蔽了通信的一端,把简单的事情复杂化了。
问题三: 无法理解多会话的关联性、无法接收公网侧的请求
我们来深入理解NAT一下对IP端到端模型的破坏力:NAT通过修改IP首部的信息变换通信的地址。但是在这个转换过程中只能基于一个会话单位。当一个应用需要保持多个双向连接时,麻烦就很大。NAT不能理解多个会话之间的关联性,无法保证转换符合应用需要的规则。当NAT网关拥有多个公有IP地址时,一组关联会话可能被分配到不同的公网地址,这通常是服务器端无法接受的。更为严重的是,当公网侧的主机要主动向私网侧发送数据时,NAT网关没有转换这个连接需要的关联表,这个数据包无法到达私网侧的主机。这些反方向发送数据的连接总有应用协议的约定或在初始建立的会话中进行过协商。但是因为NAT工作在网络层和传输层,无法理解应用层协议的行为,对这些信息是无知的。NAT希望自己对通信双方是透明的,但是在这些情况下这是一种奢望。
💡受这个问题影响最严重的是P2P技术。http://www.52im.net/thread-542-1-1.html
问题四: 对包头敏感的协议需要特殊处理
NAT工作机制依赖于修改IP包头的信息,这会妨碍一些安全协议的工作。因为NAT篡改了IP地址、传输层端口号和校验和,这会导致认证协议彻底不能工作,因为认证目的就是要保证这些信息在传输过程中没有变化。对于一些隧道协议,NAT的存在也导致了额外的问题,因为隧道协议通常用外层地址标识隧道实体,穿过NAT的隧道会有IP复用关系,在另一端需要小心处理。ICMP是一种网络控制协议,它的工作原理也是在两个主机之间传递差错和控制消息,因为IP的对应关系被重新映射,ICMP也要进行复用和解复用处理,很多情况下因为ICMP报文载荷无法提供足够的信息,解复用会失败。IP分片机制是在信息源端或网络路径上,需要发送的IP报文尺寸大于路径实际能承载最大尺寸时,IP协议层会将一个报文分成多个片断发送,然后在接收端重组这些片断恢复原始报文。IP这样的分片机制会导致传输层的信息只包括在第一个分片中,NAT难以识别后续分片与关联表的对应关系,因此需要特殊处理。
NAT类型
为了解决通讯设备地位不对等的问题,主要有四种策略,对应下面的四种NAT类型
一般NAT可以分为四种类型:
- Full Cone NAT ── 全锥NAT
- Address Restricted Cone NAT ── 地址限制NAT
- Port Restricted Cone NAT ── 端口限制NAT
- Symmetric NAT ── 对称NAT
💡现实中的很多NAT设备是将这些转换方式混合在一起工作的,而不单单使用一种,所以这些术语只适合描述一种工作方式,而不是一个设备。
比如:
很多NAT设备对内部发出的连接使用对称型NAT方式,而同时支持静态的端口映射,后者可以被看作是全锥型NAT方式。
# NAT1 ── Full Cone NAT(全锥型NAT)
发送
一旦内部主机端口对(iAddr:iPort)被NAT网关映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort);
接收
任何一个外部主机发送到(eAddr:ePort)的报文将会被转换后发到(iAddr:iPort)
Wireshark抓包分析
设备只是从192.168.1.101 发送给216.93.246.18,但是216.93.246.15可以直接给192.168.1.101发数据,这种即是Full Cone NAT
# NAT2 ── Address Restricted Cone NAT(地址限制锥型NAT)
发送
一旦内部主机端口对(iAddr:iPort)被映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort)
接收
只有 (iAddr:iPort)向特定的外部主机💡yAddr发送过数据,主机💡yAddr从任意端口发送到(eAddr:ePort)的报文才会被转发到(iAddr:iPort)。
Wireshark抓包分析
只有收过NAT内部送来的封包的地址才能将封包送入
192.168.1.101需要先给216.93.246.18和216.93.246.15发消息,这两个服务器地址才可以给192.168.1.101回复,这个只是匹配IP地址,不会去检测端口,这就是Restricted Cone NAT
# NAT3 ── Port Restricted Cone NAT(端口限制锥型NAT)
发送
一旦内部主机端口对(iAddr:iPort)被映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort)
接收
只有(iAddr:iPort)向特定的外部主机端口对(yAddr:💡yPort)发送过数据,由 (yAddr:💡yPort)发送到(eAddr:ePort)的报文才会被转发到(iAddr:iPort)
Wireshark抓包分析
只有收过NAT内部送来的封包的地址和端口号才能将封包送入
需要内网先给两个服务器发送请求,才可以正常收到服务器回复数据包 ,并且会严格匹配IP和端口号
# NAT4 ── Symmetric NAT(对称型NAT)
发送
NAT网关会把内部主机“地址端口对”和外部主机“地址端口对”完全相同的报文看作一个连接,在网关上创建一个公网“地址端口对”映射进行转换
接收
只有收到报文的外部主机从对应的端口对发送回应的报文,才能被转换。即使内部主机使用之前用过的地址端口对去连接不同外部主机(或端口)时,NAT网关也会建立新的映射关系。
💡一般情况下,上图的dAddr:dPort、eAddr:ePort、fAddr:fPort不会完全不同。通常是dAddr=eAddr=fAddr,而dPort、ePort、fPort各不相同。
💡这种NAT无法实现UDP-P2P通信:
- 因为P2P是一种C/S混合的模型(用户的主机既为下载的客户client,同时也服务器server向其他客户提供数据)。
- 但是在这种NAT模式下,要产生通信的话,必须由内网设备发出请求,所有外网设备无法主动与内网设备建立连接,这样的话内网设备就无法作为服务器被动的向其他外网客户提供数据
- 所以这种类型基本上就告别P2P了
Wireshark抓包分析
请求和回复必须要对应才可以,只有有请求才能够访问内网主机
只允许先由私有网络內的使用者发送封包到公网网路中的使用者,公网网路中的使用者才可以通过该通道回传封包
💡其他三种NAT外侧端口号可以不变,但是Symmetric NAT的一定不同
NAT类型 - 检测方法(原理)
💡检测工具推荐:
- 专门的测试工具 - 更多调试内容 - https://github.com/HMBSbige/NatTypeTester
- 比特彗星内置了检查工具
前提条件:
- 一个提供两个公网地址(通信地址分别设为:Endpoint1与Endpoint2)的服务器S进行UDP端口数据监听并根据客户的要求给出响应;
- 待检测的用户可以正常进行UDP通信。
检测步骤
- 检测主机是否位于NAT后
为了检测IP地址是不是公网地址,主机A首先发送任意一个UDP数据包给服务器S(Endpoint1),S收到包之后,用Endpoint1将接收到数据包头的IP地址和端口打成一个UDP反馈包发送给用户A。
A在收到反馈包之后,比较自身的Endpoint和反馈包中的Endpoint
- 如果一样则说明A不位于任何NAT之后
- 如果不一样说明位于NAT之后
- 检测NAT是否是完全锥型
- 主机A向服务器S(Endpoint1)发送UDP数据包后,服务器用Endpoint2将接收到数据包头的IP地址和端口打成一个UDP反馈包发送给用户A
- A在发送UDP数据包后,立即开始端口侦听,设定一个等待时间上限,防止无限堵塞(因为接收是一个While循环)。
- 如果A每次都没有收到数据包,说明A所处的NAT类型不是完全锥型的
- 在这整个过程中只要收到一次服务的包,就说明A所处的NAT类型是完全限制型的
- 检测NAT是否是对称型
- 判断为非完全锥型NAT后,主机A用同一个套接字向服务器S (Endpoint1、Endpoint2)发送UDP数据
- 服务器S (Endpoint1、Endpoint2)收到请求后将接收到的数据包头的IP地址和端口打成一个UDP反馈包发送给用户A
- 在A的整个数据接收过程中,如果收到的服务器反馈数据包中标识自身的IP地址和端口存在不相同的情况,就说明NAT是对称型的,否则就不是。
- 检测NAT是限制锥型的还是端口限制锥型
主机A向服务S的Endpoint1发送UDP数据包,服务器用与Endpoint1相同的IP地址和不同的端号将带有A的Public Endpoint的UDP反馈包发送给A。另外,A在发送数据包后,立即侦听端口和并进行数据接收,设定一个等待时间上限,防止无限堵塞(因为接收是一个While循环)。重复若干次。如过在整个过程中,用户A收到S发来的数据包,就说明NAT是限制型的;否则就说明NAT是端口限制型的。
NAT类型 - 安全性
安全性系数, 对称型 > 端口受限锥型 > 受限锥型 > 全锥型
P2P概念
P2P(point to point,点对点通信、对等联网)可以是一种通信模式、一种逻辑网络模型、一种技术、甚至一种理念。P2P在即时通讯方案中应用广泛(比如IM应用中的实时音视频通信、实时文件传输甚至文字聊天等)。
P2P模式与传统的服务器客户端模式有着明显的区别。
| 经典的CS模式 | P2P结构模式 |
示意图 | ||
节点地位 | | 所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息、处理器运算能力、存储空间等资源的共享。 |
特性 | | P2P网络具有分散性、可扩展性、健壮性等特点,这使得P2P技术在信息共享、即时通讯、协同工作、分布式计算、网络存储等领域都有广阔的应用。 |
# P2P与NAT兼容问题
NAT技术和P2P技术作为经典的两项网络技术,在现在的网络上有着广泛的应用,P2P主机位于NAT网关后面的情况屡见不鲜。NAT技术虽然在一定程度上解决了IPv4地址短缺的问题,在构建防火墙、保证网络安全方面都发挥了一定的作用,却破坏了端到端的网络通信。NAT阻碍主机进行P2P通信的主要原因是NAT不允许外网主机主动访问内网主机,但是P2P技术却要求通信双方都能主动发起访问,所以要在NAT网络环境中进行有效的P2P通信,就必须采用新的解决方案。
P2P作为一项实用的技术,有很大的优化空间,并且相对于网络设备,基于P2P的应用程序在实现上更为灵活。所以为了兼容NAT,基于P2P的应用程序在开发的时候大多会根据自身特点加入一些穿越NAT的功能以解决上述问题。
几种常见的P2P穿越NAT方案:
- ACL
- 中间件
- 打洞
- relay
更多参考《P2P中的NAT穿越(打洞)方案详解(进阶分析篇)》,下面着重介绍“打洞”方案
基于UDP协议的P2P打洞技术
原理概述
UDP打洞技术是通过中间服务器(集中服务器)的协助在各自的NAT网关上建立相关的表项,使P2P连接的双方发送的报文能够直接穿透对方的NAT网关(在NAT网关上“打洞”),从而实现P2P客户端互连。如果两台位于NAT设备后面的P2P客户端希望在自己的NAT网关上打个洞,那么他们还需要一种用于打洞的Session建立机制。
什么是集中服务器?
集中服务器本质上是一台被设置在公网上的服务器,建立P2P的双方都可以直接访问到这台服务器。位于NAT网关后面的客户端A和B都可以与一台已知的集中服务器建立连接,并通过这台集中服务器了解对方的信息并中转各自的信息。
同时集中服务器的另一个重要作用在于判断某个客户端是否在NAT网关之后。具体的方法是: 🔗参考“NAT类型检测”
P2P的Session建立原理:
假定客户端A要发起对客户端B的直接连接,具体的“打洞”过程如下:
- 请求集中服务器: Client A最初不知道如何向Client B发起连接,于是Client A向集中服务器发送消息,请求集中服务器帮助建立与Client B的UDP连接。
- 集中服务器通知两点: 集中服务器将含有Client B的外网和内网的地址二元组发给Client A,同时,集中服务器将包含有Client A的外网和内网的地址二元组信息的消息也发给Client B。这样一来,Client A与Client B就都知道对方外网和内网的地址二元组信息了。
- 各自发送UDP数据包:
- 当Client A、Client B收到由集中服务器发来的对方的外网地址二元组信息后,它们马上从内网设备中向对方外网地址二元组发送一组UDP数据包。
- ⚠️由于发送UDP数据包时,它们的NAT服务器已经记录了对方外网地址二元组的映射关系
- 当对方的UDP数据包到达NAT服务器时,NAT服务器就可以通过发送时产生的映射关系,反向的将数据包转发至内网设备中。于是Client A、Client B就能收到对方的UDP数据包
💡由于Client A与Client B互相向对方发送UDP数据包的操作是异步的,所以Client A和Client B发送数据包的时间先后并没有时序要求。
下面来看下这三者之间是如何进行UDP打洞的。在这我们分三种具体情景来讨论:
- 第一种是最简单的一种情景,两个客户端都位于同一个NAT设备后面,即位于同一内网中;
- 第二种是最普遍的一种情景,两个客户端分别位于不同的NAT设备后面,分属不同的内网;
- 第三种是客户端位于两层NAT设备之后,通常最上层的NAT是由网络提供商提供的,第二层NAT是家用的NAT路由器之类的设备提供的。
## 典型P2P场景1: 两客户端中只有一方位于NAT设备之后
此种情况是所有P2P场景中最简单的,它使用一种被称为“反向链接技术”来解决这个问题。
大致的原理如下所述
结构
- Client A位于NAT之后,它通过TCP端口1234连接到服务器(Server)的TCP端口1235上,NAT设备为这个连接重新分配了TCP端口62000。
- Client B也通过TCP端口1234连接到服务器端口1235上。
- Client A和Client B从Server处获知的对方的外网地址二元组,它们在各自的本地端口上进行侦听。
外网地址二元组 |
|
Client A |
|
Client B |
|
通信
- 由于Client B 拥有外网IP地址,所以Client A要发起与Client B的通信,可以直接通过TCP连接到Client B。
- 但如果Client B尝试通过TCP连接到Client A进行P2P通信,则会失败,原因是Client A位于NAT设备后,虽然Client B发出的TCP SYN请求能够到达NAT设备的端口62000,但NAT设备会拒绝这个连接请求。
- Client B要想与Client A通信, Client B不是直接向Client A发起连接,而是通过服务器Server给Client A转发一个连接请求,反过来请求Client A连接到Client B(即进行反向链接),Client A在收到从服务器转发过来的请求以后,会主动向B发起一个TCP的连接请求,这样在NAT设备上就会建立起关于这个连接的相关表项,使A和B之间能够正常通信,从而建立起它们之间的TCP连接。
## 典型P2P情景2: 两客户端位于同一NAT设备后面(即相同内网中)
这是最简单的一种情况:客户端A和B分别与集中服务器建立UDP连接,经过NAT转换后,A的公网端口被映射为62000,B的公网端口映射为62005。
流程
- 当A向集中服务器发出消息请求与B进行连接,集中服务器将B的外网地址二元组以及内网地址二元组发给A,同时把A的外网以及内网的地址二元组信息发给B。
- A和B发往对方公网地址二元组信息的UDP数据包不一定会被对方收到,这取决于当前的NAT设备是否支持不同端口之间的UDP数据包能否到达(即Hairpin转换特性),无论如何A与B发往对方内网的地址二元组信息的UDP数据包是一定可以到达的,内网数据包不需要路由,且速度更快。A与B推荐采用内网的地址二元组信息进行常规的P2P通信。
- 假定NAT设备支持Hairpin转换(回环转换),P2P双方也应忽略与内网地址二元组的连接,如果A 和B采用外网的地址二元组做为P2P通信的连接,这势必会造成数据包无谓地经过NAT设备,这是一种对资源的浪费。就目前的网络情况而言,应用程序在“打洞”的时候,最好还是把外网和内网的地址二元组都尝试一下。如果都能成功,优先以内网地址进行连接。
💡什么是Hairpin(回环转换)技术?
Hairpin技术又被称为Hairpin NAT、Loopback NAT或Hairpin Translation。Hairpin技术需要NAT网关支持,它能够让两台位于同一台NAT网关后面的主机,通过对方的公网地址和端口相互访问,NAT网关会根据一系列规则,将对内部主机发往其NAT公网IP地址的报文进行转换,并从私网接口发送给目标主机。目前有很多NAT设备不支持该技术,这种情况下,NAT网关在一些特定场合下将会阻断P2P穿越NAT的行为,打洞的尝试是无法成功的。好在现在已经有越来越多的NAT设备商开始加入到对该转换的支持中来。
## 典型P2P情景3: 两客户端位于不同的NAT设备后面(分属不同的内网)
这是最普遍的一种情况:Client A与Client B经由各自的NAT设备与集中服务器Server S建立UDP连接
客户端A——>本地IP:10.0.0.1,本地端口:4321,外网IP:155.99.25.11,外网端口:62000
客户端B——>本地IP:10.1.1.3,本地端口:4321,外网IP:138.76.29.7,外网端口:31000
- 在Client A、Client B向服务器Server S发送的登陆消息时,包含有客户端Client的内网地址二元组信息
- 服务器Server S会记录下客户端Client的内网地址二元组信息,同时会把自己观察到的Client的外网地址二元组信息记录下来。
| Client A | Client B |
内网 | 10.0.0.1:4321 | 10.1.1.3:4321 |
外网 | 155.99.25.11:62000 | 138.76.29.7:31000 |
- Client A或者Client B向服务器发送P2P连接请求时,服务器都会将其记录下来的上述的外网和内网地址二元组发送它们。
- A和B分属不同的内网,它们的内网地址在外网中是没有路由的,所以发往各自内网地址的UDP数据包会发送到错误的主机或者根本不存在的主机上。当A的第一个消息发往B的外网地址,该消息途经A的NAT设备,并在该设备上生成一个会话表项,该会话的源地址二元组信息是{10.0.0.1:4321},和A与服务器建立连接的时候NAT生成的源地址二元组信息一样,但它的目的地址是B的外网地址。在A的NAT设备支持保留A的内网地址二元组信息的情况下,所有来自A的源地址二元组信息为{10.0.0.1:4321}的数据包都沿用A与集中服务器事先建立起来的会话,这些数据包的外网地址二元组信息均被映射为{155.99.25.11:62000}。
- A向B的外网地址发送消息的过程就是“打洞”的过程,从A的内网的角度来看应为从{10.0.0.1:4321}发往{138.76.29.7:31000},从A在其NAT设备上建立的会话来看,是从{155.99.25.11:62000}发到{138.76.29.7:31000}。如果A发给B的外网地址二元组的消息包在B向A发送消息包之前到达B的NAT设备,B的NAT设备会认为A发过来的消息是未经授权的外网消息,并丢弃该数据包。
- B发往A的消息包也会在B的NAT设备上建立一个{10.1.1.3:4321,155.99.25.11:62000}的会话(通常也会沿用B与集中服务器连接时建立的会话,只是该会话现在不仅接受由服务器发给B的消息,还可以接受从A的NAT设备{155.99.25.11:6200}发来的消息)。
- 一旦A与B都向对方的NAT设备在外网上的地址二元组发送了数据包,就打开了A与B之间的“洞”,A与B向对方的外网地址发送数据,等效为向对方的客户端直接发送UDP数据包了。一旦应用程序确认已经可以通过往对方的外网地址发送数据包的方式让数据包到达NAT后面的目的应用程序,程序会自动停止继续发送用于“打洞”的数据包,转而开始真正的P2P数据传输。
## 典型P2P情景4: 两客户端位于两层(或多层)NAT设备之后(分属不同的内网)
这是一种最典型的情况:最上层的NAT设备通常是由ISP(Internet Service Provider,网络提供商)提供,下层NAT设备是家用路由器。
如下图所示:假定NAT C是由ISP提供的NAT设备,NAT C提供将多个用户节点映射到有限的几个公网IP的服务,NAT A和NAT B作为NAT C的内网节点将把用户的内部网络接入NAT C的内网,用户的内部网络就可以经由NAT C访问公网了。
从这种拓扑结构上来看,只有服务器Server S与NAT C是真正拥有公网可路由IP地址的设备,而NAT A和NAT B所使用的公网IP地址,实际上是由ISP服务提供商设定的(相对于NAT C而言)内网地址(我们将这种由ISP提供的内网地址称之为“伪”公网地址)。同理,隶属于NAT A与NAT B的客户端,它们处于NAT A,NAT B的内网,以此类推,客户端可以放到到多层NAT设备后面。客户端A和客户端B发起对服务器S的连接的时候,就会依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要联入公网的时候,会在NAT C上再建立向外的Session。
现在假定客户端A和B希望通过UDP“打洞”完成两个客户端的P2P直连。最优化的路由策略是客户端A向客户端B的“伪公网”IP上发送数据包,即ISP服务提供商指定的内网IP,NAT B的“伪”公网地址二元组,{10.0.1.2:55000}。由于从服务器的角度只能观察到真正的公网地址,也就是NAT A,NAT B在NAT C建立session的真正的公网地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客户端A与客户端B是无法通过服务器知道这些“伪”公网的地址,而且即使客户端A和B通过某种手段可以得到NAT A和NAT B的“伪”公网地址,我们仍然不建议采用上述的“最优化”的打洞方式,这是因为这些地址是由ISP服务提供商提供的或许会存在与客户端本身所在的内网地址重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。
因此客户端别无选择,只能使用由公网服务器观察到的A,B的公网地址二元组进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发。
数据包转发流程
当客户端A向客户端B的公网地址二元组{155.99.25.11:62005}发送UDP数据包的时候,NAT A首先把数据包的源地址二元组由A的内网地址二元组{10.0.0.1:4321}转换为“伪”公网地址二元组{10.0.1.1:45000},现在数据包到了NAT C,NAT C应该可以识别出来该数据包是要发往自身转换过的公网地址二元组,如果NAT C可以给出“合理”响应的话,NAT C将把该数据包的源地址二元组改为{155.99.25.11:62000},目的地址二元组改为{10.0.1.2:55000},即NAT B的“伪”公网地址二元组,NAT B最后会将收到的数据包发往客户端B。同样,由B发往A的数据包也会经过类似的过程。目前也有很多NAT设备不支持类似这样的“Hairpin转换”,但是已经有越来越多的NAT设备商开始加入对该转换的支持中来。
client A(10.0.0.1) request udp s{10.0.0.1:4321} -> d{155.99.25.11:62005}
↓
nat A(10.0.1.1) request udp s{10.0.1.1:4500} -> d{155.99.25.11:62005}
↓
nat C(155.99.25.11) request udp s{155.99.25.11:6200} -> d{155.99.25.11:62005}
⭐️识别出来该数据包是要发往自身转换过的公网地址二元组
nat C(155.99.25.11) request udp s{155.99.25.11:6200} -> d{10.0.1.2:55000}
↓
nat B(10.0.1.2:55000) request udp s{155.99.25.11:6200} -> d{10.0.0.3:4321}
↓
client B(10.0.0.3)
💡一个需要考虑的现实问题:UDP在空闲状态下的超时
当然,从应用的角度上来说,在完成打洞过程的同时,还有一些技术问题需要解决,如UDP在空闲状态下的超时问题。由于UDP转换协议提供的“洞”不是绝对可靠的,多数NAT设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由“打洞”过程打出来的“洞”。如果P2P应用程序希望“洞”的存活时间不受NAT网关的限制,就最好在穿越NAT以后设定一个穿越的有效期。
对于有效期目前没有标准值,它与NAT设备内部的配置有关,某些设备上最短的只有20秒左右。在这个有效期内,即使没有P2P数据包需要传输,应用程序为了维持该“洞”可以正常工作,也必须向对方发送“打洞”心跳包。这个心跳包是需要双方应用程序都发送的,只有一方发送不会维持另一方的Session正常工作。除了频繁发送“打洞”心跳包以外,还有一个方法就是在当前的“洞”超时之前,P2P客户端双方重新“打洞”,丢弃原有的“洞”,这也不失为一个有效的方法。
## NAT类型 - 穿透性
NAT之间有10种组合
NAT类型-1 | NAT类型-2 | 穿透性 |
全锥型 | 全锥型 | ✅ |
全锥型 | 地址受限锥型 | ✅ |
全锥型 | 端口受限锥型 | ✅ |
全锥型 | 对称型 | ✅ |
地址受限锥型 | 地址受限锥型 | ✅ |
地址受限锥型 | 端口受限锥型 | ✅ |
地址受限锥型 | 对称型 | ✅ |
端口受限锥型 | 端口受限锥型 | ✅ |
端口受限锥型 | 对称型 | ❌ (无法打通) |
对称型 | 对称型 | ❌ (无法打通) |
技巧
如果定义他们的值为序列号
- full cone NAT全椎
- Restricted Cone ip NAT受限
- port Restricted Cone NAT 端口受限
- Symmetric NAT对称
N为序列号相加。则N<=6可穿透。
e.g.
2 和 4 序列相加 = 6 则可穿透。 3(端口受限)和4(对称)= 7 不可穿透。
基于TCP协议的P2P打洞技术详细
建立穿越NAT设备的P2P的TCP连接只比UDP复杂一点点,TCP协议的”“打洞”从协议层来看是与UDP的“打洞”过程非常相似的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了的对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。