天天看点

点对点UDP连接(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后面)。

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就是那些客户端需要经历多级地址转换才能进入公网。

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、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。

4、Consistent prot binddings(保持端口绑定)

hole punching 技术有一个需要注意的地方:它只能工作在两台NAT都是cone NAT(或没有NAT 防火墙)的情况下,只要UDP端口还在使用,它就需要保持一个固定的端口把一个给定(私有IP,私有UDP端口)和一个(公共IP,公共UDP端口)绑定。象对称NAT一样,为每个新的连接会话分配一个新的公共端口,对一个UDP应用程序来说,为了和不同的外部通讯重用一个已经存在的地址转换是不可以的。(这边稍微有点糊涂,再多看看。) 既然cone NAT运用是相当普遍的,UDP hole punching技术应用的也相当广泛,但是还有一小部分是对等NAT配置,因此不能支持这种技术。