转: NAT 穿透
- 本文作者: 贝克街的流浪猫
- 本文链接:https://www.beikejiedeliulangmao.top/JNI-%E8%B0%83%E8%AF%95%E6%8A%80%E6%9C%AF/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 创作声明: 本文基于上述所有参考内容进行创作,其中可能涉及复制、修改或者转换,图片均来自网络,如有侵权请联系我,我会第一时间进行删除。
- 原文地址:NAT穿透
引言
之前在WebRTC简介中,我们简单地讲了WebRTC中是如何进行NAT穿透的,但是我觉得当时讲的还不够详细,所以打算通过此文更加深入的总结NAT穿透技术,其中涉及到NAT,STUN协议,TURN协议,ICE协议。
NAT
NAT概念
在计算机网络中,网络地址转换(Network Address Translation,缩写为NAT),也叫做网络掩蔽或者IP掩蔽(IP masquerading),是一种在IP数据包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术。这种技术被普遍使用在有多台主机但只通过一个公有IP地址访问因特网的私有网络中。1990年代中期,NAT是作为一种解决IPv4地址短缺以避免保留IP地址困难的方案而流行起来的。
NAPT概念
NAT虽然名为网络地址转换,但是常见的工作模式实际上是NAPT(网络地址端口转换)。这种方式支持端口的映射,并允许多台主机共享一个公网IP地址。 支持端口转换的NAT又可以分为两类:源地址转换和目的地址转换。前一种情形下发起连接的计算机的IP地址将会被重写,使得内网主机发出的数据包能够到达外网主机。后一种情况下被连接计算机的IP地址将被重写,使得外网主机发出的数据包能够到达内网主机。实际上,以上两种方式通常会一起被使用以支持双向通信。NAPT维护一个带有IP以及端口号的NAT表,结构如下:
内网IP&Port | 外网IP&Port |
---|---|
192.168.1.55:5566 | 219.152.168.222:9200 |
192.168.1.59:80 | 219.152.168.222:9201 |
192.168.1.59:4465 | 219.152.168.222:9202 |
不同类型的NAT
完全圆锥形NAT
Full cone NAT(即一对一NAT): 一旦一个内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自(iAddr:iPort)的包都经由(eAddr:ePort)向外发送。任意外部主机都能通过给(eAddr:ePort)发包到达(iAddr:iPort)。
总结:同一个内部地址(iAddr:iPort)只会映射相同的外部地址(eAddr:ePort),映射完成后,目标IP端口都无限制。
受限圆锥形NAT
Address-Restricted cone NAT:内部客户端必须首先发送数据包到对方(IP=X.X.X.X),然后才能接收来自(IP=X.X.X.X)的数据包。在限制方面,唯一的要求是数据包是来自(IP=X.X.X.X)。内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自(iAddr:iPort)的包都经由(eAddr:ePort)向外发送。外部主机(hostAddr:any)能通过给(eAddr:ePort)发包到达(iAddr:iPort)。注:any指外部主机源端口不受限制,只有发给NAT转换地址(eAddr:ePort)的数据包才被放行
总结:同一个内部地址(iAddr:iPort)只会映射相同的外部地址(eAddr:ePort),映射完成后,必须先发一个包给目标,然后才能收到目标回发的包,目标端口无限制。
端口受限圆锥形NAT
Port-Restricted cone NAT:类似受限制锥形NAT(Restricted cone NAT),但是还有端口限制。一旦一个内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自(iAddr:iPort)的包都经由(eAddr:ePort)向外发送。在受限圆锥型NAT基础上增加了外部主机源端口必须是固定的。
总结:同一个内部地址(iAddr:iPort)只会映射相同的外部地址(eAddr:ePort),映射完成后,必须先发一个包给目标(tAddr:tPort),然后才能收到目标(tAddr:tPort)回发的包,有目标端口限制。
对称NAT
Symmetric NAT:每一个来自相同内部IP与端口,到一个特定目的地地址和端口的请求,都映射到一个独特的外部IP地址和端口。同一内部IP与端口发到不同的目的地和端口的信息包,都使用不同的映射。只有曾经收到过内部主机数据的外部主机,才能够把数据包发回。
总结:同一个内部地址(iAddr:iPort)对不同目标(tAddr1:tPort1)(tAddr2:tPort2)会映射出不同的外部地址(eAddr1:ePort1)(eAddr2:ePort2),必须先发一个包给目标(iAddr:iPort)->(eAddr1:ePort1)->(tAddr1:tPort1),才能收到回发的包(tAddr1:tPort1)->(eAddr1:ePort1)->(iAddr:iPort1)。
简单的NAT穿透思路
期待的效果
NAT穿透,简单地讲就是要让处于不同NAT网络下的两个节点(Peer)建立直接连接,只知道自己的内网地址是肯定不行的,他们需要知道对方的公网IP和端口,然后双方互相向对方发送数据包,从而建立起连接。整个流程可以看做两个关键步骤:
- 发现自己的公网IP和Port
- 将自己的IP和Port共享给对方
其中,第二步,我们可以简单地通过一个第三方服务器来交换双方的IP和Port,但是第一步就比较困难,我们不妨根据不同类型的NAT的特点,分别看看在不同的NAT类型下,怎样才能拿到一个可供通讯的公网IP和Port。
不同NAT类型下的方案
注意:下面方案介绍时,都假设要通讯的双方处于同一NAT类型下,不同类型下的NAT穿透方案,我相信各位读者在理解各个方案之后,自然就能类推出来。
完全圆锥形NAT
前面提过,完全锥形NAT下的节点(Client)只要建立起(iAddr:iPort)<->(eAddr:ePort)的映射关系之后,就能收到任何IP和端口发送的数据。所以基本思路如下:
- 搭建一个具有公网IP和Port的服务(Server 1)
- (Client)发送一个数据包给这个公网服务(Server1)
- (Service1)通过解析IP协议包,就能得知(Client)的公网地址(eAddr:ePort)
- (Server1)将该公网地址(eAddr:ePort)回传给(Client)
- 两个不同的节点Client1和Client2通过第三方服务器交换公网地址(eAddr1:ePort1)(eAddr2:ePort2)
- 自由地进行通讯
受限圆锥形NAT
受限圆锥形NAT获取自己公网地址的方式和上一步完全一致,但是因为受限圆锥形NAT需要先发送一个数据包之后才能收到目标传来的包。所以基本思路如下:
- 搭建一个具有公网IP和Port的服务(Server 1)
- (Client)发送一个数据包给这个公网服务(Server1)
- (Service1)通过解析IP协议包,就能得知(Client)的公网地址(eAddr:ePort)
- (Server1)将该公网地址(eAddr:ePort)回传给(Client)
- 两个不同的节点Client1和Client2通过第三方服务器交换公网地址(eAddr1:ePort1)(eAddr2:ePort2)
- 连接双方Client1和Client2先向对方地址发送一个数据包
- 自由地进行通讯
端口受限圆锥形NAT
和受限圆锥形NAT完全一致。
对称NAT
因为对称型NAT对不同的目标(Server1)(Server2)会映射出不同的外网地址(eAddr1:ePort1)(eAddr2:ePort2),也就是说,我们通过前面用到的公网服务(Server1),获取的公网地址(eAddr1:ePort1),没办法共享给别人使用,他只能用来和(Server1)通讯。那我们不妨,将(Server1)作为一个代理,如果其他人想和(Client)进行通讯,可以通过(Server1)转发。基于这个思路,我们的做法如下:
- 搭建一个具有公网IP和Port的服务(Server 1)
- (Client)发送一个数据包给这个公网服务(Server1)
- (Service1)通过解析IP协议包,就能得知(Client)的公网地址(eAddr:ePort)
- (Server1)保存(Client)的公网地址(eAddr:ePort),并生成一个代理地址(pAddr:pPort)
- 凡是代理地址(pAddr:pPort)收到的数据都转发给Client的公网地址(eAddr:ePort)
- (Service1)将Client的代理地址(pAddr:pPort)告知Client
- 两个不同的节点Client1和Client2通过第三方服务器交换代理地址(pAddr1:pPort1)(pAddr2:pPort2)
- 连接双方Client1和Client2先向对方的代理地址(pAddr1:pPort1)(pAddr2:pPort2)发送一个数据包
- 双方通过代理地址(pAddr1:pPort1)(pAddr2:pPort2)进行通讯
理解了各个NAT类型下的解决简单方案之后,再来看STUN,TURN和ICE,你就会发现这三个协议负责的内容和上述的简单方案非常相似。
STUN
STUN概念
STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间创建UDP通信。 是不是感觉STUN干的活儿非常熟悉,没错,STUN负责的就是前面提到的Server1所做的内容。只不过STUN协议还具备另一个关键功能,就是确认Client的NAT类型(网络类型)。至于STUN是怎么获取Client公网地址这部分内容,我就不赘述了。接下来我会介绍一下STUN是如何确定Client的NAT类型(网络类型)的。
NAT类型发现算法
一旦路径通过红色箱子的终点时,UDP的直接沟通是没有可能性的。一旦通过黄色或是绿色的箱子,就有连线的可能。 上图中的大部分内容我相信各位童鞋应该都能看懂,其中关于"Public IP is link's IP?"这一步我简单的讲一下: 1. Client向STUN服务器发送请求 2. STUN将检测出的Client外网地址(eAddr:ePort)回传 3. Client比对创建Socket时使用的地址(localAddr:localPort)和(eAddr:ePort)是否一致,如果完全相同,则Client在NAT后。
TURN
TURN(全名Traversal Using Relay NAT),是一种数据传输协议(data-transfer protocol)。允许在TCP或UDP的连接跨越NAT或防火墙。 TURN是一个client-server协议。TURN的NAT穿透方法与STUN类似,都是通过获取应用层中的公有地址达到NAT穿透。但实现TURN client的终端必须在通信开始前与TURN server进行交互,并要求TURN server产生"relay port",也就是relayed-transport-address。这时TURN server会创建peer,即远程端点(remote endpoints),开始进行中继(relay)的动作,TURN client利用relay port将数据发送至peer,再由peer转传到另一方的TURN client。
ICE
交互式连接创建(Interactive Connectivity Establishment),一种综合性的NAT穿越的技术。 交互式连接创建是由IETF的MMUSIC工作组开发出来的一种framework,可集成各种NAT穿透技术,如STUN、TURN(Traversal Using Relay NAT,中继NAT实现的穿透)、RSIP(Realm Specific IP,特定域IP)等。该framework可以让SIP的客户端利用各种NAT穿透方式打穿远程的防火墙。
参考内容
[1]https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2
[2]https://zh.wikipedia.org/wiki/STUN
[3]https://zh.wikipedia.org/wiki/TURN
[4]https://zh.wikipedia.org/wiki/%E4%BA%92%E5%8B%95%E5%BC%8F%E9%80%A3%E6%8E%A5%E5%BB%BA%E7%AB%8B