WebRTC 传输之 TURN

1 TURN 要解决的问题

在 TCP/IP 网络中,一个常见的问题就是如何让两台处于内网的主机进行通信,即 NAT 穿越问题。解决 NAT 穿越问题的两个关键是:

  1. 想办法发现自己的公网 IP 和端口(只知道自己的内网地址是不行的)。
  2. 把自己的公网 IP 和端口共享给对方。

举个例子, Alice 和 Bob 要进行视频会议,然而他们的设备都是内网 IP,中间要经过很多路由器才能到达对方,他们相互不知道对方的 IP 地址。在这种场景下,该如何让 Alice 和 Bob 的音视频数据分别到达彼此呢?

一种解决方案是通过 STUN 实现 P2P:

  1. Alice 和 Bob 分别通过位于公网的 STUN 服务器获取自己的反射地址(srflx,server reflexive address),这个地址一般是路由器的公网 IP 和端口。
  2. 然后 Alice 和 Bob 通过信令服务共享彼此的反射地址并开始做 NAT 穿透,这样双方就建立了 P2P 通道。

这种方案可以解决大多数的 NAT 穿越问题,不过,并非所有的情况下都能通过 STUN 实现 P2P。如果 Alice 和 Bob 处于对称型 NAT 的网络场景下,就无法实现 P2P 通信。或者 Alice 和 Bob 分别在国内和国外的这种跨国情况,基本也是无法 P2P 的。

另一种解决方案是通过 TURN 实现 Relay(中继/转发):

WebRTC 传输之 TURN
  1. Alice 和 Bob 分别通过位于公网的 TURN 服务器获取到分配给自己的 relay 地址,这个地址是 TURN 服务器的公网 IP 和它为客户端分配的端口。
  2. 然后 Alice 和 Bob 通过信令服务共享彼此的 relay 地址,通过两台 TURN 服务器中继数据,实现通信。

该方案使用中继的方式避开了对称型 NAT 无法穿越的问题。两台 TURN 服务器分别相当于 Alice 和 Bob 的代理,帮助他们转发和接收对方的数据。如果这两台 TURN 服务器之间有专线可以打通,那么跨国通信的问题也可以解决。

还有一种方案是 SFU + ICE lite + PeerConnection:

  1. Alice 和 Bob 分别和位于公网的实现了 ICE lite 的 SFU 建 PeerConnection 连接。
  2. 通过 SFU 转发数据实现通信。

ICE lite 是 ICE 的一种轻量级实现,它省去了连通性检查的过程,实现了 ICE lite 的网元一般部署在公网上。ICE 是集成了 STUN、TURN 的一种综合性的解决方案,WebRTC 使用了 ICE 技术来解决 NAT 穿越的问题。

以上方案中,TURN Relay 方案在 WebRTC 中是优先级最低的方案,与上述的方案的相关的 RFC 文档如下:

  • RFC 3489:STUN,Simple Traversal of UDP Through NATs,简单的 UDP 穿透 NAT。
  • RFC 5389:STUN,Session Traversal Utilities for NAT,提出了关于 TCP 协议的 NAT 穿透定义。
  • RFC 5766:TURN,Traversal Using Relays around NAT,采用中继方式实现 NAT 穿透。
  • RFC 6165,TURN,TURN Extension for IPv6,TURN 协议的 IPv6 扩展。
  • RFC 8016:TURN,Mobility with TURN,提出了 TURN 的切网方案。
  • RFC 5245:ICE,Interactive Connectivity Establishment(ICE),基于 offer/answer 模型的综合性 NAT 穿越方案。

Remark:很多公司的 RTC 系统都部署有 TURN 服务,其实本质上要解决的问题可能有两个:一是作为媒体网关实现就近接入,二是降低成本。

下面,我们开始详细介绍 TURN 技术。

2 TURN 的基本概念

在 NAT 无法穿透实现 P2P 的时候,选择服务器 Relay(中继/转发)是上策,而 TURN 正是这样一种 Relay 实现。

TURN 的英文全称是  Traversal Using Relays around NAT,它是一个协议,是 STUN 协议的扩展,下面我们介绍关于 TURN 的一些基本概念:

  • TURN server

实现了 TURN 协议的服务器。

  • TURN client

实现了 TURN 协议的客户端。

  • Peer

TURN client 希望通信的目标主机。

  • TURN Message

TURN 消息,可以是指令消息,比如 Allocation Request,也可以是媒体数据,比如 Channel Data。

  • Transport Address

传输地址,是 IP 地址和端口的结合。Host Transport Address 是主机的内网地址,Server-Reflexive Transport Address 是主机在 NAT 上的公网出口地址。

  • Relay

中文译作 “中继,转发”。在广义上是指具备数据中继功能的,扮演 “中间人” 角色的服务网元,比如 TURN 服务器,称之为 relay 服务。在狭义上是指 TURN 服务器为 TURN 客户端分配的一个中继地址,可以叫做 relayed transport address 或者 relay candidate,TURN 客户端使用这个地址作为自己的替身和 Peer 进行通信,从 Peer 的视角来看,它认为与自己进行数据交换的 relay address 就是 TURN 客户端。

PS:在后面的介绍中,我会统一使用 relay 这个词,保持最原始正确的概念,而不再使用它的中文翻译。

3 TURN 的网络部署

结合上面的一些概念,我们来看下面这张典型的 TRUN 网络部署图:TURN Client 在 NAT 的私网侧,Host Transport Adress 是它的内网地址,Server-Reflexive 是它的内网地址映射到 NAT 上的公网地址。TURN Server 部署在 NAT 的公网侧,Peer B 和 TURN Server 位于同一网段,而 Peer A 则在 NAT 之后,同样,Host Transport Adress 是它的内网地址,Server-Reflexive 是它的内网地址映射到 NAT 上的公网地址。

与 Web Server、RTMP Server 不同,TURN Server 没有 url 的概念,一般用五元组做流传输的标识符。TURN Server 的 UDP 监听端口一般是 3478,TCP 监听端口一般是 9015。

WebRTC 传输之 TURN

Remark: Server-Reflexive Transport Address 就是 NAT (路由器)的出口地址。

我们简单描述一下 TURN Client 使用 TURN Server 和 Peer 通信的两个关键链路:

  • TURN Client 到 TURN Server 的通信链路
  1. TURN Client 从它的内网地址 Host Transport Address 向 TURN Server 的地址发送 TURN Message。
  2. TURN Server 收到 TURN Message,它看到的 TURN Client 的地址其实是 NAT 的出口地址,即 Server-Reflexive Transport Address,TURN Server 向这个地址发送的 TURN Message 会被 NAT 转发到 TURN Client 的内网地址。
  3. 用五元组表示这段链路,为 <TURN Client IP:Port, TURN Server IP:PORT, PROTO>,在这幅图中表示为 <192.0.2.1:7000, 192.0.2.15:3478, UDP>。
  • TURN Server 到 Peer 的通信链路
  1. TURN Server 把 TURN Client 的数据从 Relayed Transport Address 发送到 Peer 的地址。
  2. 对于 Peer A,TURN Server 把数据转发到 Peer A 的 Host Transport Address。同样, Peer A 的数据会从 Host Transport Address 发送到 TURN Server 的 Relayed Transport Address。
  3. 对于 Peer B,因为它在 NAT 之后,所以 TURN Server 要把数据转发到 PeerB 的 Server-Reflexive Transport Address,NAT 会把数据转发到 PeerB 的内网地址。同样, Peer B 的数据会从 Host Transport Address 经过 NAT 发送到 TURN Server 的 Relayed Transport Address。
  4. 用五元组表示这段链路,为 <TURN Relay IP:Port, Peer IP:PORT, PROTO>,在这幅图中表示为 <192.0.2.15:50000, 192.0.2.150:32102, UDP> 以及 <192.0.2.15:50000, 192.0.2.210:49191, UDP>。

总结一下:TURN Client、TURN Server、Peer 这三者之间存在 4 个 传输地址(Transport Address),即 TURN Client 的传输地址、TURN Server 的传输地址,Peer 的传输地址,以及最重要的 Relayed Transport Address,正是这个地址将这三者之间的两段通信链路唯一映射起来。

Note: 如上图所示,TURN Client 可以凭借与 TURN Server 之间的通信链路把数据转发到多个 Peer。

到这里,你可能会产生这样的疑问:TURN Server 如何知道自己要转发的目标 Peer 的传输地址的?Peer 又是如何知道 TURN Client 的 Relayed Transport Address 的?Relayed Transport Address 如何创建,怎样打通了三者之间的通信链路呢?通信的媒体数据格式以及机制又是怎样的?

下面我们就学习一下 TURN Message,即 TURN 协议的信令消息与媒体消息。

4 TURN 的数据消息与发送机制

我们知道 TURN Client 和 Peer 之间通过 TRUN Server 交换数据,那么(TURN Client 与 TURN Server 之间)交换数据的机制有两种:

  1. 使用 Send Indication 和 Data Indication 消息。
  2. 使用 channel 传输通道。

Send Indication 和 Data Indication

Send indication 和 Data Indication 是 TURN 的数据收发机制之一,封装了要传输的数据。TURN Client 使用 Send indication 封装数据并发送给 TURN Server,然后 TURN Server 提取数据转发给 Peer。

同样,Peer 把数据发送给 TURN Server 后,TURN Server 使用 Data Indication 封装数据,然后转发给 TURN Client。

STUN 消息的 method 有三种,分别是 request、response 和 indication。因此 Send Indication 和 Data Indication 属于 STUN 消息的一种 method,它们携带两个重要的属性:

  1. XOR-PEER-ADDRESS 属性,包含 Peer 的传输地址,指示数据要发到哪。
  2. DATA 属性,包含了真正要发发送的数据。

我们具体来看一下这两个数据消息的格式:

我们知道,STUN 的头部(有 12 字节之多)和属性都比较大,因此对于 Send Indication 和 Data Indication 来讲,每发送一次数据消息都要携带这些头和属性(XOR-PEER-ADDRESS)。

这不仅造成数据冗余,而且数据传递效率低,也增大了 TURN 的转发带宽,这种 indication 的数据收发机制的缺点很明显。其实,TURN 有更好的 Channel Data 数据交换机制,下面就介绍这种机制。

TURN Channel Data Message

Channel Data  不再属于 STUN 消息了,因此它的数据格式当然也不再是 STUN 消息的格式。

WebRTC 传输之 TURN

上图就是 Channel Data 的格式,可以看到,头部只有 4 字节,紧随其后的就是应用层数据。使用这种数据收发机制前,需要 TURN Client 发送 Channel Bind 请求给 TURN Server,目的是把 Peer 地址绑定到 Channel(Channel Number 用来唯一标识该通道),绑定之后,就不需要每次都携带 XOR-PEER-ADDRESS 属性了。因此 Channel Data 这种数据收发机制极大的改善了 Indication 数据收发机制的弊端。

tcp 场景下 turn channel data 有 padding(udp 场景下则没有)。这是因为 tcp 是流式传输,需要保证消息的对齐,所以消息长度要被填充为 4 的倍数。对于 tcp 场景下的 channel data,在数据接收时要额外接收并处理好 padding。

另外,turn tcp 场景下的调试有难度,这是因为 wireshark 无法解析 TCP 为 TURN 协议,不能一目了然的定位问题发生在哪个环节。

Remark:channel data 消息的 padding 在末尾,不计入 length 长度字段。

5 TURN 的信令消息

TURN 的信令消息主要有:

  1. Binding,用于探测 srflx 地址(用于 P2P 穿透)、心跳保活。
  2. Allocation,用于分配 Relay Transport Address。
  3. Permission,用于向 TURN Server 申请向 Peer 传输数据的权限,对应 Send/Data Indication 数据收发机制。
  4. Channel Bind,用于向 TURN Server 申请绑定向 Peer 传输数据的通道,对应 Channel Data 数据收发机制。
  5. Refresh,用于 Allocation 的保活与销毁、客户端切网(Mobility)。

探测/保活 – Binding

和 ICE 进行连通性测试时发送 STUN Binding 的流程基本一致。

分配 – Allocate

Allocate 是 “分配” 的意思,那么分配的是什么呢?答案是 Relayed Transport Address。因此,我们说 Allocate 的本质就是分配 Relayed Transport Address,一个 Relayed Transport Address 可以唯一代表一个 Allocation。

TURN Client 发起 Allocate request 的流程图如下:

WebRTC 传输之 TURN

介绍一下这个过程:

  1. TURN Client 首先向 TURN Server 发送一个无效的(没有携带 Message Integrity 属性)allocate request。
  2. TURN Server 要求所有的 TURN 信令消息都要通过 STUN long-term 机制进行认证,因此会拒绝掉这个无效的 allocate 请求,并返回 401 (未认证)错误响应,同时携带了 realm,nonce 验证信息。
  3. TURN Client 再次向 TURN Server 发送一个 allocate request,这次通过 relam nonce 等验证信息计算并携带了 Message Integity 属性。
  4. TURN Server 成功认证 allocate request 并回复 allocate success response,在响应消息中的 XOR-RELAY-ADDRESS 属性中携带了分配好的 Relayed Transport Address,这样客户端就知道了自己的 relay candidate,就可以把它共享给 Peer。
  5. Allocation 需要保活,因此 TURN Client 会定时向 TURN Server 发送 Refresh request,发送频率由 allocate 请求中携带的 LIFETIME 属性决定,Allocation 的生命周期(即 LIFETIME)默认值为为 10 分钟。

Remark:结合上述过程思考,为什首先要发送一个无效的 Allocate 请求?

权限 – Create Permission

Permission 是 “权限” 的意思,这条信令消息叫作 Create Permission,即 “创建权限”,那么到底是创建的什么样的权限呢?答案是:创建向 Peer 传输数据的权限。也就是说,TURN Client 并不是想和哪个 Peer 通信,TURN Server 就能帮它无脑转发的,TURN Client 一定要向 TURN Server 创建向 Peer 传输数据的权限,通过认证后,才会转发数据到 Peer。

TURN Client 发起 Create Permission request 的流程图如下:

WebRTC 传输之 TURN

介绍一下这个过程:

  1. TURN Client 向 TURN Server 发送 CreatePermission request,这个请求在 XOR-PEER-ADDRESS 属性中携带了 TURN Client 想要通信的 Peer A的传输地址。
  2. TURN Server 成功认证 CreatePermission 请求后回复成功响应。这时,TURN Server 就知道了要转发的 Peer 的地址,它会建立 relayed transport address 到 peer transport address 的五元组
  3. TURN Client 收到权限创建成功的响应后,通过 Send Indication 消息发送数据到 TURN Server。TURN Server 将数据转发到 Peer A。同理,Peer A 也可以把数据发到 TURN Server 的 relayed transport address,TURN Server 会把数据封装成 Data Indication 消息再转给 TURN Client。
  4. 如果 TURN Client 和 Peer B 试图通信,并相互传输数据,那么这些数据会被 TURN Server 丢弃,因为 TURN Client 没有向 TURN Server 申请向 Peer B 传输数据的权限。

你可能想问:为什么在数据传输之前,一定要 Create Permission 呢?这是因为 Send/Data Indication 属于 STUN Indication 方法,而这种 Indication 消息不会携带鉴权信息(Message Integrity),所以 long-term 消息认证机制就不能对 STUN indication 进行认证。

因此攻击者就可以向 TURN 服务器发送虚假的 Send indication,进而再通过 TURN 服务转发到 peer。为了尽可能的避免这种攻击,TURN 协议要求客户端在发送 Send indication 之前,先向 TURN Server 申请向 peer 传输数据的权限。所以,Create Permission 就是为 Indication 这种数据收发机制而生的。

Note: Indication 消息没有 response,也不会做丢包检测和重传。

create permission 消息有时效限制,在 allocation 活跃期间,一般需要每 5 分钟发送一次,以刷新传输通道。

绑定传输通道 – Channel Bind

就像 Create Permission 信令消息是为 Indication 这种数据收发机制而生,Channel Bind 信令消息则是为了 Channel Data 这种数据收发机制而生。关于为什么要绑定通道,前面在数据收发机制一节已经讲过,就是为了提升传输效率

TURN Client 发起 Channel Bind 的流程图如下:

WebRTC 传输之 TURN

介绍一下这个过程:

  1. TURN Client 向 TURN Server 发送 Channel Bind 请求,在请求中携带了通道号 16385(0x4001)以及  XOR-PEER-ADDRESS  属性,值为 Peer A 的传输地址。
  2. TURN Server 成功认证 Channel Bind 请求后回复成功响应,此时,16385 这个通道与 Peer A 的地址绑定成功。
  3. 之后所有通道号为 16385 的 Channel Data 都会被 TURN Server 转发到 Peer A。
  4. 一旦绑定通道后,TURN Client 可以自由混合发送 Channel Data 和 Send Indication,但是 TURN Server 则只能使用 Channel Data 发送数据给 TURN Client。

和 permission 消息一样,Channel Bind 消息也有时效限制,不过它的生命周期要比 permission 长,一般是 10 分钟。因此需要定时发送 channel bind 请求重新把 channel 绑定到 Peer。绑定通道后,没有显式删除 channel 通道的方法,客户端只能被动等待通道超时。

刷新 – Refresh

Refresh 是 “刷新” 的意思,该消息刷新的是 Allocation,以保证 Allocation 持续有效,否则会被回收。

6 TURN 的 long-term 鉴权机制

在 WebRTC STUN | Short-term 消息认证 一文中,我讲过 short-trem 机制。和 short-term 机制一样,long-term 机制也是一种使用 HMAC 对消息进行完整性签名以及完整性校验的手段,可以用来对客户端进行鉴权(身份认证)。

在 TURN 中,TURN Server 要对 TURN Client 的 allocate、permission、channel、refresh 消息进行 long-term 认证

key = MD5(username, ":" realm ":" SASLprep(password))

realm 是一个描述 TURN Server 的字符串,一般是服务器域名,nonce 是一个随机数。password 可以混入 nonce 和 timestamp 等信息计算,防止重放攻击,password 一般通过安全的信令通道,比如 HTTPS 进行传输。

Note:思考 long-term 和 short-term 区别是什么呢?

7 TURN 在 RTC 场景下的建连流程

下面我们介绍在 RTC 场景下的 TURN 建连流程,如下图所示:

WebRTC 传输之 TURN

client 借助 TURN 服务与 SFU 完成标准的 RTC 建连流程,即 ICE + DTLS-SRTP,我们描述一下这个过程要经过的步骤:

  1. 探测与保活。
  2. 分配 relay。准备开始启动标准的 RTC 建连流程。
  3. 申请对 Peer 的数据传输权限并使用 Indication 方式传输数据。ICE 的 Binding 流程可能会在这个过程中完成。
  4. 绑定传输通道到 Peer 并使用 Channel Bind 方式传输数据。DTLS-SRTP 握手与密钥协商可能会在这个过程完成。
  5. 数据交互。
  6. 结束会话,销毁 allocation。

8 TURN 的切网机制

当用户的网络发生变化时,其出口地址也会随之变化,比如以下场景:

  1. 用户主动在 4G/5G 和 WIFI 之间切换。
  2. 用户走出或走进大楼,移动设备自动在 4G/5G 和 WIFI 之间切换。
  3. 用户在不同楼层走动,移动设备自动在不同 WIFI 之间切换。
  4. 客户端检测链路不通,更换源端口。

以上场景称为(客户端) “切网”,专业术语叫 Mobility,切网的本质其实就是客户端的出口 IP 和端口发生了变化。

Remark: 服务端一般是部署在公网上,有固定的公网地址,因此不会发生切网

在了解了切网的概念之后,我们还要去探究 TURN 的切网机制要解决的问题是什么,也就是说,切网会带来什么样的问题。

比如 Alice 和 Bob 通过 TURN Server 进行音视频通信,分别和 TURN Server 建立了五元组 5-TupleA1 和 5-TupleB。假设中途 Alice 的设备发生切网,那么此时 Alice 的出口地址发生变化。这就导致之前的 5-TupleA1 失效,新的 5-TupleA2 上的数据被 TURN Server 丢弃,此时 Bob 就看不到 Alice 的画面了。想要恢复正常的音视频通信,Alice 只有重新建立会话通道。

为此,TURN 提出了一种在客户端侧解决切网问题的方案:切网后客户端不需要重建会话通道,只需要一个 Refresh 消息就能立即恢复音视频通信。

WebRTC 传输之 TURN

其实,解决切网问题的关键有两点:

  1. 如何快速检测客户端出口地址的变化。
  2. 检测到地址变更后如何快速恢复会话。

另外,TURN 的这种 Mobility 方案是在客户端实施,需要靠客户端自己感知自身的网络变化,然而有的客户端是感知不到自己切网的,因此这种方案存在一定的局限性。既然如此,可不可以在服务端做切网方案呢?答案是可以的,以后有机会再介绍服务端切网的方案。

感谢阅读。

9 参考文献

Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN):https://datatracker.ietf.org/doc/html/rfc8656

Mobility with Traversal Using Relays around NAT (TURN):https://datatracker.ietf.org/doc/html/rfc8016

作者:于吉太
来源:码神说
来源:https://mp.weixin.qq.com/s/zpHpyhs7xQ8U8fTCEghTDg

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论