跳到主要内容

2. 操作概览

本章概述 TURN 的操作. 本章内容为非规范性内容.

在典型配置中, TURN client 连接到专用网络 [RFC1918], 并通过一个或多个 NAT 连接到公共 Internet. 公共 Internet 上有一个 TURN server. Internet 的其他位置有一个或多个 TURN client 希望与之通信的 peer. 这些 peer 可能位于一个或多个 NAT 后方, 也可能不在 NAT 后方. client 使用 server 作为 relay, 向这些 peer 发送分组并接收来自这些 peer 的分组.

                                    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 client 与 TURN server 被一个 NAT 隔开, client 位于 NAT 的私有侧, server 位于 NAT 的公共侧. 假定该 NAT 是一个 "bad" NAT; 例如, 它的映射属性可能是 "Address-and-Port-Dependent Mapping" (见 [RFC4787]).

client 从一个称为 client 的 HOST TRANSPORT ADDRESS (主机传输地址) 的 (IP 地址, 端口) 组合与 server 通信. (IP 地址和端口的组合称为 TRANSPORT ADDRESS (传输地址).)

client 从其 host transport address 向 TURN server 上的一个 transport address 发送 TURN 消息, 后者称为 TURN SERVER TRANSPORT ADDRESS (TURN 服务器传输地址). client 通过某种未指定方式 (例如配置) 获知 TURN server transport address, 并且该地址通常会被多个 client 同时使用.

由于 client 位于 NAT 后方, server 看到来自 client 的分组时, 其来源是 NAT 自身上的某个 transport address. 该地址称为 client 的 SERVER-REFLEXIVE TRANSPORT ADDRESS (服务器反射传输地址), server 发送到 client 的 server-reflexive transport address 的分组会由 NAT 转发到 client 的 host transport address.

client 使用 TURN 命令在 server 上创建并操作 ALLOCATION (分配). allocation 是 server 上的一种数据结构. 除其他内容外, 此数据结构包含该 allocation 的 relayed transport address. relayed transport address 是 server 上的 transport address, peer 可以使用它让 server 将数据中继给 client. allocation 由其 relayed transport address 唯一标识.

创建 allocation 后, client 可以把应用数据连同应将数据发送给哪个 peer 的指示一起发送给 server, server 会将该数据中继给相应 peer. client 在 TURN 消息内向 server 发送应用数据; 在 server 处, 数据从 TURN 消息中提取出来, 并在 UDP datagram 中发送给 peer. 反向路径上, peer 可以在 UDP datagram 中把应用数据发送到该 allocation 的 relayed transport address; server 随后会把这些数据封装在 TURN 消息中发送给 client, 并附带指出哪个 peer 发送了该数据. 由于 TURN 消息始终包含 client 正在与哪个 peer 通信的指示, client 可以使用单个 allocation 与多个 peer 通信.

当 peer 位于 NAT 后方时, client 必须使用 peer 的 server-reflexive transport address 而不是其 host transport address 来标识 peer. 例如, 若要向上例中的 Peer A 发送应用数据, client 必须指定 192.0.2.150:32102 (Peer A 的 server-reflexive transport address), 而不是 192.168.100.2:49582 (Peer A 的 host transport address).

server 上的每个 allocation 都属于单个 client, 且只有一个只供该 allocation 使用的 relayed transport address. 因此, 当分组到达 server 上的 relayed transport address 时, server 知道该数据应交付给哪个 client.

client 可以同时在一个 server 上拥有多个 allocation.

2.1. 传输

按本规范定义, TURN 在 server 与 peer 之间始终使用 UDP. 但是, 本规范允许在 client 与 server 之间使用 UDP, TCP, 或 TCP 上的 Transport Layer Security (TLS) 中的任一种来承载 TURN 消息.

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

如果 client 与 server 之间使用 TCP 或 TLS-over-TCP, 则 server 在向 peer 中继数据或从 peer 中继数据时, 会在这些传输与 UDP 之间进行转换.

由于此版本的 TURN 只支持 server 与 peer 之间使用 UDP, 因此预期大多数 client 也会倾向于在 client 与 server 之间使用 UDP. 既然如此, 有些读者可能会疑惑: 为什么还要支持 TCP 和 TLS-over-TCP?

TURN 支持 client 与 server 之间的 TCP transport, 是因为某些防火墙被配置为完全阻塞 UDP. 这些防火墙阻塞 UDP 但不阻塞 TCP, 部分原因是 TCP 具有一些属性, 能让防火墙更清楚地判断防火墙后方节点的意图. 例如, TCP 有三次握手, 可以更清楚地表明受保护节点确实希望建立该特定连接; 而对于 UDP, 防火墙最多只能使用过滤规则猜测哪些流是期望的. 此外, TCP 有显式连接拆除, 而对于 UDP, 防火墙必须使用计时器猜测某个流何时结束.

TURN 支持 client 与 server 之间的 TLS-over-TCP transport, 是因为 TLS 提供了 TURN 默认 digest authentication 不具备的额外安全属性, 某些 client 可能希望利用这些属性. 特别是, TLS 为 client 提供了一种确认其正在与正确 server 通信的方法, 并为 TURN 控制消息提供机密性. TURN 不要求使用 TLS, 因为使用 TLS 的开销高于 digest authentication, 并且例如使用 TLS 可能意味着大多数应用数据会被双重加密 (一次由 TLS 加密, 一次为了确保其在 UDP datagram 中仍保持加密而加密).

计划通过 TURN 扩展来增加 server 与 peer 之间 TCP 的支持 [TURN-TCP]. 因此, server 与 peer 之间使用 UDP 的 allocation 称为 UDP ALLOCATION, 而 server 与 peer 之间使用 TCP 的 allocation 称为 TCP ALLOCATION. 本规范只描述 UDP allocation.

按本规范定义, TURN 仅支持 IPv4. 本规范中的所有 IP 地址必须是 IPv4 地址. 计划通过 TURN 扩展来增加对 IPv6 以及 IPv4 与 IPv6 之间中继的支持 [TURN-IPv6].

在 TURN 的某些应用中, client 可能能够在其用于与 server 通信的 host transport address 上发送和接收 TURN 分组之外的分组. 例如, 当 TURN 与 ICE 一起使用时会出现这种情况. 在这些情况下, client 可以通过检查到达分组的源地址来区分 TURN 分组和其他分组: 来自 TURN server 的分组就是 TURN 分组.

2.2. 分配

为在 server 上创建 allocation, client 使用 Allocate transaction. client 向 server 发送 Allocate request, server 以 Allocate success response 响应, 其中包含该 allocation 的 relayed transport address. client 可以在 Allocate request 中包含描述所需 allocation 类型的属性 (例如 allocation 的 lifetime). 由于中继数据具有安全影响, server REQUIRES client 对自身进行认证, 并要求 client 在 Allocate request 中使用认证, 通常使用 STUN 的 long-term credential mechanism, 以证明其有权使用该 server.

一旦分配了 relayed transport address, client 必须保持 allocation 存活. 为此, client 会周期性向 server 发送 Refresh request. TURN 有意使用不同的方法 (Refresh 而不是 Allocate) 进行刷新, 以确保如果 allocation 因某种原因消失, client 能被告知.

Refresh transaction 的频率由 allocation 的 lifetime 决定. allocation 的默认 lifetime 为 10 分钟 - 选择该值是为了足够长, 使刷新通常不会成为 client 的负担, 同时又足够短, 使非正常退出的 client 能及时让 allocation 过期. 但是, client 可以在 Allocate request 中请求更长 lifetime, 也可以在 Refresh request 中修改其请求, 而 server 始终在响应中指出实际 lifetime. client 必须在先前 Allocate 或 Refresh transaction 的 "lifetime" 秒内发起新的 Refresh transaction. 一旦 client 不再希望使用该 allocation, 它应该使用请求 lifetime 为 0 的 Refresh request 删除该 allocation.

server 和 client 都跟踪一个称为 5-TUPLE 的值. 在 client 上, 5-tuple 由 client 的 host transport address, server transport address, 以及 client 用于与 server 通信的 transport protocol 组成. 在 server 上, 5-tuple 值相同, 只是 client 的 host transport address 被替换为 client 的 server-reflexive address, 因为这是 server 看到 client 所使用的地址.

client 和 server 都记住 Allocate request 中使用的 5-tuple. client 与 server 之间的后续消息使用同一个 5-tuple. 通过这种方式, client 和 server 知道消息所指的是哪个 allocation. 如果 client 希望分配第二个 relayed transport address, 它必须使用不同的 5-tuple 创建第二个 allocation (例如使用不同的 client host address 或端口).

Note: 尽管本文档使用的术语指的是 5-tuple, TURN server 可以存储任何它喜欢的标识符, 只要产生相同结果即可. 具体而言, 实现可以使用 file descriptor 代替 5-tuple 来表示 TCP connection.

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 中, client 在不带凭据的情况下向 server 发送 Allocate request. 由于 server 要求所有请求都使用 STUN 的 long-term credential mechanism, server 以 401 (Unauthorized) 错误码拒绝该请求. 随后 client 再次尝试, 这次包含凭据 (图中未显示). 这一次, server 接受 Allocate request, 并返回包含 (除其他内容外) 分配给该 allocation 的 relayed transport address 的 Allocate success response. 稍后, client 决定刷新该 allocation, 因此向 server 发送 Refresh request. 刷新被接受, server 以 Refresh success response 响应.

2.3. 权限

为缓解企业 IT 管理员对 TURN 可能被用于绕过公司防火墙安全策略的担忧, TURN 包含 PERMISSION (权限) 的概念. TURN permission 模仿符合 [RFC4787] 的 NAT 的 address-restricted filtering 机制.

一个 allocation 可以有零个或多个 permission. 每个 permission 由一个 IP 地址和一个 lifetime 组成. 当 server 在某个 allocation 的 relayed transport address 上收到 UDP datagram 时, 它首先检查 permission 列表. 如果该 datagram 的源 IP 地址匹配某个 permission, 应用数据会被中继给 client; 否则, 该 UDP datagram 会被静默丢弃.

如果 permission 未经刷新而过期, 它会被删除, 并且没有显式删除 permission 的方法. 选择这种行为是为了匹配符合 [RFC4787] 的 NAT 的行为.

client 可以使用 CreatePermission request 或 ChannelBind request 安装或刷新 permission. 使用 CreatePermission request 时, 可以通过单个请求安装或刷新多个 permission - 这对于使用 ICE 的应用很重要. 出于安全原因, permission 只能通过可认证的 transaction 安装或刷新, 因此 Send indication 和 ChannelData message (用于向 peer 发送数据) 不会安装或刷新任何 permission.

注意, permission 位于 allocation 的上下文中, 因此在一个 allocation 中添加或过期 permission 不会影响其他 allocation.

2.4. Send 机制

client 和 peer 使用 TURN server 交换应用数据有两种机制. 第一种机制使用 Send 和 Data 方法, 第二种使用 channel. 对于给定 peer, 这两种机制互斥: client 可以选择对给定 peer 使用哪种机制, 但一旦选定, 该机制不能更改. 两种机制的共同点是, client 能够使用单个已分配的 relayed transport address 与多个 peer 通信; 因此, 两种机制都包含一种方法, 使 client 能向 server 指明哪个 peer 应接收数据, 并使 server 能向 client 指明哪个 peer 发送了数据.

Send 机制使用 Send indication 和 Data indication. Send indication 用于从 client 向 server 发送应用数据, 而 Data indication 用于从 server 向 client 发送应用数据.

使用 Send 机制时, client 向 TURN server 发送 Send indication, 其中包含 (a) 指定 peer 的 (server-reflexive) transport address 的 XOR-PEER-ADDRESS 属性, 以及 (b) 保存应用数据的 DATA 属性. 当 TURN server 收到 Send indication 时, 它从 DATA 属性中提取应用数据, 并在 UDP datagram 中将其发送给 peer, 使用已分配的 relayed address 作为源地址. 注意, 不需要指定 relayed transport address, 因为它由 Send indication 所使用的 5-tuple 隐含确定.

在反向路径上, 到达 TURN server 上 relayed transport address 的 UDP datagram 会被转换成 Data indication 并发送给 client, 其中 peer 的 server-reflexive transport address 包含在 XOR-PEER-ADDRESS 属性中, 数据本身包含在 DATA 属性中. 由于 relayed transport address 唯一标识 allocation, server 知道哪个 client 应接收该数据.

Send 和 Data indication 不能被认证, 因为 STUN 的 long-term credential mechanism 不支持对 indication 进行认证. 这并不像乍看起来那么严重, 因为从 client 到 server 的路径只是到 peer 总路径的一半. 需要适当安全性的应用应加密 client 与 peer 之间发送的数据.

由于 Send indication 未认证, 攻击者可能向 server 发送伪造的 Send indication, 随后 server 会把它中继给 peer. 为部分缓解此攻击, TURN REQUIRES client 在使用 Send indication 向 peer 发送数据之前, 先为该 peer 安装 permission.

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 中, client 已创建 allocation, 现在希望向其 peer 发送数据. client 首先通过向 server 发送 CreatePermission request 来创建 permission, 在 XOR-PEER-ADDRESS 属性中指定 Peer A 的 (server-reflexive) IP 地址, 因为如果不这样做, server 将不会在 client 与 server 之间中继数据. 然后, client 使用 Send indication 向 Peer A 发送数据, 在 server 处, 应用数据被提取出来, 并以 relayed transport address 作为源 transport address, 在 UDP datagram 中转发给 Peer A. 当 relayed transport address 收到来自 Peer A 的 UDP datagram 时, 其内容被放入 Data indication 并转发给 client. 稍后, client 试图与 Peer B 交换数据, 然而, 没有为 Peer B 安装 permission, 因此来自 client 的 Send indication 和来自 peer 的 UDP datagram 都会被 server 丢弃.

2.5. Channel

对于某些应用 (例如 Voice over IP), Send indication 或 Data indication 添加到应用数据上的 36 字节开销会显著增加 client 与 server 之间所需的带宽. 为解决此问题, TURN 提供了第二种方式, 用于让 client 和 server 将数据与特定 peer 关联起来.

第二种方式使用一种替代 packet format, 称为 ChannelData message. ChannelData message 不使用其他 TURN 消息使用的 STUN header, 而是使用一个 4 字节 header, 其中包含一个称为 CHANNEL NUMBER 的编号. 每个正在使用的 channel number 都绑定到特定 peer, 因而充当该 peer 的 host transport address 的简写.

为了将 channel 绑定到 peer, client 向 server 发送 ChannelBind request, 并包含一个未绑定的 channel number 和该 peer 的 transport address. 一旦 channel 已绑定, client 就可以使用 ChannelData message 向 server 发送以 peer 为目的地的数据. 类似地, server 可以使用 ChannelData message 将来自该 peer 的数据中继给 client.

channel binding 持续 10 分钟, 除非被刷新 - 选择该 lifetime 是为了长于 permission lifetime. 通过发送另一个将该 channel 重新绑定到 peer 的 ChannelBind request 来刷新 channel binding. 与 permission 一样 (但不同于 allocation), 没有显式删除 channel binding 的方法, client 只能等待其超时.

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 展示了 channel 机制的使用. client 已创建 allocation, 现在希望将 channel 绑定到 Peer A. 为此, client 向 server 发送 ChannelBind request, 指定 Peer A 的 transport address 和一个 channel number (0x4001). 之后, client 可以把封装在 ChannelData message 内的应用数据发送给 Peer A: 图中显示为 "[0x4001] data", 其中 0x4001 是 channel number. 当 ChannelData message 到达 server 时, server 将数据转移到 UDP datagram 中, 并发送给 Peer A (即绑定到 channel number 0x4001 的 peer).

反向路径上, 当 Peer A 向 relayed transport address 发送 UDP datagram 时, 此 UDP datagram 到达 server 上分配给该 allocation 的 relayed transport address. 由于该 UDP datagram 来自 Peer A, 且 Peer A 已被分配 channel number, server 在向 client 发送数据时会将数据封装在 ChannelData message 中.

一旦 channel 已绑定, client 可以自由混用 ChannelData message 和 Send indication. 图中, client 稍后决定使用 Send indication 而不是 ChannelData message 向 Peer A 发送额外数据. 例如, client 可能决定这样做, 以便使用 DONT-FRAGMENT 属性 (见下一章). 但是, 一旦 channel 已绑定, server 将始终使用 ChannelData message, 如调用流程所示.

注意, ChannelData message 只能用于 client 已绑定 channel 的 peer. 在上例中, Peer A 已绑定到 channel, 但 Peer B 没有, 因此到 Peer B 或来自 Peer B 的应用数据将使用 Send 机制.

2.6. 非特权 TURN Server

此版本 TURN 的设计目标是让 server 可以作为在常见部署的操作系统上以用户空间运行的应用实现, 而无需特殊权限. 作出此设计决策是为了便于部署 TURN server: 例如, 允许将 TURN server 集成到 peer 应用中, 从而让一个 peer 能向另一个 peer 提供 NAT traversal 服务.

此设计决策对 TURN server 中继的数据有以下影响:

  • Diffserv 字段的值在经过 server 时可能不会保留.

  • Time to Live (TTL) 字段在经过 server 时可能被重置, 而不是递减.

  • Explicit Congestion Notification (ECN) 字段可能被 server 重置.

  • ICMP 消息不会由 server 中继.

  • 不存在端到端 fragmentation, 因为分组会在 server 处重组.

未来工作可能会规定替代 TURN 语义来解决这些限制.

2.7. 避免 IP 分片

由于 [Frag-Harmful] 中描述的原因, 应用, 尤其是发送大量数据的应用, 应尽量避免其分组被分片. 使用 TCP 的应用基本可以忽略此问题, 因为避免分片现在已是 TCP 的标准组成部分; 但使用 UDP 的应用 (因此也包括使用此版本 TURN 的任何应用) 必须自行处理避免分片.

运行在 client 和 peer 上的应用可以采用两种方法之一来避免 IP fragmentation.

第一种方法是避免在 client 与 peer 之间交换的 TURN 消息/UDP datagram 中发送大量应用数据. 这是大多数 VoIP (Voice over IP) 应用采用的方法. 在这种方法中, 应用利用了 IP 规范 [RFC0791] 指定的事实: 长度不超过 576 字节的 IP packet 不应需要分片.

在避免分片的同时能包含的确切应用数据量取决于 client 与 server 之间 TURN session 的细节: 正在使用 UDP, TCP, 还是 TLS transport, 正在使用 ChannelData message 还是 Send/Data indication, 以及是否包含任何额外属性 (例如 DONT-FRAGMENT 属性). 另一个难以确定的因素是路径中某处的 MTU 是否因其他原因而降低, 例如由于使用 IP-in-IP tunneling.

作为指导原则, 在单个 TURN 消息 (由 client 在 client-to-server 路径上发送) 或 UDP datagram (由 peer 在 peer-to-server 路径上发送) 中最多发送 500 字节应用数据, 通常可以避免 IP fragmentation. 为进一步降低分片概率, 建议 client 在发送大量数据时使用 ChannelData message, 因为 ChannelData message 的开销低于 Send 和 Data indication.

client 和 peer 为避免分片可采用的第二种方法是使用 path MTU discovery algorithm, 以确定无需分片即可发送的最大应用数据量.

遗憾的是, [RFC1191] 中定义的经典 path MTU discovery algorithm 无法发现 client 与 peer 之间传输路径的 MTU, 因为实现此版本 TURN 的 server 不会中继 ICMP 消息. (即便它们确实中继 ICMP 消息, 该算法也并不总是有效, 因为 ICMP 消息经常被组合式 NAT/firewall 设备过滤掉).

因此, client 和 server 需要使用不要求 ICMP 消息的 path MTU discovery algorithm. [RFC4821] 中定义的 Packetized Path MTU Discovery algorithm 就是这样一种算法.

如何将 [RFC4821] 的算法与 TURN 结合使用的细节仍在制定中. 不过, 作为朝这一目标迈进的一步, 此版本 TURN 支持 DONT-FRAGMENT 属性. 当 client 在 Send indication 中包含该属性时, 这会告诉 server 在其发送给 peer 的结果 UDP datagram 中设置 DF bit. 由于某些 server 可能无法设置 DF bit, client 也应在 Allocate request 中包含此属性 - 任何不支持 DONT-FRAGMENT 属性的 server 都会通过拒绝 Allocate request 来表明这一点.

2.8. RTP 支持

TURN 的一个预期用途是作为 relay, 服务于希望使用 RTP 交换实时数据 (例如语音或视频) 的 client 和 peer. 为便于将 TURN 用于此目的, TURN 包含对较旧版本 RTP 的一些特殊支持.

较旧版本的 RTP [RFC3550] 要求 RTP stream 位于偶数端口号上, 关联的 RTP Control Protocol (RTCP) stream (如果存在) 位于下一个更高端口. 为使 client 能与仍有此要求的 peer 协同工作, TURN 允许 client 请求 server 分配一个端口号为偶数的 relayed transport address, 并可选地请求 server 为后续 allocation 保留下一个更高端口号.

2.9. Server 的 Anycast 发现

此版本 TURN 的设计允许未来规范启用通过 UDP 对 TURN server 进行 anycast 发现.

具体而言, TURN server 可以拒绝 Allocate request, 并建议 client 尝试另一个 server. 为防止某些类型的攻击, client 必须在备用 server 上使用与其在初始 server 上本会使用的相同凭据.