跳到主要内容

7. 执行连接检查 (Performing Connectivity Checks)

本节描述如何执行 connectivity checks. 所有 ICE implementations 都要求符合 [RFC5389], 而不是较早的 [RFC3489]. 不过, full implementation 会同时生成 checks, 作为 STUN client, 并接收 checks, 作为 STUN server; lite implementation 只接收 checks, 因而只充当 STUN server.

7.1. STUN Client 过程 (STUN Client Procedures)

这些过程定义 agent 如何发送 connectivity check, 无论它是 ordinary check 还是 triggered check. 这些过程只适用于 full implementations.

7.1.1. 为 Relayed Candidates 创建 Permissions (Creating Permissions for Relayed Candidates)

如果 connectivity check 使用 relayed local candidate 发送, client 必须先创建 permission, 除非它以前已经创建过. 如果它曾告知 TURN server 为给定 relayed candidate 朝向 remote candidate 的 IP 地址创建 permission, 则它已经创建过. 为创建 permission, agent 遵循 [RFC5766] 中定义的过程. Permission 必须朝向 remote candidate 的 IP 地址创建. 推荐将 TURN channel 的创建推迟到 ICE 完成之后; 在这种情况下, connectivity checks 的 permissions 通常使用 CreatePermission request 创建. 一旦建立, agent 必须保持该 permission 处于活动状态, 直到 ICE 结束.

7.1.2. 发送 Request (Sending the Request)

Check 通过从 local candidate 向 remote candidate 发送 Binding request 生成. [RFC5389] 描述了 Binding requests 如何构造和生成. Connectivity check 必须使用 STUN short-term credential mechanism. 对 RFC 3489 的向后兼容支持 不得用于或假定用于 connectivity checks. FINGERPRINT mechanism 必须用于 connectivity checks.

ICE 通过定义若干新属性扩展 STUN, 包括 PRIORITY, USE-CANDIDATE, ICE-CONTROLLED 和 ICE-CONTROLLING. 这些新属性在第 19.1 节正式定义, 其用法在以下小节描述. 这些 STUN extensions 只适用于 ICE 使用的 connectivity checks.

7.1.2.1. PRIORITY 和 USE-CANDIDATE

Agent 必须在其 Binding request 中包含 PRIORITY 属性. 该属性 必须设置为这样一个 priority: 如果由于此 check 学到一个 peer reflexive candidate, 见第 7.1.3.2.1 节关于如何学习 peer reflexive candidates, 则按照第 4.1.2 节算法会分配给该 peer reflexive candidate 的 priority. 该 priority 值的计算方式与 pair 的 local candidate 的 priority 计算方式相同, 不同之处在于 type preference 设置为 peer reflexive candidate 类型的值.

Controlling agent 可以在 Binding request 中包含 USE-CANDIDATE 属性. Controlled agent 不得在其 Binding request 中包含该属性. 此属性表示 controlling agent 希望停止该 component 的 checks, 并使用此 check 产生的 candidate pair 作为该 component 的 pair. 关于何时包含该属性的指导见第 8.1.1 节.

7.1.2.2. ICE-CONTROLLED 和 ICE-CONTROLLING

如果 agent 处于 controlled role, 它 必须在 request 中包含 ICE-CONTROLLED 属性; 如果处于 controlling role, 它 必须在 request 中包含 ICE-CONTROLLING 属性. 任一属性的内容 必须是第 5.2 节中确定的 tie-breaker. 这些属性在第 19.1 节完整定义.

7.1.2.3. 形成凭据 (Forming Credentials)

作为 connectivity check 的 Binding request 必须使用 STUN short-term credential mechanism. Credential 的 username 由 peer 提供的 username fragment 与发送 request 的 agent 的 username fragment 拼接形成, 中间用冒号 (":") 分隔. Password 等于 peer 提供的 password. 例如, 考虑 agent L 是 offerer, agent R 是 answerer 的情况. Agent L 为其 candidates 包含 username fragment LFRAG 和 password LPASS. Agent R 提供 username fragment RFRAG 和 password RPASS. 从 L 到 R 的 connectivity check 使用 username RFRAG:LFRAG 和 password RPASS. 从 R 到 L 的 connectivity check 使用 username LFRAG:RFRAG 和 password LPASS. Responses 使用与 requests 相同的 usernames 和 passwords, 注意 USERNAME 属性不存在于 response 中.

7.1.2.4. DiffServ 处理 (DiffServ Treatment)

如果 agent 在其 media packets 中使用 Diffserv Codepoint markings [RFC2475], 它 应该对其 connectivity checks 应用相同 markings.

7.1.3. 处理 Response (Processing the Response)

收到 Binding response 时, 按 [RFC5389] 定义使用 transaction ID 将其关联到对应 Binding request, 继而关联到发送该 Binding request 的 candidate pair. 本节定义特定于 STUN 此用途的 Binding responses 附加处理过程.

7.1.3.1. 失败情形 (Failure Cases)

如果 STUN transaction 生成 487 (Role Conflict) error response, agent 检查它是否在 Binding request 中包含 ICE-CONTROLLED 或 ICE-CONTROLLING 属性. 如果 request 包含 ICE-CONTROLLED 属性, agent 必须切换到 controlling role, 如果尚未切换. 如果 request 包含 ICE-CONTROLLING 属性, agent 必须切换到 controlled role, 如果尚未切换. 切换后, agent 必须将生成 487 的 check 所属 candidate pair 入队到 triggered check queue. 该 pair 的 state 设置为 Waiting. 当 triggered check 被发送时, 它将包含反映其新 role 的 ICE-CONTROLLING 或 ICE-CONTROLLED 属性. 但注意, tie-breaker value 不得被重新选择.

Role 变化将要求 agent 重新计算 pair priorities (第 5.7.2 节), 因为这些 priorities 是 controlling 和 controlled roles 的函数. Role 变化还会影响 agent 是否负责选择 nominated pairs, 以及是否在 ICE 结束时生成 updated offers.

Agents 可以支持接收 connectivity checks 的 ICMP errors. 如果 STUN transaction 生成 ICMP error, agent 将该 pair 的 state 设置为 Failed. 如果 STUN transaction 生成不可恢复的 STUN error response, 如 [RFC5389] 所定义, 或超时, agent 将该 pair 的 state 设置为 Failed.

Agent 必须检查 response 的源 IP 地址和端口等于 Binding request 发送到的目的 IP 地址和端口, 且 response 的目的 IP 地址和端口匹配发送 Binding request 时使用的源 IP 地址和端口. 换言之, request 和 response 中的源与目的 transport addresses 是对称的. 如果它们不对称, agent 将该 pair 的 state 设置为 Failed.

7.1.3.2. 成功情形 (Success Cases)

如果以下条件全为真, check 被认为成功:

  • STUN transaction 生成 success response.
  • Response 的源 IP 地址和端口等于 Binding request 发送到的目的 IP 地址和端口.
  • Response 的目的 IP 地址和端口匹配发送 Binding request 时使用的源 IP 地址和端口.
7.1.3.2.1. 发现 Peer Reflexive Candidates (Discovering Peer Reflexive Candidates)

Agent 检查 STUN response 中的 mapped address. 如果该 transport address 不匹配 agent 已知的任何 local candidates, 则 mapped address 表示一个新 candidate, 即 peer reflexive candidate. 与其他 candidates 一样, 它具有 type, base, priority 和 foundation. 它们按如下方式计算:

  • 其 type 等于 peer reflexive.
  • 其 base 设置为发送 STUN check 的 candidate pair 的 local candidate.
  • 其 priority 设置为 Binding request 中 PRIORITY 属性的值.
  • 其 foundation 按第 4.1.1.3 节所述选择.

随后将此 peer reflexive candidate 加入该 media stream 的 local candidates 列表. 其 username fragment 和 password 与该 media stream 的所有其他 local candidates 相同.

然而, peer reflexive candidate 不会与其他 remote candidates 配对. 这不是必要的; 稍后会基于第 7.1.3.2.2 节中的过程从它生成 valid pair. 如果 agent 希望将 peer reflexive candidate 与将要生成的 valid pair 中那个 remote candidate 以外的其他 remote candidates 配对, agent 可以生成 updated offer, 其中包含该 peer reflexive candidate. 这将使它与所有其他 remote candidates 配对.

7.1.3.2.2. 构造 Valid Pair (Constructing a Valid Pair)

Agent 构造一个 candidate pair, 其 local candidate 等于 response 的 mapped address, 其 remote candidate 等于 request 发送到的 destination address. 这称为 valid pair, 因为它已由 STUN connectivity check 验证. Valid pair 可能等于生成该 check 的 pair, 可能等于 check list 中的另一个 pair, 也可能是当前不在任何 check list 上的 pair. 如果该 pair 等于生成 check 的 pair, 或当前位于某个 check list 上, 它也会被加入 VALID LIST; agent 为每个 media stream 维护该列表. 该列表在 ICE processing 开始时为空, 随着 checks 执行而填充, 得到 valid candidate pairs.

该 pair 很常见地不会位于任何 check list 上. 回想一下, check list 中的 pairs 的 local candidates 永远不是 server reflexive; 这些 pairs 已将其 local candidates 转换为 server reflexive candidates 的 base, 并在冗余时被修剪. 当 STUN check 的 response 到达时, 如果二者之间存在 NAT, mapped address 将是 reflexive. 在这种情况下, valid pair 的 local candidate 不匹配 check list 中任何 pairs.

如果该 pair 不在任何 check list 上, agent 会基于每个 candidate 的 priority, 使用第 5.7 节中的算法计算该 pair 的 priority. Local candidate 的 priority 取决于其 type. 如果它不是 peer reflexive, 则等于 SDP 中为该 candidate 发信令的 priority. 如果它是 peer reflexive, 则等于 agent 在刚完成的 Binding request 中放置的 PRIORITY 属性. Remote candidate 的 priority 取自 peer 的 SDP. 如果 candidate 未出现在 SDP 中, 则该 check 必定是发送到新 remote candidate 的 triggered check. 在这种情况下, priority 取触发刚完成 check 的 Binding request 中 PRIORITY 属性的值. 随后将该 pair 加入 VALID LIST.

7.1.3.2.3. 更新 Pair 状态 (Updating Pair States)

Agent 将 generated 该 check 的 pair 的 state 设置为 Succeeded. 注意, generated 该 check 的 pair 可能不同于第 7.1.3.2.2 节中因 response 而构造的 valid pair. 此 check 的成功还可能导致其他 checks 的 state 变化. Agent 必须执行以下两个步骤:

  1. Agent 将同一 media stream 且同一 foundation 的所有其他 Frozen pairs 的 states 改为 Waiting. 通常, 但并非总是, 这些其他 pairs 会有不同 component IDs.

  2. 如果该 media stream 的每个 component 在 valid list 中都有一个 pair, 其中在 SDP 中信令的 component 数量在 offerer 与 answerer 之间不同时, 这里指实际使用的 component 数量, 则此 check 的成功可能解冻其他 media streams 的 checks. 注意, 不仅在正在考虑的 valid list 第一次为每个 component 都有 pair 时执行此步骤, 后续每次 check 成功并向该 valid list 添加另一个 pair 时也执行此步骤. Agent 依次检查每个其他 media stream 的 check list:

    • 如果 check list 是 active, agent 将该 check list 中所有 foundation 匹配正在考虑的 valid list 中某个 pair 的 Frozen pairs 的 state 改为 Waiting.

    • 如果 check list 是 frozen, 且该 check list 中至少有一个 pair 的 foundation 匹配正在考虑的 valid list 中某个 pair, 则该 check list 中所有 foundation 匹配该 valid list 中某个 pair 的 pairs 的 state 设置为 Waiting. 这会使 check list 变为 active, 并按第 5.8 节所述开始 ordinary checks.

    • 如果 check list 是 frozen, 且该 check list 中没有任何 pair 的 foundation 匹配正在考虑的 valid list 中的 pair, agent

      • 将具有相同 foundation 的所有 pairs 分组, 并且

      • 对每组, 将 component ID 最低的 pair 的 state 设置为 Waiting. 如果存在多个这样的 pairs, 使用 priority 最高的那个.

7.1.3.2.4. 更新 Nominated Flag (Updating the Nominated Flag)

如果 agent 是 controlling agent, 且它在 Binding request 中包含 USE-CANDIDATE 属性, 则由该 check 生成的 valid pair 的 nominated flag 被设置为 true. 此 flag 表示如果该 valid pair 是 nominated flag 被置位的 pairs 中优先级最高者, 就应将其用于媒体. 这可能结束该 media stream 或所有 media streams 的 ICE processing; 见第 8 节.

如果 agent 是 controlled agent, response 可能是 triggered check 的结果, 该 triggered check 是响应一个本身带有 USE-CANDIDATE 属性的 request 而发送的. 这种情况见第 7.2.1.5 节, 此时可能导致为从原始 request 学到的 pair 设置 nominated flag.

7.1.3.3. Check List 和 Timer 状态更新 (Check List and Timer State Updates)

无论 check 成功还是失败, transaction 的完成都可能需要更新 check list 和 timer states.

如果 check list 中所有 pairs 现在都处于 Failed 或 Succeeded state:

  • 如果 valid list 中没有该 media stream 每个 component 的 pair, 则 check list 的 state 设置为 Failed.

  • 对每个 frozen check list, agent

    • 将具有相同 foundation 的所有 pairs 分组, 并且

    • 对每组, 将 component ID 最低的 pair 的 state 设置为 Waiting. 如果存在多个这样的 pairs, 使用 priority 最高的那个.

如果 check list 中没有 pairs 处于 Waiting 或 Frozen state, 则该 check list 不再被视为 active, 并且不会计入第 5.8 节中 ordinary checks timer 计算所用的 N 值.

7.2. STUN Server 过程 (STUN Server Procedures)

Agent 必须准备好在其最近一次 offer 或 answer 中包含的每个 candidate 的 base 上接收 Binding request. 即使 peer 是 lite implementation, 此要求也成立.

Agent 必须使用 short-term credential 对 request 进行认证并执行 message integrity check. 如果 username 由冒号分隔的两个值组成, 且第一个值等于 agent 在正在进行 session 的 offer 或 answer 中生成的 username fragment, agent 必须认为该 username 有效. Offerer 可能, 事实上也很可能, 在收到 peer 的 answer 之前收到 Binding request. 如果发生这种情况, agent 必须立即生成 response, 包括按第 7.2.1.2 节所述计算 mapped address. Agent 此时有足够信息生成 response; 不需要 peer 的 password. 一旦收到 answer, 它 必须继续执行其余必需步骤, 即对 full implementations 执行 7.2.1.3, 7.2.1.4 和 7.2.1.5. 如果在 answer 之前收到多个 STUN requests, 这可能导致多个 pairs 被排入 triggered check queue.

Agent 不得使用 ALTERNATE-SERVER mechanism, 且 不得支持 RFC 3489 的向后兼容机制. 它 必须使用 FINGERPRINT mechanism.

如果 agent 在其 media packets 中使用 Diffserv Codepoint markings [RFC2475], 它 应该对 Binding requests 的 responses 应用相同 markings. 这同样适用于 endpoint 可能对 media packets 应用的任何 layer 2 markings.

7.2.1. Full Implementations 的附加过程 (Additional Procedures for Full Implementations)

本小节定义适用于 full implementations 的附加 server procedures.

7.2.1.1. 检测并修复角色冲突 (Detecting and Repairing Role Conflicts)

通常, 第 5.2 节中的 role 选择规则会导致每个 agent 选择不同 role, 一个 controlling, 一个 controlled. 然而, 在异常呼叫流中, 通常是使用 third party call control 时, 两个 agents 可能选择相同 role. 本节描述检查并修复这种情况的过程.

Agent 必须检查 Binding request 中是否存在 ICE-CONTROLLING 或 ICE-CONTROLLED 属性. 它 必须遵循以下过程:

  • 如果 request 中既不存在 ICE-CONTROLLING 也不存在 ICE-CONTROLLED, peer agent 可能实现了本规范的早期版本. 可能存在冲突, 但无法检测.

  • 如果 agent 处于 controlling role, 且 request 中存在 ICE-CONTROLLING 属性:

    • 如果 agent 的 tie-breaker 大于或等于 ICE-CONTROLLING 属性内容, agent 生成 Binding error response, 并包含值为 487 (Role Conflict) 的 ERROR-CODE 属性, 但保留其 role.

    • 如果 agent 的 tie-breaker 小于 ICE-CONTROLLING 属性内容, agent 切换到 controlled role.

  • 如果 agent 处于 controlled role, 且 request 中存在 ICE-CONTROLLED 属性:

    • 如果 agent 的 tie-breaker 大于或等于 ICE-CONTROLLED 属性内容, agent 切换到 controlling role.

    • 如果 agent 的 tie-breaker 小于 ICE-CONTROLLED 属性内容, agent 生成 Binding error response, 并包含值为 487 (Role Conflict) 的 ERROR-CODE 属性, 但保留其 role.

  • 如果 agent 处于 controlled role 且 request 中存在 ICE-CONTROLLING 属性, 或 agent 处于 controlling role 且 request 中存在 ICE-CONTROLLED 属性, 则不存在冲突.

Role 变化将要求 agent 重新计算 pair priorities (第 5.7.2 节), 因为这些 priorities 是 controlling 和 controlled roles 的函数. Role 变化还会影响 agent 是否负责选择 nominated pairs, 以及是否在 ICE 结束时生成 updated offers.

如果 server 为 Binding request 生成了 successful response, 即使 agent 改变了 roles, 也会遵循第 7.2.1 节中的其余各节.

7.2.1.2. 计算 Mapped Address (Computing Mapped Address)

对于在 relayed candidate 上接收的 requests, 用于 STUN processing 的 source transport address, 即生成 XOR-MAPPED-ADDRESS 属性所用地址, 是 TURN server 所看到的 transport address. 如果 Binding request 通过 Data Indication 递送, 该 source transport address 将出现在 Data Indication message 的 XOR-PEER-ADDRESS 属性中. 如果 Binding request 通过 ChannelData message 递送, source transport address 是绑定到该 channel 的地址.

7.2.1.3. 学习 Peer Reflexive Candidates (Learning Peer Reflexive Candidates)

如果 request 的 source transport address 不匹配任何现有 remote candidates, 它表示一个新的 peer reflexive remote candidate. 该 candidate 构造如下:

  • Candidate 的 priority 设置为 request 中 PRIORITY 属性的值.
  • Candidate 的 type 设置为 peer reflexive.
  • Candidate 的 foundation 设置为任意值, 且不同于所有其他 remote candidates 的 foundation. 如果后续任何 offer/answer 交换在 SDP 中包含该 peer reflexive candidate, 它将发信令给出该 candidate 的实际 foundation.
  • 该 candidate 的 component ID 设置为 request 发送到的 local candidate 的 component ID.

该 candidate 被加入 remote candidates 列表. 然而, agent 不会将此 candidate 与任何 local candidates 配对.

7.2.1.4. Triggered Checks (触发检查)

接下来, agent 构造一个 pair, 其 local candidate 等于接收 STUN request 的 transport address, remote candidate 等于 request 来源的 source transport address, 后者可能是刚学到的 peer reflexive remote candidate. Local candidate 要么是 host candidate, 即 request 不是通过 relay 接收的情况, 要么是 relayed candidate, 即通过 relay 接收的情况. Local candidate 永远不可能是 server reflexive candidate. 由于两个 candidates 对 agent 都已知, 它可以获得它们的 priorities 并计算 candidate pair priority. 然后在 check list 中查找该 pair. 可能有以下几种结果:

  • 如果该 pair 已在 check list 上:

    • 如果该 pair 的 state 是 Waiting 或 Frozen, 则在尚未存在时将该 pair 的 check 入队到 triggered check queue.

    • 如果该 pair 的 state 是 In-Progress, agent 取消正在进行的 transaction. 取消意味着 agent 不会重传 request, 不会把缺少 response 视为失败, 但会等待 transaction timeout 的持续时间以等待 response. 此外, agent 必须通过将该 pair 入队到 triggered check queue, 为该 pair 创建新的 connectivity check, 代表新的 STUN Binding request transaction. 然后将该 pair 的 state 改为 Waiting.

    • 如果该 pair 的 state 是 Failed, 则将其改为 Waiting, 且 agent 必须通过将该 pair 入队到 triggered check queue, 为该 pair 创建新的 connectivity check, 代表新的 STUN Binding request transaction.

    • 如果该 pair 的 state 是 Succeeded, 不再执行任何操作.

    这些步骤用于在两个 agents 都位于 NAT 后面时促进 ICE 快速完成.

  • 如果该 pair 尚不在 check list 上:

    • 该 pair 基于其 priority 插入 check list.

    • 其 state 设置为 Waiting.

    • 该 pair 入队到 triggered check queue.

当要发送 triggered check 时, 它按第 7.1.2 节所述构造和处理. 这些过程要求 agent 知道 peer 的 transport address, username fragment 和 password. Remote candidate 的 username fragment 等于刚收到的 Binding request 的 USERNAME 中冒号之后的部分. 使用该 username fragment, agent 可以检查从 peer 收到的 SDP messages, 在 forking 场景中可能不止一个, 并找到此 username fragment. 随后选择对应 password.

7.2.1.5. 更新 Nominated Flag (Updating the Nominated Flag)

如果 agent 收到的 Binding request 设置了 USE-CANDIDATE 属性, 且 agent 处于 controlled role, agent 查看第 7.2.1.4 节中计算出的 pair 的 state:

  • 如果该 pair 的 state 是 Succeeded, 表示该 pair 生成的 check 产生了 successful response. 在收到该 success response 时, 这会使 agent 构造 valid pair, 见第 7.1.3.2.2 节. Agent 现在将该 valid pair 中的 nominated flag 设置为 true. 这可能结束该 media stream 的 ICE processing; 见第 8 节.

  • 如果该 pair 的 state 是 In-Progress, 且其 check 产生 successful result, 则 response 到达时所得 valid pair 的 nominated flag 被设置. 这可能在其到达时结束该 media stream 的 ICE processing; 见第 8 节.

7.2.2. Lite Implementations 的附加过程 (Additional Procedures for Lite Implementations)

如果刚收到的 check 包含 USE-CANDIDATE 属性, agent 构造一个 candidate pair, 其 local candidate 等于接收 request 的 transport address, 其 remote candidate 等于所收到 request 的 source transport address. 该 candidate pair 被分配一个任意 priority, 并放入称为 valid list 的 valid candidates 列表. Agent 将该 pair 的 nominated flag 设置为 true. 如果 valid list 包含某个 media stream 每个 component 的 candidate pair, 则该 media stream 的 ICE processing 被认为完成.