摘要
DTLS Fragment 真是让人头疼,入门 WebRTC 时碰到了不少 BUG。但是,我们不能放弃,必须深入探讨 DTLS 的基本定义和体制,细究协议书。在做 J 和 G 时,我们遇到了很多挑战,但是我们会坚持下去!
正文
入门 WebRTC DTLS 碰到许多 BUG?探讨 DTLS Fragment
上一篇《详解 WebRTC 传输安全机制:一文读懂 DTLS 协议》详尽论述了 DTLS。文中将融合 DTLS 开发设计中碰到的难题,详尽讲解 DTLS 的一些基本定义及其 Fragment 的体制,并进一步细究 DTLS 协议书。
创作者|泰一
校审|进学、莫战
序言
近期在做 J 和 G 这两个 RTC 系统软件的 DTLS-SRTP 挥手数据加密工作中,规定应用 CA 组织 授予的资格证书。在该设备调节的全过程中发觉:G 系统软件应用 CA 资格证书,DTLS 挥手取得成功,而 J 系统软件则挥手不成功。
历经几翻调节与剖析,精准定位到缘故:J 系统软件相比于 G 系统软件多了一个 TURN 分享控制模块,该控制模块设定的接收缓冲区的上限制值为 1600 字节数,而 CA 资格证书的尺寸则有近 3000 字节数,因而 TURN 控制模块发送给手机客户端的资格证书不详细,造成 DTLS 挥手不成功。
大家都了解, WebRTC 的 DTLS 应用的是自签字的资格证书,这一资格证书一般不容易很大,如下图所显示,仅有 286 字节数。
殊不知,假如要应用 CA 授予的资格证书,那麼这一资格证书很有可能会非常大,如下图所显示,竟做到了 2772 字节数,显而易见超过了 TURN 控制模块的接收缓冲区的尺寸。
图中中,你很有可能留意到这一 CA 资格证书被分为了两块(two fragments),这实际上是 DTLS 协议书层做的。但是非常值得思索的是,CA 资格证书的每一片的尺寸都未超过 TURN 控制模块接收缓冲区的 1600 字节数的限定,可是为何 J 系统软件的 TURN 分享控制模块仍然会接受不成功呢?
这是由于资格证书尽管被分块,可是在发送至 TURN 控制模块时并沒有依照分块单独推送,依然是所有装包到同一个 UDP 数据信息报中开展推送,因此 接受毫无疑问会不成功。
下边,大家将一起掌握下 DTLS Fragment 的体制。最先要梳理好多个定义。
Message、Record、Flight
DTLS 协议书分成双层:最底层的 record protocol 和顶层的 handshake protocol、change cipher spec protocol、alert protocol 及其 application data protocol。
Remark:挥手协议书、登陆密码规格型号变更协议、警示协议书、运用数据信息协议书均在 DTLS 纪录协议书的顶层,这四种协议书通称为 DTLS 挥手协议书。
Note:有关纪录和挥手这双层协议书分别的功效,这儿就不会再过多阐释,能够 参照 WebRTC 中 DTLS 的运用。
DTLS Message 是一条详细的 DTLS 信息。例如挥手信息:Client Hello、Certificate、Client Key Exchange 等;例如登陆密码规格型号变动信息:Change Cipher Spec。
DTLS Record 是纪录层(Record Layer)的定义,能够 觉得它是一个外壳,里边装车着 DTLS Message,如下图:
Message 和 Record 是一对一或是一对多的关联。换句话说,一个 Record 不一定装了一条详细的 Message。由于有可能是好几个 Record 构成一个详细的 Message。
假如 Message 不大,未超出 MTU 的限定,那麼一个 Record 足够装下一条 Message;假如 Message 非常大,超出 MTU 的限定,那麼就必须好几个 Record 装放这一条 Message。即这一条 DTLS Message 会被切分为好几个 Fragment,随后各自装进好几个 Record。
Remark:较大传送模块(Maximum transmission Unit, MTU)是数据链路层的定义,MTU 限定的是数据链路层的 payload 尺寸,也就是其顶层协议书的尺寸,例如 IP、ICMP。在以太网接口中,链路层的 MTU 是 1500 字节数。
例如,Certificate 这一挥手信息,资格证书尺寸非常容易就超出 MTU 的限定,那麼这一信息便会被切分为好几个 Fragment 并被各自储放到好几个 DTLS Record,每一个 Fragment 的尺寸要确保不超过 MTU 的限定(PS:前言的第二张图便是一个具体的事例)。
Flight 汉语表述为 “飞机航班” 或是 “航行”,是一个或是一组装包好的 Message,这组 Message 归属于同一个 “航行”,视作一个总体,根据单独 UDP 数据信息报推送。
如上图所述所显示,此次 DTLS 挥手一共有 4 个 Flight。Flight2 是 Server Hello、Certificate、Server Hello Done 这三条 Message 的组成,在其中 Certificate 这条 Message 被切分为2个 Fragment,装到2个 Record 中。Flight2 根据尺寸为 2969 字节数的 UDP 数据信息报推送出来 。
Remark:Flight2 这一 2969 字节数的 UDP 包是在该设备自然环境下调节、抓包软件获得的,并不意味着 MTU 有这么大,在具体的互联网中,不容易发生这类远超 MTU 限定的数据文件。
到这儿,有关 Message、Record、Flight 的定义就说完了,三者中间的关如下图:
Fragment
下边大家谈一谈,DTLS 为何要对 DTLS Message 做分块。
我们知道,受以太网接口 MTU 危害,UDP 数据信息报较大为 1500 字节数,超过这一限定便会被 IP 层分块(PS:以太网接口 MTU 设定为 1500 字节数是为了更好地利润最大化无线信道传送使用率)。
可是假如 IP 层分块体制被严禁呢?这便会造成超过 1500 字节数的 UDP 数据信息报在 IP 层被丢掉。因而,DTLS 要对信息做分块,来达到 IP 层对报文格式尺寸的规定。DTLS1.2: Message Size 这一节表述了这一缘故。
By contrast, UDP datagrams are often limited to < 1500 bytes if IP fragmentation is not desired. In order to compensate for this limitation, each DTLS handshake message may be fragmented over several DTLS records, each of which is intended to fit in a single IP datagram.
因而,DTLS 的分块体制非常简单:在推送时把 DTLS Message 切分成好几个持续的 DTLS Record,在接受时缓存文件分块,直至有着详细的 DTLS Message。
我们可以应用 OpenSSL 的这两个 API 设定 MTU 的尺寸:
SSL_set_options(dtls, SSL_OP_NO_QUERY_MTU);
SSL_set_mtu(dtls, 1500);
上边的编码设定了 MTU 为 1500,那麼当 DTLS Message 尺寸超出 1500 字节数,便会开启 DTLS 的分块体制,同样,假如设定 MTU 为 300,那麼当 DTLS Message 尺寸超出 300 字节数,便会分块。如果不开展设定,那麼 MTU 会走初始值,如下图所显示,资格证书信息被切分变成数个尺寸为 288 字节数的固定不动的 Fragment。
Remark:TLS 最底层是 TCP 协议书,为字节数流式传输,因而 TLS 沒有信息分块体制。
大家还可应用下边的 API 设定 Fragment 的尺寸的限制:
SSL_set_max_send_fragment(dtls, 1500);
最终,大家返回前言叙述的难题:资格证书信息事实上的确被切分为两块并各自储存到2个 Record,可是因为在推送的情况下或是装包到一个 UDP 数据信息报,因而,过大的 UDP 数据信息报造成 TURN 控制模块仍未接受详细。
更详尽的缘故是:大家应用的是运行内存型的 BIO,在网络层启用 BIO_get_mem_data
获得的是有关 DTLS Message 的一块持续的运行内存(尽管这方面运行内存中的资格证书信息早已被 DTLS 切割成2个持续的 Fragment 并存有2个 Record 中),而网络层在获得到这方面运行内存后就立即根据 sendto
涵数发给了对端,因而,这一 UDP 报文格式自然或是尤其大,造成接受不成功。
转过头来再看看前言中资格证书信息分块的这幅图,2个 Record 的 message sequence
字段名值同样,表明它是同一个 DTLS Message 的2个 Fragment。且每一个 Record 都是有 fragment offset
和 fragment length
这两个字段名,用于标志分块的界限。因此 ,我们可以依据这两个字段名去分析出每一个单独的 Fragment。
自然,依据 Record 头顶部的 Length
字段名足够明确界限,这会使网络层的分析更为便捷。因此 ,要处理这个问题,网络层要做的是:对从 BIO 获得到的这方面信息运行内存开展分析,获得每一个 Record 的界限,随后将每一个 Record 以单独的 UDP 报文格式推送出来 。实际的分析编码这儿也不贴上去了,比较简单。
最终,结合实际发觉,DTLS Record 不可以跨 UDP 数据信息报推送,DTLS 1.2: Transport Layer Mapping 这一节也交待了这一点。换句话说,网络层要严苛的依照 Record 的界限分析出每一个 Record,各自根据单独的 UDP 数据信息报推送,而不可以依照自身的意向随便区划为数个 UDP 数据信息报推送。由于这很有可能会造成某一 DTLS Record 被分割到好几个 UDP 数据信息报推送,进而造成协调器 DTLS 没法将接到的 DTLS Records 资产重组为详细的 DTLS Message。
下面的图是 DTLS 分块单独推送后的实际效果:
有兴趣爱好的阅读者能够 参照我写的 DTLS demo,它完成了简易的 DTLS 挥手和分块单独推送。还可以参照 开源系统视频服务器 SRS 的 DTLS 完成,更为简约和详细。
汇总
针对超出 MTU 限定的 DTLS Message,DTLS 会把它切分为好几个 Fragment, 并各自储存到每个 DTLS Record 中,因而一个 Fragment 一定是一个 DTLS Record。针对未超出 MTU 限定的 DTLS Message,则不容易被分块,也是储存到 DTLS Record 中,因而一个 DTLS Record 不一定是一个 Fragment,也是有可能是一个详细的 DTLS Message。此外,MTU 的尺寸及其 Fragment 的最高值都能够应用 OpenSSL 的 API 开展设定。
因为大家根据运行内存型 BIO 获得到储存了每个 DTLS Message 的这方面持续运行内存后,立即将其装包为 Flight,并根据独立的 UDP 数据信息报文格式推送,因而这一 UDP 包依然或是那么大,超过了 TURN 控制模块接收缓冲区的限制和 MTU 的限定。因此 为了更好地保证真真正正的分块单独推送,必须网络层自身去做 Fragment 的分析(实际上便是分析 Record 的界限),并各自根据单独的 UDP 报文格式推送。
我们在解决了一个难题后,也要再问一下自身是否有引进新的难题。
单独推送每一个 DTLS Record,尽管解决了 DTLS Message 超出 MTU 限定的难题,可是这也提升了 UDP 报文格式的总数,因而丢包率的几率也会相对应的提升,DTLS 重新传输频次提升,挥手的通过率减少。处理这个问题的一个方式 是:无须每一个 DTLS Record 都独立 UDP 推送,能够 好几个 DTLS Record 推送,只需能确保他们加起來的尺寸不超过 MTU 的限定就可以。
另外,大家还要问一下自身是否有更强的方式 。
例如现阶段的解决方案是网络层自身完成 Record 分析并单独推送,那麼 OpenSSL 是不是早已有有关的 API 完成相近的作用,再例如 BIO 是否有有关的 API 能够 告知大家载入的运行内存数据信息中 Record 的总数及其每一个 Record 的界限?这个问题,之后有时间再调研吧。
谢谢阅读文章。
参照
DTLS 1.2
TLS 1.2
「视频云技术性」你最非常值得关心的音频视频技术性微信公众号,每星期消息推送来源于阿里云服务器一线的实践活动技术性文章内容,在这儿与音频视频行业一流技术工程师沟通交流传功。公众号后台回应【技术性】可添加阿里云视频云计算技术交流群,和创作者一起讨论音频视频技术性,获得大量领域最新消息。
关注不迷路
扫码下方二维码,关注宇凡盒子公众号,免费获取最新技术内幕!
评论0