2. ICE 概览 (Overview of ICE)
在典型 ICE 部署中, 有两个希望通信的端点, 在 RFC 3264 术语中称为 AGENTS. 它们能够通过某种信令协议, 例如 SIP, 进行间接通信, 并由此执行 SDP [RFC3264] 消息的 offer/answer 交换. 注意, ICE 并非用于 SIP 的 NAT 穿越; 本规范假定 SIP 的 NAT 穿越由其他机制提供 [RFC5626]. 在 ICE 过程开始时, agents 并不了解自己的拓扑. 尤其是, 它们可能位于 NAT, 或多层 NAT, 之后, 也可能不是. ICE 允许 agents 发现关于其拓扑的足够信息, 从而有可能找到一条或多条可用于通信的路径.
图 1 展示了 ICE 部署的典型环境. 两个端点标记为 L 和 R, 分别代表 left 和 right, 以便可视化呼叫流. L 和 R 都位于各自的 NAT 后面, 尽管它们可能并不知道这一点. NAT 的类型及其属性也未知. Agent L 和 R 能够参与 offer/answer 交换, 通过该交换传递 SDP 消息, 其目的是在 L 和 R 之间建立媒体会话. 通常, 该交换会通过 SIP server 发生.
除了 agents, SIP server 和 NATs 之外, ICE 通常还会与网络中的 STUN 或 TURN servers 配合使用. 每个 agent 可以拥有自己的 STUN 或 TURN server, 也可以共用同一个.
+-------+
| SIP |
+-------+ | Srvr | +-------+
| STUN | | | | STUN |
| Srvr | +-------+ | Srvr |
| | / \ | |
+-------+ / \ +-------+
/ \
/ \
/ \
/ \
/ <- Signaling -> \
/ \
/ \
+--------+ +--------+
| NAT | | NAT |
+--------+ +--------+
/ \
/ \
/ \
+-------+ +-------+
| Agent | | Agent |
| L | | R |
| | | |
+-------+ +-------+
图 1: ICE 部署场景
ICE 背后的基本思想如下: 每个 agent 都有多种可用于与另一个 agent 通信的候选 TRANSPORT ADDRESSES, 即特定传输协议的 IP 地址和端口组合; 在本规范中该传输协议始终为 UDP. 这些地址可能包括:
- 直接连接网络接口上的传输地址
- NAT 公网侧的转换后传输地址, 即 "server reflexive" 地址
- 从 TURN server 分配的传输地址, 即 "relayed address".
理论上, L 的任意候选传输地址都可以用来与 R 的任意候选传输地址通信. 然而在实践中, 许多组合无法工作. 例如, 如果 L 和 R 都位于 NAT 后面, 它们直接连接接口的地址通常不太可能直接通信, 这正是需要 ICE 的原因. ICE 的目的就是发现哪些地址 pair 可以工作. ICE 的做法是系统性地尝试所有可能的 pairs, 并且按照精心排序的顺序进行, 直到找到一个或多个可工作的 pairs.
2.1. 收集候选地址 (Gathering Candidate Addresses)
为了执行 ICE, agent 必须识别自己的所有 address candidates. CANDIDATE 是一个传输地址, 即特定传输协议的 IP 地址和端口组合, 此处仅指定 UDP. 本文档定义了三类 candidates, 有些源自物理或逻辑网络接口, 有些可通过 STUN 和 TURN 发现. 自然地, 一个可行 candidate 是直接从本地接口获得的传输地址. 这样的 candidate 称为 HOST CANDIDATE. 本地接口可以是 ethernet 或 WiFi, 也可以是通过隧道机制获得的接口, 例如 Virtual Private Network (VPN) 或 Mobile IP (MIP). 在所有情况下, 这样的网络接口在 agent 看来都是一个本地接口, 可从中分配端口, 也就是 candidates.
如果 agent 是多宿主的, 它会从每个 IP 地址获得一个 candidate. 根据 PEER, 即会话中的另一个 agent, 在 IP 网络上相对于该 agent 的位置, peer 可能可以通过其中一个或多个 IP 地址到达该 agent. 例如, 考虑一个 agent, 它在私有 net 10 网络上有一个本地 IP 地址 (I1), 另一个地址连接到公共 Internet (I2). 当与同一私有 net 10 网络上的 peer 通信时, 来自 I1 的 candidate 可直接到达; 当与公共 Internet 上的 peer 通信时, 来自 I2 的 candidate 可直接到达. Offering agent 不会在发送 offer 前猜测哪个 IP 地址可用, 而是在其 offer 中同时包含两个 candidates.
接下来, agent 使用 STUN 或 TURN 获得附加 candidates. 这些 candidate 分为两种: NAT 公网侧的转换后地址 (SERVER REFLEXIVE CANDIDATES), 以及 TURN servers 上的地址 (RELAYED CANDIDATES). 使用 TURN servers 时, 两类 candidates 都从 TURN server 获得. 仅使用 STUN servers 时, 只能从中获得 server reflexive candidates. 这些 candidates 与 host candidate 的关系如图 2 所示. 在该图中, 两类 candidates 都使用 TURN 发现. 图中的记号 X:x 表示 IP 地址 X 和 UDP 端口 x.
To Internet
|
|
| /------------ Relayed
Y:y | / Address
+--------+
| |
| TURN |
| Server |
| |
+--------+
|
|
| /------------ Server
X1':x1'|/ Reflexive
+------------+ Address
| NAT |
+------------+
|
| /------------ Local
X:x |/ Address
+--------+
| |
| Agent |
| |
+--------+
图 2: Candidate 关系
当 agent 从 IP 地址和端口 X:x 发送 TURN Allocate request 时, NAT, 假设存在 NAT, 会创建绑定 X1':x1', 将该 server reflexive candidate 映射到 host candidate X:x. 从 host candidate 发出的出站分组会被 NAT 转换为 server reflexive candidate. 发送到 server reflexive candidate 的入站分组会被 NAT 转换为 host candidate 并转发给 agent. 我们将与给定 server reflexive candidate 关联的 host candidate 称为 BASE.
注意: "Base" 指 agent 针对特定 candidate 实际发送数据所使用的地址. 因此, 作为退化情形, host candidates 也有 base, 但它与 host candidate 本身相同.
当 agent 与 TURN server 之间存在多个 NAT 时, TURN request 会在每个 NAT 上创建绑定, 但 agent 只能发现最外层的 server reflexive candidate, 即最靠近 TURN server 的那个. 如果 agent 不在 NAT 后面, 则 base candidate 与 server reflexive candidate 相同, 该 server reflexive candidate 是冗余的, 将被消除.
随后 Allocate request 到达 TURN server. TURN server 从其本地 IP 地址 Y 分配端口 y, 并生成 Allocate response, 将此 relayed candidate 通知给 agent. TURN server 还会通过把 Allocate request 的源传输地址复制到 Allocate response 中, 将 server reflexive candidate X1':x1' 通知给 agent. TURN server 充当分组中继, 在 L 和 R 之间转发流量. 为了向 L 发送流量, R 将流量发送到 TURN server 的 Y:y, TURN server 将其转发到 X1':x1', 该流量穿过 NAT, 在 NAT 中被映射到 X:x 并递送给 L.
当仅使用 STUN servers 时, agent 向其 STUN server 发送 STUN Binding request [RFC5389]. STUN server 通过把 Binding request 的源传输地址复制到 Binding response 中, 将 server reflexive candidate X1':x1' 通知给 agent.
2.2. 连接检查 (Connectivity Checks)
一旦 L 收集了其所有 candidates, 它会按优先级从高到低排序, 并通过信令信道发送给 R. Candidates 携带在 SDP offer 的属性中. 当 R 收到 offer 时, 它执行相同的收集过程, 并用自己的 candidate 列表响应. 此过程结束时, 每个 agent 都有一份完整列表, 其中包含自己的 candidates 和 peer 的 candidates. 它将二者配对, 得到 CANDIDATE PAIRS. 为了查看哪些 pairs 可工作, 每个 agent 调度一系列 CHECKS. 每个 check 都是一个 STUN request/response 事务, client 在特定 candidate pair 上执行它, 方式是从 local candidate 向 remote candidate 发送 STUN request.
Connectivity checks 的基本原则很简单:
- 按优先级顺序排序 candidate pairs.
- 按优先级顺序在每个 candidate pair 上发送 checks.
- 确认从另一个 agent 收到的 checks.
当两个 agent 都在某个 candidate pair 上执行 check 时, 结果是一个 4-way handshake:
L R
- -
STUN request -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
图 3: 基本连接检查
需要注意, STUN requests 发送到以及来自的 IP 地址和端口, 与随后用于媒体, 例如 RTP 和 RTCP, 的地址和端口完全相同. 因此, agents 使用分组内容而非接收分组的端口来多路分解 STUN 与 RTP/RTCP. 幸运的是, 这种多路分解很容易完成, 尤其是对 RTP 和 RTCP.
由于 connectivity check 使用 STUN Binding request, STUN Binding response 将包含 agent 在其与 peer 之间任何 NAT 公网侧的转换后传输地址. 如果该传输地址不同于 agent 已学到的其他 candidates, 它就代表一个新 candidate, 称为 PEER REFLEXIVE CANDIDATE, ICE 随后会像测试任何其他 candidate 一样测试它.
作为优化, R 一收到 L 的 check 消息, 就会调度一条 connectivity check 消息, 在同一 candidate pair 上发送给 L. 这会加速寻找有效 candidate 的过程, 称为 TRIGGERED CHECK.
在此握手结束时, L 和 R 都知道它们可以在两个方向上端到端发送并接收消息.
2.3. 候选项排序 (Sorting Candidates)
由于上述算法会搜索所有 candidate pairs, 如果存在可工作的 pair, 无论按什么顺序尝试 candidates, 它最终都会找到. 为了产生更快且更好的结果, candidates 会按指定顺序排序. 得到的已排序 candidate pairs 列表称为 CHECK LIST. 该算法在第 4.1.2 节描述, 但遵循两个一般原则:
- 每个 agent 为自己的 candidates 赋予一个数值 priority, 并随 candidate 一起发送给 peer.
- 本地 priority 与远端 priority 被组合, 使每个 agent 对 candidate pairs 具有相同排序.
第二个属性对于 L 和 R 前面存在 NAT 时让 ICE 正常工作很重要. NAT 通常不会允许来自某个 host 的分组进入, 直到 NAT 后面的 agent 已向该 host 发送过分组. 因此, 每个方向上的 ICE checks 都必须等双方都已通过各自 NAT 发送 check 后才会成功.
Agent 通过周期性地为 check list 上的下一个 candidate pair 发送 STUN request 来处理该列表. 这些称为 ORDINARY CHECKS.
一般而言, priority algorithm 的设计目标是让相似类型的 candidates 具有相似 priorities, 并让更直接的路由, 即经过更少媒体中继和更少 NAT 的路由, 优先于间接路由, 即经过更多媒体中继和更多 NAT 的路由. 不过, 在这些原则内, agents 对如何调优其算法有相当大的自由度.
2.4. 冻结候选项 (Frozen Candidates)
前面的描述只处理 agents 希望建立具有一个 COMPONENT 的媒体会话的情况; component 是媒体流中需要单一传输地址的一部分, 一个媒体流可能需要多个 components, 每个 component 都必须工作, 整个媒体流才能工作. 通常, 例如 RTP 和 RTCP, agents 实际上需要为多个流建立连通性.
每个 component 的网络属性很可能非常相似, 尤其是因为 RTP 和 RTCP 从相同 IP 地址发送和接收. 通常可以利用一个 media component 的信息来确定另一个 component 的最佳 candidates. ICE 通过称为 "frozen candidates" 的机制完成这一点.
每个 candidate 都关联一个称为 FOUNDATION 的属性. 当两个 candidates "相似" 时, 即类型相同, 从相同 host candidate 和 STUN server 使用相同协议获得, 它们具有相同 foundation. 否则, 它们的 foundation 不同. Candidate pair 也有 foundation, 它只是两个 candidate 的 foundation 的拼接. 初始时, 只测试具有唯一 foundations 的 candidate pairs. 其他 candidate pairs 被标记为 "frozen". 当某个 candidate pair 的 connectivity checks 成功时, 具有相同 foundation 的其他 candidate pairs 被解冻. 这避免了重复检查表面上更有吸引力但事实上很可能失败的 components.
尽管为了解释方便, 这里把 "frozen" 描述为单独机制, 但实际上它是 ICE 的组成部分, ICE prioritization algorithm 会自动确保正确的 candidates 按正确顺序解冻并检查.
2.5. 检查的安全性 (Security for Checks)
由于 ICE 用于发现哪些地址可用于在两个 agents 之间发送媒体, 确保该过程不能被劫持以把媒体发送到错误位置非常重要. 每个 STUN connectivity check 都由 message authentication code (MAC) 保护, 该 MAC 使用通过信令信道交换的密钥计算. 此 MAC 提供消息完整性和数据源认证, 从而阻止攻击者伪造或修改 connectivity check 消息. 此外, 如果 SIP [RFC3261] caller 使用 ICE, 且其呼叫发生 fork, ICE 交换会与每个 forked recipient 独立进行. 在这种情况下, 信令中交换的密钥有助于将每个 ICE 交换与每个 forked recipient 关联起来.
2.6. 结束 ICE (Concluding ICE)
ICE checks 按特定序列执行, 使高优先级 candidate pairs 先被检查, 然后才是低优先级 pairs. 结束 ICE 的一种方式是在每个媒体流的每个 component 都有一个 check 成功完成后立即宣布成功. 这确实是合理算法, 其细节如下文提供. 然而, 分组丢失可能导致更高优先级的 check 需要更长时间完成. 在这种情况下, 允许 ICE 多运行一小段时间可能产生更好结果. 更根本地说, 本规范定义的 prioritization 可能不会产生 "optimal" 结果. 例如, 如果目标是选择低时延媒体路径, 使用 relay 只是表明时延可能更高的提示, 但仅仅是提示. 可以进行实际 round-trip time (RTT) 测量, 它可能显示较低优先级 pair 实际上优于较高优先级 pair.
因此, ICE 将其中一个 agent 指派为 CONTROLLING AGENT, 另一个指派为 CONTROLLED AGENT. Controlling agent 可以在有效 pairs 中 nomination 将用于媒体的 candidate pairs. 它可以通过两种方式之一完成: 使用 REGULAR NOMINATION 或 AGGRESSIVE NOMINATION.
使用 regular nomination 时, controlling agent 让 checks 继续, 直到每个媒体流至少找到一个有效 candidate pair. 然后, 它在这些有效 pairs 中进行选择, 并在其 NOMINATED candidate pair 上发送第二个 STUN request, 但这一次设置一个 flag, 告诉 peer 该 pair 已被 nomination 用于媒体. 如图 4 所示.
L R
- -
STUN request -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
STUN request + flag -> \ L's
<- STUN response / check
图 4: 常规提名
一旦带有 flag 的 STUN transaction 完成, 双方都会取消该媒体流的任何后续 checks. ICE 现在将使用该 pair 发送媒体. ICE agent 用于媒体的 pair 称为 SELECTED PAIR.
在 aggressive nomination 中, controlling agent 在其发送的每个 STUN request 中都放入该 flag. 这样, 一旦第一个 check 成功, 该媒体流的 ICE processing 就完成, controlling agent 不必发送第二个 STUN request. Selected pair 将是 check 成功的最高优先级 valid pair. Aggressive nomination 比 regular nomination 更快, 但灵活性更低. Aggressive nomination 如图 5 所示.
L R
- -
STUN request + flag -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
图 5: 激进提名
一旦所有媒体流都完成, 如果媒体流 m 和 c lines 中的 candidates, 称为 DEFAULT CANDIDATES, 与 ICE 的 SELECTED CANDIDATES 不匹配, controlling endpoint 会发送 updated offer.
ICE 结束后, 任一 agent 都可以随时通过发送指示重启的 updated offer, 为一个或所有媒体流重启 ICE.
2.7. Lite 实现 (Lite Implementations)
为了在呼叫中使用 ICE, 两个 agents 都需要支持它. 然而, 某些 agents 将始终连接到公共 Internet, 并具有可从任何 correspondent 接收分组的公共 IP 地址. 为了让这些设备更容易支持 ICE, ICE 定义了一种特殊类型的 implementation, 称为 LITE, 与普通 FULL implementation 相对. Lite implementation 不收集 candidates; 它只为任何媒体流包含 host candidates. Lite agents 不生成 connectivity checks, 也不运行状态机, 尽管它们需要能够响应 connectivity checks. 当 lite implementation 与 full implementation 连接时, full agent 扮演 controlling agent 角色, lite agent 扮演 controlled 角色. 当两个 lite implementations 连接时, 不发送 checks.
关于何时适合使用 lite implementation 的指导, 见 Appendix A 中的讨论.
需要注意, lite implementation 被加入本规范, 是为了提供通往 full implementation 的过渡步骤. 即使对于始终连接到公共 Internet 的设备, 如果可实现, full implementation 仍然更可取.