华琪软通国内电话信息领域中的领跑者
设为首页 | 加入收藏 | 联系我们
你所在的位置: 首页 > 公司新闻
华琪软通HaKey SoftComm

公司新闻

 老登们常说:理想很丰满,现实很残酷。事情开干之前,先语重心长来一句。互联网传输,例如语音呼叫的服务,如果用户满意,则需要付出很高的代价,无论是从技术角度还是从经济角度,保持稳定的传输是非常难的一个挑战。可是,不幸的是,无论你如何努力,总有一定比例的节点无法进行打洞。这是因为它们的NAT行为不可预测,或者它们受到集中部署的业务控制策略,不允许随便进行打洞。下面分享一篇针对P2P场景使用QUIC的技术愿景讨论,帮助大家头脑风暴,提供新学习思路,把握未来方向。

背景说明

基于UDP的协议非常适合P2P,因为如果你有可预测的端口映射,打洞过程就很简单,你无法禁止它。按照这种思考方式,我们目前正在探索这个方向。

多年来,IETF 标准化了许多协议,用于通过 NAT 和防火墙建立 IP 数据包流,包括 STUN、ICE 和 TURN。

这本质上是一个复杂的主题,我强烈推荐阅读 Eric Rescorla 关于 NAT 的博客系列文章(第 1 部分、第 2 部分、第 3 部分)。但简而言之,它们重写了通过 NAT 的数据包的 IP 地址。

 

QUIC的处理方式

QUIC连接迁移 

RFC 9000 定义了客户端如何将现有的 QUIC 连接迁移到不同的 IP:端口组合。当我们设计这一机制时,我们设想的主要用例是解决"停车场问题"。想象一下,你有一部手机,从办公室(那里手机有WiFi)走到停车场(WiFi覆盖很差或没有)。在这种情况下,客户端可以检测到 WiFi 连接正在恶化,并将连接迁移到其蜂窝网络接口。至关重要的是,这将保持所有连接状态(例如,开放的流、数据报流等)不变,因此对应用程序而言是透明的。

在检测到网络接口变更时,例如离开办公室,QUIC 路径迁移通过首先向服务器发送所谓的探测包来工作。这个数据包的目的是探测路径是否真的可用。客户端在此数据包中包含一个 PATH_CHALLENGE 帧,服务器以 PATH_RESPONSE 帧回应。这确保了新路径实际可用(例如,该路径不会阻止 UDP 数据包),并支持 QUIC(例如,允许满足 QUIC 的 MTU 要求的数据包)。

在网络上,这种路径探测过程看起来与打洞尝试非常相似。我们只需要一个微小的修改就能使其适用于 p2p 用例:如果我们能让服务器也发送探测包,我们就能一举两得:我们既能穿透防火墙,同时又能验证路径上的连接性。

当然,这并不是实现打洞所需的唯一条件。在节点甚至能发送探测包之前,我们需要了解对等节点的反射地址,并能够协调时机。我们稍后会讲到这一点,但首先我们将描述如何替代 STUN 来发现我们的反射地址。

QUIC地址发现 

通常,节点使用 STUN 来发现其反射地址。本质上,STUN 在这里是一个请求-响应协议,客户端请求服务器报告请求数据包的观察地址。

原则上,我们可以在 QUIC 连接内实现相同的功能:服务器可以使用例如一个新定义的 QUIC 帧报告客户端的地址,反之亦然。这正是 QUIC 地址发现草案所规定的。

这个机制非常简单:每当建立新路径时(包括用于握手的路径),端点会互相通知观察到的地址。这是一种非常高效的机制:由于 OBSERVED_ADDRESS 帧被定义为探测帧,它可以与用于探测新路径的 PATH_CHALLENGE 和 PATH_RESPONSE 帧捆绑在一起。

通过 QUIC 进行地址发现有多个优势:

QUIC 数据包是加密的。观察者无法观察 OBSERVED_ADDRESS 帧的交换,也无法干扰这种交换(例如通过篡改帧内容)。 它不需要运行任何额外服务(即 STUN 服务器/客户端)。只需在足够多的节点上启用地址发现扩展即可。 当然,无论哪种情况,客户端都必须信任服务器发送的是诚实的响应。行为不端的服务器可能会回应虚假地址,导致"打洞"数据包后来被发送到这些地址。这并不难防御:客户端可以通过联系多个服务器并比较它们的响应来获得一些保护。

打洞协调 

节点现在已经了解了其反射地址,我们知道如何使用 QUIC 的连接迁移机制建立所需的 NAT 端口映射,以允许建立直接路径。我们现在想要建立与另一个节点的连接,该节点位于其各自的 NAT 之后。目前,我们假设两个节点能够通过(代理的)QUIC 连接进行通信。我们将在下一节中详细了解这是如何工作的。现在唯一重要的是节点能够相互通信。

NAT 穿透草案定义了两个节点如何相互协商打洞尝试。为了方便起见,该过程几乎完全由客户端(即发起 QUIC 连接的节点)驱动。这不是因为对等方的角色有根本不同(它们都是同一 p2p 网络中的对等节点),但它会大大简化协议。它还减少了与 RFC 9000 的差异,在 RFC 9000 中,连接迁移只能由客户端发起。

服务器使用ADD_ADDRESS帧向客户端通知其反射地址。如果服务器有多个反射地址,可以发送多个ADD_ADDRESS帧。

ICE RFC详细介绍了如何从两个节点的反射地址形成候选地址对,因为两个节点需要就候选地址对的排序达成一致。由于客户端驱动这个过程,我们不需要指定客户端和服务器需要同意的任何地址匹配逻辑。

一旦客户端形成了地址对(并且一旦它认为是开始打洞尝试的合适时机),它会向服务器发送一个PUNCH_ME_NOW帧。PUNCH_ME_NOW包含客户端和服务器的反射地址。

发送PUNCH_ME_NOW帧后,客户端立即开始在由这两个地址形成的路径上进行路径探测。同样地,服务器一收到PUNCH_ME_NOW帧,就开始从其端进行路径探测。时机在这里至关重要:如上所示,路径探测数据包创建了NAT绑定,允许另一方的数据包穿透NAT。

客户端和服务器在发送或响应PUNCH_ME_NOW时都会在新路径上发送PATH_CHALLENGE帧。他们需要为尝试建立的新路径分配一个尚未使用的连接ID。这意味着并行尝试的数量受可用连接ID数量的限制。这既有优点也有缺点。一方面,有限制可以减少对等方可能被迫消耗的资源量,使协议更稳定。另一方面,如果达到限制,下一次尝试只有在放弃前一个挑战并且对等方提供了替代连接ID之后才可能进行。这可能是一个缓慢的过程。

这个过程是否太慢是有争议的。计划参与p2p打洞的端点可能被配置为提供足够多的连接ID以满足大多数实际尝试需求。此外,初始路径将一直可用,直到迁移成功,这意味着应用程序端点不需要等待协商成功就可以开始交换数据。

添加新的QUIC帧如ADD_ADDRESS或PUNCH_ME_NOW有些争议。行为不当的对等方可能在这些帧中发送伪造的地址,导致对等方向第三方发送打洞数据包。这类似于RFC 9000安全部分描述的请求伪造攻击,至少需要相同类型的防御措施。这是我们在发展NAT穿越草案时会牢记的事情。

通过HTTP中继UDP数据包 

RFC 9298定义了如何在HTTP中代理UDP数据包。交换从常规HTTP请求开始:客户端在QUIC流上向代理发送所谓的扩展CONNECT请求,指示代理打开UDP套接字并将UDP数据包流代理到目标服务器。

一旦代理接受了代理请求,UDP数据包就会在HTTP数据报(RFC 9297)中发送,这些数据报本身是QUIC DATAGRAM帧的一个薄包装。QUIC数据报是RFC 9221中定义的新QUIC帧,在完成QUIC握手后在交换的QUIC数据包中发送。因此,它们的加密方式与通过QUIC连接交换的任何其他数据相同。然而,如果包含DATAGRAM的数据包丢失,则DATAGRAM帧不会重新传输。这使得DATAGRAM适合代理不可靠的数据包,如UDP数据包。

可以在同一QUIC连接中代理到不同目标服务器的多个UDP流。

代理UDP数据包几乎是我们在点对点场景中实现中继所需的,但不完全是:虽然客户端可以通过代理访问任何IP,但其他节点仍然无法与客户端通信(除非客户端首先联系它们)。

幸运的是,已经有一个草案描述了如何在HTTP中代理UDP监听器。这个草案的主要用例是通过CONNECT-UDP运行WebRTC。这与我们试图解决的问题非常相似:WebRTC对等点实际上使用ICE协议建立直接连接,为此它们需要知道自己的反射传输地址。

这个机制相当直接:代理为客户端分配一个新的IP:端口,并将此套接字上的所有UDP数据包转发给客户端。当然,它还必须包括数据包来源的2元组。

这种方法的简单性同时也是其最大的限制:由于只有65535个端口号(其中许多是保留的),一个代理同时只能处理有限数量的客户端。需要明确的是,这仍然允许数万个并发客户端,而且许多部署场景永远不会遇到这个限制。

未来可能通过使用类似于QUIC感知代理草案的方法来解决这个限制。

准备多路径 

QUIC工作组正在完成QUIC的多路径扩展。顾名思义,这些扩展允许同时使用多条路径。对于点对点用例来说,这意味着即使在通过NAT穿越创建直接路径后,终端也可以保持初始路径可用。这些路径可以用于负载共享或作为备份。

要获得这些好处,我们需要对此处描述的机制进行微小调整——实际上,在PUNCH_ME_NOW帧的多路径版本中管理连接ID和路径ID。一旦IETF的QUIC多路径扩展取得更多进展,我们应该着手处理这个问题。

聚合-将所有部分组合在一起 

现在我们已经探索了所有组件,让我们将它们组合起来,构建一个基于QUIC的小型点对点应用程序。

当节点启动时,它首先连接到几个硬编码的引导节点。大多数这些节点支持QUIC地址发现扩展,因此节点能够了解到它位于NAT后面,以及NAT的公共地址是什么。

然后它连接到一个中继服务器并在中继服务器上预留一个IP:端口组合。现在节点可以向网络中的其他对等点广播此地址,例如通过在某种对等目录中注册,或者通过在点对点网络的DHT中注册自己。

此时,其他节点可以连接到此端口的中继服务器,并让所有数据包被转发。我们已经实现了第一个目标:建立了连接。中继连接可以立即用于交换应用程序数据。现在的目标是减轻中继服务器的负担,并获得到对等点的直接(可能具有更低延迟、更高吞吐量)连接。

节点使用NAT穿越草案中描述的机制在各自的NAT中打洞。这个打洞过程可能需要几次尝试,取决于候选对的数量(以及是否并行运行打洞尝试),但通常只需要几秒钟。

最重要的是,这对应用程序来说是完全透明的:应用程序可以开始使用中继连接,并在QUIC栈尝试建立直接路径的同时一直使用它。

在这方面我们进展

到目前为止,这个协议在生产环境中还没有实施,但是很多文档已经通过了IETF流程,现在已经成为广泛部署的RFC。具体包括:

  1. HTTP中的代理UDP监听器草案,它允许客户端在中继服务器上保留一个IP:端口元组。 

  2. QUIC地址发现草案,它允许端点了解他们的公共地址。这个草案的当前版本已经被两个不同的QUIC堆栈实现:picoquic和quinn的一个分支。 

  3. QUIC的NAT穿透草案,它定义了如何协调对等点之间的打洞尝试。

 

 

合作伙伴:

友情链接:

立即咨询