1 TURN 要解决的问题
在 TCP/IP 网络中,一个常见的问题就是如何让两台处于内网的主机进行通信,即 NAT 穿越问题。解决 NAT 穿越问题的两个关键是:
- 想办法发现自己的公网 IP 和端口(只知道自己的内网地址是不行的)。
- 把自己的公网 IP 和端口共享给对方。
举个例子, Alice 和 Bob 要进行视频会议,然而他们的设备都是内网 IP,中间要经过很多路由器才能到达对方,他们相互不知道对方的 IP 地址。在这种场景下,该如何让 Alice 和 Bob 的音视频数据分别到达彼此呢?
一种解决方案是通过 STUN 实现 P2P:
- Alice 和 Bob 分别通过位于公网的 STUN 服务器获取自己的反射地址(srflx,server reflexive address),这个地址一般是路由器的公网 IP 和端口。
- 然后 Alice 和 Bob 通过信令服务共享彼此的反射地址并开始做 NAT 穿透,这样双方就建立了 P2P 通道。
这种方案可以解决大多数的 NAT 穿越问题,不过,并非所有的情况下都能通过 STUN 实现 P2P。如果 Alice 和 Bob 处于对称型 NAT 的网络场景下,就无法实现 P2P 通信。或者 Alice 和 Bob 分别在国内和国外的这种跨国情况,基本也是无法 P2P 的。
另一种解决方案是通过 TURN 实现 Relay(中继/转发):
- Alice 和 Bob 分别通过位于公网的 TURN 服务器获取到分配给自己的 relay 地址,这个地址是 TURN 服务器的公网 IP 和它为客户端分配的端口。
- 然后 Alice 和 Bob 通过信令服务共享彼此的 relay 地址,通过两台 TURN 服务器中继数据,实现通信。
该方案使用中继的方式避开了对称型 NAT 无法穿越的问题。两台 TURN 服务器分别相当于 Alice 和 Bob 的代理,帮助他们转发和接收对方的数据。如果这两台 TURN 服务器之间有专线可以打通,那么跨国通信的问题也可以解决。
还有一种方案是 SFU + ICE lite + PeerConnection:
- Alice 和 Bob 分别和位于公网的实现了 ICE lite 的 SFU 建 PeerConnection 连接。
- 通过 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。
Remark: Server-Reflexive Transport Address 就是 NAT (路由器)的出口地址。
我们简单描述一下 TURN Client 使用 TURN Server 和 Peer 通信的两个关键链路:
- TURN Client 到 TURN Server 的通信链路
- TURN Client 从它的内网地址 Host Transport Address 向 TURN Server 的地址发送 TURN Message。
- TURN Server 收到 TURN Message,它看到的 TURN Client 的地址其实是 NAT 的出口地址,即 Server-Reflexive Transport Address,TURN Server 向这个地址发送的 TURN Message 会被 NAT 转发到 TURN Client 的内网地址。
- 用五元组表示这段链路,为
<TURN Client IP:Port, TURN Server IP:PORT, PROTO>
,在这幅图中表示为 <192.0.2.1:7000, 192.0.2.15:3478, UDP>。
- TURN Server 到 Peer 的通信链路
- TURN Server 把 TURN Client 的数据从 Relayed Transport Address 发送到 Peer 的地址。
- 对于 Peer A,TURN Server 把数据转发到 Peer A 的 Host Transport Address。同样, Peer A 的数据会从 Host Transport Address 发送到 TURN Server 的 Relayed Transport Address。
- 对于 Peer B,因为它在 NAT 之后,所以 TURN Server 要把数据转发到 PeerB 的 Server-Reflexive Transport Address,NAT 会把数据转发到 PeerB 的内网地址。同样, Peer B 的数据会从 Host Transport Address 经过 NAT 发送到 TURN Server 的 Relayed Transport Address。
- 用五元组表示这段链路,为
<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 之间)交换数据的机制有两种:
- 使用 Send Indication 和 Data Indication 消息。
- 使用 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,它们携带两个重要的属性:
- XOR-PEER-ADDRESS 属性,包含 Peer 的传输地址,指示数据要发到哪。
- DATA 属性,包含了真正要发发送的数据。
我们具体来看一下这两个数据消息的格式:
我们知道,STUN 的头部(有 12 字节之多)和属性都比较大,因此对于 Send Indication 和 Data Indication 来讲,每发送一次数据消息都要携带这些头和属性(XOR-PEER-ADDRESS)。
这不仅造成数据冗余,而且数据传递效率低,也增大了 TURN 的转发带宽,这种 indication 的数据收发机制的缺点很明显。其实,TURN 有更好的 Channel Data 数据交换机制,下面就介绍这种机制。
TURN Channel Data Message
Channel Data 不再属于 STUN 消息了,因此它的数据格式当然也不再是 STUN 消息的格式。
上图就是 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 的信令消息主要有:
- Binding,用于探测 srflx 地址(用于 P2P 穿透)、心跳保活。
- Allocation,用于分配 Relay Transport Address。
- Permission,用于向 TURN Server 申请向 Peer 传输数据的权限,对应 Send/Data Indication 数据收发机制。
- Channel Bind,用于向 TURN Server 申请绑定向 Peer 传输数据的通道,对应 Channel Data 数据收发机制。
- Refresh,用于 Allocation 的保活与销毁、客户端切网(Mobility)。
探测/保活 – Binding
和 ICE 进行连通性测试时发送 STUN Binding 的流程基本一致。
分配 – Allocate
Allocate 是 “分配” 的意思,那么分配的是什么呢?答案是 Relayed Transport Address。因此,我们说 Allocate 的本质就是分配 Relayed Transport Address,一个 Relayed Transport Address 可以唯一代表一个 Allocation。
TURN Client 发起 Allocate request 的流程图如下:
介绍一下这个过程:
- TURN Client 首先向 TURN Server 发送一个无效的(没有携带 Message Integrity 属性)allocate request。
- TURN Server 要求所有的 TURN 信令消息都要通过 STUN long-term 机制进行认证,因此会拒绝掉这个无效的 allocate 请求,并返回 401 (未认证)错误响应,同时携带了 realm,nonce 验证信息。
- TURN Client 再次向 TURN Server 发送一个 allocate request,这次通过 relam nonce 等验证信息计算并携带了 Message Integity 属性。
- TURN Server 成功认证 allocate request 并回复 allocate success response,在响应消息中的
XOR-RELAY-ADDRESS
属性中携带了分配好的 Relayed Transport Address,这样客户端就知道了自己的 relay candidate,就可以把它共享给 Peer。 - 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 的流程图如下:
介绍一下这个过程:
- TURN Client 向 TURN Server 发送 CreatePermission request,这个请求在
XOR-PEER-ADDRESS
属性中携带了 TURN Client 想要通信的 Peer A的传输地址。 - TURN Server 成功认证 CreatePermission 请求后回复成功响应。这时,TURN Server 就知道了要转发的 Peer 的地址,它会建立 relayed transport address 到 peer transport address 的五元组。
- TURN Client 收到权限创建成功的响应后,通过 Send Indication 消息发送数据到 TURN Server。TURN Server 将数据转发到 Peer A。同理,Peer A 也可以把数据发到 TURN Server 的 relayed transport address,TURN Server 会把数据封装成 Data Indication 消息再转给 TURN Client。
- 如果 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 的流程图如下:
介绍一下这个过程:
- TURN Client 向 TURN Server 发送 Channel Bind 请求,在请求中携带了通道号 16385(0x4001)以及
XOR-PEER-ADDRESS
属性,值为 Peer A 的传输地址。 - TURN Server 成功认证 Channel Bind 请求后回复成功响应,此时,16385 这个通道与 Peer A 的地址绑定成功。
- 之后所有通道号为 16385 的 Channel Data 都会被 TURN Server 转发到 Peer A。
- 一旦绑定通道后,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 建连流程,如下图所示:
client 借助 TURN 服务与 SFU 完成标准的 RTC 建连流程,即 ICE + DTLS-SRTP,我们描述一下这个过程要经过的步骤:
- 探测与保活。
- 分配 relay。准备开始启动标准的 RTC 建连流程。
- 申请对 Peer 的数据传输权限并使用 Indication 方式传输数据。ICE 的 Binding 流程可能会在这个过程中完成。
- 绑定传输通道到 Peer 并使用 Channel Bind 方式传输数据。DTLS-SRTP 握手与密钥协商可能会在这个过程完成。
- 数据交互。
- 结束会话,销毁 allocation。
8 TURN 的切网机制
当用户的网络发生变化时,其出口地址也会随之变化,比如以下场景:
- 用户主动在 4G/5G 和 WIFI 之间切换。
- 用户走出或走进大楼,移动设备自动在 4G/5G 和 WIFI 之间切换。
- 用户在不同楼层走动,移动设备自动在不同 WIFI 之间切换。
- 客户端检测链路不通,更换源端口。
以上场景称为(客户端) “切网”,专业术语叫 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 消息就能立即恢复音视频通信。
其实,解决切网问题的关键有两点:
- 如何快速检测客户端出口地址的变化。
- 检测到地址变更后如何快速恢复会话。
另外,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 举报,一经查实,本站将立刻删除。