试验UDP打洞穿透NAT

目标

路由穿透,实现广域网P2P通信。html

4种典型NAT类型

按照NAT设备在进行地址映射时行为的不一样,NAT能够分为如下四种: java

  1. Full Cone git

  2. Restricted Cone github

  3. Port Restricted Cone 安全

  4. Symmentric 服务器

如何判断本机NAT类型

能够经过PyStun来判断:网络

1
2
3
NAT Type: Full Cone
External IP: 180.160.213.93
External Port: 32130

几种现代穿透协议

STUN

STUN协议为终端提供一种方式可以获知本身通过NAT映射后的地址,从而替代位于应用层中的私网地址,达到NAT穿透的目的。STUN协议是典型的Client-Server协议,各类具体应用经过嵌入STUN客户端与STUN Server端通信来完成交互。 并发

在典型的运用STUNNAT穿透场景中STUN客户端首先向位于公网上的STUN器 发送Binding RequestSTUN器接收到请求消息后识别出通过NAT转换后的公网地址60.1.1.1:12345,将其加在Binding Response中返回给客户端。客户端获得这个地址 后用它替换SDP中的私地址与终端B完成媒体协商。使用STUN进行NAT穿透对应用的要 求是必须使用一样的端口与STUN务器交互和进行应用层通信,好比当但愿使用端口 37000进行RTP包的NAT穿透时必须一样使用37000端口与STUN服务器通信不然从STUN 服务器得到的NAT后的地址通常与实际地址时不同的另外一个要求是STUN客户端与 服务器端的通信和应用使用得到的NAT映射地址进行应用层通信在时间上必须有连贯性, 这源于NAT设备创建的绑定有生存时间,当原绑定消亡后,NAT为同一个私网地址建 立的新绑定每每不一样,所以转换后的公网地址是不一样的。app

 

STUN方案特性以下表:框架

 

特性

说明

实现复杂度

实现简单

TCP穿透支持

不支持

对现有设备的要求

要求客户端支持对现有NAT无改动要求需增长STUN服务器

可扩展性

可扩展性好,与具体协议无关

安全性

通常

健壮性

差,不支持symmentricNAT

其余

支持自动检测NAT使用户即便在使用STUN协议无实现NAT

穿透时还能够根据NAT类型自主选择其余可以使用的NAT穿透方案


TURN

TURN解决NAT穿透的思路与STUN相似,都是经过修改应用层中的私网地址达到NAT穿透。 与STUN不一样的是,TURN是经过两方通信的“中间人”的方式实现穿透,在这种方式下, 要进行通信的两方分别与位于公网上的TURN服务器创建各自的链接进行通信,由服务器负 责在两方之间进行数据转发。要达到这个目的,实现TURN客户端的终端必须在通信开始前 与TURN服务器进行交互,获得服务器为其临时分配的位于TURN服务器上的公网地址,客户端使用它替换位于应用层中的私网地址。

TURN方案的特性以下表:

 

特性

说明

实现复杂度

难于实现。TURN的安全性设计增长终端设置的复杂度

TCP穿透支持

支持

对现有设备的要求

对现有NAT设备无要求,要求客户端支持,需增长TURN服务器s

可扩展性

可扩展性好,与具体协议无关

安全性

通常

健壮性

好,支持全部类型的NAT

其余

P2P穿透方式相比,性能时relay穿透方式的弱点。另外TURN没法

实现负载分担,解决的方式是media relay服务器的分配工做放在 SIP proxy完成

ICE

与STUN和TURN相比,ICE并不是是解决NAT穿透问题的协议,而是一个框架,在这个框架中, 能够整合其余现存的NAT穿透协议,如STUN、TURN、RSIP等。区别于其余的NAT穿透解 决方案,ICE是一种探索和更新式的解决方案,经过搜集自身和对端尽量多的网络信息(各类网络地址),尝试在这些地址间创建数据通道,并在这一过程当中不断更新先前收集到的信息,从而找出和选择可以进行NAT穿透的数据通道。

ICE方案的特性以下表:

特性

说明

实现复杂度

通常

TCP穿透支持

支持

对现有设备的要求

NAT无要求,支持全部类型的NAT。客户端必须支持,

网路结构中需增长STUN/TURN服务器

可扩展性

可扩展性好,与具体协议无关

安全性

较好

健壮性

好,适用与全部NAT及NAT拓扑类型,且因为存在中继服务器,NAT

穿透通常老是能成功

其余

 

试验环境

两台PC——

A位于公网IP为180.160.213.93 的路由下,内网IP为192.168.1.100,经过端口8888通信。

B位于公网IP为180.160.233.193的路由下,内网IP为192.168.1.107,经过端口6666通信。

大体如图所示:


STUN服务器采用免费的公用STUN服务器,具体STUN服务器列表在此

试验过程

基于开源的Java ICE库ice4j,编写客户端,其中,客户端调用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package  com.hankcs.network;
 
import  java.io.BufferedReader;
import  java.io.IOException;
import  java.io.InputStreamReader;
import  java.net.DatagramPacket;
import  java.net.DatagramSocket;
import  java.net.SocketAddress;
 
 
public  class  Peer
{
     public  static  void  main(String[] args)  throws  Throwable
     {
         try
         {
             IceClient client =  new  IceClient( 8888 "text" );
             client.init();
             client.exchangeSdpWithPeer();
             client.startConnect();
             final  DatagramSocket socket = client.getDatagramSocket();
             final  SocketAddress remoteAddress = client
                     .getRemotePeerSocketAddress();
             System.out.println(socket.toString());
             new  Thread( new  Runnable()
             {
 
                 public  void  run()
                 {
                     while  ( true )
                     {
                         try
                         {
                             byte [] buf =  new  byte [ 1024 ];
                             DatagramPacket packet =  new  DatagramPacket(buf,
                                                                        buf.length);
                             socket.receive(packet);
                             System.out.println(packet.getAddress() +  ":"  + packet.getPort() +  " says: "  new  String(packet.getData(),  0 , packet.getLength()));
                         }
                         catch  (IOException e)
                         {
                             e.printStackTrace();
                         }
                     }
                 }
             }).start();
 
             new  Thread( new  Runnable()
             {
 
                 public  void  run()
                 {
                     try
                     {
                         BufferedReader reader =  new  BufferedReader( new  InputStreamReader(System.in));
                         String line;
                         // 从键盘读取
                         while  ((line = reader.readLine()) !=  null )
                         {
                             line = line.trim();
                             if  (line.length() ==  0 )
                             {
                                 break ;
                             }
                             byte [] buf = (line).getBytes();
                             DatagramPacket packet =  new  DatagramPacket(buf, buf.length);
                             packet.setSocketAddress(remoteAddress);
                             socket.send(packet);
                         }
                     }
                     catch  (Exception e)
                     {
                         e.printStackTrace();
                     }
 
                 }
             }).start();
         }
         catch  (Exception e)
         {
             e.printStackTrace();
         }
 
     }
 
}

为了简便,没有引入SIP服务器,直接用人工复制粘贴的方式交换了两台设备的SDP信息,其中

A的SDP信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v=0
o=ice4j.org 0 0 IN IP4 66.228.45.110
s=-
t=0 0
a=ice-options:trickle
a=ice-ufrag:26iqs1932ucrkp
a=ice-pwd:7rnncil3dqga9q5fl5e3jppobk
m=text 50659 RTP/AVP 0
c=IN 66.228.45.110 IP4
a=mid:text
a=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
a=candidate:2 1 udp 2130706431 fe80:0:0:0:38f0:a54b:a8a6:e8b6 8888 typ host
a=candidate:3 1 udp 1677724415 180.160.213.93 25627 typ srflx raddr 192.168.1.100 rport 8888
a=candidate:4 1 udp 2815 66.228.45.110 50659 typ relay raddr 180.160.213.93 rport 25627

B的SDP信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v=0
o=ice4j.org 0 0 IN IP4 66.228.45.110
s=-
t=0 0
a=ice-options:trickle
a=ice-ufrag:dpmfo1912ud8p2
a=ice-pwd:2q4c0689j98st3jmbr5k4jgc9k
m=text 50656 RTP/AVP 0
c=IN 66.228.45.110 IP4
a=mid:text
a=candidate:1 1 udp 2130706431 192.168.1.107 6666 typ host
a=candidate:2 1 udp 2130706431 fe80:0:0:0:448b:e2e1:7f4b:f0b8 6666 typ host
a=candidate:3 1 udp 1677724415 180.160.233.193 44639 typ srflx raddr 192.168.1.107 rport 6666
a=candidate:4 1 udp 2815 66.228.45.110 50656 typ relay raddr 180.160.233.193 rport 44639

交换信息后A的配对过程输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
九月 30, 2014 5:32:53 下午 org.ice4j.ice.Component addRemoteCandidate
信息: Add remote candidate for text.RTP: 192.168.1.107:6666/udp/host
九月 30, 2014 5:32:53 下午 org.ice4j.ice.Component addRemoteCandidate
信息: Add remote candidate for text.RTP: [fe80:0:0:0:448b:e2e8:7f8b:f0b8]:6666/udp/host
九月 30, 2014 5:32:53 下午 org.ice4j.ice.Component addRemoteCandidate
信息: Add remote candidate for text.RTP: 180.160.233.193:44639/udp/srflx
九月 30, 2014 5:32:53 下午 org.ice4j.ice.Component addRemoteCandidate
信息: Add remote candidate for text.RTP: 66.228.45.110:50656/udp/relay
九月 30, 2014 5:32:55 下午 org.ice4j.ice.Agent startConnectivityEstablishment
信息: Start ICE connectivity establishment
九月 30, 2014 5:32:55 下午 org.ice4j.ice.Agent initCheckLists
信息: Init checklist for stream text
九月 30, 2014 5:32:55 下午 org.ice4j.ice.ConnectivityCheckClient startChecks
信息: Start connectivity checks!
17:32:55.651 [main] INFO  com.hankcs.network.IceClient - Agent entered the Running state.
九月 30, 2014 5:33:06 下午 org.ice4j.ice.ConnectivityCheckClient processTimeout
信息: timeout for pair: 192.168.1.100:8888/udp/host -> 192.168.1.107:6666/udp/host (text.RTP), failing.
九月 30, 2014 5:33:16 下午 org.ice4j.ice.ConnectivityCheckClient processTimeout
信息: timeout for pair: [fe80:0:0:0:38f0:a54b:a8a6:e8b6]:8888/udp/host -> [fe80:0:0:0:448b:e2e8:7f8b:f0b8]:6666/udp/host (text.RTP), failing.
九月 30, 2014 5:33:26 下午 org.ice4j.ice.ConnectivityCheckClient processTimeout
信息: timeout for pair: 192.168.1.100:8888/udp/host -> 180.160.233.193:44639/udp/srflx (text.RTP), failing.
九月 30, 2014 5:33:36 下午 org.ice4j.ice.ConnectivityCheckClient processTimeout
信息: timeout for pair: 66.228.45.110:50659/udp/relay -> 192.168.1.107:6666/udp/host (text.RTP), failing.
九月 30, 2014 5:33:36 下午 org.ice4j.ice.Agent incomingCheckReceived
信息: Received check from 192.168.1.100:8888/udp/host -> 180.160.233.193:44724/udp/prflx (text.RTP) triggered a check
九月 30, 2014 5:33:36 下午 org.ice4j.ice.Agent triggerCheck
信息: Add peer CandidatePair with new reflexive address to checkList
九月 30, 2014 5:33:45 下午 org.ice4j.ice.ConnectivityCheckClient processSuccessResponse
信息: Pair succeeded: 192.168.1.100:8888/udp/host -> 180.160.233.193:44724/udp/prflx (text.RTP)
九月 30, 2014 5:33:45 下午 org.ice4j.ice.ConnectivityCheckClient processSuccessResponse
信息: Pair validated: 180.160.213.93:25627/udp/srflx -> 180.160.233.193:44724/udp/prflx (text.RTP)
九月 30, 2014 5:33:45 下午 org.ice4j.ice.ConnectivityCheckClient processSuccessResponse
信息: IsControlling: false USE-CANDIDATE:false
九月 30, 2014 5:33:45 下午 org.ice4j.ice.ConnectivityCheckClient processSuccessResponse
信息: Nomination confirmed for pair: 180.160.213.93:25627/udp/srflx -> 180.160.233.193:44724/udp/prflx (text.RTP)
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Agent entered the Completed state.
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Total ICE processing time: 139649ms
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Stream name: text
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - ------------------------------------------
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Component of stream:RTP,selected of pair:CandidatePair (State=Succeeded Priority=7926347438766424062):
     LocalCandidate=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
     RemoteCandidate=candidate:10000 1 udp 1845496575 180.160.233.193 44724 typ prflx
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - ------------------------------------------
17:33:45.695 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Printing the completed check lists:
17:33:45.696 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - Check list for  stream: text
17:33:45.696 [Stun4J Message Processor] INFO  com.hankcs.network.IceClient - nominated check list:CheckList. (num pairs=5)
CandidatePair (State=Failed Priority=9151314442783293438):
     LocalCandidate=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
     RemoteCandidate=candidate:1 1 udp 2130706431 192.168.1.107 6666 typ host
CandidatePair (State=Failed Priority=9151314442783293438):
     LocalCandidate=candidate:2 1 udp 2130706431 fe80:0:0:0:38f0:a54b:a8a6:e8b6 8888 typ host
     RemoteCandidate=candidate:2 1 udp 2130706431 fe80:0:0:0:448b:e2e8:7f8b:f0b8 6666 typ host
CandidatePair (State=Failed Priority=7205771498387144702):
     LocalCandidate=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
     RemoteCandidate=candidate:3 1 udp 1677724415 180.160.233.193 44639 typ srflx raddr 192.168.1.107 rport 6666
CandidatePair (State=Failed Priority=12094594351103):
     LocalCandidate=candidate:4 1 udp 2815 66.228.45.110 50659 typ relay raddr 180.160.213.93 rport 25627
     RemoteCandidate=candidate:1 1 udp 2130706431 192.168.1.107 6666 typ host
CandidatePair (State=Succeeded Priority=7926347438766424062):
     LocalCandidate=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
     RemoteCandidate=candidate:10000 1 udp 1845496575 180.160.233.193 44724 typ prflx
 
九月 30, 2014 5:33:45 下午 org.ice4j.ice.CheckList handleNominationConfirmed
信息: Selected pair for stream text.RTP: 192.168.1.100:8888/udp/host -> 180.160.233.193:44724/udp/prflx (text.RTP)
九月 30, 2014 5:33:45 下午 org.ice4j.ice.Agent checkListStatesUpdated
信息: CheckList of stream text is COMPLETED
九月 30, 2014 5:33:45 下午 org.ice4j.ice.Agent checkListStatesUpdated
信息: ICE state is COMPLETED
17:33:45.696 [main] INFO  com.hankcs.network.IceClient - Component id=1 parent stream=text
4 Local candidates:
default candidate: candidate:4 1 udp 2815 66.228.45.110 50659 typ relay raddr 180.160.213.93 rport 25627
candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
candidate:2 1 udp 2130706431 fe80:0:0:0:38f0:a54b:a8a6:e8b6 8888 typ host
candidate:3 1 udp 1677724415 180.160.213.93 25627 typ srflx raddr 192.168.1.100 rport 8888
candidate:4 1 udp 2815 66.228.45.110 50659 typ relay raddr 180.160.213.93 rport 25627
4 Remote candidates:
default remote candidate: null
candidate:1 1 udp 2130706431 192.168.1.107 6666 typ host
candidate:2 1 udp 2130706431 fe80:0:0:0:448b:e2e8:7f8b:f0b8 6666 typ host
candidate:3 1 udp 1677724415 180.160.233.193 44639 typ srflx raddr 192.168.1.107 rport 6666
candidate:4 1 udp 2815 66.228.45.110 50656 typ relay raddr 180.160.233.193 rport 44639
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - Remote candinate transport address:180.160.233.193:44724/udp
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - Remote candinate host address:null
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - Remote candinate mapped address:null
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - Remote candinate relayed address:null
17:33:45.697 [main] INFO  com.hankcs.network.IceClient - Remote candinate reflexive address:180.160.233.193:44724/udp
org.ice4j.socket.MultiplexingDatagramSocket@46160dbd
九月 30, 2014 5:33:45 下午 org.ice4j.ice.Agent logCandTypes
信息: Harvester used for selected pair for text.RTP: host
17:33:48.700 [TerminationThread] INFO  com.hankcs.network.IceClient - Agent entered the Terminated state.
17:33:48.700 [TerminationThread] INFO  com.hankcs.network.IceClient - ice processing TERMINATED
九月 30, 2014 5:33:48 下午 org.ice4j.ice.Agent$TerminationThread run
信息: ICE state is TERMINATED

其中,值得注意的是两台设备经过地址对

1
Nomination confirmed for pair: 180.160.213.93:25627/udp/srflx -> 180.160.233.193:44724/udp/prflx (text.RTP)

创建了UDP链接。

试验结果

以后B经过键盘输入聊天消息并发送

1
2
3
4
5
hello
/180.160.213.93:25627 says: AS
yes
haha
/180.160.213.93:25627 says: ZA

A收到消息并回复

1
2
3
4
5
/180.160.233.193:44724 says: hello
AS
/180.160.233.193:44724 says: yes
/180.160.233.193:44724 says: haha
ZA

一些尝试

将B经过手机热点接入中国电信3G网络后,NAT穿透失败,缘由不明。可能此时B属于Symmentric NAT,同时免费的TURN服务器也没有起做用。接下来应当深刻研究这几种协议,以及TURN服务器的搭建。

Reference

《NAT穿透解决方案》

http://www.cnblogs.com/javaminer/p/3575282.html