《LwIP協(xié)議棧源碼詳解——TCP/IP協(xié)議的實(shí)現(xiàn)》IP包分片與重裝1

姓名:朱小鵬 ? ?學(xué)號(hào):16010130023

轉(zhuǎn)載:

http://blog.sina.com.cn/s/blog_62a85b950101anwh.html

【嵌牛導(dǎo)讀】:較低版本的LWIP協(xié)議并不支數(shù)據(jù)包持?jǐn)?shù)據(jù)包的分片與重裝功能,較高版本的LWIP協(xié)議,關(guān)于分片數(shù)據(jù)包的重裝,采用了與標(biāo)準(zhǔn)協(xié)議中差別較大的方式來(lái)實(shí)現(xiàn)。

【嵌牛鼻子】:IP層

【嵌牛提問(wèn)】:LWIP中的IP層如何進(jìn)行信息包的接收、分片數(shù)據(jù)包重裝、信息包的發(fā)送和轉(zhuǎn)發(fā)?

【嵌牛正文】:

較低版本的LWIP協(xié)議并不支數(shù)據(jù)包持?jǐn)?shù)據(jù)包的分片與重裝功能,較高版本的LWIP協(xié)議,關(guān)于分片數(shù)據(jù)包的重裝,采用了與標(biāo)準(zhǔn)協(xié)議中差別較大的方式來(lái)實(shí)現(xiàn)。它采用了一種更為簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)來(lái)組裝分片包,但是這樣導(dǎo)致的結(jié)果是組裝過(guò)程源代碼晦澀難懂,代碼執(zhí)行效率低。個(gè)人認(rèn)為L(zhǎng)WIP的這種實(shí)現(xiàn)機(jī)制不是太好,有時(shí)間的話想自己將整個(gè)數(shù)據(jù)包分片重裝機(jī)制全部重新實(shí)現(xiàn),指定更好。

需要注意的是,在一般的嵌入式產(chǎn)品中,數(shù)據(jù)量是比較小的,基本不會(huì)出現(xiàn)數(shù)據(jù)分片的情況,而且嵌入式產(chǎn)品也不會(huì)在網(wǎng)絡(luò)中充當(dāng)路由器,實(shí)現(xiàn)數(shù)據(jù)包的重組、轉(zhuǎn)發(fā)等功能。因此,較低版本的LWIP依然能在大多數(shù)應(yīng)用中發(fā)揮其功能。

首先,我們來(lái)看看標(biāo)準(zhǔn)協(xié)議中例舉的BSD是怎么來(lái)實(shí)現(xiàn)數(shù)據(jù)包的重組的,再講LWIP的重裝機(jī)制與該機(jī)制對(duì)比講解。

標(biāo)準(zhǔn)中主要采用了兩個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)數(shù)據(jù)包的重裝,ipq和ipasfrag。其中ipq是為某個(gè)將要重組的數(shù)據(jù)包建立的節(jié)點(diǎn)信息數(shù)據(jù)結(jié)構(gòu),包括目標(biāo)IP地址,數(shù)據(jù)包編號(hào)等信息,該結(jié)構(gòu)還有指針ipf_next、ipf_prev使得該數(shù)據(jù)包的各個(gè)分片數(shù)據(jù)組成一個(gè)雙向鏈表,圖中ipasfrag用于包裝各個(gè)分片信息包。同時(shí),將所有ipq結(jié)構(gòu)體夠成一個(gè)雙向鏈表,便于某個(gè)ipq結(jié)構(gòu)的查找、插入和刪除操作。整個(gè)ipq構(gòu)成的鏈表有一個(gè)固定的鏈表頭,該表頭不存儲(chǔ)任何數(shù)據(jù)包的數(shù)據(jù),只做標(biāo)識(shí)用。

在LWIP中,結(jié)構(gòu)體ip_reassdata與上面的ipq起著類似的作用,但結(jié)構(gòu)成員大不相同, ip_reassdata被用來(lái)構(gòu)成的是單向鏈表,以實(shí)現(xiàn)對(duì)某個(gè)ip_reassdata結(jié)構(gòu)的查找刪除等操作。所有ip_reassdata結(jié)構(gòu)構(gòu)成的單向鏈表有一個(gè)頭節(jié)點(diǎn)指針,即reassdatagrams,用于指向鏈表頭。同時(shí)LWIP中沒(méi)有類似ipasfrag的結(jié)構(gòu)對(duì)收到的各個(gè)分片數(shù)據(jù)包進(jìn)行封裝,而是直接將分片數(shù)據(jù)包掛接在對(duì)應(yīng)的ip_reassdata結(jié)構(gòu)之后。至于怎樣掛接,那是后話。

現(xiàn)在來(lái)看看傳說(shuō)中的ip_reassdata結(jié)構(gòu),源代碼:

struct ip_reassdata {

struct ip_reassdata *next;// 用于構(gòu)建單向鏈表的指針

struct pbuf *p;// 該數(shù)據(jù)報(bào)的數(shù)據(jù)鏈表

struct ip_hdr iphdr;// 該數(shù)據(jù)報(bào)的IP報(bào)頭

u16_t datagram_len;// 已經(jīng)收到的數(shù)據(jù)報(bào)長(zhǎng)度

u8_t flags;// 是否收到最后一個(gè)分片包

u8_t timer;// 設(shè)置超時(shí)間隔

};

ip_reassdata主要用于描述一份正在被組裝的數(shù)據(jù)報(bào),完成數(shù)據(jù)包組裝的函數(shù)叫做ip_reass,該函數(shù)以IP分片數(shù)據(jù)包為輸入?yún)?shù),輸出組裝好的數(shù)據(jù)包指針或空指針,這里我們來(lái)看看它的操作流程。

首先,判斷該IP數(shù)據(jù)包的頭部大小,目前LWIP不支持有IP選項(xiàng)的IP數(shù)據(jù)包,所以IP數(shù)據(jù)包頭部大小只能為20個(gè)字節(jié),如果大于該值,ip_reass直接丟棄該數(shù)據(jù)包后返回。

然后,由于LWIP對(duì)所有ip_reassdata結(jié)構(gòu)連接的pbufs個(gè)數(shù)總和有個(gè)上限限定,即IP_REASS_MAX_PBUFS,所以ip_reass檢查當(dāng)前數(shù)據(jù)包分片占用的pbufs個(gè)數(shù),假設(shè)如果這么多個(gè)pbufs被連接在某個(gè)ip_reassdata后,所使用了的pbufs個(gè)數(shù)是否大于了我們剛才提的上限值,如果是大于,LWIP是不會(huì)允許這樣的情況下將pbufs被連接入ip_reassdata的。此時(shí)用戶可以有兩種選擇,一是直接刪除數(shù)據(jù)包后返回,二是刪除ip_reassdata鏈表中生存時(shí)間最長(zhǎng)的ip_reassdata結(jié)構(gòu)體及其相關(guān)pbufs,直到有了足夠的pbufs個(gè)數(shù)能使用。這種選擇是通過(guò)宏定義IP_REASS_FREE_OLDEST來(lái)實(shí)現(xiàn)的。哎,這段說(shuō)得太凌亂了!為什么要定義一個(gè)最大允許pbufs使用個(gè)數(shù)呢,不懂!

到這步就可以進(jìn)行分片數(shù)據(jù)報(bào)的插入操作了,首先要做的就是在鏈表reassdatagrams中找到對(duì)應(yīng)的ip_reassdata結(jié)構(gòu)體,此時(shí)又有兩種情況:沒(méi)有找到匹配的結(jié)構(gòu)體,即該分片是第一個(gè)到來(lái)的分片,此時(shí)需要?jiǎng)?chuàng)建一個(gè)新的ip_reassdata結(jié)構(gòu)體并插入鏈表reassdatagrams中;找到匹配的結(jié)構(gòu)體,即在該分片到來(lái)之前已經(jīng)有分片到來(lái),此時(shí)要判斷該分片是不是整個(gè)數(shù)據(jù)包的第一個(gè)分片,如果是,則用對(duì)應(yīng)ip_reassdata結(jié)構(gòu)體的iphdr字段記錄該IP數(shù)據(jù)包頭部。

到這里,必然找到了分片數(shù)據(jù)包對(duì)應(yīng)的ip_reassdata了,接著我們就判斷該分片是不是整個(gè)數(shù)據(jù)包的最后一個(gè)分片包,因?yàn)樽詈笠粋€(gè)分片數(shù)據(jù)包的IP_MF位為0。如果是最后一個(gè)分片包,則設(shè)置對(duì)應(yīng)ip_reassdata的flag標(biāo)志和datagram_len值,表示收到最后一個(gè)分片和設(shè)置整個(gè)數(shù)據(jù)包的長(zhǎng)度值。PS:第一個(gè)到來(lái)的分片包不一定是第一個(gè)分片包,最后一個(gè)到來(lái)的數(shù)據(jù)包也不一定是最后一個(gè)分片包。

最后調(diào)用函數(shù)ip_reass_chain_frag_into_datagram_and_validate對(duì)分片數(shù)據(jù)包進(jìn)行插入操作,這是我們后續(xù)會(huì)重點(diǎn)將的一個(gè)函數(shù),先在這里打住。上面這個(gè)函數(shù)還會(huì)檢測(cè)某個(gè)數(shù)據(jù)包是否被組裝完畢,如果某個(gè)數(shù)據(jù)包被組裝完畢,那ip_reass還要做以下工作:

根據(jù)ip_reassdata結(jié)構(gòu)找到第一個(gè)分片數(shù)據(jù)包,將ip_reassdata結(jié)構(gòu)中的IP報(bào)頭字段iphdr拷貝至第一個(gè)分片數(shù)據(jù)包頭部(第一個(gè)分片數(shù)據(jù)包頭部頭部數(shù)據(jù)已經(jīng)被覆蓋,在討論函數(shù)ip_reass_chain_frag_into_datagram_and_validate時(shí)我們會(huì)講到),并重新計(jì)算校驗(yàn)和。同時(shí)將第一個(gè)分片以后的各個(gè)分片數(shù)據(jù)包去掉頭部信息,這個(gè)整個(gè)數(shù)據(jù)包就恢復(fù)出來(lái)了,然后將ip_reassdata結(jié)構(gòu)從鏈表中刪除,將重組后的數(shù)據(jù)包指針作為返回參數(shù)返回。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容