@zhongdao
2018-02-22T10:12:21.000000Z
字数 6485
阅读 1879
未分类
当实现一个P2P网络时,每个节点既是客户端,有时服务端,就需要对外提供服务,然而现在随着IP地址的减少,家庭用或者办公用的大部分可以上网的节点都处于内网之内,需要通过网关或者路由器借由NAT技术来对外提供服务,所以需要了解相关的概念和实现技术。
然而网上NAT的资料很多,能分类讲得让人明白清晰的不多,本文参考相关的文章,然后简明扼要格式清晰地呈现出来,方便自己日后查询和其他人了解。
NAT: 网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就是为了能够地址重用。NAT从历史发展上分为两大类,基本的NAT和NAPT(Network Address/Port Translator)。
通过NAT,子网内的计算机向外连结是很容易的(NAT相当于透明的,子网内的和外网的计算机不用知道NAT的情况)。
但是如果外部的计算机想访问子网内的计算机就比较困难了(而这正是P2P所需要的)。
如果想从外部发送一个数据报给内网的计算机有什么办法呢?首先,我们必须在内网的NAT上打上一个“洞”(也就是前面我们说的在NAT上建立一个Session),这个洞不能由外部来打,只能由内网内的主机来打。
打洞也叫作穿透,穿透的整体过程基本就是这样。
A NAT_A 分别是内网主机,对应的网关。
NAT_B B 分别是另一个内网主机,对应的网关。
A往B发送数据的唯一阻碍就是NET_B,所以想要成功发送数据,必须把NET_B穿一个洞,A是无法完成这项工作的,所以就得让B完成这个打洞操作,也就是让B往A发送数据,这样NET_B就会误以为A发送的数据是上次会话的一部分从而不予阻拦。
为什么不能直接打洞,涉及到了NAT相关的一些概念,再下面列出。
为了保存建立的映射,路由器或者NAT设备上要保存相应的会话session,并不是长期存在的,不同的路由器session储存时间不同,短的有的几十秒,长的可能有的几分种。要想维持这个session可以通过心跳包来维持,比如10秒向服务器发送一个心跳包。
实现内网穿透的软件有很多,例如Ngrok, Frp, 花生壳,NAT123等等,实现的原理是不变的。
基本的NAT可以参看RFC 1631
基本的NAT会改变IP包中的原IP地址,但是不会改变IP包中的端口)
基本上这种类型的NAT设备已经很少了。
另外一种NAT叫做NAPT,从名称上我们也可以看得出,NAPT不但会改变经过这个NAT设备的IP数据报的IP地址,还会改变IP数据报的TCP/UDP端口。
目前绝大部分的路由器应该都是支持的。
NAPT只解决了内网主机在没有公网IP的情况下如何访问公网主机的问题,但是并不能解决公网主机如何主动向内网主机发起请求的问题。
对于NAPT,又分了两个大的类型。
在网关时,两个不同session但端口号不同,这种形式会让很多p2p软件失灵。
在网关时,两个不同session但端口号相同,
目前绝大多数属于这种。Cone NAT又分了3种类型:
(1)全圆锥( Full Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。任何一个外部主机均可通过该映射发送IP包到该内部主机。
(2)限制性圆锥(Restricted Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。但是,只有当内部主机先给IP地址为X的外部主机发送IP包,该外部主机才能向该内部主机发送IP包。
(3)端口限制性圆锥( Port Restricted Cone) :端口限制性圆锥与限制性圆锥类似,只是多了端口号的限制,即只有内部主机先向IP地址为X,端口号为P的外部主机发送1个IP包,该外部主机才能够把源端口号为P的IP包发送给该内部主机。
NAT穿透/打洞的网络架构如上图所示,需要有个中间服务器让两者建立通讯。
两内网用户要实现通过各自网关的直接呼叫,需要以下过程:
1、 客户机A1、B1顺利通过格子网关访问服务器C ,均没有问题(类似于登录)
2、 服务器C保存了 A1、B1各自在其网关的信息(1.2.3.4:62000、1.2.3.5:31000)没有问题。并可将该信息告知A1、B2。
3、 此时A1发送给B1网关的1.2.3.5:31000是否会被B1收到?答案是基本上不行(除非Y1设置为完全圆锥型,但这种设置非常少),因为Y1上检测到其存活的会话中没有一个的目的IP或端口于1.2.3.4:62000有关而将数据包全部丢弃!
4、 此时要实现A1、B1通过X1、Y1来互访,需要服务器C告诉它们各自在自己的网关上建立“UDP隧道”,即命令A1发送一个 192.168.0.8:4000——1.2.3.5:31000的数据报,B1发送一个192.168.1.8:4000——1.2.3.4:62000的数据报,UDP形式,这样X1、Y1上均存在了IP端口相同的两个不同会话(很显然,这要求网关为Cone NAT型,否则,对称型Symmetric NAT设置网关将导致对不同会话开启了不同端口,而该端口无法为服务器和对方所知,也就没有意义)。
5、 此时A1发给Y1,或者B1发给X1的数据报将不会被丢弃且正确的被对方收到.
P2P可实现的条件需要:
1、 中间服务器保存信息、并能发出建立UDP隧道的命令
2、 网关均要求为Cone NAT类型。Symmetric NAT不适合。
3、 完全圆锥型网关可以无需建立udp隧道,但这种情况非常少,要求双方均为这种类型网关的更少。
4、 假如X1网关为Symmetric NAT, Y1为Address Restricted Cone NAT 或Full Cone NAT型网关,各自建立隧道后,A1可通过X1发送数据报给Y1到B1(因为Y1最多只进行IP级别的甄别),但B2发送给X1的将会被丢弃(因为发送来的数据报中端口与X1上存在会话的端口不一致,虽然IP地址一致),所以同样没有什么意义。
5、 假如双方均为Symmetric NAT的情形,新开了端口,对方可以在不知道的情况下尝试猜解,也可以达到目的,但这种情形成功率很低,且带来额外的系统开支,不是个好的解决办法。
6、 不同网关型设置的差异在于,对内会采用替换IP的方式、使用不同端口不同会话的方式,使用相同端口不同会话的方式;对外会采用什么都不限制、限制IP地址、限制IP地址及端口。
7、 这里还没有考虑同一内网不同用户同时访问同一服务器的情形,如果此时网关采用AddressRestricted Cone NAT 或Full Cone NAT型,有可能导致不同用户客户端可收到别人的数据包,这显然是不合适的。
是TCP和UDP在打洞上却有点不同。这是因为伯克利socket(标准socket规范)的API造成的。UDP的socket允许多个socket绑定到同一个本地端口,而TCP的socket则不允许。
假设现在有内网客户端A和内网客户端B,有公网服务端S。
如果A和B想要进行UDP通信,则必须穿透双方的NAT路由。假设为NAT-A和NAT-B。
A发送数据包到公网S,B发送数据包到公网S,则S分别得到了A和B的公网IP,
S也和A B 分别建立了会话,由S发到NAT-A的数据包会被NAT-A直接转发给A,
由S发到NAT-B的数据包会被NAT-B直接转发给B,除了S发出的数据包之外的则会被丢弃。所以:现在A B 都能分别和S进行全双工通讯了,但是A B之间还不能直接通讯。
解决办法是:A向B的公网IP发送一个数据包,则NAT-A能接收来自NAT-B的数据包 并转发给A了(即B现在能访问A了);再由S命令B向A的公网IP发送一个数据包,则NAT-B能接收来自NAT-A的数据包并转发给B了(即A现在能访问B了)。
UDP打洞的过程大致如此:
1、双方都通过UDP与服务器通讯后,网关默认就是做了一个外网IP和端口号 与你内网IP与端口号的映射,这个无需设置的,服务器也不需要知道客户的真正内网IP
2、用户A先通过服务器知道用户B的外网地址与端口
3、用户A向用户B的外网地址与端口发送消息,
4、在这一次发送中,用户B的网关会拒收这条消息,因为它的映射中并没有这条规则。
5、但是用户A的网关就会增加了一条允许规则,允许接收从B发送过来的消息
6、服务器要求用户B发送一个消息到用户A的外网IP与端口号
7、用户B发送一条消息,这时用户A就可以接收到B的消息,而且网关B也增加了允许规则
8、之后,由于网关A与网关B都增加了允许规则,所以A与B都可以向对方的外网IP和端口号发送消息。
tcp打洞也需要NAT设备支持才行。
tcp的打洞流程和udp的基本一样,但tcp的api决定了tcp打洞的实现过程和udp不一样。
tcp按cs方式工作,一个端口只能用来connect或listen,所以需要使用端口重用,才能利用本地nat的端口映射关系。(设置SO_REUSEADDR,在支持SO_REUSEPORT的系统上,要设置这两个参数。)
The following NAT traversal techniques are available:
部分常用的技术说明:
ALG(应用层网关):
应用层网关(ALG , Application Layer Gateway )是指能识别指定协议(如H.323和SIP等)的设备。在网络中增加了ALG设备可以很好地配合NAT完成应用协议消息中的地址字段翻译。NAT和NAPT只能对IP报文的头部地址和TCPILIDP头部的端口信息进行转换,对于报文的数据部分可能包含IP地址或端口信息的特殊协议(如H.323,SIP.MGCP等),则无法实现有效的转换,而许多应用中需要对数据包负载中的数据进行分析转换。
它可以是一个设备或插件。
UpnP:它是让网关设备在进行工作时寻找一个全球共享的可路由IP来作为通道,这样避免端口造成的影响。要求设备支持且开启upnp功能,但大部分时候,这些功能处于安全考虑,是被关闭的。即时开启,实际应用效果还没经过测试。
STUN(Simple Traversalof UDP Through Network):这种方式即是类似于我们上面举例中服务器C的处理方式。也是目前普遍采用的方式。但具体实现要比我们描述的复杂许多,光是做网关Nat类型判断就由许多工作,RFC3489中详细描述了。 如果采用STLFN方式,不但无需改动NATI防火墙,而且能够很好地适应多个NAT串联的网络环境但STUN的局限性在于需要终端应用程序支持STUN客户端的功能;同时STUN不支持TCP连接的穿越;另外STUN方案不支持业务对防火墙的穿越以及对称NAT(Symmetric NAT )的穿越。
TURN(Traveral Using Relay NAT):即采用中继的NAT穿越,该方式是将所有的数据交换都经由服务器来完成,这样NAT将没有障碍,但服务器的负载、丢包、延迟性就是很大的问题。目前很多游戏均采用该方式避开NAT的问题。这种方式不叫p2p。该方式与STUN在技术机制上是相同的,也来用了服务器/客户端模式,通过访问处于公网中的服务器得到内网中主机所分配得到的公共地址,进而进行通信。不同的只是在STUN方式中得到的公共地址是由NAT分配的,而在TURN模式中内网主机得到的公共地址是由TURN服务器分配的。TURN通过分配TURN Server的地址和端口作为客户端对外的接收地址和端口,即内网主机发出的报文都要经过TURN Server进行中继转发,这种方式除了具有STUN方式的优点外,还解决了STAN应用无法穿越对称式NAT (Symmetric NAT ),同时支持基一于TCP的应用.然而,由于服务器的存在,当数据量变得非常大时,也会造成网络瓶颈,而且由于所有报文都必须经过TURN服务器转发,增大了报文的延迟和丢包的可能性,会给Qo5带来问题。目前网络流行的是将TURN与STUN结合在一起使用。
ICE(Interactive Connectivity Establishment):ICE本身是一种方法,它综合运用STUN、 TURN等协议来提供一个通用的解决方案,使之在最适合的情况下工作,以弥补单独使用其中任何一种所带来的固有缺陷。是对上述各种技术的综合,但明显带来了复杂性。
NAT原理与NAT穿透
http://blog.csdn.net/byxdaz/article/details/52785697
内网穿透工具的原理与开发实战
https://zhuanlan.zhihu.com/p/30351943
p2p网络中的NAT穿透技术----常见NAT穿越解决方案
http://blog.csdn.net/cllzw/article/details/46438257
NAT traversal
https://en.wikipedia.org/wiki/NAT_traversal