通过middlebox实施P2P通讯
[文章信息]
作者blog: http://www.blogcn.com/User6/caimouse/index.html
作者的话: 我是一边看一边随手翻的,翻的很差,本来不好意思贴出来的,可能大家看原文更明白些。希望对大家有一些帮助,我的目的是希望能和有兴趣和正在做P2P的程序员们结交朋友,谢谢大家支持。
作者的MSN:blovearcher@hotmail.com
作者的 QQ: 27443675
epan提示:下面有些图看不清楚的可以copy到记事本看,记事本不要自动换行.
1. 介绍
今天的Internet的"middleboxes"已经普遍存在, 比如象网络地址转换(NAT),主要是因为IPv4的地址空间消耗危机中产生的一个解决方案。然而,由这些"middleboxes"建立的不对称寻址和连接,已经成为点对点 (P2P)应用和协议中独特的问题, 这些应用和协议包括例如网络电话和多人在线游戏。这些话题甚至可能在使用 IPv6 协议后继续存在, 比如说在 NAT 常被当作兼容 IPv4 机制[NAT-PT]的地方,还有当NAT 不再需要之后,防火墙将会依然存在(这些问题)。
当前发展的"middleboxes"最初计划用在C/S结构中,即在那些相关的匿名客户端主动去连接有着固定IP地址和DNS域名的可连接主机。大多数的"middleboxes"实现一个不对称的沟通模型,即那些私有的内部网络上的主机可以和公网上的主机连接通讯,但是公网上的外部主机不能够和内网上的主机通讯除了被 middlebox''s 的管理者明确地配置之外。 在 NAPT 的通常情形中,在内部网络上的一位客户机在公网上并没有一个唯一独特的IP地址,但是可以在同一私网上的其他客户机一样,分享一个公网IP地址,并有NAPT管理。 这些在一台"middlebox"后的不知道名称和不易访问的内部主机对客户端软件比如网页浏览器并不是一个问题,它们之需要向外连接。而且这种不易访问的特性有时候被视为对保护隐私有利。
但是,在点对点的应用中,英特网上的主机通常会考虑要和"客户"建立直接和彼此访问的通话连接。呼叫者和被叫者可能会在不同的"middleboxes" 后面,两者都可能没有任何的固定IP地址或者其他的公网存在表现。举例来说,一个通常的在线游戏架构,是让参加游戏的主人连接到一个大家都知道的服务器上设定一些初识值,以及连接后的使用目的。然后,为了在游戏期间有更加快速和有效的游戏速度,需要建立彼此直接的连接。同样地,一个可共享的文件可能可以让一个大家都知道的资源搜索引擎发现或者查找到,但如果需要文件数据传输,就需要和那台共享文件的主机建立直接的连接了。在点对点连接时,"middlebox"就生成了一个问题。因为在"middlebox"后面的那些需要用TCP或者UDP和其他机器连接的主机通常没有固定可用的公网端口可以进行连接。 RFC 3235[ NAT-APPL]简短地说明了这个问题,但是没有提供任何的通常解决方案。
在这一份文档中,我们就 P2P/ middlebox 问题有2点说明。 首先,我们总结那些在middleboxes存在时P2P应用程序可以工作的已知方法。其次,我们提供基于这些实践的一套应用程序设计指导方针使P2P在middleboxes下应用的更健康。更进一步,我们提供的设计指导方针可以让将来的 middleboxes 更有效率的支持支援 P2P 应用。 我们的重点是要能够穿透 middleboxes,以提供更广阔和更直接的P2P 应用。
2. 术语
在本章中我们首先简要描述一下一些"middlebox"术语,我们这里的重点是两种常导致P2P应用出现问题的middlebox.
防火墙
一个防火墙限制私人内网和公众英特网之间的通讯,典型地防火墙就是丢弃那些它认为未经许可的数据包。在数据包穿越一个防火墙时,它检查但是不修改包里的 IP地址和TCP/ UDP 端口信息。
网络地址转换(NAT)
当数据包穿过NAT时,NAT不仅检查同时也修改数据的包头信息,并且允许更多的在NAT后的主机分享少数公网IP地址(通常只有1个)。
NAT通常有2种主要类型:
Basic Nat
一个Basic NAT映射一个内在的私有IP地址到一个公网IP地址,但当数据包穿过NAT时,不更换它的TCP/UDP端口号。Basic Nat通常是只用在一些具备公共IP地址池的NAT上,通过它可以地址绑定,即代表一台内部主机。
Network Address/Port Translator (NAPT)
但是最通常的,当数据包穿过NAT时,一个NAPT检查并修改它的TCP/UDP端口,那么就可以允许多台内网主机同时共享一个单独的公网IP地址了。
关于 NAT 的分类和术语,[NAT-TRAD] 和 [NAT-TERM]中有更多的信息。那些将来分类的NAPT的附加术语在较近的工作[STUN]中被定义。当一个内网的主机经过一个NAT和外部进行TCP或者UDP连接的期间,NAPT分配一个公网IP 住址和端口,以便来自外部终端响应的数据包能被NAPT接收,解释,并转发给内网的主机。这个结果是由 NAPT 建立一个(私有IP地址,私有端口)和(公网IP地址,公网端口)之间的端口绑定实现的。在这个期间NAPT将为绑定的端口执行地址翻译。一个关于P2P应用的问题是,当一个内部主机从一个私有IP,私有端口同时与外网上的多台不同的主机建立多个连接时,NAT是如何运作的。
Cone NAT
建立一个端口,把一个(私有IP,私有端口)和(公用IP,公用端口)绑定后,对于以后来自同一私有IP和端口号的应用连接,cone NAT将重复使用这个绑定的端口,只要有一个连接会话,这个绑定端口就会保持激活状态。
例如,下面图表中,推想一下客户端A通过一个cone NAT同时建立2个外部会话,从同样的内部网络终端(10.0.0.1:1234)到2个不同的外部服务器,S1和S2。cone NAT只会分配一个公用的终端,155.99.25.11:62000,都会到两个会话,并在地址转换期间确保客户端端口的一致。Basic NAT和防火墙不修改通过middlebox的数据包中的端口号,这些类型的middlebox可以被认为是一种特殊的Cone Nat。
Server S1 Server S2
18.181.0.31:1235 138.76.29.7:1235
| |
| |
+----------------------+----------------------+
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 155.99.25.11:62000 v | v 155.99.25.11:62000 v
|
Cone NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 10.0.0.1:1234 v | v 10.0.0.1:1234 v
|
Client A
10.0.0.1:1234
Symmetric NAT
对称的NAT(Symmetric NAT),与Cone NAT有明显差别,在所有会话期间中不会保持绑定(私有IP,私有端口)和(公共IP,公共端口)的端口不变。相反,它会为每个新对话重新分配一个新的公共端口。
举例来说,设想客户A从同样端口上要发起两个外部对话,一个和S1连接,另一个和S2连接。Symmetric NAT可能会为第一个会话分配一个公共的端点 155.99.25.11:62000,而为第二个会话分配一个不同的公共端点155.99.25.11:62001。为了地址转换,NAT可以区分这两个会话传输的目的,因为和这些会话有关的外部端点(就是S1、S2)是不同的,甚至在通过地址转换时丢失了客户端的目的标示。(即丢了S1、S2的IP地址NAT也知道如何区分,我是这么理解的,可能有误。)
Server S1 Server S2
18.181.0.31:1235 138.76.29.7:1235
| |
| |
+----------------------+----------------------+
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 155.99.25.11:62000 v | v 155.99.25.11:62001 v
|
Symmetric NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 10.0.0.1:1234 v | v 10.0.0.1:1234 v
|
Client A
10.0.0.1:1234
Cone NAT和Symmetric NAT之间的比较与TCP/UDP之间的比较有些类似。(TCP需要绑定,UDP不需要,Cone NAT需要绑定,Symmetric NAT不需要)
按照NAT从已知的公共IP,公共端口接收的数据限制,Cone NAT可以更进一步的进行分类。这种分类通常都是UDP连接的,因为NAT和防火墙会拒绝任何无条件的TCP连接,除非明确地以别的方式配置。
Full Cone NAT
在给一个新的外部会话建立了一个公共/私有的端口绑定后,一个full cone NAT就可以通过这个公共端口从公网上的任何外部端点接收数据通讯了。Full cone NAT也常常叫做"混合"NAT。
Restricted Cone NAT
当网络主机发一个或者几个数据包给外部主机后,一个受限的cone NAT(Restricted Cone NAT)才会接受来自这个外部(源)IP地址的数据包。受限的cone NAT有效的运用了防火墙的原理,拒绝没有要求的数据,只接收已知道的外部IP地址传来的数据。(偏开原文,大意就是网络主机需要发一个请求给外部IP地址要求要数据,NAT才会接收这个外部IP地址传来的数据,不然不接收。)
Port-Restricted Cone NAT
一个端口受限的cone NAT(Port-Restricted Cone NAT),也是这样,只接收那些网络主机曾经发给一个外部IP地址和端口相匹配的数据。一个Port-Restricted cone NAT可以和对称 NAT一样(symmetric NAT),保护内部节点不接收未被请求的数据包,但是保持一个私有端口在连接过程中不变。
(偏开原文,即不仅请求的IP和外部发来数据的IP是一样的,PORT也要是一样,这样才接收数据。对称NAT也有这个效果,但这种cone NAT保持端口不变,而对称NAT要变端口号的)。
最后,在这篇文档中我们定义一些新的术语来给middleboxes中有关P2P的行为进行分类:
P2P-Application
在这篇文档中P2P-Application被当做一个应用程序,在那些参与P2P连接的并在一个公共的登录服务器注册的终端运行,可以是一个私有端点,也可以是一个公共端点,或者两者都是,并建立一个初步的会话。
P2P-Middlebox
P2P- Middlebox就是允许P2P应用程序交换数据的的middlebox。
P2P-firewall
一个P2P-防火墙就是提供防火墙功能的P2P- Middlebox,但不具备地址映射功能。
P2P-NAT
P2P-NAT就是提供NAT功能,也提供防火墙功能的P2P- Middlebox。一台P2P- Middlebox必须为UDP传输至少提供Cone NAT功能,允许应用程序使用UDP建立完整的P2P连接。
loopback translation(自环映射)
当一台位于私有域的主机,它的NAT试图用此主机在公网的映射地址连接其他位于同样NAT后的其他主机,相当于NAT装置在数据包上做了两次NAT转换。在数据报到达目标主机之前,源主机的私有端点被转换成NAT分配的公共端点,然后把目标主机的公共端点转换成目标主机的私有端点。我们在上面提到的地址转换用一台NAT装置完成就称为"Loopback translation"。
3. 用middleboxes进行P2P通讯的技术
这段文章详细的回顾一下使用现有的middlebox实现P2P通讯的技术,主要从应用程序或协议设计上来述说。
3.1 Relaying(传输)
最可靠的,但是效率最低的,实现P2P通讯的方法就是在传输过程中,把P2P通讯看成向C/S通讯的网络一样。例如,推想2个客户主机,A和B,各自发起一个TCP或UDP的连接,连接到一个大家都知道的拥有固定IP地址的服务器S上。客户主机都在各自的私有网络中,但是它们各自的middlebox不允许自己的客户端可以直接和其它主机连接。
Server S
|
|
+----------------------+----------------------+
| |
NAT A NAT B
| |
| |
Client A Client B
不能直接连接,两个客户端就使用S服务器进行消息的传递。例如,要发送一条信息到客户端B,客户端A以C/S连接方式简单的发送一条信息到S服务器,然后S服务器使用已经和客户端B建立的C/S连接发送这条信息到客户端B。
这种方法的优势在于只要两个客户端都连在服务器上,它就是有效的。它的明显缺点是它需要了服务器的处理并占用了带宽,而且即使服务器的网络状况良好,也有一定的通讯滞后问题。TRUN协议[TURN]定义了这种P2P应用的相关方法。
3.2 逆向连接 (Connection reversal)
第二种技术是在只有一个客户位于middlebox后的条件下运用的。举例来说, 推想客户A在 NAT 后面但是客户 B 有一个全球有效的 IP 地址, 参照下面图表:
Server S
18.181.0.31:1235
|
|
+----------------------+----------------------+
| |
NAT A |
155.99.25.11:62000 |
| |
| |
Client A Client B
10.0.0.1:1234 138.76.29.7:1234
客户A有一个私有IP地址10.0.0.1,并且一个应用程序使用TCP端口1234。这个客户端和服务器S的公网IP地址18.181.0.31和端口1235建立了一个连接。NAT A为了客户端A和服务器S的会话,临时分配了一个终端地址,其TCP端口62000,它自己的IP地址是155.99.25.11:因此,服务器S认为客户端A用的是IP地址155.99.25.11,端口是62000。然而,客户端B有着自己的固定IP地址,138.76.29.7,并且在它上面的P2P应用程序可以在端口1234接收TCP连接。
现在推想客户端B想要与客户端A建立一个P2P连接会话。B可能用客户端A本身的地址,即10.0.0.1:1234,也可能用在服务器S上得到到的地址,155,99.25.11:62000,去尝试连接。然而无论在哪一种情况下,连接都会失败。第一种情况下,指向IP地址10.0.0.1的通讯包会被丢弃,因为10.0.0.1不是一个公网固定IP地址。第二种情况下,来自客户端B的TCP SYN请求包将会到达NAT A的62000端口,但NAT A将会拒绝这个请求,因为NAT A只允许向外发送数据。
在尝试和客户端A建立直接连接失败后,客户端B会利用服务器S传递一个请求,让客户端A去主动连接客户。客户端A在通过服务器S接收到传递的请求后,会使用客户端B的公共IP地址和端口建立一个TCP连接。 因为这个连接是在防火墙内部发起的,所以NAT A允许这个连接建立,而客户端B也能接收这个连接,因为它并不处于middlebox后面。当前实现P2P系统的一种技术,它有一个主要的局限性,就是它只能允许P2P中一方在NAT后面:而两方都在NAT后面的情况是很常见的,这种方法就会失败。因为这种逆向连接并不是解决问题的普遍方法,通常不推荐这个方法。应用程序可以选择试一试逆向连接,但当"向前"或"逆向"都不能建立连接时,应用程序应该能够自动的可以选择另外的连接机制,比如relaying(即3.1说的)。
3.3 UDP hole punching
第三种技术,也是这篇文章的一个重要点之一,就是被称为"UDP Hole Punching"的技术。当两个需要通讯的主机可能都在middlebox后面的时候,UDP hole punching依赖于cone NAT和普通防火墙的一些特性,允许合适的P2P应用程序以"punch holes"方式通过middlebox并且建立彼此之间直接的连接。这种技术在RFC 3027[NAT- PORT]的5.1节中简要的提及,并且在英特网[KEGEL]非证实的提到,也在最近的一些协议[TEREDO, ICE]中用到。正如名字中的所提到的,这种技术只能用于UDP连接。
我们将会考虑两个特别情况,并且考虑应用程序如何完善的处理两者之间的握手连接。第一种情况下,也是较为普通的情况,两个在不通的NAT后面的客户端要求直接的进行P2P连接。第二种情况,两台客户端位于同一个NAT后面,但不能肯定(两台客户端位于同一个NAT后面)。
3.3.1 位于不同NAT后面(Peers behind different NATs)
假设客户端A和B都有自己的私有IP地址,也都位于不同的NAT后面。P2P应用程序在A、B和服务器S上运行,用的都是UDP端口1234。A和B各自和服务器S建立UDP通讯连接,使NAT A为A的连接分配一个自己的公共端口62000,而NAT B为B的连接分配的是31000端口。
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
NAT A NAT B
155.99.25.11:62000 138.76.29.7:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
现在推想一下,客户端A想要直接和B建立一个UDP通讯会话。假设A简单的发一个UDP信息包到B的公共地址138.76.29.7:31000,然而NAT B将会丢弃这些进入的数据信息(除非它是一个FULL cone NAT),原因是NAT B和S已经建立的外部会话,而A发送的信息中的源地址和端口号是和S不匹配的(可以参照一下上面的内容,匹配才能接受)。同样,假如B发送一个条UDP数据包给A的公网地址,NAT A也会丢弃。
但是,假设A发出一个UDP数据信息给B的公网IP地址,同时也通过服务器S传递一个请求给B,要求B也发一个UDP信息给A的公网IP地址。A直接向B的公共IP地址(138.76.29.7:31000)发送的数据包会让NAT A在A的私有地址和B的公网地址之间建立了一个新的连接会话。同时,B到A的公网地址(155.99.25.11:62000)的信息会导致NAT B在B的私有地址和A的公共地址之间建立一个新的连接会话。一旦这种新的UDP连接在两者之间建立起来,客户端A和B就不需要服务器S的"介绍"就能彼此直接通讯了。
UDP hole punching技术有几个很有用的特点。一旦在两个位于middlebox后面的客户端建立了一个直接的P2P连接,在连接中的任何一方都可以扮演一个"介绍人"的角色,依次继续和另一个客户端建立连接,减少了最初的服务器S的负担。如果说有[STUN]的话,假如两个中的任意一个或两个都碰巧不在middlebox后面,上述应用程序将同样可以建立P2P通讯通道,应用程序不需要尝试明确middlebox的类型。Hole punching技术甚至可以自动的运用在多级NAT下面,多重NAT就是那些客户端需要经历多级地址转换才能进入公网。
3.3.2 位于同一NAT后(Peers behind the same NAT)
现在考虑两台客户端(但并不确定)都在同一个NAT后面的情况,因此会有私有IP地址空间。客户端A与服务器S建立一个UDP会话,NAT会分配一个公共端口62000。客户端B与服务器S也建立一个简单的连接,NAT为此分配一个公共端口62001。
Server S
18.181.0.31:1234
|
|
NAT
A-S 155.99.25.11:62000
B-S 155.99.25.11:62001
|
+----------------------+----------------------+
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
假想A和B使用UDP hole punching技术与服务器S的建立一个外部的通讯路线做为中间介绍。然后A和B将可以通过服务器S得到各自公共IP地址和端口号,然后使用这些地址各自向对方发送数据。两个客户能够以这种方式彼此通讯,只要NAT不仅仅允许外网上的主机可以和内网上的主机进行UDP传输会话,也可以允许内网上的主机可以和其他内网的主机进行UDP会话。我们在"loopback translation"中设计到这种情况,因为来自私有网络的数据包到达NAT后,会"looped back"到私有网络上就象从公网来的一样。例如,当A向B的公共IP地址发送一个UDP包,这个包的包头有一个源IP地址和端口,是10.0.0.1:1234,而目的地址是155.99.25.11.62001。NAT接受到这个包,会把源地址转换(映射)为155.99.25.11:62000(就是A的公网地址),把目的地址转换为10.1.1.3:1234,然后发给B。即使NAT支持回环映射,NAT的转换和发送步骤看上去是多余的,在A和B通讯时似乎为NAT添加了潜在的负担。
这个问题的解决方法是直接的。当A和B一开始在服务器S上交换地址信息时,它们就可以包含他们自己的IP地址和端口号,并且是可见的,对服务器S也是可见的。客户端根据它们得到的地址同时开始向对方发数据包,并建立成功的通讯。假如这两个客户端都在同一NAT后面,数据包象通讯一开始就能直接到达,而不需要通过NAT就能建立直接连接。假如这两个客户端位于不同的NAT后,到达彼此私有地址的数据包会被丢弃,但是客户端可以通过各自的公共地址来建立连接。重要的是这些数据包需要通过一些方法去鉴别,然而,在这种情况下,A发到B的私有地址的数据包完全有可能到达A私网内其他无关的终端,B发到A的包也是这样。
3.3.3 Peers separated by multiple NATs(多级NAT)
在有多重NAT设备的拓扑结构中,如果没有一些拓扑的知识,在两个客户端之间建立理想的P2P链路是不可能的。看看下面的举的例子。
Server S
18.181.0.31:1234
|
|
NAT X
A-S 155.99.25.11:62000
B-S 155.99.25.11:62001
|
|
+----------------------+----------------------+
| |
NAT A NAT B
192.168.1.1:30000 192.168.1.2:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
假设NAT X是由一个英特网服务提供者(ISP)设置的一个大型NAT,在一些公网IP地址上拥有许多用户,NAT A和B是小用户群的NAT网关,由ISP的用户自己独自配置,有各自的私有网络和用户群,使用的是ISP提供的IP地址。只有SERVER S和NAT X有自己全球固定的IP地址,而NAT A和B用的"公共"IP地址实际上是ISP地址域中私有地址,而客户端A和B的地址对NAT A和B来说也是私有的地址。每当客户端需要和服务器S建立一个外部的连接,都会导致NAT A和B和客户端建立一个单独的公共/私有连接,然后让NAT X为每个连接会话建立一个公共/私有连接。
现在推想客户A和B尝试建立一个直接的P2P UDP连接。对客户端A来说,最佳的方法是发送一个数据信息到客户端B在NAT B上,属于ISP的地址域的公共IP地址192.168.1.2:31000,对客户端B来说就是发信息到A在NAT A的公共IP地址192.168.1.1:30000(原文是NAT B,是不是笔误,还是我理解有问题?)。不幸的是,A和B并没有知道这些地址的方法,因为服务器S只能看到客户端"全局"的公共IP地址,就是155.99.25.11:62000和155.99.25.11:62001。甚至当A和B有某些方法可以得到这些地址,但他们依然不能保证这些地址是有用的,因为这些由ISP的私有地址域分配的地址可能与客户自己分配的私有地址由冲突。客户端因此没有选择只能使用由服务器S知道的公共IP地址来通讯,并且依赖NAT X来提供loopback translation。
3.3.4 Consistent prot binddings(保持端口绑定)
hole punching 技术有一个需要注意的地方:它只能工作在两台NAT都是cone NAT(或没有NAT 防火墙)的情况下,只要UDP端口还在使用,它就需要保持一个固定的端口把一个给定(私有IP,私有UDP端口)和一个(公共IP,公共UDP端口)绑定。象对称NAT一样,为每个新的连接会话分配一个新的公共端口,对一个UDP应用程序来说,为了和不同的外部通讯重用一个已经存在的地址转换是不可以的。(这边稍微有点糊涂,再多看看。) 既然cone NAT运用是相当普遍的,UDP hole punching技术应用的也相当广泛,但是还有一小部分是对等NAT配置,因此不能支持这种技术。
3.4 UDP port number prediction
有关UDP hole punching技术在上面已经被讨论过,它可以允许在一些对等NAT存在的地方也能建立P2P UDP连接会话。这种方法有时被称为"N+1"技术 [BIDIR ]并且由Takeda[SYM-STUN]详细介绍。这种方法分析NAT的工作方式并且试图预测它为将来的连接会话分配的公共端口。再次考虑那两个客户的状态,A和B,在各自分开的NAT后面,已经与一台拥有永久地址的服务器S建立了UDP连接。
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
Symmetric NAT A Symmetric NAT B
A-S 155.99.25.11:62000 B-S 138.76.29.7:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
NAT A分配一个属于自己的UDP端口62000以在A和S之间建立通讯连接,而NAT B分配一个31000端口用于在B和S之间建立连接。通过与服务器的通讯,A和B可以从服务器S上得到对方的公共IP地址和端口号。客户端A现在发送一个UDP数据包到地址138.76.29.7,端口31001(注意端口数目的增加),而客户端B同时发送一个数据包到地址的155,99.25.11,端口62001上。如果NAT A和B依次为新的连接分配端口,如果从A-S和B-S连接建立后没过多少时间,那在A和B之间的一个双向通讯通道就可以工作起来。A到B的数据包让NAT A建立一个新的连接,NAT A(所期望的)分配一个公共端口62001,因为之前A和S的连接会话用的62000端口,接下来就是62001。同样的,B到A的数据包将让NAT B打开一个新连接,并将(也是所期望的)分配一个端口31001。如果客户端可以正确的预测到NAT为新的连接分配的端口,一条双向的UDP通讯通道就会象如下图所示一样建立起来。
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
NAT A NAT B
A-S 155.99.25.11:62000 B-S 138.76.29.7:31000
A-B 155.99.25.11:62001 B-A 138.76.29.7:31001
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
显而易见有很多情况都能导致这种方法失败。假如任意一个预测的端口碰巧已经被其他无关的连接占用,NAT将会错过正确的端口,连接尝试也将失败。假如任意一个NAT有时或者总是选择非连续的端口号,这个方法也将失败。假如在A(B)建立了它和S的连接之后,但在发送第一个数据包到B(A)之前,一个不同的客户端在NAT(也或者B)打开一个新的外部连接到任何外部主机,无关的客户端会不注意的"偷"了(A TO B或者B TO A)所要求的端口。因此在任一NAT都包含不止一台客户端时,这种方法很少使用。
实际上,如果那些NAT是cone NAT,或者一个是cone NAT,另一个是对称NAT,这种情况下的P2P应用程序依然需要工作,应用程序需要实现查明在任何一个上与end [STUN]有关的NAT是哪一钟,并按此来修改它的工作方式,这样增加了算法的复杂程序并让网络变的脆弱。最终,假如任何一方客户端在2级以上的NAT下并且离客户端最近的NAT是对称的,预测端口的方式是无法工作的。对所有这些原因来说,应用程序是无法实现这种方法的,在这里被提及是为了历史和信息目的(就是告诉大家有这么回事,我想)
3.5. Simultaneous TCP open(TCP同时打开)
在一对节点都在已存在middlebox后,有一种建立直接P2P TCP连接的方法有时候会被使用。大多数TCP连接都是从一个终端发从一个SYN包到另一个终端,另一个中断同步响应一个SYN-ACK包。无论怎样,对于两个终端来说,同时通过发送同步包到对方然后用一个ACK包应答来建立一个TCP连接是可行的。这种过程就被称为"simultaneous open"(同时打开)
如果一个middlebox从尝试建立一个TCP连接的私有网络的外面接受一个TCP SYN包,middlebox通常以丢弃这个SYN包或者发送一个TCP RST(连接复位)包的方式来拒绝这个连接尝试。但是,如果同步包与源和目的地址端口一起到达,那么会让middlebox相信一个TCP连接已经建立起来,然后middlebox将会允许数据包通过。特别是如果middlebox刚刚得到并转换了一个从同样地址和端口来的SYN包,它将认为连接是成立的并允许进来的SYN通过。如果客户端A和B能彼此预测公共端口,它们各自的middlebox将分配下一个TCP连接端口,如果其中一个客户端和另一个客户端建立一个外部的TCP连接,可以在对方SYN到达本地middlebox之前就发送SYN包通过它本地自己的middlebox,那么P2P TCP连接就可以工作了。
令人遗憾的是,这个方法也可能比上面说的UDP端口号预测方法更脆弱并对时效更加敏感。首先,除非在进行TCP连接时,两个middleboxes是简单的防火墙或者cone NAT,在各自尝试猜测公共端口号来让NAT分配新的连接时,和上面(UDP端口预测)说到的完全一样的事情会导致连接失败。另外,如果有一方的客户发送的同步包太迅速的到达对面的middlebox,远端middlebox可能会用一个RST包拒绝SYN包,接下来就会导致本地的middlebox关闭对话并且在将来SYN重发时使用了相同但无用的端口号。最终,对simultaneous open的支持作为一个TCP的特殊应用,没有在广泛的系统中被使用。因此,这个方法也只为历史因素在这里被同样提及;它不建议被应用程序使用。在现有NAT上想要实现P2P直接通讯的应用程序应该使用UDP。
4. Application design guidelines(应用程序设计思路)
4.1 What works with P2P middleboxes(如何和P2P middlebox一起工作)
既然UDP hole punching是在两个都位于NAT后的主机之间建立P2P直接通讯方法中最有效率的一个,并且它可以在多种现有NAT上使用,如果要求建立有效的P2P通讯,通常建议这种方法,但当直接通讯不能建立时,就得依靠简单的传播。(还不怎么明白)
4.2 Peers behind the same NAT(主机在同一个NAT后面)
实际上有相当数量的用户不止2个IP地址,会有3个或者更多的IP地址。这样的话,告诉登录服务器究竟是哪一个地址变的就有些困难了。因此在这种情况下,应用程序应该发送它所有的IP地址。
4.3 Peer discovery(主机发现)
应用程序会发送一些数据包到几个地址,以发现哪一个地址是最合适的,应用程序可能变成(后面的不太明白,自己理解吧,sorry), 由于主机可能会不恰当的选择一个路由地址当做一个内部局域网(例如11.0.1.1,已经被DOD网络分配,DOD是一种网络模型)。因此应用程序应该小心的发送推测的呼叫包。
申请把包送到几地址发现, 哪一个适宜适合一规定贵族使用可能成为一个乱扔净价的'' 空间废品''的显著的源头,
4.4 TCP P2P applications (TCP P2P应用程序)
被程序员们广泛使用的SOCKET API,常用于C/S结构应用设计中。在它的通常使用方式中,一个SOCKET能绑定一个TCP或UDP端口。一个应用程序不会被允许用同样的端口(TCP or UDP)和多个SOCKET绑定来和多个外部主机同时建立连接(或)用一个SOCKET在端口上监听而其他SOCKET来建立外部连接。但是上述单个SOCKET的端口绑定限制在UDP上不是问题,因为UDP是基于数据报文的协议。UDP P2P应用程序设计者可以用recvfrom()和sendto()函数来让一个SOCKET不仅发送而且可以从多个主机上接受数据报文。
这不是TCP具有的情况。由于TCP,每个输入和输出连接都要和一个单独的SOCKET有联系。Linux Sockets API用SO_REUSEADDR选项的帮助来解决这个问题(是不是应该这么说?),这个选项好象不起作用,但可以
(在标准Single Unix里没有这个函数)。Win32 API提供了一个相同的调用SetReuseAddress。使用任何上述的选择,应用程序可以复用一个TCP端口的多个SOCKET。就是说,可以打开两个绑定在同样端口上的TCP stream Socket,一个用与listen(),另一个用与其它主机connect()
4.5 Use of midcom protocol()
如果应用程序知道它们需要穿越的middlebox并且这些middlebox实现midcom 协议,应用程序能使用midcom协议更容易的穿越middlebox。
例如,P2P应用程序需要NAT middlebox保持终端端口的绑定状态。假如middlebox可以支持midcom,P2P应用程序可以控制修改绑定端口(或者绑定地址)的参数,例如生存时间,maxidletime(?),因此应用程序不仅可以直接的连接外部主机而且也可以从外部主机接受连接;这样就不需要定期保持端口绑定的状态。当应用程序不再需要绑定,也可以使用midcom协议简单的取消绑定。
5. NAT Design Guidelines (NAT设计指导)
这部分讨论网络地址转换的设计,他们会影响P2P应用程序。
5.1 Deprecat the use of symmetric NATs (不赞成使用对等NAT)
对等NAT在那些C/S结构的应用中比如网络浏览中得到广泛应用,它们只需要建立一个向外的连接即可。但是现在,比如实时消息和语音会议等P2P应用程序被广泛的应用。对等NAT不能支持保留终端的定义并且不适用于P2P应用程序。不建议使用对等NAT来支持P2P应用程序。
一个P2P-middlebox必须在UDP通讯时具备Cone NAT的特点,允许应用程序可以建立使用UDP hole punching技术建立稳定的P2P连接。理论上,一个P2P-middlebox应该也允许应用程序既可以经过TCP,也可以通过UDP建立P2P连接。
5.2 Add incremental cone-NAT support to symmetric NAT devices (增加递增的cone-NAT以支持对等NAT设备)
一种可以让对等NAT设备扩展支持P2P应用程序的是分配它的可转让的端口空间,为一到一的连接预订一个合适的端口,为一个一到多的连接预订合适的一套不同的端口。
更进一步(未来?),一个NAT装置可以明确的被那些P2P应用程序和主机配置,因此NAT装置可以自动由正确的端口数据块来分配一个P2P端口。
5.3 Maintain consisten port bindings for UDP ports (保持UDP端口的绑定)
这份资料对NAT设计者最主要和最重要的建议是NAT保持一个固定的端口,绑定一个给定的(内部IP地址,内部UDP端口)和一个相应的(公共IP地址,公共UDP端口)
只要有任何连接存在并使用这个端口绑定,这个端口绑定就要存在。通过检查每一个包的源和目的IP地址和端口号,NAT可能会过滤关于每一个连接基础的数据包(? 俺8懂)。当在一个私有网络上的节点建立了一个新的外部连接时,使用了一个现有的已经转换过的UDP连接会话的IP地址和UDP端口号,NAT应该保证新的UDP连接作为现有连接给定一个同样的公共IP地址和UDP端口。
5.3.1 Preserving port numbers(保持端口号) (就是客户端用啥端口,NAT分配啥端口这个意思吧)
一些NAT,当建立一个新的UDP连接时,会尝试给一个相应的私有端口号分配一个同样的公共端口号,如果这个端口号碰巧是可用的。例如,假如地址是10.0.0.1的客户端A用一个从端口1234发送的数据包建立一个外部的UDP连接,NAT的公共端口1234碰巧是可用的,那么NAT会为连接使用NAT公共IP地址上的端口号1234作为转换客户端A的地址。由于如果内部网络的最多一个节点正在使用这个端口号,它是对一个NAT保持端口唯一可行的方法,这种方式可能对一些希望只用特别的UDP端口号的过去的UDP程序有帮助,但不建议应用程序依靠这种方式。
另外, 一个NAT不应该在一个新连接中保持端口号,如果确实如此将与保持公共和私有终端地址绑定的目的相抵触。例如,假定客户端A在内部的1234端口上与外部服务器S建立了一个连接,NAT A已经为这个连接分配了公共端口62000,因为端口号1234在NAT上此时是不可用的。现在假想在NAT上的端口号1234后来可以使用了,而此时A和S的连接仍然存在,客户端A用同样的内部端口1234建立一个新的连接到外部节点B上。在这种情况下,因为端口绑定已经在客户端A的端口1234和NAT的公共端口62000上建立,所以这个绑定应该继续保持,新连接也应该用62000端口作为公共端口,来对应客户端A的端口1234。NAT不应该仅仅因为端口1234变的可用了就为新的连接分配公共端口1234:这种特点不可能在任何情况下都对应用程序有帮助,由于应用程序已经对一个转换的端口号进行操作,并且它会中断应用程序用UDP hole punching技术建立P2P连接的尝试。
5.4 Maintaining consistent port bindings for TCP ports (为TCP端口保持端口绑定)
和UDP地址转换的特点一致,cone NAT也应该对TCP连接保持私有和公共IP地址,TCP端口号的绑定不变,就如上面的关于UDP描述的方式一样。保持TCP终端绑定不变将增加NAT对P2P TCP应用程序在从同样源端口建立多个TCP连接时的兼容性。
5.5 Large timeout for P2P applications (P2P程序的大超时?)
我们推荐middlebox在P2P应用时使用的最小超时大约5分钟(300秒),即,在P2P应用时为端口绑定或为分配端口时,来给middlebox配置这个空闲超时时间。当middlebox惯用目前配置时,就常常尝试使用更短的时间。但是更短的超时时间是有些问题的。考虑一个有16个终端节点的P2P应用程序,他们将每10秒发给网络一个保持活动状态的数据包以避免NAT超时。这样做是因为一个可能会以middlebox超时时间的5倍发送保持活动的数据包,在这种情况下,保持活动的包将在网络上被丢弃。
5.6 Support loopback translation(支持自环转换)
我们强烈建议middlebox 支持自环转换,允许在一台middlebox后面的主机可以和其它位于同一middlebox后的主机通过它们的可能被转换的公共端点通讯。支持自环转换是相当重要的,特别是在那些大容量多层NAT中,作为第一级的NAT。如第3.3.3 部分的描述,位于同一台一级NAT下,但是第二级NAT不同的主机无法通过UDP hole punching彼此通讯,即使全部middlebox保持端点在NAT上的映射不变,除非第一级NAT支持自环转换。