穿越NAT的p2p通信方法研究
穿越NAT的p2p通信方法研究
日期:2008-12-08 来源:P2P网 作者:未知 字体:大 中 小
|
<script src="http://www.ppcn.net/ads/baiducpro.js" type="text/javascript"></script>
内容概述:在p2p通信领域中,由NAT(Network Address Translation,网络地址转换)引起的问题已经众所周知了,它会导致在NAT内部的p2p客户端在无论以何种有效的公网ip都无法访问的问题。虽 然目前已经发展出多种穿越NAT的技术,但相关的技术文档却很少,用来证明这些技术的稳定性和优点的实际数据更少。本文的目的在于描述和分析在实际中运用 得最广泛、最可靠同时也是最简单的一种NAT穿越技术,该技术通常被称为“打洞”技术。目前,“打洞”技术已经在UDP通信领域中得到了广泛的理解和应 用,在此,也将讨论如何利用它实现可靠的p2p的TCP流通信。在收集了大量的“打洞”技术可以穿越的NAT设备和网络的数据以后,我们发现82%的已测 NAT设备支持UDP形式的“打洞”穿越,64%的已测NAT设备支持TCP流形式的“打洞”穿越。由于重量级p2p应用程序(如,VOIP、BT、在线 游戏等)的用户需求量持续上升,并且该事实也已经引起了NAT设备生产厂商的广泛关注,因此,我们认为未来会有越来越多的NAT设备提供对“打洞”穿越技 术的支持。
1、介绍
用户量高速增长以及大量安全问题的巨大压力迫使Internet技术不断向前发展,但是这些新兴的技术很大程度地增加了应用程序开发的成本和复杂 性。Internet最初的地址体系是每个节点有一个唯一不变的全局地址,可以通过该地址直接与任何其它的节点进行通信,而现如今,该地址体系已经被新的 实际上广泛使用的地址体系所替换,新的地址体系是由全局地址域和通过NAT接入全局地址域的大量私有地址域组成。在新的地址体系中(如图1所示),只有在 “main”全局地址域中的节点可以在网络中很容易地与任何其它的拥有全局地址的节点通信,因为该节点拥有全局的、唯一的、可路由的地址。在私有网络中的 节点可以与在同一个私有网络中的其它节点进行通信,并且在通常情况下可以向全局地址中的某个“著名”的节点发起TCP连接或发送UDP数据包。NAT设备 在此扮演的角色就是为从内网向公网发起的连接的节点分配临时的转发session,将来自内网的数据包的地址和端口转换为公网的地址和端口,将来自公网的 数据包的地址和端口转换为内网的端口和地址,同时NAT将屏蔽所有未经授权的来自公网的数据包。
4 关于TCP打洞技术建立穿越NAT设备的p2p的TCP连接只比UDP复杂一点点,TCP协议的“打洞”从协议层来看是与UDP的“打洞”过程非常相似 的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的 “打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的p2p技术的健壮性将比基于UDP的技术的 更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。4.1 套接字和TCP端口的重用实现基于TCP协议的p2p“打洞”过程中,最主要的问题不是来自于TCP协议,而是来自于来自于应用程序的API接口。这是由 于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序而设计的,API允许TCP流套接字通过调用connect()函数来 建立向外的连接,或者通过listen()和accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接,又能 够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个 套接字绑定到该端口的操作都会失败。为了让TCP“打洞”能够顺利工作,我们需要使用一个本地的TCP端口来监听来自外部的TCP连接,同时建立多个向外 的TCP连接。幸运的是,所有的主流操作系统都能够支持特殊的TCP套接字参数,通常叫做“SO_REUSEADDR”,该参数允许应用程序将多个套接字 绑定到本地的一个endpoint(只要所有要绑定的套接字都设置了SO_REUSEADDR参数即可)。BSD系统引入了SO_REUSEPORT参 数,该参数用于区分端口重用还是地址重用,在这样的系统里面,上述所有的参数必须都设置才行。4.2 打开p2p的TCP流假定客户端A希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知服务器S建立了TCP连接。服务器记录下来每个 联入的客户端的公网和内网的endpoints,如同为UDP服务的时候一样。从协议层来看,TCP“打洞”与UDP“打洞”是几乎完全相同的过程。1、 客户端A使用其与服务器S的连接向服务器发送请求,要求服务器S协助其连接客户端B。2、S将B的公网和内网的TCP endpoint返回给A,同时,S将A的公网和内网的endpoint发送给B。3、客户端A和B使用连接S的端口异步地发起向对方的公网、内网 endpoint的TCP连接,同时监听各自的本地TCP端口是否有外部的连接联入。4、A和B开始等待向外的连接是否成功,检查是否有新连接联入。如果 向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,客户端只需要延迟一小段时间(例如延迟一秒钟),然后重新发起连接即可,延 迟的时间和重复连接的次数可以由应用程序编写者来确定。5、TCP连接建立起来以后,客户端之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。 如果鉴权失败,客户端将关闭连接,并且继续等待新的连接联入。客户端通常采用“先入为主”的策略,只接受第一个通过鉴权操作的客户端,然后将进入p2p通 信过程不再继续等待是否有新的连接联入。
与UDP 不同的是,使用UDP协议的每个客户端只需要一个套接字即可完成与服务器S通信,并同时与多个p2p客户端通信的任务,而TCP客户端必须处理多个套接字 绑定到同一个本地TCP端口的问题,如图7所示。现在来看更加实际的一种情景,A与B分别位于不同的NAT设备后面,如图5所示,并且假定图中的端口号是 TCP协议的端口号,而不是UDP的端口号。图中向外的连接代表A和B向对方的内网endpoint发起的连接,这些连接或许会失败或者无法连接到对方。 如同使用UDP协议进行“打洞”操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP重复造成连接失败或者错误连接之类的问题。 客户端向彼此公网endpoint发起连接的操作,会使得各自的NAT设备打开新的“洞”允许A与B的TCP数据通过。如果NAT设备支持TCP“打洞” 操作的话,一个在客户端之间的基于TCP协议的流通道就会自动建立起来。如果A向B发送的第一个SYN包发到了B的NAT设备,而B在此前没有向A发送 SYN包,B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,由于A已经向B发送过SYN包,B发往A的SYN包将被看 作是由A发往B的包的回应的一部分,所以B发往A的SYN包会顺利地通过A的NAT设备,到达A,从而建立起A与B的p2p连接。4.3 从应用程序的角度来看TCP“打洞”从应用程序的角度来看,在进行TCP“打洞”的时候都发生了什么呢?假定A首先向B发出SYN包,该包发往B的公网 endpoint,并且被B的NAT设备丢弃,但是B发往A的公网endpoint的SYN包则通过A的NAT到达了A,然后,会发生以下的两种结果中的 一种,具体是哪一种取决于操作系统对TCP协议的实现:(1)A的TCP实现会发现收到的SYN包就是其发起连接并希望联入的B的SYN包,通俗一点来说 就是“说曹操,曹操到”的意思,本来A要去找B,结果B自己找上门来了。A的TCP协议栈因此会把B做为A向B发起连接connect的一部分,并认为连 接已经成功。程序A调用的异步connect()函数将成功返回,A的listen()等待从外部联入的函数将没有任何反映。此时,B联入A的操作在A程 序的内部被理解为A联入B连接成功,并且A开始使用这个连接与B开始p2p通信。由于收到的SYN包中不包含A需要的ACK数据,因此,A的TCP将用 SYN-ACK包回应B的公网endpoint,并且将使用先前A发向B的SYN包一样的序列号。一旦B的TCP收到由A发来的SYN-ACK包,则把自 己的ACK包发给A,然后两端建立起TCP连接。简单的说,第一种,就是即使A发往B的SYN包被B的NAT丢弃了,但是由于B发往A的包到达了A。结果 是,A认为自己连接成功了,B也认为自己连接成功了,不管是谁成功了,总之连接是已经建立起来了。(2)另外一种结果是,A的TCP实现没有像(1)中所 讲的那么“智能”,它没有发现现在联入的B就是自己希望联入的。就好比在机场接人,明明遇到了自己想要接的人却不认识,误认为是其它的人,安排别人给接走 了,后来才知道是自己错过了机会,但是无论如何,人已经接到了任务已经完成了。然后,A通过常规的listen()函数和accept()函数得到与B的 连接,而由A发起的向B的公网endpoint的连接会以失败告终。尽管A向B的连接失败,A仍然得到了B发起的向A的连接,等效于A与B之间已经联通, 不管中间过程如何,A与B已经连接起来了,结果是A和B的基于TCP协议的p2p连接已经建立起来了。第一种结果适用于基于BSD的操作系统对于TCP的 实现,而第二种结果更加普遍一些,多数linux和windows系统都会按照第二种结果来处理。
Peer-to-Peer Communication Across Network Address Translators
Bryan Ford
Massachusetts Institute of Technology
baford (at) mit.edu
Pyda Srisuresh
Caymas Systems, Inc.
srisuresh (at) yahoo.com
Dan Kegel
dank (at) kegel.com
J'fais des trous, des petits trous
toujours des petits trous
- S. Gainsbourg
Abstract:
1 Introduction
The Internet's new de facto address architecture is suitable for client/server communication in the typical case when the client is on a private network and the server is in the global address realm. The architecture makes it difficult for two nodes on different private networks to contact each other directly, however, which is often important to the “peer-to-peer” communication protocols used in applications such as teleconferencing and online gaming. We clearly need a way to make such protocols function smoothly in the presence of NAT.
One of the most effective methods of establishing peer-to-peer communication between hosts on different private networks is known as “hole punching.” This technique is widely used already in UDP-based applications, but essentially the same technique also works for TCP. Contrary to what its name may suggest, hole punching does not compromise the security of a private network. Instead, hole punching enables applications to function within the the default security policy of most NATs, effectively signaling to NATs on the path that peer-to-peer communication sessions are “solicited” and thus should be accepted. This paper documents hole punching for both UDP and TCP, and details the crucial aspects of both application and NAT behavior that make hole punching work.
Unfortunately, no traversal technique works with all existing NATs, because NAT behavior is not standardized. This paper presents some experimental results evaluating hole punching support in current NATs. Our data is derived from results submitted by users throughout the Internet by running our “NAT Check” tool over a wide variety of NATs by different vendors. While the data points were gathered from a “self-selecting” user community and may not be representative of the true distribution of NAT implementations deployed on the Internet, the results are nevertheless generally encouraging.
While evaluating basic hole punching, we also point out variations that can make hole punching work on a wider variety of existing NATs at the cost of greater complexity. Our primary focus, however, is on developing the simplest hole punching technique that works cleanly and robustly in the presence of “well-behaved” NATs in any reasonable network topology. We deliberately avoid excessively clever tricks that may increase compatibility with some existing “broken” NATs in the short term, but which only work some of the time and may cause additional unpredictability and network brittleness in the long term.
Although the larger address space of IPv6 [3 ] may eventually reduce the need for NAT, in the short term IPv6 is increasing the demand for NAT, because NAT itself provides the easiest way to achieve interoperability between IPv4 and IPv6 address domains [24 ]. Further, the anonymity and inaccessibility of hosts on private networks has widely perceived security and privacy benefits. Firewalls are unlikely to go away even when there are enough IP addresses: IPv6 firewalls will still commonly block unsolicited incoming traffic by default, making hole punching useful even to IPv6 applications.
The rest of this paper is organized as follows. Section 2 introduces basic terminology and NAT traversal concepts. Section 3 details hole punching for UDP, and Section 4 introduces hole punching for TCP. Section 5 summarizes important properties a NAT must have in order to enable hole punching. Section 6 presents our experimental results on hole punching support in popular NATs, Section 7 discusses related work, and Section 8 concludes.
2 General Concepts
2.1 NAT Terminology
2.2 Relaying
Relaying always works as long as both clients can connect to the server. Its disadvantages are that it consumes the server's processing power and network bandwidth, and communication latency between the peering clients is likely increased even if the server is well-connected. Nevertheless, since there is no more efficient technique that works reliably on all existing NATs, relaying is a useful fall-back strategy if maximum robustness is desired. The TURN protocol [18 ] defines a method of implementing relaying in a relatively secure fashion.
2.3 Connection Reversal
Some P2P applications use a straightforward but limited technique, known as connection reversal , to enable communication when both hosts have connections to a well-known rendezvous server and only one of the peers is behind a NAT, as shown in Figure 3 . If wants to initiate a connection to , then a direct connection attempt works automatically, because is not behind a NAT and 's NAT interprets the connection as an outgoing session. If wants to initiate a connection to , however, any direct connection attempt to is blocked by 's NAT. can instead relay a connection request to through a well-known server , asking to attempt a “reverse” connection back to . Despite the obvious limitations of this technique, the central idea of using a well-known rendezvous server as an intermediary to help set up direct peer-to-peer connections is fundamental to the more general hole punching techniques described next.
3 UDP Hole Punching
3.1 The Rendezvous Server
3.2 Establishing Peer-to-Peer Sessions
- initially does not know how to reach , so asks for help establishing a UDP session with .
- replies to with a message containing 's public and private endpoints. At the same time, uses its UDP session with to send a connection request message containing 's public and private endpoints. Once these messages are received, and know each other's public and private endpoints.
- When receives 's public and private endpoints from , starts sending UDP packets to both of these endpoints, and subsequently “locks in” whichever endpoint first elicits a valid response from . Similarly, when receives 's public and private endpoints in the forwarded connection request, starts sending UDP packets to at each of 's known endpoints, locking in the first endpoint that works. The order and timing of these messages are not critical as long as they are asynchronous.
We now consider how UDP hole punching handles each of three specific network scenarios. In the first situation, representing the “easy” case, the two clients actually reside behind the same NAT, on one private network. In the second, most common case, the clients reside behind different NATs. In the third scenario, the clients each reside behind two levels of NAT: a common “first-level” NAT deployed by an ISP for example, and distinct “second-level” NATs such as consumer NAT routers for home networks.
It is in general difficult or impossible for the application itself to determine the exact physical layout of the network, and thus which of these scenarios (or the many other possible ones) actually applies at a given time. Protocols such as STUN [19 ] can provide some information about the NATs present on a communication path, but this information may not always be complete or reliable, especially when multiple levels of NAT are involved. Nevertheless, hole punching works automatically in all of these scenarios without the application having to know the specific network organization, as long as the NATs involved behave in a reasonable fashion. (“Reasonable” behavior for NATs will be described later in Section 5 .)
3.3 Peers Behind a Common NAT
First consider the simple scenario in which the two clients (probably unknowingly) happen to reside behind the same NAT, and are therefore located in the same private IP address realm, as shown in Figure 4 . Client has established a UDP session with server , to which the common NAT has assigned its own public port number 62000. Client has similarly established a session with , to which the NAT has assigned public port number 62005.
Suppose that client uses the hole punching technique outlined above to establish a UDP session with , using server as an introducer. Client sends a message requesting a connection to . responds to with 's public and private endpoints, and also forwards 's public and private endpoints to . Both clients then attempt to send UDP datagrams to each other directly at each of these endpoints. The messages directed to the public endpoints may or may not reach their destination, depending on whether or not the NAT supports hairpin translation as described below in Section 3.5 . The messages directed at the private endpoints do reach their destinations, however, and since this direct route through the private network is likely to be faster than an indirect route through the NAT anyway, the clients are most likely to select the private endpoints for subsequent regular communication.
By assuming that NATs support hairpin translation, the application might dispense with the complexity of trying private as well as public endpoints, at the cost of making local communication behind a common NAT unnecessarily pass through the NAT. As our results in Section 6 show, however, hairpin translation is still much less common among existing NATs than are other “P2P-friendly” NAT behaviors. For now, therefore, applications may benefit substantially by using both public and private endpoints.
3.4 Peers Behind Different NATs
Suppose clients and have private IP addresses behind different NATs, as shown in Figure 5 . and have each initiated UDP communication sessions from their local port 4321 to port 1234 on server . In handling these outbound sessions, NAT has assigned port 62000 at its own public IP address, 155.99.25.11, for the use of 's session with , and NAT has assigned port 31000 at its IP address, 138.76.29.7, to 's session with .
In 's registration message to , reports its private endpoint to as 10.0.0.1:4321, where 10.0.0.1 is 's IP address on its own private network. records 's reported private endpoint, along with 's public endpoint as observed by itself. 's public endpoint in this case is 155.99.25.11:62000, the temporary endpoint assigned to the session by the NAT. Similarly, when client registers, records 's private endpoint as 10.1.1.3:4321 and 's public endpoint as 138.76.29.7:31000.
Now client follows the hole punching procedure described above to establish a UDP communication session directly with . First, sends a request message to asking for help connecting with . In response, sends 's public and private endpoints to , and sends 's public and private endpoints to . and each start trying to send UDP datagrams directly to each of these endpoints.
Since and are on different private networks and their respective private IP addresses are not globally routable, the messages sent to these endpoints will reach either the wrong host or no host at all. Because many NATs also act as DHCP servers, handing out IP addresses in a fairly deterministic way from a private address pool usually determined by the NAT vendor by default, it is quite likely in practice that 's messages directed at 's private endpoint will reach some (incorrect) host on 's private network that happens to have the same private IP address as does. Applications must therefore authenticate all messages in some way to filter out such stray traffic robustly. The messages might include application-specific names or cryptographic tokens, for example, or at least a random nonce pre-arranged through .
Now consider 's first message sent to 's public endpoint, as shown in Figure 5 . As this outbound message passes through 's NAT, this NAT notices that this is the first UDP packet in a new outgoing session. The new session's source endpoint (10.0.0.1:4321) is the same as that of the existing session between and , but its destination endpoint is different. If NAT is well-behaved, it preserves the identity of 's private endpoint, consistently translating all outbound sessions from private source endpoint 10.0.0.1:4321 to the corresponding public source endpoint 155.99.25.11:62000. 's first outgoing message to 's public endpoint thus, in effect, “punches a hole” in 's NAT for a new UDP session identified by the endpoints (10.0.0.1:4321, 138.76.29.7:31000) on 's private network, and by the endpoints (155.99.25.11:62000, 138.76.29.7:31000) on the main Internet.
If 's message to 's public endpoint reaches 's NAT before 's first message to has crossed 's own NAT, then 's NAT may interpret 's inbound message as unsolicited incoming traffic and drop it. 's first message to 's public address, however, similarly opens a hole in 's NAT, for a new UDP session identified by the endpoints (10.1.1.3:4321, 155.99.25.11:62000) on 's private network, and by the endpoints (138.76.29.7:31000, 155.99.25.11:62000) on the Internet. Once the first messages from and have crossed their respective NATs, holes are open in each direction and UDP communication can proceed normally. Once the clients have verified that the public endpoints work, they can stop sending messages to the alternative private endpoints.
3.5 Peers Behind Multiple Levels of NAT
Now suppose and attempt to establish a direct peer-to-peer UDP connection via hole punching. The optimal routing strategy would be for client to send messages to client 's “semi-public” endpoint at NAT , 10.0.1.2:55000 in the ISP's addressing realm, and for client to send messages to 's “semi-public” endpoint at NAT , namely 10.0.1.1:45000. Unfortunately, and have no way to learn these addresses, because server only sees the truly global public endpoints of the clients, 155.99.25.11:62000 and 155.99.25.11:62005 respectively. Even if and had some way to learn these addresses, there is still no guarantee that they would be usable, because the address assignments in the ISP's private address realm might conflict with unrelated address assignments in the clients' private realms. (NAT 's IP address in NAT 's realm might just as easily have been 10.1.1.3, for example, the same as client 's private address in NAT 's realm.)
The clients therefore have no choice but to use their global public addresses as seen by for their P2P communication, and rely on NAT providing hairpin or loopback translation. When sends a UDP datagram to 's global endpoint, 155.99.25.11:62005, NAT first translates the datagram's source endpoint from 10.0.0.1:4321 to 10.0.1.1:45000. The datagram now reaches NAT , which recognizes that the datagram's destination address is one of NAT 's own translated public endpoints. If NAT is well-behaved, it then translates both the source and destination addresses in the datagram and “loops” the datagram back onto the private network, now with a source endpoint of 155.99.25.11:62000 and a destination endpoint of 10.0.1.2:55000. NAT finally translates the datagram's destination address as the datagram enters 's private network, and the datagram reaches . The path back to works similarly. Many NATs do not yet support hairpin translation, but it is becoming more common as NAT vendors become aware of this issue.
3.6 UDP Idle Timeouts
Since the UDP transport protocol provides NATs with no reliable, application-independent way to determine the lifetime of a session crossing the NAT, most NATs simply associate an idle timer with UDP translations, closing the hole if no traffic has used it for some time period. There is unfortunately no standard value for this timer: some NATs have timeouts as short as 20 seconds. If the application needs to keep an idle UDP session active after establishing the session via hole punching, the application must send periodic keep-alive packets to ensure that the relevant translation state in the NATs does not disappear.
Unfortunately, many NATs associate UDP idle timers with individual UDP sessions defined by a particular pair of endpoints, so sending keep-alives on one session will not keep other sessions active even if all the sessions originate from the same private endpoint. Instead of sending keep-alives on many different P2P sessions, applications can avoid excessive keep-alive traffic by detecting when a UDP session no longer works, and re-running the original hole punching procedure again “on demand.”
4 TCP Hole Punching
4.1 Sockets and TCP Port Reuse
4.2 Opening Peer-to-Peer TCP Streams
- Client uses its active TCP session with to ask for help connecting to .
- replies to with 's public and private TCP endpoints, and at the same time sends 's public and private endpoints to .
- From the same local TCP ports that and used to register with , and each asynchronously make outgoing connection attempts to the other's public and private endpoints as reported by , while simultaneously listening for incoming connections on their respective local TCP ports.
- and wait for outgoing connection attempts to succeed, and/or for incoming connections to appear. If one of the outgoing connection attempts fails due to a network error such as “connection reset” or “host unreachable,” the host simply re-tries that connection attempt after a short delay (e.g., one second), up to an application-defind maximum timeout period.
- When a TCP connection is made, the hosts authenticate each other to verify that they connected to the intended host. If authentication fails, the clients close that connection and continue waiting for others to succeed. The clients use the first successfully authenticated TCP stream resulting from this process.
Unlike with UDP, where each client only needs one socket to communicate with both and any number of peers simultaneously, with TCP each client application must manage several sockets bound to a single local TCP port on that client node, as shown in Figure 7 . Each client needs a stream socket representing its connection to , a listen socket on which to accept incoming connections from peers, and at least two additional stream sockets with which to initiate outgoing connections to the other peer's public and private TCP endpoints.
Consider the common-case scenario in which the clients and are behind different NATs, as shown in Figure 5 , and assume that the port numbers shown in the figure are now for TCP rather than UDP ports. The outgoing connection attempts and make to each other's private endpoints either fail or connect to the wrong host. As with UDP, it is important that TCP applications authenticate their peer-to-peer sessions, due of the likelihood of mistakenly connecting to a random host on the local network that happens to have the same private IP address as the desired host on a remote private network.
The clients' outgoing connection attempts to each other's public endpoints, however, cause the respective NATs to open up new “holes” enabling direct TCP communication between and . If the NATs are well-behaved, then a new peer-to-peer TCP stream automatically forms between them. If 's first SYN packet to reaches 's NAT before 's first SYN packet to reaches 's NAT, for example, then 's NAT may interpret 's SYN as an unsolicited incoming connection attempt and drop it. 's first SYN packet to should subsequently get through, however, because 's NAT sees this SYN as being part of the outbound session to that 's first SYN had already initiated.
4.3 Behavior Observed by the Application
What the client applications observe to happen with their sockets during TCP hole punching depends on the timing and the TCP implementations involved. Suppose that 's first outbound SYN packet to 's public endpoint is dropped by NAT , but 's first subsequent SYN packet to 's public endpoint gets through to before 's TCP retransmits its SYN. Depending on the operating system involved, one of two things may happen:
- 's TCP implementation notices that the session endpoints for the incoming SYN match those of an outbound session was attempting to initiate. 's TCP stack therefore associates this new session with the socket that the local application on was using to connect() to 's public endpoint. The application's asynchronous connect() call succeeds, and nothing happens with the application's listen socket.
Since the received SYN packet did not include an ACK for 's previous outbound SYN, 's TCP replies to 's public endpoint with a SYN-ACK packet, the SYN part being merely a replay of 's original outbound SYN, using the same sequence number. Once 's TCP receives 's SYN-ACK, it responds with its own ACK for 's SYN, and the TCP session enters the connected state on both ends.
- Alternatively, 's TCP implementation might instead notice that has an active listen socket on that port waiting for incoming connection attempts. Since 's SYN looks like an incoming connection attempt, 's TCP creates a new stream socket with which to associate the new TCP session, and hands this new socket to the application via the application's next accept() call on its listen socket. 's TCP then responds to with a SYN-ACK as above, and TCP connection setup proceeds as usual for client/server-style connections.
Since 's prior outbound connect() attempt to used a combination of source and destination endpoints that is now in use by another socket, namely the one just returned to the application via accept() , 's asynchronous connect() attempt must fail at some point, typically with an “address in use” error. The application nevertheless has the working peer-to-peer stream socket it needs to communicate with , so it ignores this failure.
The first behavior above appears to be usual for BSD-based operating systems, whereas the second behavior appears more common under Linux and Windows.
4.4 Simultaneous TCP Open
4.5 Sequential Hole Punching
5 Properties of P2P-Friendly NATs
5.1 Consistent Endpoint Translation
5.2 Handling Unsolicited TCP Connections
5.3 Leaving Payloads Alone
5.4 Hairpin Translation
6 Evaluation of Existing NATs
6.1 Test Method
6.1.1 UDP Test
When server 2 receives a UDP request from the client, besides replying directly to the client it also forwards the request to server 3, which in turn replies to the client from its own IP address. If the NAT's firewall properly filters “unsolicited” incoming traffic on a per-session basis, then the client never sees these replies from server 3, even though they are directed at the same public port as the replies from servers 1 and 2.
To test the NAT for hairpin translation support, the client simply opens a second UDP socket at a different local port and uses it to send messages to the public endpoint representing the client's first UDP socket, as reported by server 2. If these messages reach the client's first private endpoint, then the NAT supports hairpin translation.
6.1.2 TCP Test
The TCP test follows a similar pattern as for UDP. The client uses a single local TCP port to initiate outbound sessions to servers 1 and 2, and checks whether the public endpoints reported by servers 1 and 2 are the same, the first precondition for reliable TCP hole punching.
The NAT's response to unsolicited incoming connection attempts also impacts the speed and reliability of TCP hole punching, however, so NAT Check also tests this behavior. When server 2 receives the client's request, instead of immediately replying to the client, it forwards a request to server 3 and waits for server 3 to respond with a “go-ahead” signal. When server 3 receives this forwarded request, it attempts to initiate an inbound connection to the client's public TCP endpoint. Server 3 waits up to five seconds for this connection to succeed or fail, and if the connection attempt is still “in progress” after five seconds, server 3 responds to server 2 with the “go-ahead” signal and continues waiting for up to 20 seconds. Once the client finally receives server 2's reply (which server 2 delayed waiting for server 3's “go-ahead” signal), the client attempts an outbound connection to server 3, effectively causing a simultaneous TCP open with server 3.
What happens during this test depends on the NAT's behavior as follows. If the NAT properly just drops server 3's “unsolicited” incoming SYN packets, then nothing happens on the client's listen socket during the five second period before server 2 replies to the client. When the client finally initiates its own connection to server 3, opening a hole through the NAT, the attempt succeeds immediately. If on the other hand the NAT does not drop server 3's unsolicited incoming SYNs but allows them through (which is fine for hole punching but not ideal for security), then the client receives an incoming TCP connection on its listen socket before receiving server 2's reply. Finally, if the NAT actively rejects server 3's unsolicited incoming SYNs by sending back TCP RST packets, then server 3 gives up and the client's subsequent attempt to connect to server 3 fails.
To test hairpin translation for TCP, the client simply uses a secondary local TCP port to attempt a connection to the public endpoint corresponding to its primary TCP port, in the same way as for UDP.
6.2 Test Results
The NAT Check data we gathered consists of 380 reported data points covering a variety of NAT router hardware from 68 vendors, as well as the NAT functionality built into different versions of eight popular operating systems. Only 335 of the total data points include results for UDP hairpin translation, and only 286 data points include results for TCP, because we implemented these features in later versions of NAT Check after we had already started gathering results. The data is summarized by NAT vendor in Table 1 ; the table only individually lists vendors for which at least five data points were available. The variations in the test results for a given vendor can be accounted for by a variety of factors, such as different NAT devices or product lines sold by the same vendor, different software or firmware versions of the same NAT implementation, different configurations, and probably occasional NAT Check testing or reporting errors.
|
Out of the 380 reported data points for UDP, in 310 cases (82%) the NAT consistently translated the client's private endpoint, indicating basic compatibility with UDP hole punching. Support for hairpin translation is much less common, however: of the 335 data points that include UDP hairpin translation results, only 80 (24%) show hairpin translation support.
Out of the 286 data points for TCP, 184 (64%) show compatibility with TCP hole punching: the NAT consistently translates the client's private TCP endpoint, and does not send back RST packets in response to unsolicited incoming connection attempts. Hairpin translation support is again much less common: only 37 (13%) of the reports showed hairpin support for TCP.
Since these reports were generated by a “self-selecting” community of volunteers, they do not constitute a random sample and thus do not necessarily represent the true distribution of the NATs in common use. The results are nevertheless encouraging: it appears that the majority of commonly-deployed NATs already support UDP and TCP hole punching at least in single-level NAT scenarios.
6.3 Testing Limitations
There are a few limitations in NAT Check's current testing protocol that may cause misleading results in some cases. First, we only learned recently that a few NAT implementations blindly translate IP addresses they find in unknown application payloads, and the NAT Check protocol currently does not protect itself from this behavior by obfuscating the IP addresses it transmits.
Second, NAT Check's current hairpin translation checking may yield unnecessarily pessimistic results because it does not use the full, two-way hole punching procedure for this test. NAT Check currently assumes that a NAT supporting hairpin translation does not filter “incoming” hairpin connections arriving from the private network in the way it would filter incoming connections arriving at the public side of the NAT, because such filtering is unnecessary for security. We later realized, however, that a NAT might simplistically treat any traffic directed at the NAT's public ports as “untrusted” regardless of its origin. We do not yet know which behavior is more common.
Finally, NAT implementations exist that consistently translate the client's private endpoint as long as only one client behind the NAT is using a particular private port number, but switch to symmetric NAT or even worse behaviors if two or more clients with different IP addresses on the private network try to communicate through the NAT from the same private port number. NAT Check could only detect this behavior by requiring the user to run it on two or more client hosts behind the NAT at the same time. Doing so would make NAT Check much more difficult to use, however, and impossible for users who only have one usable machine behind the NAT. Nevertheless, we plan to implement this testing functionality as an option in a future version of NAT Check.
6.4 Corroboration of Results
Despite testing difficulties such as those above, our results are generally corroborated by those of a large ISP, who recently found that of the top three consumer NAT router vendors, representing 86% of the NATs observed on their network, all three vendors currently produce NATs compatible with UDP hole punching [25 ]. Additional independent results recently obtained using the UDP-oriented STUN protocol [12 ], and STUNT, a TCP-enabled extension [8 ,9 ], also appear consistent with our results. These latter studies provide more information on each NAT by testing a wider variety of behaviors individually, instead of just testing for basic hole punching compatibility as NAT Check does. Since these more extensive tests require multiple cooperating clients behind the NAT and thus are more difficult to run, however, these results are so far available on a more limited variety of NATs.
7 Related Work
8 Conclusion
Acknowledgments
Bibliography
- 1
-
Andrew Biggadike, Daniel Ferullo, Geoffrey Wilson, and Adrian Perrig.
NATBLASTER: Establishing TCP connections between hosts behind NATs.
In ACM SIGCOMM Asia Workshop , Beijing, China, April 2005. - 2
-
David Clark, Robert Braden, Aaron Falk, and Venkata Pingali.
FARA: Reorganizing the addressing architecture.
In ACM SIGCOMM FDNA Workshop , August 2003. - 3
-
S. Deering and R. Hinden.
Internet protocol, version 6 (IPv6) specification, December 1998.
RFC 2460. - 4
-
Jeffrey L. Eppinger.
TCP connections for P2P apps: A software approach to solving the NAT problem.
Technical Report CMU-ISRI-05-104, Carnegie Mellon University, January 2005. - 5
-
Bryan Ford.
Scalable Internet routing on topology-independent node identities.
Technical Report MIT-LCS-TR-926, MIT Laboratory for Computer Science, October 2003. - 6
-
Bryan Ford.
Unmanaged internet protocol: Taming the edge network management crisis.
In Second Workshop on Hot Topics in Networks , Cambridge, MA, November 2003. - 7
-
Paul Francis and Ramakrishna Gummadi.
IPNL: A NAT-extended Internet architecture.
In ACM SIGCOMM , August 2002. - 8
-
Saikat Guha and Paul Francis.
Simple traversal of UDP through NATs and TCP too (STUNT).
http://nutss.gforge.cis.cornell.edu/ . - 9
-
Saikat Guha, Yutaka Takeday, and Paul Francis.
NUTSS: A SIP-based approach to UDP and TCP network connectivity.
In SIGCOMM 2004 Workshops , August 2004. - 10
-
M. Holdrege and P. Srisuresh.
Protocol complications with the IP network address translator, January 2001.
RFC 3027. - 11
-
C. Huitema.
Teredo: Tunneling IPv6 over UDP through NATs, March 2004.
Internet-Draft (Work in Progress). - 12
-
C. Jennings.
NAT classification results using STUN, October 2004.
Internet-Draft (Work in Progress). - 13
-
Dan Kegel.
NAT and peer-to-peer networking, July 1999.
http://www.alumni.caltech.edu/~dank/peer-nat.html . - 14
-
M. Leech et al.
SOCKS protocol, March 1996.
RFC 1928. - 15
-
R. Moskowitz and P. Nikander.
Host identity protocol architecture, April 2003.
Internet-Draft (Work in Progress). - 16
-
NAT check.
http://midcom-p2p.sourceforge.net/ . - 17
-
J. Rosenberg.
Interactive connectivity establishment (ICE), October 2003.
Internet-Draft (Work in Progress). - 18
-
J. Rosenberg, C. Huitema, and R. Mahy.
Traversal using relay NAT (TURN), October 2003.
Internet-Draft (Work in Progress). - 19
-
J. Rosenberg, J. Weinberger, C. Huitema, and R. Mahy.
STUN - simple traversal of user datagram protocol (UDP) through network address translators (NATs), March 2003.
RFC 3489. - 20
-
J. Saltzer.
On the naming and binding of network destinations.
In P. Ravasio et al., editor, Local Computer Networks , pages 311-317. North-Holland, Amsterdam, 1982.
RFC 1498. - 21
-
P. Srisuresh and M. Holdrege.
IP network address translator (NAT) terminology and considerations, August 1999.
RFC 2663. - 22
-
P. Srisuresh, J. Kuthan, J. Rosenberg, A. Molitor, and A. Rayhan.
Middlebox communication architecture and framework, August 2002.
RFC 3303. - 23
-
Transmission control protocol, September 1981.
RFC 793. - 24
-
G. Tsirtsis and P. Srisuresh.
Network address translation - protocol translation (NAT-PT), February 2000.
RFC 2766. - 25
-
Justin Uberti.
E-mail on IETF MIDCOM mailing list, February 2004.
Message-ID: <402CEB11.1060906@aol.com> . - 26
-
UPnP Forum.
Internet gateway device (IGD) standardized device control protocol, November 2001.
http://www.upnp.org/
. - 27
-
Michael Walfish, Jeremy Stribling, Maxwell Krohn, Hari Balakrishnan, Robert Morris, and Scott Shenker.
Middleboxes no longer considered harmful.
In USENIX Symposium on Operating Systems Design and Implementation , San Francisco, CA, December 2004.
穿越NAT的p2p通信方法研究相关推荐
- P2P通信原理与实现(C++),NAT,网络穿透原理
1.简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信.这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还 ...
- 内网穿透实现P2P通信
P2P 通信最大的障碍就是 NAT(网络地址转换),NAT 使得局域网内的设备可以与公网进行通讯,但是不同 NAT 下的设备之间通讯将会变得很困难.UDP 打洞就是用来使得设备间绕过 NAT 进行通讯 ...
- P2P通信基本原理与实现
本文转载自: https://www.pppan.net/blog/detail/2017-12-16-p2p-over-middle-box 如有侵权,通知删除 P2P通信基本原理与实现 #P2P ...
- P2P通信原理与实现(C++)
1.简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信.这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还 ...
- TCP实现P2P通信、TCP穿越NAT的方法、TCP打洞
原文地址:http://www.vckbase.com/document/viewdoc/?id=1773 这个标题用了两个顿号三个名称,其实说得是同一个东西,只是网上有不同的说法罢了,另外好像还有人 ...
- P2P通信中的NAT/FW穿越
摘要:P2P(Peer-to-Peer)通信的发展极其迅速,形成了很大的影响.和传统通信一样,P2P通信同样受到NAT/FW穿越问题的制约,因此解决好其相关的NAT/FW穿越问题非常重要.和传统通信相 ...
- stun turn ice等穿越NAT方法
STUN(Simple Traversal of User Datagram Protocol through Network Address Translators (NATs),NAT的UDP简单 ...
- P2P网络“自由”穿越NAT的“秘密”
P2P网络"自由"穿越NAT的"秘密"<?xml:namespace prefix = o ns = "urn:schemas-microsof ...
- P2P网络穿越 NAT穿越
http://blog.csdn.net/mazidao2008/article/details/4933730 ------------------------------------------- ...
最新文章
- 可遇不可求的BUG之采用MYSQL odbc 3.51访问数据库返回值缺失
- jQuery中DOM操作
- centos7 mysql安装教程_centos7环境下MySQL安装教程
- freebsd java 能用吗_在FreeBSD 4.9下安装JAVA环境
- oracle form执行后左上角没出现oracle标记,oracle form学习笔记
- 关于Python中的错误与异常,你是否了解的够仔细?
- 55.函数模板指针匹配(模板自动匹配*多的)
- 验证方式二 html标签验证码,Django标签、转义及验证码生成
- JNI读取assets资源文件
- 图像处理六:预处理方法
- Cocoa中Core Data的简单介绍
- 手机 物理分辨率 逻辑分辨率
- php多合一安装包,DOXCX多合一小程序系统Sass平台源码分享
- Apple账号密码自动填充
- JAVA毕设项目html5在线医疗系统(Vue+Mybatis+Maven+Mysql+sprnig+SpringMVC)
- 微信最近点赞拿东西服务器,微信朋友圈点赞说明什么?点赞的行为背后隐藏着什么含义呢?...
- 你是胡萝卜,是鸡蛋,还是咖啡豆
- java计算机毕业设计手机测试管理系统源代码+数据库+系统+lw文档
- W10安装NoteBook(一个Python笔记软件)及使用方法
- 计算机及相关专业的同学如何规划大学四年?
热门文章
- PWM(脉冲宽度调制)信号原理
- 用命令提示符打开资源管理器目录
- 数据挖掘项目:金融风控-贷款违约预测
- 什么软件可以听学业水平测试网课,免费听网课app推荐
- Centos7.2安装搜狗拼音
- Solr之拼音检索。
- 英语六级试卷软件测试,背单词软件_2018年12月英语六级考试真题测试(11)含答案_沪江英语...
- 计算广告(一):在线广告概述
- 企业群发短信时为什么要找短信平台公司而不是直接找运营商发送
- vba 添加outlook 签名_如何在Outlook中使用宏发送邮件,并且使用已有签名?