存档

文章标签 ‘NAT’

State of Peer-to-Peer Communication across NAT(rfc5128中文)

四月 29th, {2010 1 条评论 10,806 人阅读过  

翻译了一下RFC5128,并为我拙劣的英语水准和汉语言表达技巧感到汗颜,如果有人不幸搜到这篇文章,英语不是太差的话就不要看了。
英文原版地址:http://tools.ietf.org/html/rfc5128

1.Introduction and Scope

当前的Internet已经普遍布署了网络地址转换器(NATs).存在各种各样的NAT设备和各种各样使用NAT设备布署的网络拓扑。由NAT设备创建的非对称的地址和连接形式,给一些p2p应用程序和协议带来了它们特有的问题,像电话会议,多媒体播放器,网络游戏,这些问题甚至有可能持续到IPv6世界,在向IPv6过渡的时期,有些NAT可能需要使只支持IPv4的节点跟只支持IPv6的节点进行通信,尽管这种使用NAT的合适的协议或方法尚未解决。即使是将来的纯IPv6世界可能仍然包括防火墙,它仍然拥有和NAT类似的过滤功能,但没有地址转换功能,这种过滤行为也影响着P2P应用程序的功能。出于这个原因,这篇文章中讨论的NAT转换所使用的技术同样也适用于和带有和NAT转换类似的过滤功能的某些防火墙。

当前部署的NAT设备主要围绕client/server这种模式设计的,在这种模式下匿名,私有网络中的匿名客户端设备使用固定的IP地址和DNS域名访问处于公有网络的服务器。中途遇到的NAT设备为客户端主机动态地分配地址。这种匿名性和NAT设备后面的主机的不可访问性对于像web浏览器这样的应用程序来说并不是问题,web浏览器只需要创建outgoint连接。这种匿名性和不可访问性有时候对于隐私的保护是有利的。

在p2p的模式中,Internet主机通常情况下被称为是“客户端”,这种客户端不仅能够向其它的peer节点发起会话,还能接受来自其它节点发起的会话。发起者和响应者可能处于不同的NAT设备的后面,它们可能都没有永久的IP地址或者其它形式的公有网络属性。举个例子来说,一个普通的网络游戏的结构中,所有参与游戏的应用程序主机和一台公网可寻址的公共服务器交互来进行注册,并且发现其它的peer主机。在与这台公共服务器进行通信之后,主机彼此之间建立直接的连接以在游戏期间提供快速高效的数据更新。类似的,一个文件共享程序可能跟一台公共服务器进行交互进行资源发现和搜索,但却在peer主机之间建立直接连接进行数据传输。NAT设备给p2p的连接带来了麻烦,因为NAT设备后面的主机在Internet上没有固定的公网可见端口,其它的peer也就无法对起发起Incoming TCP或UDP连接。RFC 3234[NAT-APPL]简单地强调了这个问题。

在这篇文章中,我们总结了当前熟知的一些方法,使应用程序工作在NAT设备环境中而不需要直接改变NAT设备。

2. Terminology and Conventions Used

2.1 端点(Endpoint)

一个Endpint是指在一台终端主机上的和会话相关的元组。对于每一种IP协议,endpoint的表示方式也可能不一样。比如,UDP或者TCP会话的endpoint是以(IP地址,UDP/TCP端口号)这种元组的形式表示的。

2.2 端点映射(Endpoint Mapping)
当私有网络中的主机通过NAT设备向公有网络发起一个outgoing的会话,NAT设备分配一个公有的endpoint来转换私有endpoint,于是随后来自外部主机的响应包就可以被NAT接收,转换,并且转发到私有endpoint去。NAT设备将私有endpoint转换成公有endpoint,或者反过来的这种分配就称为是端点映射。NAT使用端点映射在会话的过程中执行转换。

2.3 端点独立映射(Endpoint-Independent Mapping)

“端点独立映射”在 [BEH-UDP]中像下面这样定义:

对于从一个相同的内部IP地址和端口(X:x)发往任何外部IP地址和端口的随后的数据包,NAT会重用端口映射。

2.4 端点依赖性映射(Endpoint-Dependent Mapping)

“端点依赖性映射”是指“地址依赖性映射”和“地址和端口依赖性映射”的组合,在[BEH-UDP]中定义。

地址依赖性映射: (Address-Dependent Mapping)

对于接下来要从同一个内部IP地址和端口(X:x)发往同一个外部地址,不管外部端口号是多少,NAT都会重用端口映射。

地址和端口依赖性映射:(Address and Port-Dependent Mapping)

对于接下来要从同一个同部IP地址和端口(X:x)发往同一个外部地址和端口号的数据包,当映射依然处于活动状态的时候,NAT设备会重用端口映射。

2.5 端点独立的过滤(Endpoint-Independent Filtering)

“端点独立的过滤”在[BEH-UDP]中像下面这样定义:

不管外部IP地址和端口号(Z:z)是多少,NAT都只过滤不发往内部IP地址和端口号(X:x)的数据包。NAT会转发所有发往X:x的数据包。换句话说,从NAT内侧发信外部IP地址的数据包足以让任何数据包发回到内部endpoint。

使用“端点独立映射”和“端点独立过滤”的NAT设备可以接受任何位于公有网络的外部endpoint上的已经映射的端口的incoming流量。

2.6 端点依赖性过滤(Endpoint-Dependent Filtering)

“端点依赖性过滤”是指“地址依赖性过滤”和“地址和端口依赖性过滤”的组合,在[BEH-UDP]中定义

地址依赖性过滤(Address-Dependent Filtering)

NAT过滤不发往内部地址X:x的数据包。并且,如果X:x之前没有发送任何数据包给Y(和Y使用的端口无关),那么NAT设备就会过滤从Y:y发往X:x的数据包。换句话说,要从一个特定的外部endpoint接收数据包,内部endpoint必须先向特定的外部endpoint发送过数据包。

地址和端口依赖性过滤(Address and Port-Dependent Filtering)

NAT过滤不发往内部地址X:x的数据包。并且,如果X:x之前没有发送任何数据包给Y:y,那么NAT设备就会过滤从Y:y发往X:x的数据包。换句话说,要从一个特定的外部endpoint接收数据包,内部endpoint必须先向特定的外部endpoint发送过数据包。

(译者注:区别就仅仅是和外部端点的端口有没有关系)

使用“端点依赖性过滤”的NAT设备只会接受有限的一组公网外部端点的发往一个已经映射的公有端口的incoming流量。(这句话我认为我翻译的很失败,附上原文)

A NAT device employing “Endpoint-Dependent Filtering” will accept
incoming traffic to a mapped public port from only a restricted set
of external endpoints on the public network.

2.7 P2P程序(P2P Application)

P2P程序是指使用同一个endpoint向peer主机发起outgoing会话,并且接收来自peer主机的incoming会话的应用程序。P2P程序可以使用多endpoint进行p2p通信。

2.8 NAT友好的P2P应用程序(NAT-Friendly P2P Application)

NAT友好的P2P程序是指,即使peer节点处于由一个或者多个NAT连接不同的IP地址域中,也能够有效运行的p2p程序。

一种P2P程序建立peer会话并保持NAT友好的通用方法是使用一台公网可寻址的公共服务器用做注册和peer发现。

2.9 端点独立映射NAT(EIM-NAT)

EIM-NAT是使用端点独立映射的NAT设备。EMI-NAT可以有任何过滤行为。BEHAVE-comiliant NAT设备设备就是EIM-NAT设备的很好的例子。使用地址依赖映射的NAT设备就是非EIM-NAT设备的例子。

2.10 发夹(Hairpinning)

发夹在[BEH-UDP]中像下面这样定义:

如果两台主机(称为X1和X2)在同一个NAT设备之后,并且交换流量,NAT可以在它外面为X2分配地址,称为X2′x2′,如果X1向X2′x2′发送流量,它就会到达NAT,然后NAT再将流量从X1传递到X1,这被称为是发夹。

并不是目前所有的NAT设备都支持发夹。

3.P2P程序穿越NAT设备所用到的技术。

这一小节从软件设备者或协议设计者的角度上,详细地回顾了在现有的NAT设备基础上实现p2p通信的一些熟知的技术。

3.1中继

在存在NAT设备的环境中实现p2p通信,最可靠但效率最低的方法是让p2p通过中继,使用看上去像client/server的方式进行通信。考虑图表1中的场景。两个客户端主机,A和B,都向一台公共服务器ServerS发起了TCP或UDP连接,这台公共服务器有一个公网可寻址的IP地址,用来进行注册,发现和中继。NAT设备后面的主机向这台服务器注册。Peer可以发现NAT设备后面的主机,并且使用这台服务器中继端对端的消息。客户端驻留在各自的私有网络里面,它们各自的NAT设备阻止它们直接同对方发起连接。

Registry, Discovery
Combined with Relay
Server S
192.0.2.128:20001
|
+—————————-+—————————-+
| ^ Registry/              ^   ^ Registry/              ^ |
| | Relay-Req Session(A-S) |   | Relay-Req Session(B-S) | |
| | 192.0.2.128:20001      |   |  192.0.2.128:20001     | |
| | 192.0.2.1:62000        |   |  192.0.2.254:31000     | |
|                                                         |
+————–+                                 +————–+
| 192.0.2.1    |                                 | 192.0.2.254  |
|              |                                 |              |
|    NAT A     |                                 |    NAT B     |
+————–+                                 +————–+
|                                                         |
| ^ Registry/              ^   ^ Registry/              ^ |
| | Relay-Req Session(A-S) |   | Relay-Req Session(B-S) | |
| |  192.0.2.128:20001     |   |  192.0.2.128:20001     | |
| |     10.0.0.1:1234      |   |     10.1.1.3:1234      | |
|                                                         |
Client A                                                 Client B
10.0.0.1:1234                                        10.1.1.3:1234

Figure 1: Use of a Relay Server to communicate with peers

两个客户端可以简单地通过Server S在他们之间传递消息,而不是试图进行直接连接。举个例子,要发送一条消息给Client B,Client A只需要在同Server S建立了cs连接之后将消息发给Server S即可,Server S可以通过它和Client B之间已经建立的cs连接将消息转发给Client B。

这种方法的优点在于,只要两个Client都能够连接到Server,这种方法就一直有效。中间的NAT设备也不需要是EIM-NAT.这种方法的明显缺点在于,它消息了Server的处理器能力和网络带宽,同时,即使Server拥有足够的I/O带宽并且处于正确良好的拓扑结构中,client之间的通信延迟也会有所增长。The TURN protocol
[TURN] defines a method of implementing application agnostic,
session-oriented, packet relay in a relatively secure fashion.

3.2 连接逆转(Connection Reversal)

接下来要讨论的进行直接通信的连接逆转技术只有在一个peer处在NAT设备的后面,而另一个peer不处在NAT设备后面时才有效。举个例子,考虑图表2中的场景。client A处在NAT设备的后面,而client B拥有一个公网可寻址的IP地址。公共server S有一个公网可寻址的IP地址,它用做注册和发现。处在NAT后面的设备通过server注册他们的endpoint,peer主机通过这台服务器发现处在NAT后面的主机的endpoint。

Registry and Discovery
Registry and Discovery
Server S
192.0.2.128:20001
|
+—————————-+—————————-+
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| | 192.0.2.128:20001     |     |  192.0.2.128:20001    | |
| | 192.0.2.1:62000       |     |  192.0.2.254:1234     | |
|                                                         |
| ^ P2P Session (A-B)     ^     |  P2P Session (B-A)    | |
| | 192.0.2.254:1234      |     |  192.0.2.1:62000      | |
| | 192.0.2.1:62000       |     v  192.0.2.254:1234     v |
|                                                         |
+————–+                                            |
| 192.0.2.1    |                                            |
|              |                                            |
|    NAT A     |                                            |
+————–+                                            |
|                                                         |
| ^ Registry Session(A-S) ^                               |
| |  192.0.2.128:20001    |                               |
| |     10.0.0.1:1234     |                               |
|                                                         |
| ^ P2P Session (A-B)     ^                               |
| |  192.0.2.254:1234     |                               |
| |     10.0.0.1:1234     |                               |
|                                                         |
Private Client A                                 Public Client B
10.0.0.1:1234                                    192.0.2.254:1234

Figure 2: Connection reversal using Rendezvous server

client A拥有私有地址10.0.0.1,程序使用TCP端口1234. 这台client已经使用公有地址192.0.2.128,端口20001同server S建立了连接。NAT A在它的公有地址192.0.2.1上为client A分配了TCP端口62000作为client A同server S会话的临时公有端点地址。因此,server S认为client A的IP地址为192.0.2.1,使用端口62000。client B有它自己的固定IP地址192.0.2.254,client B上的程序正在监听端口1234以接收TCP连接。

现在假设client B希望同client A直接建立通信会话。B可能首先尝试连接A自己认为拥有的地址,也就是10.0.0.1:1234,或者连接由server S观察到的A的地址,也就是192.0.2.1:62000,在任何情况下连接都会失败。
在第一种情况下,发告往IP地址10.0.0.1的流量会直接被网络丢弃,因为10.0.0.1不是公网可路由的IP地址。在第二种情况下,从B发来的TCP SYN请求包将会到达NAT A并发送到端口62000,但NAT A会拒绝连接请求,因为NAT A只允许outgoint连接。

尝试和A直接建立连接并失败后,B可以使用server S传递一个请求给client A立一个到client B的“逆转”连接。client A在通过S收到这个中继请求后,向client B的公网地址和端口创建一个TCP连接,NAT A允许这个连接通过,因为这个连接是在防火墙的内部发起的,而client B也可以接收到这个连接,因为它不处在NAT设备的后面。

当前各种p2p程序都实现了这种技术。当然,它的最大的限制在于,它仅仅工作在只有一个通信的peer处在NAT设备后面。如果NAT设备是EIM-NAT,public client就可以联系外部的server S来确定可以接收从client A发起连接并允许这些连接的特定端点。如果NAT设备不是EIM-NAT,public client就不能确定哪些端点可以接收由client A发起的连接。在两个端点都处于NAT后面的情况越来越普遍的情况下,这种连接逆转的技术就会失败。连接逆转不是这种p2p连接问题的通用解决办法。如果前向连接和逆转连接都不能建立,程序只能退回去使用其它的机制,比如中继。

3.3 UDP 打孔技术

UDP打孔技术依赖于此EIM-NAT的一些属性,从而允许设计恰当的p2p程序在NAT设备上“打孔”来建立彼此之间的直接连接,即使两个客户端同时处于NAT设备之后也可以。当一台主机不是处在EIM-NAT之后时,peering主机不能获知要给哪个已经映射的endpoint发起连接。进一步说,处在非EIM-NAT设备后面的主机上的程序不能够使用已经建立的endpoint映射和外部目的地址进行通信,打孔技术将会失败。

我们将会考虑两个特定的场景,看看如何设计程序来优雅地处理这两种情况。第一个场景代表最常见的情况,处在两个不同的NAT设备后面的两个client希望直接建立p2p通信。第二种情况下,两个客户端事实上处在同一个NAT后面,但它们自己并不知道。

3.3.1 处在不同的NAT设备之后的Peers

看一下图表3中的场景。client A和client B都拥有私有地址,并且处在不同的NAT设备后面。公共服务器S拥有公网可路由的IP地址,用做注册,发现和有限制的中继。NAT后面的主机在这个Server上注册他们的公共endpoint。peer主机使用这台服务器来发现NAT后面主机的公共endpoint。不像3.1中描述的那样,peer主机只使用server传递连接初始控制信息,而是发送end-to-end信息。

运行在client A和client B上的p2p程序使用UDP端口1234.公共server使用UDP端口20001。A和B各自向server S创建了通信会话,从而分别使NAT A为clinet A和 serverA的会话分配了它的公共UDP端口62000,NAT B为clinet B和server S的会话分配了它的公共端口31000。

Registry and Discovery Combined
with Limited Relay
Server S
192.0.2.128:20001
|
+—————————-+—————————-+
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| | 192.0.2.128:20001     |     |  192.0.2.128:20001    | |
| | 192.0.2.1:62000       |     |  192.0.2.254:31000    | |
|                                                         |
| ^ P2P Session (A-B)     ^     ^  P2P Session (B-A)    ^ |
| | 192.0.2.254:31000     |     |  192.0.2.1:62000      | |
| | 192.0.2.1:62000       |     |  192.0.2.254:31000    | |
|                                                         |
+————–+                                 +————–+
| 192.0.2.1    |                                 | 192.0.2.254  |
|              |                                 |              |
| EIM-NAT A    |                                 | EIM-NAT B    |
+————–+                                 +————–+
|                                                         |
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| |  192.0.2.128:20001    |     |  192.0.2.128:20001    | |
| |     10.0.0.1:1234     |     |     10.1.1.3:1234     | |
|                                                         |
| ^ P2P Session (A-B)     ^     ^  P2P Session (B-A)    ^ |
| |  192.0.2.254:31000    |     |  192.0.2.1:62000      | |
| |     10.0.0.1:1234     |     |     10.1.1.3:1234     | |
|                                                         |
Client A                                                 Client B
10.0.0.1:1234                                        10.1.1.3:1234

Figure 3: UDP Hole Punching to set up direct connectivity

现在假设client A想和client B直接建立UDP通信会话。如果A简单地发送一个UDP消息到B的公共endpoint 192.0.2.254:31000,B通常会丢弃这些incoming消息(除非它使用了端点独立(Endpoint-Indepentent)的过滤),因为这条消息的源地址和端口号不和S的匹配,而outgoing会话是和S建立的。同样,B简单地发送一个UDP消息到A的公共endpoint,那么NAT A也通常会丢弃这些消息。

假设A开始发送UDP消息到B的公共endpoint,同时通过server S向B传递请求,让B也发送UDP消息到A的公共endpoint。A直接发往B的公共endpoint(192.0.2.254:31000)的outgoint消息将会导致EIM-NAT A在A的私有endpoint和B的公共endpoint之间打开一个新的通信会话。同时,B发往A的公共endpoint(192.0.2.1:62000)的消息会导致EMI-NAT B在B的私有endpoint和B的公共endpoint之音打开一个新的通信会话。一旦在每个方向上都打开了UDP会话之后,client A和client B就可以直接和彼此进行通信,而不会进一步带来server S的负担。帮助NAT后面的peer节点传递连接初始化请求的server S最后被称为peer主机的“中介”节点。

UDP打孔技术有很多有用的属性。一旦两个处在NAT设备之后的peer主机建立了直接的p2p-udp连接,连接的每一部分都可以反过来充当“中介”来帮助其它的部分和其它的peer建立p2p连接,减少了初始中介服务器S的负载。既然上面的过程在即使任一个或者两个client恰好都不处于NAT设备之后的情况下,一样可以很好创建p2p的通信连接,那么程序也就不需要检测它前面的NAT设备的类型。UDP打孔技术甚至在多个NAT的情况下也可以自动地工作,这种情况下一个或者两个客户端在到达公有Internet网络时经过了两级或者更多的地址转换。

3.3.2 处于同一个NAT设备后面的peers

现在考虑这样一个场景:两个客户恰巧处于同一个EMI-NAT后面,因此也处于同一个私有地址空间中,像图表4描述的那样。公共服务器S拥有公网可路由的IP地址用于注册,发现和有限制地中继。处在NAT后面的主机向服务器注册。peer主机通过服务器发现NAT后面的主机,并且通过服务器传递消息。不像3.1节描述的那样,peer主机只传递控制信息,而是传递所有的点对点的消息。

client A已经和server S建立了UDP会话,EIM-NAT也已经给client A分配了公共端口号62000。同样client B也同server S建立了UDP会话,EIM-NAT也为它分配了公共端口号62001。

Registry and Discovery Combined
with Limited Relay
Server S
192.0.2.128:20001
|
^ Registry Session(A-S) ^  | ^ Registry Session(B-S) ^
| 192.0.2.128:20001     |  | |  192.0.2.128:20001    |
| 192.0.2.1:62000       |  | |  192.0.2.1:62001      |
|
+————–+
| 192.0.2.1    |
|              |
|   EIM-NAT    |
+————–+
|
+—————————–+—————————-+
| ^ Registry Session(A-S) ^      ^ Registry Session(B-S) ^ |
| |  192.0.2.128:20001    |      |  192.0.2.128:20001    | |
| |     10.0.0.1:1234     |      |     10.1.1.3:1234     | |
|                                                          |
| ^ P2P Session-try1(A-B) ^      ^ P2P Session-try1(B-A) ^ |
| | 192.0.2.1:62001       |      |  192.0.2.1:62000      | |
| |     10.0.0.1:1234     |      |     10.1.1.3:1234     | |
|                                                          |
| ^ P2P Session-try2(A-B) ^      ^ P2P Session-try2(B-A) ^ |
| |     10.1.1.3:1234     |      |     10.0.0.1:1234     | |
| |     10.0.0.1:1234     |      |     10.1.1.3:1234     | |
|                                                          |
Client A                                                   Client B
10.0.0.1:1234                                         10.1.1.3:1234

Figure 4: Use of local and public endpoints to communicate with peers

假设A和B使用上面所描述的UDP打孔技术通过server S做为中介建立一个通信信道,然后A和B将会获取对方由server S所观察到的公共endpoint。只要NAT设备允许内网主机同其它的内网主机建立转换的UDP会话,而不仅仅是和外部主机建立,两台client就能够彼此之间进行通信。这种情况被称为“Hairpinning”,因为数据包从私有网络到达NAT之后被转换然后又环回到私有网络,而不是转发到公有网络。

举个例子,考虑上面的p2p session-try1。当A发送一个UDP包到B的公有endpoint时,这个包最初拥有源endpoint(10.0.0.1:1234)和目的endpoint(192.0.2.1:2001)。NAT收到这个包然后把它的源endpoint转换成192.0.2.1:62000,目的endpoint转换成10.1.1..3:1234,然后转发给B.

即便NAT设备支持hairpinning,在这种情况下转换和转发的步骤明显是没有必要的,并且增加了A和B之间会话的延迟,同时也增加了NAT的负载。解决的办法是直接转发。下面将会介绍:

当A和B最初通过公共服务器S交换地址信息的时候,他们包含他们自己所看到的IP地址和端口号,以及服务器S所看到的他们的公网IP地址和端口号。客户端同步地向它们知道的对方的任一个地址发送数据包,并且使用第一个连接成功的第址。如果两个客户端处理同一个NAT之后,那么直接发送到他们私有endpoint的数据包很可能先到达,从而实现了不需要NAT参与的直接通信信道。如果两个客户端处在不同的NAT后面,那么直接发往它们私有endpoint的数据包将不会到达,但可客户端将会按我们所期望的使用他们各自的公有endpoint建立连接。既然在不同的NAT的情况下,A发往B的私有endpoint的消息很有可能到达A的私有网络上的不相关的节点,反过来也也一样,那么这些对数据包在某种方式上经过认证是很重要的。

The [ICE] protocol employs this technique effectively, in that
multiple candidate endpoints (both private and public) are
communicated between peering end hosts during an offer/answer
exchange.  Endpoints that offer the most efficient end-to-end
connection(s) are selected eventually for end-to-end data transfer.

3.3.3 被多个NAT分隔的peers

在某些包含NAT设备的拓扑中,在对拓扑结构没有确定的了解的情况下,很难在两个客户端之间建立“最佳的”p2p路由。举个例子,考虑一下图表5中的场景:

Registry and Discovery Combined
with Limited Relay
Server S
192.0.2.128:20001
|
^ Registry Session(A-S) ^ | ^ Registry Session(B-S) ^
| 192.0.2.128:20001     | | | 192.0.2.128:20001     |
| 192.0.2.1:62000       | | | 192.0.2.1:62001       |
|
+————–+
| 192.0.2.1    |
|              |
|  EIM-NAT X   |
| (Supporting  |
| Hairpinning) |
+————–+
|
+—————————-+—————————-+
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| |  192.0.2.128:20001    |     |  192.0.2.128:20001    | |
| |  192.168.1.1:30000    |     |  192.168.1.2:31000    | |
|                                                         |
| ^ P2P Session (A-B)     ^     ^ P2P Session (B-A)     ^ |
| |  192.0.2.1:62001      |     |  192.0.2.1:62000      | |
| |  192.168.1.1:30000    |     |  192.168.1.2:31000    | |
|                                                         |
+————–+                                  +————–+
| 192.168.1.1  |                                  | 192.168.1.2  |
|              |                                  |              |
| EIM-NAT A    |                                  | EIM-NAT B    |
+————–+                                  +————–+
|                                                        |
| ^ Registry Session(A-S) ^    ^ Registry Session(B-S) ^ |
| |  192.0.2.128:20001    |    |  192.0.2.128:20001    | |
| |     10.0.0.1:1234     |    |     10.1.1.3:1234     | |
|                                                        |
| ^ P2P Session (A-B)     ^    ^  P2P Session (B-A)    ^ |
| |  192.0.2.1:62001      |    |  192.0.2.1:62000      | |
| |     10.0.0.1:1234     |    |     10.1.1.3:1234     | |
|                                                        |
Client A                                                  Client B
10.0.0.1:1234                                        10.1.1.3:1234

Figure 5: Use of Hairpinning in setting up direct communication

假设NAT X是由大型的ISP部署的一个EMI-NAT,用来将大量的用户复用到少数的公有IP地址上,NAT A和NAT B是两个ISP的用户独立部署的小型的用户NAT网关,用来将它们的私有家庭网络复用到他们分别由ISP提供的IP地址上。NAT A和NAT B所使用的”公有”IP地址对于ISP的地址域来说是私有的,而相应的,客户端A和客户端B的地址对于NAT A和NAT B来说也是私有的。像上一节一样,服务器S也是用来注册,发现和有限制的中继的。peer主机使用服务器传递连接初始化控制信息,而不是所有的点到点信息。

现在假设client A和client B要建立直接的p2p-udp连接。最佳方法是client A发送信息到client B在NAT B处的公有地址,这个地址在ISP的地址池中是192.168.1.2:31000,client B发送信息到client A在NAT A处的公有地址,也就是192.168.1.2:31000。不幸的是,A和B没有办法获取这些地址,因为server S只能观察到客户端的公有地址,192.0.2.1:62000和192.0.2.1:62001。即使A和B通过某种方式获取了这些地址,也不能保证这些地址一定可用,因为这些在ISP的私有地址池中分配的地址很有可能和客户端的私有地址池中分配的不相关的地址冲突。客户端因此别无选择只能使用它们由S观察到的全局公有endpoint来进行p2p通信,并且依赖NAT X来提供hairpinning。

3.4 TCP打孔技术

在这一节里,我们将会讨论“TCP打孔”技术,用来在一对都处在NAT设备后面的节点之间建立直接的TCP连接。和UDP打洞打孔技术一样,TCP打孔技术也依赖于EIM-NAT的一些属性,允许设计恰当的p2p程序通过在NAT设备上打孔来建立彼此之间的直接连接,即使通信的主机都处在NAT设备之后也一样奏效。这种技术有时也被叫做“TCP同时打开”。

很多TCP会话都以一个endpoint发送SYN数据包,接收方回复一个SYN-ACK数据包开始。然而两个endpoint同时打开TCP会话也是可以的,即同时发给对方一个SYN数据包,然后每一方收到后回复一个ACK数据包。这个过程被称为“TCP同时打开”。然而在很多系统上包括一些NAT设备上,“TCP同时打开”技术并不能正确的执行。如果一个NAT设备从私有网络外部收到一个试图创建incoming TCP连接的TCP SYN数据包,NAT设备通常丢弃这个SYN数据包,或者回复一个TCP RST数据包,以此来拒绝连接。在SYN超时或者连接重置的情况下,程序endpoint就会继续重新发送SYN包,直到对端peer和它做同样的事情。

我们考虑NAT设备支持“TCP同时打开”会话的情况。如果NAT设备认为一个SYN数据包的源endpoint和目的endpoint是和一个活动的TCP连接相关的,那么这个SYN数据包到达NAT设备的时候,NAT设备就会允许这个包通过。特别的,如果一个NAT设备已经看到并且使用相同的地址和端口号转换了一个outgoint连接,它就会认为这个会话是活动的,并且允许incoming SYN包通过。如果client A和client B各自向发起的outgoing TCP连接中另一方超时了,那么每个客户端的SYN包在到达对方的NAT设备之前都通过了自己本地的NAT设备,那么一个TCP连接就会建立起来。

出于下面的原因,这种技术可能并不会一直有效。如果任何一个SYN包到达远程NAT太快(在这个peer节点有机会发送SYN包之前),那么远程NAT设备可能会丢弃这个SYN包或者发送一个RST包来拒绝这个SYN包。这会导致本地NAT设备立即结束这个新的NAT会话,或者开始会话结束的超时等待,在超时结束的时候结束这个会话。即使peer节点继续同步地发起SYN重传尝试,如果一个NAT会话处于一种(end-of-session timeout)会话结束超时状态,一些远程的NAT主机可能允许incoming SYN通过,这就有可能会阻止TCP连接的建立。

事实上,大多数的NAT设备(超过50%)都支持端点独立(Endpoint-Indenptent)映射,并不会发送ICMP错误或者RST包来响应未经同意的incoming SYN包。结果是,对于大多数的TCP连接尝试,TCP同时打开技术在穿越NAT设备的时候并不会生效。

3.5 UDP端口预测

在一些支持端点独立映射的NAT存在的情况下,仍然有一种UDP打孔技术的变种可以用来创建p2p-UDP连接。这种方法有时候被称为”N+1″技术[BIDIR],Takeda[SYM-STUN]详细讨论了这种方法。这种方法分析NAT的行为并尝试预测在将来的会话中它将给分配的公网端口号。分配的公有端口号一般是可预测的,因为大多数的NAT映射端口的分配是按顺序来的。

考虑图表6中的避场景。两个客户端A和B,每一个都处于一个单独的NAT后面,并且同公共服务器S建立了单独的UDP连接。公共服务器有公网可寻址的IP地址,用来注册和发现。NAT后面的主机在服务器上注册他们的endpoint。peer主机通过这台服务器发现NAT后面主机的endpoint。

Registry and Discovery
Server S
192.0.2.128:20001
|
|
+—————————-+—————————-+
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| | 192.0.2.128:20001     |     |  192.0.2.128:20001    | |
| | 192.0.2.1:62000       |     |  192.0.2.254:31000    | |
|                                                         |
| ^ P2P Session (A-B)     ^     ^  P2P Session (B-A)    ^ |
| | 192.0.2.254:31001     |     |  192.0.2.1:62001      | |
| | 192.0.2.1:62001       |     |  192.0.2.254:31001    | |
|                                                         |
+———————+                       +——————–+
| 192.0.2.1           |                       |        192.0.2.254 |
|                     |                       |                    |
|    NAT A            |                       |        NAT B       |
| (Endpoint-Dependent |                       | (Endpoint-Dependent|
|  Mapping)           |                       |  Mapping)          |
+———————+                       +——————–+
|                                                         |
| ^ Registry Session(A-S) ^     ^ Registry Session(B-S) ^ |
| |  192.0.2.128:20001    |     |  192.0.2.128:20001    | |
| |     10.0.0.1:1234     |     |     10.1.1.3:1234     | |
|                                                         |
| ^ P2P Session (A-B)     ^     ^ P2P Session (B-A)     ^ |
| |  192.0.2.254:31001    |     |  192.0.2.1:62001      | |
| |     10.0.0.1:1234     |     |     10.1.1.3:1234     | |
|                                                         |
Client A                                                 Client B
10.0.0.1:1234                                        10.1.1.3:1234

Figure 6: UDP Port Prediction to set up direct connectivity

NAT A为A和S的通信会话分配了它的UDP端口62000,NAT B为B和S之间的会话分配了它的端口31000。通过和服务器S通信,A和B获取了对方由服务器S观察到的公有endpoint。client A现在向地址192.0.2.254的端口31001发送UDP消息(注意端口的增长),client B同时开始向地址192.0.2.1的端口62001发送UDP消息。如果NAT A和NAT B分新的会话连续地分端口,并且如果A-S和B-S的会话建立之后并没有经过太长时间,那么A和B之间的就会建立起来双向的可用通信信道。A发给B的消息将会导致NAT A打开一个新的会话,NAT A将会按我的期望地分配端口号62001,因为62001是它刚才分配给A和S的端口的下一个端口。同样,B发送给A的消息也会导致NAT B打开一个新的会话,NAT B为这个新的会话会配了端口号31001。如果两个客户端都正确地猜到了它们各自对应的NAT分配给新会话的端口号,那么一个双向的UDP通信信道就可以建立起来了。

很明显,很多因素会导致这种策略失败。如果任何一个NAT预测到的端口恰好已经被分配给了一个不相关的会话,那么NAT就会略过这个端口号,那么连接尝试就会失败。如果任何一个NAT有时候或者一直都不是按顺序地分配端口号,那么这种策略也会失败。如果NAT A后面的另一个客户端(不是A)在A同服务器S建立连接之后,并且还发送第一条消息给B之前,打开一个新的outgoing UDP会话到任何外部目的地,那么这个不相关的客户端就会不经意地“偷走”这个我们需要的端口号。因此这种方法在任何一个相关的NAT过载的情况下都可能不会生效。

既然实现了这种策略的程序即使有一个NAT使用了端点独立的映射时仍然需要能够运行,程序就需要预先知道每一个端点所涉及的NAT都是什么类型,并且相应地修改它们的行为,这就提高了算法的复杂度,同时也增大了网络的弱性。最后,端口预测方法在每一个客户端都处在两级或者更多级的NAT之后很少能够工作。

3.6 TCP端口预测

有一种“TCP打孔技术”的变种可以创建穿越依赖性映射NAT的p2p-TCP直接会话。不幸的是,这种方法相比如刚才介绍的UDP端口映射方法相比更加脆弱,对时间更加敏感。首先,预测到的一个NAT可能分配的端口可能是错的。其实,如果任何一个客户端的SYN包到达对方的NAT设备太快,那么远程NAT设备就可能丢掉SYN包或者发送一个RST包,导致本地的NAT设备转而关闭新的会话,接着徒劳地使用相同的端口继续进行失败的SYN重传。

4. Recent Work on NAT Traversal

5. Summary of Observations

5.1  TCP/UDP打孔(TCP/UDP Hole Punching)

TCP/UDP打孔看上去是现存的在两个都处于NAT后面的节点之间建立基于TCP/UDP的p2p通信的最有效方法。在很多种现存的NAT上都广泛使用了这项技术。然而程序在直接通信不能建立的情况下需要随时准备好回归到最简单的中继方式进行通信。

使用TCP/UDP打孔技术有一点需要注册,它只对转换NAT是EIM-NAT的情况生效。当参与路由的NAT设备不是EIM-NAT的时候,程序就不能够重用已经建立的端点映射和不同的外部目的地进行通信,这种技术就会失败。然而现在在Internet中部署的NAT大多数都是EIM-NAT,那就保证了TCP/UDP打孔技术广泛适用。不管怎样,仍然有大量的部署的NAT使用端点依赖性映射,但却不支持TCP/UDP打孔。

5.2 使用端点依赖性映射的NAT(NATs Employing Endpoint-Dependent Mapping)

端点依赖性映射的NAT对于像web浏览器这种cs程序来说并不是问题,它只需要创建outgoing连接。然而在近期像IM和VOIP这样的p2p程序现在广泛应用。使用端点依赖性映射的NAT对p2p程序的一些技术并不适用,像TCP/UDP打孔技术在穿越这些NAT设备的时候就会失败。

5.3 peer发现

应用程序peer可以出现在同一个NAT域内,也可以处于不同的NAT域内。为了让所有的peer发现程序的endpoint,一个程序可以选择把它自己的私有endpoint和公有endpoint到一台公共服务器上注册。

5.4 发夹(Hairpinning)

支持发夹技术有很大的好处,它可以允许EIM-NAT后面的主机和同一个NAT设备之后的其它主机通过它们的公有的,可能被转换的endpoints进行通信。支持发夹技术在多级NAT的场景中第一次部署了大容量的NAT的情况是非常有用的。像3.3.3节描述的那样。处在相同第一级NAT但不同第二级NAT后面的主机无法使用TCP/UDP打孔技术和彼此进行通信。

分类: Protocol 标签: , ,

对于飞信文件p2p文件传输的疑惑

四月 27th, {2010 1 条评论 9,502 人阅读过  

近期在写飞信文件传输部分的代码,又分析了一下飞信文件传输的协议。

飞信的文件传输应该有三种模式,p2p,relay和block这三种方式。block我没弄明白是怎么回事,其它两种是比较熟悉的,p2p现在应该算是一种比较成熟的技术了,年轻的时候不知道NAT是怎么回事,以为p2p简单的只需要一台主机充当server的角色,另一台主机充当client的角色,然后client向服务器发送连接(TCP)或者直接发送数据(UDP)就可以了,可当后来知道了在这个主流的IPv4世界里面,NAT的存在给p2p程序带来了很大的麻烦,两台主机之间建立连接并不像想象中地那么简单,数据流必须要能成功地穿越NAT设备,当然也有可能存在疑问,为什么连接一台web服务器的时候不需要考虑NAT的存在呢?因为web服务器一般是位于公网的,它具有一个公网的地址,任何一种NAT都可以允许它隐藏下的内网设备主动访问公网,并且会允许回传的数据流,而p2p程序面临的问题就是两台主机可能同时处于私有网络(无论是同一个私有网络还是不同的私有网络),它们直接建立连接的时候,NAT设备就很有可能会丢掉他们的数据包,导致连接失败,这个时候p2p穿越成为一种必不可少的手段。

在进行p2p穿越之前确定主机与NAT设备的相对位置是一种很好的策略,可以根据它们的相对位置来决定p2p-NAT穿越的具体策略,确定p2p和NAT设备的相对位置实际上是一种很简单的操作,IETF为我们提供了一处轻量级的协议STUN(前两篇文章做了比较详细地说明),之间对STUN这个协议的具体细节并没有了解太多,昨天读了一下rfc,然后写程序测试了一下,后来苦于在国内找不到免费的STUN服务器程序只简单了完成了Binding Request的发送和Binding Response的最简单接收,各个属性的实现原理都无法通过程序来一一检测,后来就突然想到了飞信,飞信的p2p无疑也要进行上面的一系统操作来使两台主机之间建立起p2p连接,那飞信的STUN服务器???猜测始终不如亲眼证实,打开飞信的配置文件(就是那个xml),果然在里面找到了stun-server-urls这一项,它用stun-server用的是stunserver.org,这是一个国外的免费服务器,于是便解决了我一直以来对于飞信文件输过程的一个疑惑,当我在教育网私有网络内向公网发起文件传输请求时,不管我请求的方式是不是p2p,最终进行传输所用的始终都是中继的方式,HTTP中继是一种效率多低的传输方式啊,不考虑HTTP服务器的带宽和负载,单单是上行传输的带宽限制就可以把文件传办理的速率拉低。所以我不明白的是为什么飞信不自己开发一个stun服务器,而是要用stunserver.org这个国外的免费服务器,这样对于教育网的用户来说是很无奈的。

也可能我对飞信了解还不够,但它确实用了stunserver.org,这是我不能理解的。

分类: My Life 标签: , , ,

STUN协议的C程序

四月 26th, {2010 2 条评论 14,059 人阅读过  

写了一个小程序测试STUN协议,只可惜国内没有可用的STUN Server,UDP协议又不能穿透HTTP代理,所以我在教育网内没法测试更多功能了,只能写这么多,发出去的包wireshark显示为STUN,但由于测试用Server是国外的服务器,不加代理没法访问,所以就没办法了…

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
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
 
#define STUN_SERVER_IP "132.177.123.13"
#define STUN_SERVER_PORT 3478
 
int socketfd;
 
struct stun_header{
	unsigned short type;
	unsigned short length;
	unsigned int transId1;
	unsigned int transId2;
	unsigned int transId3;
	unsigned int transId4;
};
 
struct mapped_address{
	unsigned padding : 8;
	unsigned family : 8;
	unsigned short port;
	unsigned int address;
};
 
int 
init_socket(){
	socketfd = socket(AF_INET , SOCK_DGRAM , 0);
}
 
int 
stun_rand(){
	srand(time(NULL));
	return rand();
}
 
struct stun_header* 
build_stun_header(unsigned short type){
 
	struct stun_header *header ;
 
	header = (struct stun_header*)malloc(sizeof(struct stun_header));
	header->type = htons(type);
	header->length = 0;
	header->transId1 = stun_rand();
	header->transId2 = stun_rand();
	header->transId3 = stun_rand();
	header->transId4 = stun_rand();
 
	return header;
}
 
int 
udp_send(void *data , int len
		, const char *ip , int port){
 
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(ip);
	addr.sin_port = htons(port);
 
	return sendto(socketfd , data , len , 0
			, (struct sockaddr*)&addr , sizeof(struct sockaddr));
 
}
 
int 
main(int argc , char *argv[]){
 
	init_socket();
	struct stun_header recv_header;
	struct sockaddr_in addr;
	size_t length;
 
	struct stun_header* header
		= build_stun_header(0x0001);
 
	udp_send(header , sizeof(struct stun_header)
			, STUN_SERVER_IP , STUN_SERVER_PORT);
 
	length = sizeof(struct sockaddr);
	recvfrom(socketfd , (void*)&recv_header , sizeof(recv_header) , 0
			, (struct sockaddr*)&addr , &length);
 
	return 0;
}

分类: C/C++ 标签: , , ,

STUN协议简介(部分翻译自rfc3489)

四月 26th, {2010 1 条评论 10,766 人阅读过  

STUN( Simple Traversal of User Datagram Protocol (UDP) Through Network Address Translators (NATs))是一种轻量级的协议,它允许应用程序发现它们和公网之间是否存在NATs和防火墙,并确定NATs和防火墙的类型。它也可以让应用程序确定NAT分配给它们的公网IP地址和端口号。STUN是一种client-server的协议,也是一种request-response的协议,STUN的默认端口是3478。

NAT的类型:

Full Cone NAT:

所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP地址和端口号。而且任何一个外网主机都可以通过这个映射的外网IP和端口号向这台内网主机发关怉。

Restricted Cone:

它也是所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号。和Full-Cone NAT不同的是,一个拥有IP地址X的外网主机如果想要给内网主机发送数据包,必须是这台内网主机之前给IP地址X发送过数据包才可以。

Port Restricted Cone:

它和Restricted Cone很相似,只不过它包括端口号,也就是,一台IP地址X和端口P的外网主机想给内网主机发送数据包,必须是这台内网主机之前给这个IP地址X和端口P发送过数据包才可以。

Symmetric:

对称NAT就是,所有从同一个内网IP和端口发送到一个特定的目的IP和端口的请求,都会被映射到同一个IP和端口。如果同一台主机用相同的源地址和端口号发送数据包,但是发往不同的目的地,NAT将会使用不同的映射。进一步说,只有当外网主机收到内网主机发送来的数据包之后才能向内网主机往回发送数据包。

STUN Binding Request使用UDP协议发送到STUN服务器,当Binding Request消息到达服务器的时候它可能经过了一个或者多个NAT。结果是STUN服务器收到的request消息的源IP地址被映射成最靠近STUN服务器的NAT的IP地址,STUN服务器把这个源IP地址和端口号复制到一个Bind Response消息中,通过发送回拥有这个IP地址和端口号的客户端,对于上面提到的所有类型的NAT,这个消息都会到达客户端。

当STUN客户端收到STUN Binding Response消息之后,它会将自己发送Request时bind的本地IP地址和端口号同Response消息中的IP地址端口号进行比较,如果不匹配,就表示客户端正处于一个或者多个NAT的前面。在Full-Cone NAT的情况下,在STUN Response消息中的IP地址和端口是属于公网的,公网上的任何主机都可以使用这个IP地址和端口号向这个应用程序发送数据包,应用程序只需要在刚才发送STUN Binding Request的IP地址和端口上监听即可。

当然,主机可能并不在一个full-core NAT的前面,实际上,它并不知道自己在一个什么类型的NAT的前面。为了确定NAT的类型,客户端使用附加的STUN Binding Request.具体过程是很灵活的,但一般都会像下面这样工作。客户端再发送一个STUN Binding Request,这次发往另一个IP地址,但是使用的是跟上一次同一个源IP地址和源端口号,如果返回的数据包里面的IP地址和端口号和第一次返回的数据包中的不同,客户端就会知道它是在一个对称NAT的前面。客户端为了确定自己是否在一个完全锥形NAT的前面,客户端可以发送一个带有标志的STUN Binding Request,这个标志告诉STUN Server另一个IP地址和端口发送Response,这个IP地址和端口要和刚才收到Request的IP地址和端口不同。换句话说,如果客户端使用x:y的IP地址:端口对向A:B的IP地址:端口对发送Binding Request,STUN Server会使用源IP地址和源端口号为C:D的地址对向X:Y发送Response.如果客户端收到了这个Response,它就知道它是在一个Full-Cone NAT前面。

STUN协议允许客户端请求服务器从收到Request的IP地址往回发Binding Response,但是要使用不同的端口号。这可以用来检查客户端是在Port Restricted Cone NAT的前面还是在Restricted Cone NAT的前面。

STUN 消息是使用大端字节流编码的TLV(type-length-value).所有的STUN消息都以一个STUN头开始,紧跟着STUN的载核数据(Payload)。Payload是一系列的STUN属性集合,它们取决于STUN消息的类型。STUN消息的type可以是Binding Request,Binding Response,Binding ERROR Response, Shared Secret Request,Shared Secrect Response 或 Shared Secret Error Response.

Transaction ID的作用是将请求(Request)和响应(Response)联系起来。长度字段代表STUN Payload数据的整个长度。Shared Secret Requests一直都是承载于TCP之上发送的(实际上,是使用了承载于TCP这上的TLS发送的)。

STUN协议也定义了很多的STUN属性。第一个是MAPPED-ADDRESS属性,它是一个IP地址和端口对,Binding Response里面一直都有它,它代表了服务器在Binding Request中看到的源IP地址和源端口号。还有一个RESPONSE-ADDRESS属性,包含一个IP地址和端口。RESPONSE-ADDRESS可以被放到Binding Request中,它告诉服务器Binding Request将会被发送到哪里。它是可选的,当不填写的时候,Binding Request会被卧发送到Binding Request的源IP地址和源端口号。

第三个属性是CHANGE-REQUEST 属性,它包含了两个flag,这两个flag控制用来发送Response的IP地址和端口号。这两个标志被称为“change IP”和“change port”标志,CHANGE-REQUEST标志只允许在Binding Request中出现,在确定客户端是在Restricted Cone NAT之前还是Port Restricted Cone NAT之前的时候,这两个标志是很有用的。它们指示Server从不同的源地址和源端口发送Binding Response。在Binding Request中CHANGE-REQUEST属性是可选的。

第四个属性是CHANGED-ADDRESS属性,它出现在Binding Response中。如果客户端请求使用”change IP”和”change port”行为,它会通知客户端将会使用的源IP地址和源端口号。

第五个属性是SOURCE-ADDRESS属性,它只出现在Binding Response中,它指示发送Response的源IP地址和源端口号,它在检测两个NAT配置的时候是很有用的。

第五个是USERNAME,它只在Shared Secret Response中出现,忽略了…

第六个属性是ERROR-CODE属性,它出现在Binding Error Response和Shared Secret Error Response中,它指出发生的错误。

忽略了三四个属性…..

消息头:
所有的STUN消息都包含20个字节的消息头:
0                                    1                                  2                                     3
0  1  2  3  4  5  6 7 8  9  0  1  2  3  4  5 6 7 8  9 0  1  2 3 4 5 6 7  8  9  0  1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          STUN Message Type                   |        Message Length                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Transaction ID
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Message Type可以取下面这些值:

0×0001 : Binding Request
0×0101 : Binding Response
0×0111 : Binding Error Response
0×0002 : Shared Secret Request
0×0102 : Shared Secret Response
0×0112 : Shared Secret Error Response

Message Length是载核数据的字节长度,不包含消息头的长度。

Transaction ID是一个128位的标识符,可以随机生成。

消息属性

STUN消息头后面跟着0个或多个属性,所有的属性都是TLV形式的,包含16位的类型,16位的长度,和变长的值。

0                                 1                                  2                                      3
0  1 2  3 4  5 6 7 8  9  0  1 2  3  4  5 6  7 8  9 0  1 2  3  4  5 6  7 8  9 0  1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Type                     |                           Length                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value ….
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

下面是类型的定义:

0×0001: MAPPED-ADDRESS
0×0002: RESPONSE-ADDRESS
0×0003: CHANGE-REQUEST
0×0004: SOURCE-ADDRESS
0×0005: CHANGED-ADDRESS
0×0006: USERNAME
0×0007: PASSWORD
0×0008: MESSAGE-INTEGRITY
0×0009: ERROR-CODE
0x000a: UNKNOWN-ATTRIBUTES
0x000b: REFLECTED-FROM

MAPPED-ADDRESS

这个属性表示映射的IP地址和端口。

0 1 2 3
0 1  2 3  4  5  6 7  8  9  0 1  2 3  4 5  6 7  8  9  0 1  2 3  4 5  6 7  8 9  0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   x x x x x x x x  |        Family        |                     Port                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                 Address                                                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Family一般都是0×01,表示IPV4

RESPONSE-ADDRESS

这个属性的数据包形式跟MAPPED-ADDRESS相同。

其它的就不列出来了,具体去看rfc就好了

http://www.faqs.org/rfcs/rfc3489.html

分类: Protocol 标签: , ,