分享

NAT穿透

 心不留意外尘 2016-08-29


http://blog./uid-11572501-id-2868691.html

2011-03-30

1. NAT简介
NAT(Network Address Translation)又称"网络地址转换",它是一种把内部私有网络地址翻译成合法网络IP地址的技术
简单地说,NAT就是在局域网内部使用私有地址(前面文章说到过每类IP地址都有私有地址),而当内部节点要与外部网络进行通讯时,就在网关(可以理解为 出口)处将内部地址替换成公用地址,从而在公网(internet)上正常使用.通过这种方法,您可以只申请一个合法IP地址,就把整个局域网中的计算机 接入Internet中.

2. NAT类型

NAT有三种类型:静态NAT(Static NAT),动态地址NAT(Pooled NAT),网络地址端口转换NAPT.

静态NAT:内部网络中的每个主机都被永久映射成外部网络中的某个合法的地址.
动态地址NAT:则是在外部 网络中定 义了一系列的合法地址,采用动态分配的方法映射到内部网络.
NAPT:则是把内部地址映射到外部网络的一个IP地址的不同端口上.

网络地址端口转换NAPT(Network Address Port Translation)是人们比较熟悉的一种转换方式,NAPT普遍应用于接入设备中,它可以将中小型的网络隐藏在一个合法的IP地址后面.这个优点在 小型办公室内非常实用,通过从ISP处申请的一个IP地址,将多个连接通过NAPT接入Internet.NAPT类型:
NAPT主要分两大类:锥形NAT和对称NAT.

锥形NAT类型:
锥形NAT在根据如何能创建端到端有效连接上有更多的分类.这个分类一般应用在Udp通信(而不是Tcp通信上),因为NATs和防火墙阻止了试图无条件传入的TCP连接,除非明确设置NAT不这样做.这些分类如下:完全锥形(Full Cone):
在一个完全锥形NAT中,任务主机A都可以通过主机B映射在NAT上的外部地址发包给内部主机B.(A可在公网也可在内网,以下也是).
受限锥形(Restricted Cone):
在一个受限锥形NAT,只有内部主机B向其发送过包的主机A才可以对这个内部主机B发包(通过主机B映射在NAT上的外部地址).
端口受限锥形(Port Restricted Cone):
端口受限锥形NAT与受限锥形NAT类似,但是增加了对端口的限制,如内部主机B向一个端口为q的主机A发送过包,那么只有从这个主机A的端口q发出的包才能被发送到内部主机B上去。
对称NAT(Symmetric NAT):
对称NAT,与Cone NAT是大不相同的,并不对会话进行端口绑定,而是分配一个全新的公网端口给每一个新的会话.

如果Client A同时发起两个会话到S1和S2,对称NAT会分配公共地址155.99.25.11:62000给Session1,然后分配另一个不同的公共地址 155.99.25.11:62001给Session2.对称NAT能够区别两个不同的会话并进行地址转换,应用程序每发出一个会话都会使用一个新的端 口.

3.NAT工作机制NAPT映射条目:记录上面所说的转化IP地址和端口的条目.
NAPT映射表:NAPT映射条目的集合.
NAPT带来的后果:路由器只能根据动态建立的NAPT表来转发来自外网的数据包,外网不能主动建立到内网的连接.
虚拟服务器和UPNP等转发设置就是为了解决这个问题而产生的,人为(虚拟服务器)或者通过软件(UPNP组件)打开一条到内网的通道.NAPT使得一组主机可以共享唯一的外部地址,当位于内部网络中的主机通过NAT设备向外部主机发起会话请求时,
NAT设备就会查询NAT表,看是否有相关会话记录,如果有相关记录,就会将内部IP地址及端口同时进行转换,再转发出去;
如果没有相关记录,进行IP地址和端口转换的同时,还会在NAT表增加一条该会话的记录。外部主机接收到数据包后,
用接受到的合法公网地址及端口作为目的IP地址及端口来响应,NAT设备接收到外部回来的数据包,再根据NAT表中的记录把目的地址及端口转换成对应的内部IP地址及端口,
转发给该内部主机。
4. NAPT具体类型检测
前提条件:有一个公网的Server并且绑定了两个公网IP(IP-1,IP-2).这个Server做UDP监听(IP-1,Port-1),(IP-2,Port-2)并根据客户端的要求进行应答.

第一步:检测客户端是否有能力进行UDP通信以及客户端是否位于NAT后,是否有firewall?

第二步:检测客户端NAT是否是Full Cone NAT?
第三步:检测客户端NAT是否是Symmetric NAT?
第四步:检测客户端NAT是否是Restricted Cone NAT还是Port Restricted Cone NAT?

5. NAT穿透
5.1 转发    
最可靠但又是最低 效的点对点通信方法,莫过于将p2p网络通信看作一个C/S结构,通过服务器来转发信息.如下图,两个客户端A和B,均与服务器S初始化了一个TCP或 UDP连接,服务器S具有公网固定IP地址,两个客户端分布在不同的私网中,这样,他们各自的NAT代理服务器将不允许他们进行直连.
                                    Server S
                                          |
                                          |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
            |                                                           |
            |                                                           |
         Client A                                              Client B
         
取而代之的方式是,两个客户端可以把服务器S当作信使来转发消息.比如,为了将消息发送到B,A先发送一条信息给服务器S,服务器S再利用初始化时已经建立的连接,将信息转发给B. 

这个方法的优势是:它适合于任何NAT包括Symmetric NAT.但是它的劣势也很明显:它将全面依赖并消耗服务器的资源和网络带宽.名为 TURN 的协议定义了一个利用转发技术进行可靠通信的模型.          
5.2 反向连接 
这里介绍第二种技术,但是它只能在通信的两端只有一端处于NAT之后的情况下.举例来说,假设客户端A处于NAT之后,而客户端B有一个公网IP地址,如下图所示.       
  
                                   Server S
                            18.181.0.31:1235
                                         |
                                         |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                     |
    155.99.25.11:62000                                    |
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                               138.76.29.7:1234


现在我们假设客户端B将会与客户端A初始化一个端对端连接会话.B将首先试图连接A的一个地址---客户端A认为是它自己的地址 10.0.0.1:1234或者是从服务器S观察到的地址155.99.25.11:62000.然而不论是连接哪一个,都不可能成功.第一种情况:试图 直接连到10.0.0.1肯定会失败,因为10.0.0.1根本就不是一个可以在公网上路由的IP地址;第二种情况,从B传来的请求将能够到达端口NAT A的端口62000,但NAT A却会拒绝这个连接请求,因为只有外出的连接才允许进入.    在所有的尝试都失败之后,客户端B就只能通过服务器S来请求A做一个"反向"连接到客户端B,客户端A将打开一个与客户端B通讯的连接(在B的公网IP地 址和端口号上).NAT A允许这个连接通过,因为这个连接起源于NAT A的内部,并且同时客户端B能够受这个连接因为B并不位于NAT之后. 

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

5.3 UDP打洞
第三种技术,也是这篇文章主要要介绍的,就是非常有名的"UDP打洞技术".这里将 考虑两种典型场景,来介绍连接的双方应用程序如何按照计划的进行通信的,第一种场景,我们假设两个客户端都处于不同的NAT之后;第二种场景,我们假设两 个客户端处于同一个NAT之后,但是它们彼此都不知道(他们在同一个NAT中).

5.3.1 处于不同NAT之后的客户端通信
我们假设 Client A和Client B都拥有自己的私有IP地址,并且都处在不同的NAT之后,端对端的程序运行于 CLIENT A,CLIENT B,S之间,并且它们都开放了UDP端口1234. CLIENT A和CLIENT B首先分别与S建立通信会话,这时NAT A把它自己的UDP端口62000分配给CLIENT A与S的会话,NAT B也把自己的UDP端口31000分配给CLIENT B与S的会话.如下图所示:
                                Server S
                            18.181.0.31:1234
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    155.99.25.11:62000                            138.76.29.7:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
     

假如这个时候 CLIENT A 想与 CLIENT B建立一条UDP通信直连,如果 CLIENT A只是简单的发送一个UDP信息到CLIENT B的公网地址
138.76.29.7:31000 的话,NAT B会不加考虑的将这个信息丢弃(除非NAT B是一个 full cone NAT),因为这个UDP信息中所包含的地址信息,与CLIENT B和服务器S建立连接时存储在NAT B中的服务器S的地址信息不符.同样的,CLIENT B如果做同样的事情,发送的UDP信息也会被NAT A丢弃.

假如 CLIENT A 开始发送一个UDP信息到CLIENT B的公网地址上,与此同时,他又通过S中转发送了一个邀请信息给CLIENT B,请求CLIENT B也给CLIENT A发送一个UDP信息到 CLIENT A的公网地址上.这时CLIENT A向CLIENT B的公网IP(138.76.29.7:31000)发送的信息导致 NAT A 打开一个处于CLIENT A的私有地址和CLIENT B的公网地址之间的新的通信会话,与此同时NAT B也打开了一个处于CLIENT B的私有地址和CLIENT A的公网地址(155.99.25.11:62000)之间的新的通信会话.一旦这个新的UDP会话各自向对方打开了,CLIENT A和CLIENT B之间就可以直接通信,而无需S来牵线搭桥了.这就是所谓的打洞技术.
一旦这种处于NAT之后的端对端的直连建立之后,连接的双方可以轮流担任对方的"媒人",把对方介绍给其他的客户端,这样就极大的降低了服务器S的工作量.

5.3.2 处于相同NAT之后的客户端通信
我们假设 Client A和Client B都拥有自己的私有IP地址,并且都处在相同的NAT之后,端对端的程序运行于 CLIENT A,CLIENT B,S之间,
CLIENT A和CLIENT B分别与S建立通信会话,经过NAT转换后,A的公网端口被映射为62000,B的公网端口映射为62001.如下图所示:

                               Server S
                            18.181.0.31:1234
                                   |
                                   |
                                  NAT
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
            +----------------------+----------------------+
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
     
根 据前面介绍的"打洞"技术,CLIENT A将发送一个UDP信息到CLIENT B的公网地址上,数据包源端为(10.0.0.1:124),目的端为(155.99.25.11:62001).该数据包能否被B收到,取决于当前的 NAT是否支持"发夹"转换(hairpin转换,也就是同一台设备不同端口之间的UDP数据包能否到达).
首先,支持"发夹"转换的NAT设备还远没有支持"打洞"技术的NAT设备多,其次,即使NAT设备支持"发夹"转换,在这种情况下也应该通过网内端到端实现,而不是将数据包无谓 地经过NAT设备,这是一种对资源的浪费.

5.3.3 一般"打洞"过程
综合上面介绍的客户端处于不同NAT之后和处于同一NAT之后,我们说下一般的"打洞"过程.
1. 打洞技术假定客户端A和B可以与公网内的已知的集中服务器建立UDP连接(可以互发UDP数据包).当一个客户端在S上登陆的时候,服务器记录下该客户端 的两个endpoints(IP地址,UDP端口),一个是该客户端确信自己是通过该ip和端口与服务器S进行通信的,另一个是服务器S记录下的由服务 器"观察"到的该客户端实际与自己通信所使用的ip和端口.我们可以把前一个endpoint看作是客户端的内网ip和端口,把后一个endpoint看 作是客户端的内网ip和端口经过NAT转换后的公网ip和端口.服务器可以从客户端的登陆消息的消息体中得到该客户端的内网endpoint相关信息,可 以通过对登陆消息的IP或UDP头得到该客户端的公网endpoint.
2.假设Client A想向B发起连接,于是A向服务器S发送消息,请求S帮助建立与B的UDP连接.这时,S将B的公网和内网的endpoint发给A.可知,A与B通过与S的一次通信就可以知道对方的公网和内网的endpoint.    
3.Client A通过B的内网endpoint发送UDP数据包.针对5.3.2节问题的解决方案.如果B和A在同一NAT后,则很快收到响应.如果B和A不在同一NAT后,则超时.
4.Client A通过B的外网endpoint发送UDP数据包.
回 到5.3.1节介绍的具体方法.CLIENT A发出UDP包(10.0.0.1:1234,138.76.29.7:31000),经NAT A转换为(155.99.25.11:62000,138.76.29.7:31000),经NAT B转换为(155.99.25.11:62000,10.1.1.3:1234).如果在此数据包到达NAT B前,B发送过UDP包到A的公网endpoint,则NAT B允许此包到达B机.在5.3.2节下,A发出UDP包(10.0.0.1:1234,155.99.25.11:62001),NAT 先转换为(155.99.25.11:62000,155.99.25.11:62001),再转换为 (155.99.25.11:62000,10.1.1.3:1234).
5.步骤3,4发送的数据包是为了"打洞",打洞成功后,就进入真正的P2P传输了.
还有一种情况,B和A不在同一NAT后,C和A在同一NAT后,且B和C的内网endpoint一致,这个时候A从S拿到的目的端应该有2个.所以针对3,4步骤取先有回应的目的端不可取,应该先做步骤3,有回应直接到步骤5,没有回应到步骤4.

5.3.4 客户端分别处于多层NAT之后
在有些网络拓扑中就存在多层NAT设备,让我们来看看下图这种情况:
假如 NAT X 是由 Internet服务供应商(ISP)配置的一个大型NAT,它使用少量的公网IP地址来为一些客户群提供服务,NAT A和NAT B则是
为ISP的两个客户群所配置的小一点的独立NAT网关,它们为各自客户群的私人家庭网络提供IP地址.只有Server S和NAT X拥有公网固定IP地址,而NAT A 和 NAT B所拥有的"公网"IP地址对于ISP的寻址域来说则实际上"私有"的.
                                Server S
                            18.181.0.31:1234
                                   |
                                   |
                                 NAT X
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    192.168.1.1:30000                             192.168.1.2:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
现在让我们假设Client A和Client B想要建立一条端对端 的UDP直连.Client A和 Client B只知道Server S记录的他们真正的公网地址
155.99.25.11:62000和155.99.25.11:62001,而且他们只能通过这个公网地址建立连接,即NAT X必须得支持"loopback translation"(也称hairpin转换)才行.

Client A和 Client B也知道对方内网地址10.0.0.1:1234和10.1.1.3:1234,毫无疑问,通过内网地址是建立不了连接的.Client A和 Client B并不知道对方NAT B和NAT A的地址192.168.1.2:31000和192.168.1.1:30000,即便假设我们通过某种途径得知了这些地址,还是不能够保证这样就能进 行通话了,因为这些地址是由ISP的私有寻址域分配的,可能会与私有域所分配的其他无关客户端地址相冲突.

5.3.5 UDP在空闲状态下的超时问题
由于UDP转换协议提供的"洞"不是绝对可靠的,多数NAT 设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由"打洞"操作打出来的"洞",做为应用程序来讲如 果想要做到与设备无关,就最好在穿越NAT的以后设定一个穿越的有效期.很遗憾目前没有标准有效期,这个有效期与NAT设备内部的配置有关,最短的只有 20秒左右.在这个有效期内,即使没有p2p数据包需要传输,应用程序为了维持该"洞"可以正常工作,也必须向对方发送"打洞"维持包.这个维持包是需要 双方应用都发送的,只有一方发送不会维持另一方的session正常工作.除了频繁发送"打洞"维持包以外,还有一个方法就是在当前的"洞"有效期过期之 前,p2p客户端双方重新"打洞",丢弃原有的"洞",这也不失为一个有效的方法.

5.4 . UPD端口号预言
在使用"UDP打洞技术"时有一点必须要注意:它只能在双方的NAT都是cone NAT时才能正常工作.这些NAT在使用时保持着端口的
绑定----[私有IP,私有UDP端口]对和[公网IP,公网UDP端口]对的一一对应.
如果像 symmetricNAT那样给每个新的会话都分配一个新的公网端口,那么UDP应用程序想要与其他外部客户端进行通话,就无法重复使用已经建立好的通信转换.

让我们来考虑这样一种情况,有两个客户端A和B,他们都藏在不同的Symmetric NAT后面,他们都开放了一个UDP连接给具有固定IP的Server S.如下图:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
       Symmetric NAT A                               Symmetric NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
              |                                                           |
              |                                                           |
           Client A                                             Client B
        10.0.0.1:1234                                 10.1.1.3:1234
       
NAT A 分配了它自己的UDP端口62000,用来保持客户端A与服务器S的通信会话,NAT B 也分配了31000端口,用来保持客户端B与
服务器S的通信会话.通过与服务器S的对话,客户端A和客户端B都相互知道了对方所映射的真实IP和端口.
客户端A发送一条UDP消息到 138.76.29.7:31001(请注意到端口号的增加),同时客户端B发送一条UDP消息到 155.99.25.11:62001.
如果NAT A 和NAT B继续分配端口给新的会话,并且从A-S和B-S的会话时间消耗得并不多的话,那么一条处于客户端A和客户端B之间的双向会话通道就建立了.
客户端A发出的消息送达B导致了NAT A打开了一个新的会话,并且我们希望 NAT A将会指派62001端口给这个新的会话,因为62001是
继62000后,NAT会自动指派给 从服务器S到客户端A之间的新会话的端口号;类似的,客户端B发出的消息送达A导致了NAT B打开了
一个新的会话,并且我们希望 NAT B将会指派31001这个端口给新的会话;如果两个客户端都正确的猜测到了对方新会话被指派的端口号,
那么这个客户端A----客户端B的双向连接就被打通了.其结果如下图所示:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
            NAT A                                                  NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
   A-B 155.99.25.11:62001                        B-A 138.76.29.7:31001
              |                                                           |
              |                                                           |
           Client A                                              Client B
        10.0.0.1:1234                                 10.1.1.3:1234
明 显的,有许多因素会导致这个方法失败:如果这个预言的新端口(62001和31001) 恰好已经被一个不相关的会话所使用,那么NAT就会跳过这个端口号,这个连接就会宣告失败;如果两个NAT有时或者总是不按照顺序来生成新的端口号,那么 这个方法也是行不通的.如果隐藏在NAT A后的一个不同的客户端X(或者在NAT B后)打开了一个新的"外出"UDP 连接,并且无论这个连接的目的如何,只要这个动作发生在客户端A建立了与服务器S的连接之后,客户端A与客户端B建立连接之前,那么这个无关的客户端X 就会趁人不备地"偷"到这个我们渴望分配的端口.所以,这个方法变得如此脆弱而且不堪一击,只要任何一个NAT方包含以上碰到的问题,这个方法都不会奏 效. 
 最后,如果P2P的一方处在两级或者两级以上的NAT下面,并且这些NAT接近这个客户端是symmetric的话,端口号预言是无效的.
因此,并不推荐使用这个方法来写新的P2P应用程序. 


5.5 同时开放TCP连接

这里有一种方法能够在某种情况下建立一个穿透NAT的端对端TCP直连.我们知道,绝大多数的TCP会话的建立,都是通过一端先发送一个SYN包开 始,另一方则回发一个SYN-ACK包的过程.然而,这里确实存在另外一种情况,就是P2P的双方各自同时地发出一个SYN包到对方的公网地址上,然后各 自都单独地返回一个ACK响应来建立一个TCP会话.这个过程被称之为"Simultaneous open"(同时开放连接).

如果一个NAT接收到一个来自私有网络外面的TCP SYN包,这个包想发起一个"引入"的TCP连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN 包,或者回送一个TCP RST(connection reset,重建连接)包给请求方.但是,有一种情况,当这个接收到的SYN包中的源IP地址和端口,目标IP地址和端口都与NAT登记的一个已经激活的 TCP会话中的地址信息相符时,NAT将会放行这个SYN 包,让它进入NAT内部.特别要指出,如果NAT恰好看到一个刚刚发送出去的一个SYN包也和上面接收到的SYN包中的地址信息相符合的话,那么NAT将 会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部.

如果Client A和Client B能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个"外出"的TCP连接,并在对方的 SYN包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就成功地建立了.
不幸的是,这个诡计比5.4节所讲的UDP端口预言更容易被粉碎,并且对时间的敏感性的依赖更多.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多