写点什么

Webrtc ICE 框架

用户头像
糖米唐爹
关注
发布于: 2 小时前
Webrtc ICE 框架

一,引言

现在互联网 p2p 应用的领域非常的多,包括文件下载,流媒体点播,音视频通信等,我们已经见识到了 p2p 的威力,它被列为影响 Internet 未来的四项科技之一。互联网的主机为了安全一般都会部署在 NAT 之后,且不同的 NAT 处理外部主机发送的数据策略不同,因此需要一种框架使各种 NAT 穿透技术可以实现统一 ,ICE(Interactive Connectivity Establishment) 诞生了。

二,NAT


功能

介绍 ICE 之前我们先说下 NAT(Network Address Translation,网络地址转换),主要作用有两个

  • IP 地址复用:让多台主机共用同一个外网 IP,解决了 IP 短缺的问题。

  • IP 隔离:当内网主机经过 NAT 访问外部机器,首先 NAT 会隐藏内网主机的 ip,其次有些 NAT 对来自外部的数据查看其 NAT 映射记录,对没有记录的数据包进行拒绝,这样就与外部主机进行了隔离,增加了网络安全性。

类型

  1. 全锥型(Full Cone)

NAT 将来在内网某一主机(ip1:port1) 映射为(ip2:port2) ,任意外部主机访问 ip2:port2,NAT 都能将信息转发到(ip1:port1)。


  1. IP 受限锥型(Restricted Cone):

NAT 将来在内网某一主机(ip1:port1) 映射为(ip2:port2) ,内网主机必须访问过外部主机,这台外部主机访问 ip2:port2,NAT 才能将信息转发到(ip1:port1)。


  1. 端口受限锥型(Port Restricted Cone)

相对 Restricted Cone ,Port Restricted Cone 更加严格,假设现在有一台外部主机地址为 IP1,开的端口为 PORT1,只有内网主机访问过 IP1: PORT1 访问这台外部,这台外网主机才能 IP1: PORT1 进行回访,使用 IP1 和其他端口都是不行的。


  1. 对称型(Symmetric)

当内网主机需要访问两个目标地址, 只要这两个地址不同,NAT 都会内网分配新公网地址(哪怕是 ip 相同但端口不同也是一样),且只有收到过内部主机消息的外部主机才能向内部主机发送数据。


由上面的信息可知有些 NAT 可能无法打通,我整理了张表见下图:

Y = allowed, N = disallowed


三,'穿墙术'


上节我们了解了 NAT 的 4 种类型,由于对外部主机进来数据处理的策略不同,会导致主机之间网络不可见,这就形成了互联网的' 墙 ',这节我们主要讲下如何通过工具打通这些墙,这就是我们的’术‘。

1,RFC 3489

Simple Traversal of UDP Through NATs ,简单 UDP 穿越,stun client 通过向一个具有两个公网 IP stun server 发送三种类型 stun 测试消息来判断该 client 所处的网络环境,根据网络环境就可以知道是否具备 P2P 通信的能力了,测试分为 3 步:


Test1:STUN Client 通过端口{IP-C1:Port-C1}向 STUN Server{IP-S1:Port-S1}发送一个 Binding Request(没有设置任何属性)。STUN Server 收到该请求后,通过端口{IP-S1:Port-S1}把它所看到的 STUN Client 的 IP 和端口{IP-M1,Port-M1}作为 Binding Response 的内容回送给 STUN Client。 Test1#2:STUN Client 通过端口{IP-C1:Port-C1}向 STUN Server{IP-S2:Port-S2}发送一个 Binding Request(没有设置任何属性)。STUN Server 收到该请求后,通过端口{IP-S2:Port-S2}把它所看到的 STUN Client 的 IP 和端口{IP-M1#2,Port-M1#2}作为 Binding Response 的内容回送给 STUN Client。


Test2:STUN Client 通过端口{IP-C1:Port-C1}向 STUN Server{IP-S1:Port-S1}发送一个 Binding Request(设置了 Change IP 和 Change Port 属性)。STUN Server 收到该请求后,通过端口{IP-S2:Port-S2}把它所看到的 STUN Client 的 IP 和端口{IP-M2,Port-M2}作为 Binding Response 的内容回送给 STUN Client。


Test3:STUN Client 通过端口{IP-C1:Port-C1}向 STUN Server{IP-S1:Port-S1}发送一个 Binding Request(设置了 Change Port 属性)。STUN Server 收到该请求后,通过端口{IP-S1:Port-S2}把它所看到的 STUN Client 的 IP 和端口{IP-M3,Port-M3}作为 Binding Response 的内容回送给 STUN Client。

探测算法



Open Internet:主机具有公网 IP,允许主动发起和被动响应两种方式的 UDP 通信。

UDP Blocked:位于防火墙之后,并且防火墙阻止了 UDP 通信。

Symmetric Firewall:主机具有公网 IP,但位于防火墙之后,且防火墙阻止了外部主机的主动 UDP 通信。

真实的网络环境还有上面三种网络情况,通过上面的探测算法就可以判断出客户端是否处于 NAT 之后,以及 NAT 的类型及其公网 IP,就可以判断客户端是否具备 P2P 通信的能力了

路经通过红色箱子的终点时,UDP 的沟通是没有可能性的。箱子是黄色或是绿色的箱子,就有连线的可能)


2,RFC 5389


STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重 NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的 NAT 之后以及 NAT 为某一个本地端口所绑定的 Internet 端端口。这些信息被用来在两个同时处于 NAT 路由器之后的主机之间建立 UDP 通信.

RFC3489 vs 5389

  • RFC3489 用来查询 NAT 或 firewall 之下,並且取得 client 的公网 ip

  • RFC5389 定义如何使用 STUN 来完成穿透 NAT 的功能

3,RFC5766

如果一台主机处于 NAT 后面,那么在一定条件下两台主机无法之间进行通讯。在这种条件下,那么使用中继服务提供通讯是有必要的。这个规范定义了一个名为 TURN(中继穿透NAT:STUN的中继扩展)的协议,它允许一台主机使用中继服务与对端进行报文传输。TURN 不同于其它中继协议在于它允许客户机使用一个中继地址与多个对端同时进行通讯。


工作原理


  • Allocate 请求



  1. client A 发送 Allocate 请求,

  2. turn server 为 client A 分配 relay 地址。

  3. trun server 发送 Allocate responce,含有 relay 地址。

  4. client A 收到 response 就知道了自己的 relay 地址,该 relay 地址是个公网地址,可以理解为 client A 的代理地址。


  • client A 接收数据


  • 发送数据



4,RFC8455


交互式连接建立( ICE:Interactive Connectivity Establishment ),它 是一种框架 ,集成了 Stun/Turn 协议,利用 Stun 和 Turn 服务器来帮助端两端点立连接。RFC5245 对 ICE 做了规范定义,RFC8445 是 2018 年发布的最新的规范,对 RFC5245 进行了部分修改。


四,Webrtc ICE 框架

上一节我们简单了解了实现 p2p 穿墙的几种协议,这一章我们讲下 webrtc 是如何实现 ice 的


4.1,ICE 模型

RFC5245 定义了 ICE 模型,webrtc 对其了实现,见:WebRTC\src\p2p\base\transport_description.h

enum IceMode {  ICEMODE_FULL,  // As defined in http://tools.ietf.org/html/rfc5245#section-4.1  ICEMODE_LITE   // As defined in http://tools.ietf.org/html/rfc5245#section-4.2};
复制代码


  • FULL ICE:双方都要进行连通性检查,完成的走一遍建立流程。

  • Lite ICE :下文来自webrtcglossary 的介绍

ice-lite is a minimal version of the ICE specification, intended for servers running on a public IP address.ice-lite is easy to implement, requiring the media server to only answer incoming STUN binding requests and acting as a controlled entity in the ICE process itself. This simplicity makes it quite popular among implementations of SFUs and other media servers.

Support for ice-lite is announced in the SDP as a=ice-lite.


4.2,webrtc ice 建立流程图


总结如下:

  1. 应用层调用 SetLocalDescription 接口,rtc 开始收集网卡。

  2. 根据网卡信息生成 host candidates,如果 RTCConfiguration 配置了 stun/turn server 则会生成 Srflx /Relay candidates。

  3. 当 client 收到 anser sdp,通过调用 SetRemoteDescription,rtc 会提取出 sdp 中的所有 candidate ,作为 remote candidate。

  4. 将本地 candidate 与 remote candidate 进行组合生成多个 conection,创建 Connection 会触发 ping,ping 成功后会触发 Connection 的状态切换。

  5. 对所有的 conection 进行稳定排序,选出一个最好的 conection ,作为传输通道。排序规则下面介绍。


4.3,candidate

candidate 类型

  • Host Candidate :由本地的物理网卡或逻辑网卡上生成的 candidate。

  • Srflx candidate:端发送 Binding 请求到 STUN/TURN server 经过 NAT 时,由 NAT 上分配的地址和端口生成的 candidate。

  • Relayed Candidate:端发送 Allocate 请求到 TURN server ,由 TURN server 用于中继的地址和端口(这个可能是本机或 NAT 地址)生成的 candidate。

  • Peer Reflexive Candidate:端发送 Binding 请求到对等端经过 NAT 时,由 NAT 上分配的地址和端口生成的 candidate。

candidate 格式

candidate: | foundation | component-id | transport | priority | [connection-address port] | typ candidate-types | [raddr <connection-address>rport <port>]


  • foundation:id 标识

  • component-id:媒体传输类型,1 代表 RTP;2 代表 RTCP。

  • transport:候选者的传输协议 udp 或者 tcp

  • [connection-address /port]:candidate ip 地址和端口 。

  • cand-type:候选者类型,取值有 "host", "srflx", "prflx", and "relay"四种。

  • [raddr <connection-address> rport <port>]: 仅用于分析和诊断目的ICE 本身完全不使用相关的地址和端口,当 candidate 类型为"srflx", "prflx"和"relay" 时该值必须存在,为“host”时省略。

  • priority :Candidate 的优先级。

candidate priority 计算

计算 candidate pair 优先级中使用,优先级高低一般是 host > srvflx > relay,看下计算公式

priority = (2^24)*(type preference) + (2^8)*(local preference) + (2^0)*(256 – component ID)

  • type preference 取值如下:

enum IcePriorityValue {  ICE_TYPE_PREFERENCE_RELAY_TLS = 0,  ICE_TYPE_PREFERENCE_RELAY_TCP = 1,  ICE_TYPE_PREFERENCE_RELAY_UDP = 2,  ICE_TYPE_PREFERENCE_PRFLX_TCP = 80,  ICE_TYPE_PREFERENCE_HOST_TCP = 90,  ICE_TYPE_PREFERENCE_SRFLX = 100,  ICE_TYPE_PREFERENCE_PRFLX = 110,  ICE_TYPE_PREFERENCE_HOST = 126};
复制代码
  • local_preference

// |local_preference| length is 2 bytes, 0-65535 inclusive.  // In our implemenation we will partion local_preference into  //              0                 1  //       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  //      |  NIC Pref     |    Addr Pref  |  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  // NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired.  // Addr Pref - Address preference value as per RFC 3484.  // local preference =  (NIC Type << 8 | Addr_Pref) - relay preference.
int addr_pref = IPAddressPrecedence(address_.ipaddr()); int local_preference = ((network_adapter_preference << 8) | addr_pref) + relay_preference;
复制代码
  • component ID 取值定义


enum{	COMPONENT_TYPE_RTP = 1,	COMPONENT_TYPE_RTCP = 2};
复制代码


candidate pairs priority 计算

candidate pairs 由相同 Component ID 和 transport protocol candidate 组成 ,后面 Connection 排序会用到,计算公式:

priority = 2^32*MIN(G,D) + 2 * MAX(G,D) + (G>D?1:0)

G:controlling candidate 优先级。

D:controlled candidate 优先级。

4.4,Connection 排序

Connection 排序需要如下几个参考条件,简单介绍

  • Connection States:连接状态有 writable non-writable,presumed-writable,receiving ,non-receiving 几种

  • Nomination:提名:见RFC5245 section-8.1.1

  • Priority:见 candidate pairs priority 定义

  • network cost

网络类型,其定义如下:

constexpr uint16_t kNetworkCostMax = 999;constexpr uint16_t kNetworkCostCellular2G = 980;constexpr uint16_t kNetworkCostCellular3G = 910;constexpr uint16_t kNetworkCostCellular = 900;constexpr uint16_t kNetworkCostCellular4G = 500;constexpr uint16_t kNetworkCostCellular5G = 250;constexpr uint16_t kNetworkCostUnknown = 50;constexpr uint16_t kNetworkCostLow = 10;constexpr uint16_t kNetworkCostMin = 0;
复制代码
  • rtt: 延时


排序规则如下,具体实现见函数 BasicIceController::CompareConnections

1,Connection States:writable(presumed-writable)、receiving 的排在 non-writable、non-receiving 的前面;

2,Nomination,越大优先级越高

3,Priority,越大优先级越高

4,network cost,值越小,优先级越高

5,rtt:越小优先级越高


当有 Connection 建立连接,如果新连接的 RTT 与原连接相比没有明显降低(kMinImprovement = 10),则不切换。


const int kMinImprovement = 10;IceControllerInterface::SwitchResult BasicIceController::ShouldSwitchConnection(    IceControllerEvent reason,    const Connection* new_connection) {  			.......  bool missed_receiving_unchanged_threshold = false;  absl::optional<int64_t> receiving_unchanged_threshold(      rtc::TimeMillis() - config_.receiving_switching_delay_or_default());  //1,先拿原来的连接与新建立连接进行比较  int cmp = CompareConnections(selected_connection_, new_connection,                               receiving_unchanged_threshold,                               &missed_receiving_unchanged_threshold);
//2, 新的连接rtt 是否更优。 if (new_connection->rtt() <= selected_connection_->rtt() - kMinImprovement) { return {new_connection, absl::nullopt}; }}
复制代码


用户头像

糖米唐爹

关注

还未添加个人签名 2020.08.12 加入

还未添加个人简介

评论

发布
暂无评论
ICE 框架