欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

解读P2P中的NAT穿越技术详解

最编程 2024-02-07 13:21:37
...

[TOC]

NAT背景

NAT(Network Address Translation,),称为网络地址转换或者网络地址掩蔽。NAT是一种网络地址翻译技术,主要是将内部的私有IP地址(private IP)转换成可以在公网使用的公网IP(public IP)。

由于现有的IPv4地址编码只有32位空间,最多可容纳2的32次幂台终端设备连入互联网;再加上IPv4地址空间被按照不同前缀长度划分为A,B,C,D类地址网络和保留地址,更加剧了IP地址的不足。NAT出现的背景正是源于IPv4地址不够用,所以才会采取这种地址转换的策略。可见,NAT的本质就是让一群机器共用同一个IP,这种方案暂时解决了IP短缺的问题。

NAT类型

在RFC3489中定义了4种NAT类型(如下),即早期的STUN协议。STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即简单的用UDP穿透NAT,是个轻量级的协议,是基于UDP的完整的穿透NAT的解决方案,最早定义在RFC3489。它允许应用程序发现它们与公共互联网之间存在的NAT和防火墙及其他类型。后来在新的RFC5389修订中把STUN协议定位于为穿透NAT提供工具,而不是一个完整的解决方案,英文全称为Session Traversal Utilities for NAT,即NAT会话穿透工具。

  • 完全锥型NAT(Full Cone NAT)
    即一对一(one-to-one)NAT。一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。任意外部主机都能通过给eAddr:port2发包到达iAddr:port1。
完全锥型NAT
  • 受限锥型NAT(Address-Restricted Cone NAT)
    限制地址,即只接收曾经发送到对端的IP地址来的数据包。一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。任意外部主机(hostAddr:any)都能通过给eAddr:port2发包到达iAddr:port1的前提是:iAddr:port1之前发送过包到hostAddr:any. "any"也就是说端口不受限制。
受限锥型NAT
  • 端口受限型NAT(Port Restricted Cone NAT)
    类似受限制锥形NAT(Address-Restricted cone NAT),但是还有端口限制。一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。一个外部主机(hostAddr:port3)能够发包到达iAddr:port1的前提是:iAddr:port1之前发送过包到hostAddr:port3。
端口受限型NAT
  • 对称型NAT(Symmetric NAT)
    对每个外部主机或端口的会话(session)都会映射为不同的端口。只有来自同一内部IP:PORT、且针对同一外部目标IP:PORT的请求才被NAT转换至同一个公网(外部)IP:PORT,否则的话,NAT将为之分配一个新的外部(公网)IP:PORT。并且,只有曾经收到过内部主机请求的外部主机才能向内部主机发送数据包。重点在于NAT设备会分配不同的外部port来区分不同的会话(session)。
对称型NAT

NAT穿透之UDP打洞

UDP打洞技术是通过中间服务器的协助在各自的NAT网关上建立相关的表项,使P2P连接的双方发送的报文能够直接穿透对方的NAT网关,从而实现P2P客户端互连。如果两台位于NAT设备后面的P2P客户端希望在自己的NAT网关上打个洞,那么他们需要一个协助者——中间服务器,并且还需要一种用于打洞的Session建立机制。目前通用的就是使用STUN协议。

P2P建立Session的通用过程

假定客户端A要发起对客户端B的直接连接,具体的“打洞”过程如下:

  1. A最初不知道如何向客户端B发起连接,于是A向集中服务器发送消息,请求中间服务器S帮助建立与客户端B的UDP连接。
  2. 中间服务器S将含有B的外网和内网的地址二元组发给A,同时,中间服务器S将包含有A的外网和内网的地址二元组信息的消息也发给B。这样一来, A与B就都知道对方外网和内网的地址二元组信息了。
  3. 当A收到由中间服务器S发来的包含B的外网和内网的地址二元组信息后, A开始向B的地址二元组发送UDP数据包,并且A会自动锁定第一个给出响应的B的地址二元组。同理,当B收到由中间服务器S发来的A的外网和内网地址二元组信息后,也会开始向A的外网和内网的地址二元组发送UDP数据包,并且自动锁定第一个得到A回应的地址二元组。由于A与B互相向对方发送UDP数据包的操作是异步的,所以A和B发送数据包的时间先后并没有时序要求。

P2P的3种典型NAT穿透场景

  • 两客户端位于同一NAT设备后面(即相同内网中)
场景1
1. A向中间服务器S请求与B进行连接,S将B的外网以及内网的地址二元组发给A
2. 中间服务器S把A的外网以及内网的地址二元组信息发给B
3. A和B各自给对方公网以及内网地址二元组信息各发送UDP数据包
4. 选择最先响应的地址二元组信息进行常规P2P通信

我们注意到这时里的公网地址其实只有一个,只是目标端口不同。往公网地址发送的UDP数据如果想成功穿透,NAT设备需要支持Hairpin技术。Hairpin技术又被称为Hairpin NAT、Loopback NAT或Hairpin Translation。它能够让两台位于同一台NAT设备后面的主机,通过对方的公网地址和端口相互访问,NAT设备会根据一系列规则,将对内部主机发往其NAT公网IP地址的报文进行转换,并从私网接口发送给目标主机,类似一种中继(relay)。目前有很多NAT设备不支持该技术,这种情况下,NAT网关在一些特定场合下将会阻断P2P穿越NAT的行为,打洞的尝试是无法成功的。

  • 两客户端位于不同的NAT设备后面(分属不同的内网)
场景2
1. A向中间服务器S请求与B进行连接,S将B的外网以及内网的地址二元组发给A
2. 中间服务器S把A的外网以及内网的地址二元组信息发给B
3. A和B各自给对方公网以及内网地址二元组信息各发送UDP数据包
4. 由于内网不存在对端机器,所以内网不会有响应,只有外网响应后才可以通信
  • 两客户端位于两层(或多层)NAT设备之后(分属不同的内网)
场景3

假定NAT C是由ISP提供的NAT设备,NAT C提供将多个用户节点映射到有限的几个公网IP的服务,NAT A和NAT B作为NAT C的内网节点将把用户的内部网络接入NAT C的内网,用户的内部网络就可以经由NAT C访问公网了。从这种拓扑结构上来看,只有服务器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}。由于从服务器S的角度只能观察到真正的公网地址,也就是NAT A,NAT B在NAT C建立session的真正的公网地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客户端A与客户端B是无法通过服务器S知道这些“伪”公网的地址,而且即使客户端A和B通过某种手段可以得到NAT A和NAT B的“伪”公网地址,我们仍然不建议采用上述的“最优化”的打洞方式,这是因为这些地址是由ISP服务提供商提供的或许会存在与客户端本身所在的内网地址重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。

因此客户端别无选择,只能使用由公网服务器S观察到的A,B的公网地址二元组进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发,此时如果NAT C不支持Hairpin转换,打洞将失败。

P2P做NAT穿透的约束条件
  • 一制性映射
    在4种NAT类型中,目前只有完全锥型NAT(Full Cone NAT)符合这个要求,可以成功在Peer之间做穿透,其它3种类型基本都无法做穿透;只能依赖Relay服务器做中继(relay)才可完成Peer之间的通信。
    但也有一些例外情况,如一些NAT设备的对称型NAT(Symmetric NAT)在给连续的会话(session)分配外部端口时也会是连续的,这样分配的外部端口就具有高度可预测性;所有穿透算法就可以利用这个特性在穿透时用估算的端口进行穿透测试,如此反复多次测试是否可以打通;但这种方法也只是在某些特定NAT设备中有效。

  • 忽略数据包的payload数据
    一些老的NAT设备在做NAT转换时,由于算法过于简单陈旧,不仅会转换包头的IP地址和端口号,还会把数据包的payload数据中出现的IP地址和端口号也同时转换,这样就会影响STUN协议正常工作。

  • Hairpin转换
    如上面提到的第3个NAT穿透场景中,当两客户端位于两层(或多层)NAT设备之后,就需要NAT设备支持Hairpin转换才可以实现穿透。

全文完

参考资料

Peer-to-Peer Communication Across NATs