Skip to main content

2. Overview of Operation (操作概述)

本节概述了 TURN 的操作。本节内容为非规范性 (Non-Normative) 的。

在典型配置中,TURN 客户端 (TURN Client) 连接到专用网络 [RFC1918],并通过一个或多个 NAT 连接到公共互联网。公共互联网上有一个 TURN 服务器 (TURN Server)。互联网上的其他地方有一个或多个对等方 (Peers),TURN 客户端希望与这些对等方通信。这些对等方可能位于一个或多个 NAT 后面,也可能不在。客户端使用服务器作为中继,向这些对等方发送数据包并从这些对等方接收数据包。

                                    Peer A
Server-Reflexive +---------+
Transport Address | |
192.0.2.150:32102 | |
| /| |
TURN | / ^| Peer A |
Client's Server | / || |
Host Transport Transport | // || |
Address Address | // |+---------+
10.1.1.2:49721 192.0.2.15:3478 |+-+ // Peer A
| | ||N| / Host Transport
| +-+ | ||A|/ Address
| | | | v|T| 192.168.100.2:49582
| | | | /+-+
+---------+| | | |+---------+ / +---------+
| || |N| || | // | |
| TURN |v | | v| TURN |/ | |
| Client |----|A|----------| Server |------------------| Peer B |
| | | |^ | |^ ^| |
| | |T|| | || || |
+---------+ | || +---------+| |+---------+
| || | |
| || | |
+-+| | |
| | |
| | |
Client's | Peer B
Server-Reflexive Relayed Transport
Transport Address Transport Address Address
192.0.2.1:7000 192.0.2.15:50000 192.0.2.210:49191

Figure 1

图 1 显示了典型的部署。在此图中,TURN 客户端和 TURN 服务器被 NAT 分隔,客户端位于 NAT 的专用侧,服务器位于 NAT 的公共侧。假设此 NAT 是一个"不良"NAT,例如,它可能具有"地址和端口依赖映射 (Address-and-Port-Dependent Mapping)"的映射属性(参见 [RFC4787])。

客户端从一个 (IP 地址, 端口) 组合与服务器通信,该组合称为客户端的主机传输地址 (HOST TRANSPORT ADDRESS)。(IP 地址和端口的组合称为传输地址 (TRANSPORT ADDRESS)。)

客户端从其主机传输地址向 TURN 服务器上的传输地址发送 TURN 消息,该地址称为 TURN 服务器传输地址 (TURN SERVER TRANSPORT ADDRESS)。客户端通过某种未指定的方式(例如配置)了解 TURN 服务器传输地址,并且此地址通常被多个客户端同时使用。

由于客户端位于 NAT 后面,服务器看到来自客户端的数据包来自 NAT 本身的传输地址。此地址称为客户端的服务器反射传输地址 (SERVER-REFLEXIVE Transport Address),服务器向客户端的服务器反射传输地址发送的数据包将由 NAT 转发到客户端的主机传输地址。

客户端使用 TURN 命令在服务器上创建和操纵分配 (ALLOCATION)。分配是服务器上的一个数据结构。该数据结构包含分配的中继传输地址 (RELAYED TRANSPORT ADDRESS) 等内容。中继传输地址是服务器上的传输地址,对等方可以使用它让服务器将数据中继到客户端。分配由其中继传输地址唯一标识。

创建分配后,客户端可以将应用程序数据发送到服务器,并指示应将数据发送到哪个对等方,服务器将把这些数据中继到适当的对等方。客户端在 TURN 消息内部将应用程序数据发送到服务器,在服务器处,数据从 TURN 消息中提取出来,并在 UDP 数据报中发送到对等方。在相反的方向上,对等方可以在 UDP 数据报中向分配的中继传输地址发送应用程序数据,服务器将把这些数据封装在 TURN 消息内部并发送到客户端,同时指示是哪个对等方发送了数据。由于 TURN 消息始终包含客户端正在与哪个对等方通信的指示,因此客户端可以使用单个分配与多个对等方通信。

当对等方位于 NAT 后面时,客户端必须使用其服务器反射传输地址而不是其主机传输地址来标识对等方。例如,要向上面示例中的对等方 A 发送应用程序数据,客户端必须指定 192.0.2.150:32102(对等方 A 的服务器反射传输地址),而不是 192.168.100.2:49582(对等方 A 的主机传输地址)。

服务器上的每个分配都属于单个客户端,并且恰好有一个中继传输地址,该地址仅由该分配使用。因此,当数据包到达服务器上的中继传输地址时,服务器知道数据的目标客户端是谁。

客户端可以在服务器上同时拥有多个分配。

2.1. Transports (传输协议)

根据本规范的定义,TURN 在服务器和对等方之间始终使用 UDP。但是,本规范允许在客户端和服务器之间使用 UDP、TCP 或传输层安全性 (Transport Layer Security, TLS) over TCP 中的任何一种来传输 TURN 消息。

+----------------------------+---------------------+
| TURN client to TURN server | TURN server to peer |
+----------------------------+---------------------+
| UDP | UDP |
| TCP | UDP |
| TLS over TCP | UDP |
+----------------------------+---------------------+

如果在客户端和服务器之间使用 TCP 或 TLS-over-TCP,则服务器在向对等方中继数据或从对等方中继数据时将在这些传输和 UDP 传输之间进行转换。

由于此版本的 TURN 仅支持服务器和对等方之间的 UDP,因此预计大多数客户端也会更喜欢在客户端和服务器之间使用 UDP。既然如此,一些读者可能会想:为什么还要支持 TCP 和 TLS-over-TCP?

TURN 支持客户端和服务器之间的 TCP 传输,因为某些防火墙配置为完全阻止 UDP。这些防火墙阻止 UDP 但不阻止 TCP,部分原因是 TCP 具有一些属性,使得受防火墙保护的节点的意图对防火墙来说更加明显。例如,TCP 有一个三次握手,这使得受保护的节点确实希望建立该特定连接这一点更加清楚,而对于 UDP,防火墙最多只能使用过滤规则来猜测哪些流是需要的。此外,TCP 具有显式的连接拆除,而对于 UDP,防火墙必须使用定时器来猜测流何时结束。

TURN 支持客户端和服务器之间的 TLS-over-TCP 传输,因为 TLS 提供了 TURN 默认摘要认证 (Digest Authentication) 不提供的额外安全属性,某些客户端可能希望利用这些属性。特别是,TLS 提供了一种方法,让客户端确定它正在与正确的服务器通信,并为 TURN 控制消息提供保密性。TURN 不要求使用 TLS,因为使用 TLS 的开销高于摘要认证,例如,使用 TLS 可能意味着大多数应用程序数据将被双重加密(一次由 TLS 加密,一次以确保它在 UDP 数据报中仍然加密)。

计划对 TURN 进行扩展,以增加对服务器和对等方之间的 TCP 的支持 [TURN-TCP]。因此,在服务器和对等方之间使用 UDP 的分配称为 UDP 分配 (UDP Allocations),而在服务器和对等方之间使用 TCP 的分配称为 TCP 分配 (TCP Allocations)。本规范仅描述 UDP 分配。

根据本规范的定义,TURN 仅支持 IPv4。本规范中的所有 IP 地址必须 (MUST) 是 IPv4 地址。计划对 TURN 进行扩展,以增加对 IPv6 以及 IPv4 和 IPv6 之间中继的支持 [TURN-IPv6]。

在 TURN 的某些应用中,客户端可以在用于与服务器通信的主机传输地址上发送和接收 TURN 数据包以外的数据包。例如,当将 TURN 与 ICE 一起使用时,就会发生这种情况。在这些情况下,客户端可以通过检查到达数据包的源地址来区分 TURN 数据包和其他数据包:来自 TURN 服务器的数据包将是 TURN 数据包。

2.2. Allocations (分配)

要在服务器上创建分配,客户端使用 Allocate 事务。客户端向服务器发送 Allocate 请求,服务器回复包含分配的中继传输地址的 Allocate 成功响应。客户端可以在 Allocate 请求中包含描述其期望的分配类型的属性(例如,分配的生存时间 (Lifetime))。由于中继数据具有安全含义,服务器要求 (REQUIRES) 客户端进行身份验证,通常使用 STUN 的长期凭证机制 (Long-Term Credential Mechanism),以证明其被授权使用该服务器。

一旦分配了中继传输地址,客户端必须 (MUST) 保持分配处于活动状态。为此,客户端定期向服务器发送 Refresh 请求。TURN 特意使用不同的方法(Refresh 而不是 Allocate)进行刷新,以确保如果分配因某种原因消失,客户端会被告知。

Refresh 事务的频率由分配的生存时间决定。分配的默认生存时间为 10 分钟 - 选择此值是为了足够长,使得刷新通常不会成为客户端的负担,同时在客户端意外退出时及时使分配过期。但是,客户端可以在 Allocate 请求中请求更长的生存时间,并可以在 Refresh 请求中修改其请求,服务器始终在响应中指示实际的生存时间。客户端必须 (MUST) 在先前的 Allocate 或 Refresh 事务的"生存时间"秒内发出新的 Refresh 事务。一旦客户端不再希望使用分配,它应该 (SHOULD) 使用请求生存时间为 0 的 Refresh 请求删除分配。

服务器和客户端都跟踪一个称为 5 元组 (5-TUPLE) 的值。在客户端,5 元组由客户端的主机传输地址、服务器传输地址以及客户端用于与服务器通信的传输协议组成。在服务器,5 元组值相同,只是客户端的主机传输地址被替换为客户端的服务器反射地址,因为这是服务器看到的客户端地址。

客户端和服务器都记住 Allocate 请求中使用的 5 元组。客户端和服务器之间的后续消息使用相同的 5 元组。这样,客户端和服务器知道正在引用哪个分配。如果客户端希望分配第二个中继传输地址,它必须 (MUST) 使用不同的 5 元组创建第二个分配(例如,通过使用不同的客户端主机地址或端口)。

注意:虽然本文档中使用的术语指的是 5 元组,但 TURN 服务器可以存储它喜欢的任何标识符,只要产生相同的结果。具体来说,实现可以使用文件描述符代替 5 元组来表示 TCP 连接。

TURN                                 TURN           Peer          Peer
client server A B
|-- Allocate request --------------->| | |
| | | |
|<--------------- Allocate failure --| | |
| (401 Unauthorized) | | |
| | | |
|-- Allocate request --------------->| | |
| | | |
|<---------- Allocate success resp --| | |
| (192.0.2.15:50000) | | |
// // // //
| | | |
|-- Refresh request ---------------->| | |
| | | |
|<----------- Refresh success resp --| | |
| | | |

Figure 2

在图 2 中,客户端在没有凭证的情况下向服务器发送 Allocate 请求。由于服务器要求使用 STUN 的长期凭证机制对所有请求进行身份验证,因此服务器以 401(未授权,Unauthorized)错误代码拒绝该请求。然后客户端再次尝试,这次包含凭证(未显示)。这一次,服务器接受 Allocate 请求并返回 Allocate 成功响应,其中包含(以及其他内容)分配给分配的中继传输地址。稍后,客户端决定刷新分配,因此向服务器发送 Refresh 请求。刷新被接受,服务器回复 Refresh 成功响应。

2.3. Permissions (权限)

为了缓解企业 IT 管理员对 TURN 可能被用于绕过公司防火墙安全的担忧,TURN 包含了权限 (Permissions) 的概念。TURN 权限模仿符合 [RFC4787] 的 NAT 的地址受限过滤机制。

一个分配可以有零个或多个权限。每个权限由一个 IP 地址和一个生存时间组成。当服务器在分配的中继传输地址上接收到 UDP 数据报时,它首先检查权限列表。如果数据报的源 IP 地址与权限匹配,则应用程序数据将被中继到客户端,否则 UDP 数据报将被静默丢弃。

如果权限未被刷新,它将在 5 分钟后过期,并且没有办法显式删除权限。选择此行为是为了匹配符合 [RFC4787] 的 NAT 的行为。

客户端可以使用 CreatePermission 请求或 ChannelBind 请求安装或刷新权限。使用 CreatePermission 请求,可以使用单个请求安装或刷新多个权限 - 这对于使用 ICE 的应用程序很重要。出于安全原因,只能通过可以进行身份验证的事务来安装或刷新权限,因此,Send 指示 (Send Indications) 和 ChannelData 消息(用于向对等方发送数据)不会安装或刷新任何权限。

请注意,权限是在分配的上下文中的,因此在一个分配中添加或使权限过期不会影响其他分配。

2.4. Send Mechanism (发送机制)

客户端和对等方使用 TURN 服务器交换应用程序数据有两种机制。第一种机制使用 Send 和 Data 方法,第二种方式使用通道 (Channels)。两种方式的共同点是客户端能够使用单个分配的中继传输地址与多个对等方通信,因此,两种方式都包括一种方法,让客户端向服务器指示哪个对等方应该接收数据,以及让服务器向客户端指示哪个对等方发送了数据。

Send 机制使用 Send 和 Data 指示。Send 指示用于从客户端向服务器发送应用程序数据,而 Data 指示用于从服务器向客户端发送应用程序数据。

当使用 Send 机制时,客户端向 TURN 服务器发送一个 Send 指示,其中包含 (a) 一个 XOR-PEER-ADDRESS 属性,指定对等方的(服务器反射的)传输地址,以及 (b) 一个 DATA 属性,保存应用程序数据。当 TURN 服务器接收到 Send 指示时,它从 DATA 属性中提取应用程序数据,并在 UDP 数据报中将其发送到对等方,使用分配的中继地址作为源地址。请注意,不需要指定中继传输地址,因为它由用于 Send 指示的 5 元组暗示。

在相反的方向上,到达 TURN 服务器上的中继传输地址的 UDP 数据报将被转换为 Data 指示并发送到客户端,对等方的服务器反射传输地址包含在 XOR-PEER-ADDRESS 属性中,数据本身包含在 DATA 属性中。由于中继传输地址唯一标识分配,服务器知道哪个客户端应该接收数据。

Send 和 Data 指示无法进行身份验证,因为 STUN 的长期凭证机制不支持对指示进行身份验证。这不像乍看起来那么大的问题,因为客户端到服务器的路径只是到对等方的总路径的一半。希望获得适当安全性的应用程序应该加密在客户端和对等方之间发送的数据。

由于 Send 指示未经过身份验证,攻击者可能向服务器发送伪造的 Send 指示,然后服务器将这些指示中继到对等方。为了部分缓解此攻击,TURN 要求 (REQUIRES) 客户端在使用 Send 指示向对等方发送数据之前安装到该对等方的权限。

TURN                                 TURN           Peer          Peer
client server A B
| | | |
|-- CreatePermission req (Peer A) -->| | |
|<-- CreatePermission success resp --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<-------------- Data ind (Peer A) --| | |
| | | |
| | | |
|--- Send ind (Peer B)-------------->| | |
| | dropped | |
| | | |
| |<== data ==================|
| dropped | | |
| | | |

Figure 3

在图 3 中,客户端已经创建了一个分配,现在希望向其对等方发送数据。客户端首先通过向服务器发送 CreatePermission 请求来创建权限,在 XOR-PEER-ADDRESS 属性中指定对等方 A 的(服务器反射的)IP 地址,如果不这样做,服务器将不会在客户端和服务器之间中继数据。然后,客户端使用 Send 指示向对等方 A 发送数据,在服务器处,应用程序数据被提取并在 UDP 数据报中转发到对等方 A,使用中继传输地址作为源传输地址。当从对等方 A 收到 UDP 数据报到中继传输地址时,内容被放入 Data 指示并转发到客户端。稍后,客户端尝试与对等方 B 交换数据,但是,没有为对等方 B 安装权限,因此来自客户端的 Send 指示和来自对等方的 UDP 数据报都被服务器丢弃。

2.5. Channels (通道)

对于某些应用程序(例如,IP 语音 (Voice over IP)),Send 指示或 Data 指示添加到应用程序数据的 36 字节开销可能会大大增加客户端和服务器之间所需的带宽。为了解决这个问题,TURN 提供了第二种方式,让客户端和服务器将数据与特定对等方关联。

第二种方式使用一种称为 ChannelData 消息的替代数据包格式。ChannelData 消息不使用其他 TURN 消息使用的 STUN 头,而是具有一个 4 字节的头,其中包括一个称为通道号 (Channel Number) 的数字。使用中的每个通道号都绑定到特定的对等方,因此用作对等方主机传输地址的简写。

要将通道绑定到对等方,客户端向服务器发送 ChannelBind 请求,并包括一个未绑定的通道号和对等方的传输地址。通道绑定后,客户端可以使用 ChannelData 消息向服务器发送发往对等方的数据。类似地,服务器可以使用 ChannelData 消息将来自该对等方的数据中继到客户端。

通道绑定持续 10 分钟,除非刷新 - 选择此生存时间是为了比权限生存时间更长。通道绑定通过发送另一个 ChannelBind 请求将通道重新绑定到对等方来刷新。与权限类似(但与分配不同),没有办法显式删除通道绑定,客户端必须简单地等待它超时。

TURN                                 TURN           Peer          Peer
client server A B
| | | |
|-- ChannelBind req ---------------->| | |
| (Peer A to 0x4001) | | |
| | | |
|<---------- ChannelBind succ resp --| | |
| | | |
|-- [0x4001] data ------------------>| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |

Figure 4

图 4 显示了正在使用的通道机制。客户端已经创建了一个分配,现在希望将通道绑定到对等方 A。为此,客户端向服务器发送 ChannelBind 请求,指定对等方 A 的传输地址和通道号(0x4001)。之后,客户端可以发送封装在 ChannelData 消息内部的应用程序数据到对等方 A:这显示为"[0x4001] data",其中 0x4001 是通道号。当 ChannelData 消息到达服务器时,服务器将数据传输到 UDP 数据报并将其发送到对等方 A(即绑定到通道号 0x4001 的对等方)。

在相反的方向上,当对等方 A 向中继传输地址发送 UDP 数据报时,此 UDP 数据报到达分配给分配的中继传输地址上的服务器。由于 UDP 数据报是从对等方 A 接收的,对等方 A 已分配了通道号,因此服务器在向客户端发送数据时将数据封装到 ChannelData 消息中。

通道绑定后,客户端可以自由地混合使用 ChannelData 消息和 Send 指示。在图中,客户端后来决定使用 Send 指示而不是 ChannelData 消息向对等方 A 发送额外的数据。例如,客户端可能决定这样做,以便它可以使用 DONT-FRAGMENT 属性(参见下一节)。但是,一旦通道被绑定,服务器将始终使用 ChannelData 消息,如调用流所示。

请注意,ChannelData 消息只能用于客户端已绑定通道的对等方。在上面的示例中,对等方 A 已绑定到通道,但对等方 B 没有,因此与对等方 B 之间的应用程序数据将使用 Send 机制。

2.6. Unprivileged TURN Servers (非特权 TURN 服务器)

此版本的 TURN 被设计为可以将服务器实现为在常用操作系统下在用户空间中运行的应用程序,而无需特殊权限。做出此设计决策是为了使部署 TURN 服务器变得容易:例如,允许将 TURN 服务器集成到对等应用程序中,以便一个对等方可以向另一个对等方提供 NAT 穿透服务。

此设计决策对 TURN 服务器中继的数据具有以下含义:

  • Diffserv 字段的值可能不会跨服务器保留。

  • 生存时间 (Time to Live, TTL) 字段可能会跨服务器重置,而不是递减。

  • 显式拥塞通知 (Explicit Congestion Notification, ECN) 字段可能会被服务器重置。

  • ICMP 消息不会被服务器中继。

  • 没有端到端分片,因为数据包在服务器处重新组装。

未来的工作可能会指定解决这些限制的替代 TURN 语义。

2.7. Avoiding IP Fragmentation (避免 IP 分片)

出于 [Frag-Harmful] 中描述的原因,应用程序,尤其是那些发送大量数据的应用程序,应该努力避免其数据包被分片。使用 TCP 的应用程序可以或多或少地忽略此问题,因为分片避免现在是 TCP 的标准部分,但使用 UDP 的应用程序(因此使用此版本 TURN 的任何应用程序)必须自己处理分片避免。

在客户端和对等方上运行的应用程序可以采取两种方法之一来避免 IP 分片。

第一种方法是避免在客户端和对等方之间交换的 TURN 消息/UDP 数据报中发送大量应用程序数据。这是大多数 VoIP(IP 语音)应用程序采取的方法。在这种方法中,应用程序利用了 IP 规范 [RFC0791] 指定最多 576 字节的 IP 数据包永远不需要分片这一事实。

可以包含的应用程序数据的确切数量(同时避免分片)取决于客户端和服务器之间的 TURN 会话的详细信息:是使用 UDP、TCP 还是 TLS 传输,是使用 ChannelData 消息还是 Send/Data 指示,以及是否包含任何额外的属性(例如 DONT-FRAGMENT 属性)。另一个难以确定的因素是,路径上的某个地方是否由于其他原因(例如使用 IP-in-IP 隧道)而减小了 MTU。

作为指导原则,在单个 TURN 消息(客户端在客户端到服务器路径上)或 UDP 数据报(对等方在对等方到服务器路径上)中发送最多 500 字节的应用程序数据通常可以避免 IP 分片。为了进一步减少分片的机会,建议客户端在传输大量数据时使用 ChannelData 消息,因为 ChannelData 消息的开销小于 Send 和 Data 指示。

客户端和对等方可以采取的第二种避免分片的方法是使用路径 MTU 发现算法来确定可以发送的应用程序数据的最大数量而不会分片。

不幸的是,由于实现此版本 TURN 的服务器不会中继 ICMP 消息,[RFC1191] 中定义的经典路径 MTU 发现算法无法发现客户端和对等方之间传输路径的 MTU。(即使它们确实中继了 ICMP 消息,该算法也不会总是有效,因为 ICMP 消息经常被组合的 NAT/防火墙设备过滤掉)。

因此,客户端和服务器需要使用不需要 ICMP 消息的路径 MTU 发现算法。[RFC4821] 中定义的分组化路径 MTU 发现 (Packetized Path MTU Discovery) 算法就是这样一种算法。

如何将 [RFC4821] 的算法与 TURN 一起使用的细节仍在研究中。但是,作为朝着这个目标迈出的一步,此版本的 TURN 支持 DONT-FRAGMENT 属性。当客户端在 Send 指示中包含此属性时,这会告诉服务器在发送到对等方的结果 UDP 数据报中设置 DF 位。由于某些服务器可能无法设置 DF 位,客户端还应该在 Allocate 请求中包含此属性 - 任何不支持 DONT-FRAGMENT 属性的服务器都会通过拒绝 Allocate 请求来表示这一点。

2.8. RTP Support (RTP 支持)

TURN 的设想用途之一是作为希望使用 RTP 交换实时数据(例如,语音或视频)的客户端和对等方的中继。为了便于将 TURN 用于此目的,TURN 包括对旧版本 RTP 的一些特殊支持。

旧版本的 RTP [RFC3550] 要求 RTP 流位于偶数端口号上,而关联的 RTP 控制协议 (RTP Control Protocol, RTCP) 流(如果存在)位于下一个最高端口上。为了允许客户端与仍然需要这样做的对等方一起工作,TURN 允许客户端请求服务器分配具有偶数端口号的中继传输地址,并可选择请求服务器为后续分配保留下一个最高端口号。

2.9. Anycast Discovery of Servers (服务器的任播发现)

此版本的 TURN 旨在允许未来规范通过 UDP 对 TURN 服务器进行任播发现的方法。

具体来说,TURN 服务器可以拒绝 Allocate 请求,并建议客户端尝试备用服务器。为了避免某些类型的攻击,客户端必须 (MUST) 对备用服务器使用与对初始服务器使用的相同凭证。