About ICE

Posted on Tue 29 June 2021 in Journal

ICE

                               +---------+
             +--------+        |Signaling|         +--------+
             | STUN   |        |Server   |         | STUN   |
             | Server |        +---------+         | Server |
             +--------+       /           \        +--------+
                             /             \
                            /               \
                           / <- Signaling -> \
                          /                   \
                   +--------+               +--------+
                   |  NAT   |               |  NAT   |
                   +--------+               +--------+
                      /                             \
                     /                               \
                 +-------+                       +-------+
                 | Agent |                       | Agent |
                 |   L   |                       |   R   |
                 +-------+                       +-------+

                     Figure 1: ICE Deployment Scenario

The basic idea behind ICE is as follows: each agent has a variety of candidate transport addresses (combination of IP address and port for a particular transport protocol, which is always UDP in this specification) it could use to communicate with the other agent.

These might include:

  • A transport address on a directly attached network interface

  • A translated transport address on the public side of a NAT (a "server-reflexive" address)

  • A transport address allocated from a TURN server (a "relayed address")

Potentially, any of L's candidate transport addresses can be used to communicate with any of R's candidate transport addresses. In practice, however, many combinations will not work.

For instance, if L and R are both behind NATs, their directly attached interface addresses are unlikely to be able to communicate directly (this is why ICE is needed, after all!).

The purpose of ICE is to discover which pairs of addresses will work. The way that ICE does this is to systematically try all possible pairs (in a carefully sorted order) until it finds one or more that work.

连接候选者地址的类型有:

  • Local Address
  • Server Reflexive Address
  • Relayed Address
                     To Internet

                          |
                          |
                          |  /------------  Relayed
                      Y:y | /               Address
                      +--------+
                      |        |
                      |  TURN  |
                      | Server |
                      |        |
                      +--------+
                          |
                          |
                          | /------------  Server
                   X1':x1'|/               Reflexive
                    +------------+         Address
                    |    NAT     |
                    +------------+
                          |
                          | /------------  Local
                      X:x |/               Address
                      +--------+
                      |        |
                      | Agent  |
                      |        |
                      +--------+


                     Figure 2: Candidate Relationships

connectivity checks

The basic principle of the connectivity checks is simple:

  1. Sort the candidate pairs in priority order.

  2. Send checks on each candidate pair in priority order.

  3. Acknowledge checks received from the other agent.

With both agents performing a check on a candidate pair, the result is a 4-way handshake:

                  L                        R
                  -                        -
                  STUN request ->             \  L's
                            <- STUN response  /  check

                             <- STUN request  \  R's
                  STUN response ->            /  check

                    Figure 3: Basic Connectivity Check

Nominating Candidate Pairs and Concluding ICE

ICE assigns one of the ICE agents in the role of the controlling agent, and the other in the role of the controlled agent. For each component of a data stream, the controlling agent nominates a valid pair (from the valid list) to be used for data. The exact timing of the nomination is based on local policy.

When nominating, the controlling agent lets the checks continue until at least one valid pair for each component of a data stream is found, and then it picks a valid pair and sends a STUN request on that pair, using an attribute to indicate to the controlled peer that it has been nominated. This is shown in Figure 4.

             L                        R
             -                        -
             STUN request ->             \  L's
                       <- STUN response  /  check

                        <- STUN request  \  R's
             STUN response ->            /  check

             STUN request + attribute -> \  L's
                       <- STUN response  /  check

                           Figure 4: Nomination

Once the controlled agent receives the STUN request with the attribute, it will check (unless the check has already been done) the same pair.

If the transactions above succeed, the agents will set the nominated flag for the pairs and will cancel any future checks for that component of the data stream. Once an agent has set the nominated flag for each component of a data stream, the pairs become the selected pairs. After that, only the selected pairs will be used for sending and receiving data associated with that data stream.

Program

Literate programming

Literate programming was first introduced by Knuth in 1984. The main intention behind this approach was to treat a program as literature understandable to human beings.

Work flow

Implementing literate programming consists of two steps:

  1. Weaving: Generating a comprehensive document about the program and its maintenance.

  2. Tangling: Generating machine executable code Weaving and tangling are done on the same source so that they are consistent with each other.

文学化编程包括两个步骤

  1. 编织: 生成一个可理解的关于程序和其维护过程的文档

  2. 缠绕: 生成机器可执行的代码