溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

DTLS Fragment的功能介紹

發(fā)布時(shí)間:2021-06-23 09:36:32 來(lái)源:億速云 閱讀:234 作者:chen 欄目:編程語(yǔ)言

這篇文章主要介紹“DTLS Fragment的功能介紹”,在日常操作中,相信很多人在DTLS Fragment的功能介紹問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”DTLS Fragment的功能介紹”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!


前言

最近在做 J 和 G 這兩套 RTC 系統(tǒng)的 DTLS-SRTP 握手加密工作,要求使用 CA 機(jī)構(gòu)頒發(fā)的證書(shū)。在本機(jī)調(diào)試的過(guò)程中發(fā)現(xiàn):G 系統(tǒng)使用 CA 證書(shū),DTLS 握手成功,而 J 系統(tǒng)則握手失敗。

經(jīng)過(guò)幾番調(diào)試與分析,定位到了原因:J 系統(tǒng)相較于 G 系統(tǒng)多了一個(gè) TURN 轉(zhuǎn)發(fā)模塊,該模塊設(shè)置的接收緩沖區(qū)的上限值為 1600 字節(jié),而 CA 證書(shū)的大小則有近 3000 字節(jié),因此 TURN 模塊轉(zhuǎn)發(fā)給客戶端的證書(shū)不完整,導(dǎo)致 DTLS 握手失敗。

大家都知道, WebRTC 的 DTLS 使用的是自簽名的證書(shū),這個(gè)證書(shū)一般不會(huì)太大,如下圖所示,只有 286 字節(jié)。 DTLS Fragment的功能介紹

然而,如果要使用 CA 頒發(fā)的證書(shū),那么這個(gè)證書(shū)可能會(huì)很大,如下圖所示,竟達(dá)到了 2772 字節(jié),顯然超出了 TURN 模塊的接收緩沖區(qū)的大小。 DTLS Fragment的功能介紹

上圖中,你可能注意到了這個(gè) CA 證書(shū)被分成了兩片(two fragments),這其實(shí)是 DTLS 協(xié)議層做的。不過(guò)值得思考的是,CA 證書(shū)的每一片的大小都未超出 TURN 模塊接收緩沖區(qū)的 1600 字節(jié)的限制,但是為什么 J 系統(tǒng)的 TURN 轉(zhuǎn)發(fā)模塊依然會(huì)接收失敗呢?

這是因?yàn)樽C書(shū)雖然被分片,但是在發(fā)送到 TURN 模塊時(shí)并沒(méi)有按照分片獨(dú)立發(fā)送,仍然是全部打包到了同一個(gè) UDP 數(shù)據(jù)報(bào)中進(jìn)行發(fā)送,所以接收肯定會(huì)失敗。

下面,我們將一起了解下 DTLS Fragment 的機(jī)制。首先要理清幾個(gè)概念。

Message、Record、Flight

DTLS 協(xié)議分為兩層:底層的 record protocol 和上層的 handshake protocol、change cipher spec protocol、alert protocol 以及 application data protocol。 DTLS Fragment的功能介紹

Remark:握手協(xié)議、密碼規(guī)格變更協(xié)議、警告協(xié)議、應(yīng)用數(shù)據(jù)協(xié)議均在 DTLS 記錄協(xié)議的上層,這四種協(xié)議統(tǒng)稱為 DTLS 握手協(xié)議。

Note:關(guān)于記錄和握手這兩層協(xié)議各自的作用,這里就不再贅述,可以參考 WebRTC 中 DTLS 的應(yīng)用。

DTLS Message 是一條完整的 DTLS 消息。比如握手消息:Client Hello、Certificate、Client Key Exchange 等;比如密碼規(guī)格變更消息:Change Cipher Spec。

DTLS Record 是記錄層(Record Layer)的概念,可以認(rèn)為它是一個(gè)殼子,里面裝載著 DTLS Message,如下圖:

DTLS Fragment的功能介紹

Message 和 Record 是一對(duì)一或者一對(duì)多的關(guān)系。也就是說(shuō),一個(gè) Record 不一定裝了一條完整的 Message。因?yàn)橛锌赡苁嵌鄠€(gè) Record 組成一個(gè)完整的 Message。

如果 Message 很小,未超過(guò) MTU 的限制,那么一個(gè) Record 足以裝下一條 Message;如果 Message 很大,超過(guò) MTU 的限制,那么就需要多個(gè) Record 來(lái)裝這條 Message。即這條 DTLS Message 會(huì)被分割為多個(gè) Fragment,然后分別裝入多個(gè) Record。

Remark:最大傳輸單元(Maximum transmission Unit, MTU)是數(shù)據(jù)鏈路層的概念,MTU 限制的是數(shù)據(jù)鏈路層的 payload 大小,也就是其上層協(xié)議的大小,比如 IP、ICMP。在以太網(wǎng)中,鏈路層的 MTU 是 1500 字節(jié)。

比如,Certificate 這個(gè)握手消息,證書(shū)大小很容易就超過(guò) MTU 的限制,那么這個(gè)消息就會(huì)被分割為多個(gè) Fragment 并被分別存放到多個(gè) DTLS Record,每個(gè) Fragment 的大小要保證不超過(guò) MTU 的限制(PS:導(dǎo)讀的第二張圖就是一個(gè)實(shí)際的例子)。

Flight 中文解釋為 “航班” 或者 “航程”,是一個(gè)或者一組打包好的 Message,這組 Message 屬于同一個(gè) “航程”,視為一個(gè)整體,通過(guò)單個(gè) UDP 數(shù)據(jù)報(bào)發(fā)送。

DTLS Fragment的功能介紹

如上圖所示,本次 DTLS 握手一共有 4 個(gè) Flight。Flight2 是 Server Hello、Certificate、Server Hello Done 這三條 Message 的組合,其中 Certificate 這條 Message 被分割為兩個(gè) Fragment,裝到兩個(gè) Record 中。Flight2 通過(guò)大小為 2969 字節(jié)的 UDP 數(shù)據(jù)報(bào)發(fā)送出去。

Remark:Flight2 這個(gè) 2969 字節(jié)的 UDP 包是在本機(jī)環(huán)境下調(diào)試、抓包得到的,并不代表 MTU 有這么大,在實(shí)際的網(wǎng)絡(luò)中,不會(huì)出現(xiàn)這種遠(yuǎn)超 MTU 限制的數(shù)據(jù)包。

到這里,關(guān)于 Message、Record、Flight 的概念就講完了,三者之間的關(guān)如下圖:

DTLS Fragment的功能介紹

Fragment

下面我們談?wù)?,DTLS 為什么要對(duì) DTLS Message 做分片。

我們知道,受以太網(wǎng) MTU 影響,UDP 數(shù)據(jù)報(bào)最大為 1500 字節(jié),超出這個(gè)限制就會(huì)被 IP 層分片(PS:以太網(wǎng) MTU 設(shè)置為 1500 字節(jié)是為了最大化信道傳輸利用率)。

但是如果 IP 層分片機(jī)制被禁止呢?這就會(huì)導(dǎo)致大于 1500 字節(jié)的 UDP 數(shù)據(jù)報(bào)在 IP 層被丟棄。因此,DTLS 要對(duì)消息做分片,來(lái)滿足 IP 層對(duì)報(bào)文大小的要求。DTLS1.2: Message Size 這一節(jié)解釋了這個(gè)原因。

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 的分片機(jī)制很簡(jiǎn)單:在發(fā)送時(shí)把 DTLS Message 分割成多個(gè)連續(xù)的 DTLS Record,在接收時(shí)緩存分片,直到擁有完整的 DTLS Message。

我們可以使用 OpenSSL 的這兩個(gè) API 設(shè)置 MTU 的大小:

SSL_set_options(dtls, SSL_OP_NO_QUERY_MTU);
SSL_set_mtu(dtls, 1500);

上面的代碼設(shè)置了 MTU 為 1500,那么當(dāng) DTLS Message 大小超過(guò) 1500 字節(jié),就會(huì)觸發(fā) DTLS 的分片機(jī)制,同理,如果設(shè)置 MTU 為 300,那么當(dāng) DTLS Message 大小超過(guò) 300 字節(jié),就會(huì)分片。如果不進(jìn)行設(shè)置,那么 MTU 會(huì)走默認(rèn)值,如下圖所示,證書(shū)消息被分割成了若干個(gè)大小為 288 字節(jié)的固定的 Fragment。

DTLS Fragment的功能介紹

Remark:TLS 底層是 TCP 協(xié)議,為字節(jié)流式傳輸,因此 TLS 沒(méi)有消息分片機(jī)制。

我們還可使用下面的 API 設(shè)置 Fragment 的大小的上限:

SSL_set_max_send_fragment(dtls, 1500);

最后,我們回到導(dǎo)讀描述的問(wèn)題:證書(shū)消息實(shí)際上確實(shí)被分割為兩片并分別存儲(chǔ)到兩個(gè) Record,但是由于在發(fā)送的時(shí)候還是打包到了一個(gè) UDP 數(shù)據(jù)報(bào),因此,過(guò)大的 UDP 數(shù)據(jù)報(bào)導(dǎo)致 TURN 模塊并未接收完整。

更詳細(xì)的原因是:我們使用的是內(nèi)存型的 BIO,在應(yīng)用層調(diào)用 BIO_get_mem_data 得到的是關(guān)于 DTLS Message 的一塊連續(xù)的內(nèi)存(雖然這塊內(nèi)存中的證書(shū)消息已經(jīng)被 DTLS 切成兩個(gè)連續(xù)的 Fragment 并存在兩個(gè) Record 中),而應(yīng)用層在獲取到這塊內(nèi)存后就直接通過(guò) sendto 函數(shù)發(fā)送給了對(duì)端,因此,這個(gè) UDP 報(bào)文當(dāng)然還是特別大,導(dǎo)致接收失敗。

回過(guò)頭來(lái)再看下導(dǎo)讀中證書(shū)消息分片的這張圖,兩個(gè) Record 的 message sequence 字段值相同,說(shuō)明這是同一個(gè) DTLS Message 的兩個(gè) Fragment。且每個(gè) Record 都有 fragment offsetfragment length 這兩個(gè)字段,用來(lái)標(biāo)識(shí)分片的邊界。所以,我們可以根據(jù)這兩個(gè)字段去解析出每一個(gè)獨(dú)立的 Fragment。

當(dāng)然,根據(jù) Record 頭部的 Length 字段足以確定邊界,這會(huì)使應(yīng)用層的解析更加方便。所以,要解決這個(gè)問(wèn)題,應(yīng)用層要做的是:對(duì)從 BIO 獲取到的這塊消息內(nèi)存進(jìn)行解析,得到每個(gè) Record 的邊界,然后將每個(gè) Record 以獨(dú)立的 UDP 報(bào)文發(fā)送出去。具體的解析代碼這里就不貼出來(lái)了,非常簡(jiǎn)單。

最后,在實(shí)踐中發(fā)現(xiàn),DTLS Record 不能跨 UDP 數(shù)據(jù)報(bào)發(fā)送,DTLS 1.2: Transport Layer Mapping 這一節(jié)也交代了這一點(diǎn)。也就是說(shuō),應(yīng)用層要嚴(yán)格的按照 Record 的邊界解析出每一個(gè) Record,分別通過(guò)獨(dú)立的 UDP 數(shù)據(jù)報(bào)發(fā)送,而不能按照自己的意愿隨意劃分為若干個(gè) UDP 數(shù)據(jù)報(bào)發(fā)送。因?yàn)檫@可能會(huì)導(dǎo)致某個(gè) DTLS Record 被切分到多個(gè) UDP 數(shù)據(jù)報(bào)發(fā)送,從而導(dǎo)致接收端 DTLS 無(wú)法將收到的 DTLS Records 重組為完整的 DTLS Message。

下圖是 DTLS 分片獨(dú)立發(fā)送后的效果:

DTLS Fragment的功能介紹

有興趣的讀者可以參考我寫(xiě)的 DTLS demo,它實(shí)現(xiàn)了簡(jiǎn)單的 DTLS 握手和分片獨(dú)立發(fā)送。也可以參考 開(kāi)源視頻服務(wù)器 SRS 的 DTLS 實(shí)現(xiàn),更加簡(jiǎn)潔和詳盡。

總結(jié)

對(duì)于超過(guò) MTU 限制的 DTLS Message,DTLS 會(huì)把它分割為多個(gè) Fragment, 并分別存儲(chǔ)到各個(gè) DTLS Record 中,因此一個(gè) Fragment 一定是一個(gè) DTLS Record。對(duì)于未超過(guò) MTU 限制的 DTLS Message,則不會(huì)被分片,也是存儲(chǔ)到 DTLS Record 中,因此一個(gè) DTLS Record 不一定是一個(gè) Fragment,也有可能是一個(gè)完整的 DTLS Message。另外,MTU 的大小以及 Fragment 的最大值都可以使用 OpenSSL 的 API 進(jìn)行設(shè)置。

由于我們通過(guò)內(nèi)存型 BIO 獲取到了存儲(chǔ)了各個(gè) DTLS Message 的這塊連續(xù)內(nèi)存后,直接將其打包為 Flight,并通過(guò)單獨(dú)的 UDP 數(shù)據(jù)報(bào)文發(fā)送,因此這個(gè) UDP 包仍然還是那么大,超出了 TURN 模塊接收緩沖區(qū)的上限和 MTU 的限制。所以為了做到真正的分片獨(dú)立發(fā)送,需要應(yīng)用層自己去做 Fragment 的解析(其實(shí)就是解析 Record 的邊界),并分別通過(guò)獨(dú)立的 UDP 報(bào)文發(fā)送。

我們?cè)诮鉀Q了一個(gè)問(wèn)題后,還要再問(wèn)一下自己有沒(méi)有引入新的問(wèn)題。

獨(dú)立發(fā)送每個(gè) DTLS Record,雖然解決了 DTLS Message 超過(guò) MTU 限制的問(wèn)題,但是這也增加了 UDP 報(bào)文的數(shù)量,因此丟包的概率也會(huì)相應(yīng)的增加,DTLS 重傳次數(shù)增加,握手的成功率降低。解決這個(gè)問(wèn)題的一個(gè)方法是:不必每個(gè) DTLS Record 都單獨(dú) UDP 發(fā)送,可以多個(gè) DTLS Record 發(fā)送,只要能保證它們加起來(lái)的大小不超過(guò) MTU 的限制就可以。

同時(shí),我們也要問(wèn)一下自己有沒(méi)有更好的方法

比如目前的解決方法是應(yīng)用層自己實(shí)現(xiàn) Record 解析并獨(dú)立發(fā)送,那么 OpenSSL 是否已經(jīng)有相關(guān)的 API 實(shí)現(xiàn)類似的功能,再比如 BIO 有沒(méi)有相關(guān)的 API 可以告訴我們讀取的內(nèi)存數(shù)據(jù)中 Record 的數(shù)量以及每個(gè) Record 的邊界?這個(gè)問(wèn)題,以后有時(shí)間再調(diào)研吧。

到此,關(guān)于“DTLS Fragment的功能介紹”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI