NAT对待UDP的实现方式有4种,分别以下:git
Full Cone NAT: 彻底锥形NAT,全部从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号,而且任何一个外网主机均可以经过这个映射的外网IP和端口号向这台内网主机发送包。github
Restricted Cone NAT: 限制锥形NAT,它也是全部从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号。与彻底锥形不一样的是,外网主机只可以向先前已经向它发送过数据包的内网主机发送包。算法
Port Restricted Cone NAT: 端口限制锥形NAT,与限制锥形NAT很类似,只不过它包括端口号。也就是说,一台IP地址X和端口P的外网主机想给内网主机发送包,必须是这台内网主机先前已经给这个IP地址X和端口P发送过数据包。安全
Symmetric NAT: 对称NAT,全部从同一个内网IP和端口号发送到一个特定的目的IP和端口号的请求,都会被映射到同一个IP和端口号。若是同一台主机使用相同的源地址和端口号发送包,可是发往不一样的目的地,NAT将会使用不一样的映射。此外,只有收到数据的外网主机才能够反过来向内网主机发送包。服务器
STUN是一种网络协议,它容许位于NAT(或多重NAT)后的客户端找出本身的公网地址,查出本身位于哪一种类型的NAT以后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器以后的主机之间创建UDP通讯。该协议由RFC 5389定义。网络
STUN由三部分组成:app
STUN客户端;socket
STUN服务器端;ide
NAT路由器。oop
STUN服务端部署在一台有着两个公网IP的服务器上,大概的结构参考下图。STUN客户端经过向服务器端发送不一样的消息类型,根据服务器端不一样的响应来作出相应的判断,一旦客户端得知了Internet端的UDP端口,通讯就能够开始了。
Test1:STUN Client经过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(没有设置任何属性)。STUN Server收到该请求后,经过端口{IP-S1:Port-S1}把它所看到的STUN Client的IP和端口{IP-M1,Port-M1}做为Binding Response的内容回送给STUN Client。
Test1#2:STUN Client经过端口{IP-C1:Port-C1}向STUN Server{IP-S2:Port-S2}发送一个Binding Request(没有设置任何属性)。STUN Server收到该请求后,经过端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M1#2,Port-M1#2}做为Binding Response的内容回送给STUN Client。
Test2:STUN Client经过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(设置了Change IP和Change Port属性)。STUN Server收到该请求后,经过端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M2,Port-M2}做为Binding Response的内容回送给STUN Client。
Test3:STUN Client经过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(设置了Change Port属性)。STUN Server收到该请求后,经过端口{IP-S1:Port-S2}把它所看到的STUN Client的IP和端口{IP-M3,Port-M3}做为Binding Response的内容回送给STUN Client。
1)公网IP和Port; 2)防火墙是否设置; 3)客户端是否在NAT以后,及所处的NAT的类型。
A:公开的互联网IP:主机拥有公网IP,而且没有防火墙,可自由与外部通讯;
B:彻底锥形NAT;
C:受限制锥形NAT;
D:端口受限制形NAT;
E:对称型UDP防火墙:主机出口处没有NAT设备,但有防火墙,且防火墙规则以下:从主机UDP端口A发出的数据包保持源地址,但只有从以前该主机发出包的目的IP/PORT发出到该主机端口A的包才能经过防火墙;
F:对称型NAT;
G:防火墙限制UDP通讯。
客户端创建UDP socket,而后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器返回客户端的IP和Port,客户端发送请求后当即开始接受数据包。重复几回。
a)若是每次都超时收不到服务器的响应,则说明客户端没法进行UDP通讯,多是:G防火墙阻止UDP通讯
b)若是能收到回应,则把服务器返回的客户端的(IP:PORT)同(Local IP: Local Port)比较:若是彻底相同则客户端不在NAT后,这样的客户端是:A具备公网IP能够直接监听UDP端口接收数据进行通讯或者E。不然客户端在NAT后要作进一步的NAT类型检测(继续)。
STUN客户端向STUN服务器发送请求,要求服务器从其余IP和PORT向客户端回复包:
a)收不到服务器从其余IP地址的回复,认为包前被前置防火墙阻断,网络类型为E
b)收到则认为客户端处在一个开放的网络上,网络类型为A
客户端创建UDP socket而后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器用另外一对(IP-2,Port-2)响应客户端的请求往回发一个数据包,客户端发送请求后当即开始接受数据包。 重复这个过程若干次。
a)若是每次都超时,没法接受到服务器的回应,则说明客户端的NAT不是一个Full Cone NAT,具体类型有待下一步检测(继续)。
b)若是可以接受到服务器从(IP-2,Port-2)返回的应答UDP包,则说明客户端是一个Full Cone NAT,这样的客户端可以进行UDP-P2P通讯。
客户端创建UDP socket而后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器返回客户端的IP和Port, 客户端发送请求后当即开始接受数据包。 重复这个过程直到收到回应(必定可以收到,由于第一步保证了这个客户端能够进行UDP通讯)。用一样的方法用一个socket向服务器的(IP-2,Port-2)发送数据包要求服务器返回客户端的IP和Port。比较上面两个过程从服务器返回的客户端(IP,Port),若是两个过程返回的(IP,Port)有一对不一样则说明客户端为Symmetric NAT,这样的客户端没法进行UDP-P2P通讯(检测中止)由于对称型NAT,每次链接端口都不同,因此没法知道对称NAT的客户端,下一次会用什么端口。不然是Restricted Cone NAT,是否为Port Restricted Cone NAT有待检测(继续)。
客户端创建UDP socket而后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器用IP-1和一个不一样于Port-1的端口发送一个UDP 数据包响应客户端, 客户端发送请求后当即开始接受数据包。重复这个过程若干次。若是每次都超时,没法接受到服务器的回应,则说明客户端是一个Port Restricted Cone NAT,若是可以收到服务器的响应则说明客户端是一个Restricted Cone NAT。以上两种NAT均可以进行UDP-P2P通讯。
由前述的一对多转换模型得知,除对称型NAT之外的模型,NAT网关对内部主机地址端口的映射都是相对固定的,因此比较容易实现NAT穿越。而对称型NAT为每一个链接提供一个映射,使得转换后的公网地址和端口对不可预测。此时TURN能够与STUN绑定提供穿越NAT的服务,即在公网服务器上提供一个“地址端口对”,全部此“地址端口对”接收到的数据会经由探测创建的链接转发到内网主机上。TURN分配的这个映射“地址端口对”会经过STUN响应发给内部主机,后者将此信息放入创建链接的信令中通知通讯的对端。这种探针技术是一种通用方法,不用在NAT设备上为每种应用协议开发功能,相对于ALG方式有必定广泛性。可是TURN中继服务会成为通讯瓶颈。并且在客户端中增长探针功能要求每一个应用都要增长代码才能支持。
我理解上述stun+turn的方式能够解决对称nat的nat穿越问题,由于turn提供公网ip+port,中继转发。 就至关于源服务器和turn只创建一个链接,因此映射的ip和port不会变。 因此这个方案实际上是从源server到turn至少两层nat。 其实至关于turn服务器提供了port mapping服务?
实际上大部运营商提供的光猫上网服务都是锥形nat的。 而光纤入户,3g 4g网络,公共wifi登由于安全因素都是对称nat。 对称型nat是能够打洞的。只要请求连接的对方是非端口限制锥型nat就都能实现打洞p2p连接的。 只有双方都是对称是必定没法实现。 (ICE能够解决?) 或一方对称一方是端口限制锥型nat的状况也没法实现打洞。 生日攻击算法(端口预测)确实能够解决对称型与端口限制型之间的穿透
对于一端是对称nat,一端是端口限制性Cone nat的状况是能够打洞成功的,特别是咱们实验的对称nat的端口变化仍是有规律的(加1),咱们使用端口猜想的方法进行打洞成功率仍是很是高的。对于端口变化无规律的对称nat,这个猜想仍是靠算法的设计,你能够看看A New Method for Symmetric NAT Traversal in UDP and TCP (http://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf)另外若是你是作应用的话建议不要始终把本身局限于必定要打洞成功的思路上,对于一些路由器是能够经过配置支持穿透的,好比upnp;对于实在打洞不成功的状况你能够经过设计一个中转服务器来完成本身的应用;
对于Cone NAT要采用UDP打洞.须要一个公网机器C来充当”介绍人”. 内网的A,B先分别和C通讯.打开各自的NAT端口. C这个时候知道A,B的公网IP: Port. 如今A和B想直接链接.好比A给B发.除非B是Full Cone.不然不能通讯.反之亦然. 可是咱们能够这样: A要链接B.A给B发一个UDP包.同时.A让那个介绍人给B发一个命令,让B同时给A发一个UDP包.这样双方的NAT都会记录对方的IP,而后就会容许互相通讯.
若是A,B在同一个NAT后面.若是用上面的技术来进行互连.那么若是NAT支持loopback(就是本地到本地的转换),A,B能够链接,可是比较浪费带宽和NAT.有一种办法是,A,B和介绍人通讯的时候,同时把本身的local IP也告诉服务器.A,B通讯的时候,同时发local ip和公网IP.谁先到就用哪一个IP.可是local ip就有可能不知道发到什么地方去了.好比A,B在不一样的NAT后面可是他们各自的local ip段同样.A给B的local IP发的UDP就可能发给本身内部网里面的了.
还有一个办法是服务器来判断A,B是否在一个NAT后面.(网络拓朴不一样会不会有问题?)
把4种类型分别标为1234,有两台主机A:portA和B:portB(port都为外网端口,是与打洞服务器通讯的端口),以及打洞服务器S,情景是B拿到了A:portA的信息,要与A通讯。 (1)、A为类型1;不管B为哪一种类型,均可以直接与A:portA udp通讯; (2)、A为类型2;不管B为哪一种类型,在A知道B以前都没法直接链接,B给S发一个打洞请求,S转发该请求到A。若B的类型为1,则A:portA可直接与B:portB udp通讯;若B的类型为2或3,则A:portA和B:portB各自向对方发送一个一字节的udp包,分别在本身的路由器上打洞,今后A:portA和B:portB可进行udp通讯;若B为类型4,则portB在与不一样的ip:port通讯时会不同,因此A:portA先向B发送一个一字节的udp包,在路由器上打洞,而后等待B:portB先发送数据,A:portA接收到B:portB的数据后,即知道portB,也可互通数据了; (3)、A为类型3;不管B为哪一种类型,在A知道B以前都没法直接链接,B给S发一个打洞请求,S转发该请求到A。若B的类型为1,则A:portA可直接与B:portB udp通讯;若B的类型为2或3,则A:portA和B:portB各自向对方发送一个一字节的udp包,分别在本身的路由器上打下洞,今后A:portA和B:portB可进行udp通讯;若B为类型4,则portB在与不一样的ip:port通讯时会不同,而A又要求知道portB的才可以让B:portB连进来,因此这种状况A只能猜想与A:portA通讯的portB,通讯几率小; (4)、A为类型4;不管B为哪一种类型,在A知道B以前都没法直接链接,B给S发一个打洞请求,S转发该请求到A。若B的类型为1,则A:portA可直接与B:portB udp通讯;若B的类型为2,则B:portB先向A发送一个一字节的udp包,在路由器上打洞,而后等待A:portA先发送数据,B:portB接收到A:portA的数据后,即知道portA,也可互通数据了;若B的类型为3,见(3)中B为类型4的描述;若B为4,双方没法知道对方的端口,没法通讯。
STUN :RFC 5389 - Session Traversal Utilities for (NAT) (STUN) ICE:RFC 5245 - Interactive Connectivity Establishment (ICE): A Methodology for Network Address Translator (NAT) Traversal for Offer/Answer Protocols
目前在公司和在家中测试,stun返回结果都是对称型nat。
<pre class="md-fences md-end-block" lang="" contenteditable="false" cid="n186" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: ; background-position: var(--code-block-bg-color); background-size: ; background-repeat: var(--code-block-bg-color); background-attachment: ; background-origin: ; background-clip: ; position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit;">
bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
go build
bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
./go-stun.exe -v
2019/01/06 08:05:34 Do Test1
2019/01/06 08:05:34 Send To: 217.10.68.145:10000
2019/01/06 08:05:34 Received: {packet nil: false, local: 42.101.65.133:23317, remote: 217.10.68.145:10000, changed: 217.116 .122.141:10001, other: <nil>, identical: false}
2019/01/06 08:05:34 Do Test2
2019/01/06 08:05:34 Send To: 217.10.68.145:10000
2019/01/06 08:05:44 Received: Nil
2019/01/06 08:05:44 Do Test1
2019/01/06 08:05:44 Send To: 217.116.122.141:10001
2019/01/06 08:05:44 Received: {packet nil: false, local: 42.101.65.134:44004, remote: 217.116.122.141:10001, changed: 217.1 0.68.145:10000, other: <nil>, identical: false}
NAT Type: Symmetric NAT
External IP Family: 1
External IP: 42.101.65.133
External Port: 23317
bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
$ ./go-stun.exe -v
2019/01/06 08:06:02 Do Test1
2019/01/06 08:06:02 Send To: 217.10.68.145:10000
2019/01/06 08:06:03 Received: {packet nil: false, local: 42.101.65.133:13858, remote: 217.10.68.145:10000, changed: 217.116 .122.141:10001, other: <nil>, identical: false}
2019/01/06 08:06:03 Do Test2
2019/01/06 08:06:03 Send To: 217.10.68.145:10000
2019/01/06 08:06:12 Received: Nil
2019/01/06 08:06:12 Do Test1
2019/01/06 08:06:12 Send To: 217.116.122.141:10001
2019/01/06 08:06:13 Received: {packet nil: false, local: 42.101.65.134:25117, remote: 217.116.122.141:10001, changed: 217.1 0.68.145:10000, other: <nil>, identical: false}
NAT Type: Symmetric NAT
External IP Family: 1
External IP: 42.101.65.133
External Port: 13858</pre>
stun 配合upnp(爱奇艺等内容服务商的方案)
https://github.com/jflyup/nat_traversal (c语言实现的,repo里声称解决了including symmetric NATs问题,网上有测试成功案例。)
https://github.com/sjtcumt/wp/tree/master/p2p/p2psrv(解决了对称nat-对称nat,对称nat-端口限制锥形nat的问题,但不稳定)
https://github.com/ccding/go-stun
以上stun解决对称nat的问题,基本上都是利用了端口猜想和生日攻击算法。
ICE,可解决所有nat穿越问题。