姓名:朱小鵬 ? ?學(xué)號(hào):16010130023
轉(zhuǎn)載:
http://blog.sina.com.cn/s/blog_62a85b950101anw5.html
【嵌牛導(dǎo)讀】:對(duì)于IP層主要討論信息包的接收草描、分片數(shù)據(jù)包重裝适篙、信息包的發(fā)送和轉(zhuǎn)發(fā)三個(gè)內(nèi)容趣惠。IP數(shù)據(jù)報(bào)頭結(jié)構(gòu)如下所示,其中,選項(xiàng)字段是可以沒(méi)有的,所以通常的IP數(shù)據(jù)報(bào)頭長(zhǎng)度為20個(gè)字節(jié)。
【嵌牛鼻子】:IP層
【嵌牛提問(wèn)】:LWIP中的IP層如何進(jìn)行信息包的接收舔琅、分片數(shù)據(jù)包重裝、信息包的發(fā)送和轉(zhuǎn)發(fā)洲劣?
【嵌牛正文】:
對(duì)于IP層主要討論信息包的接收备蚓、分片數(shù)據(jù)包重裝、信息包的發(fā)送和轉(zhuǎn)發(fā)三個(gè)內(nèi)容囱稽。IP數(shù)據(jù)報(bào)頭結(jié)構(gòu)如下所示郊尝,其中,選項(xiàng)字段是可以沒(méi)有的战惊,所以通常的IP數(shù)據(jù)報(bào)頭長(zhǎng)度為20個(gè)字節(jié)流昏。
第一個(gè)字段是4bit的版本號(hào),對(duì)于IPv4吞获,該值為4况凉;對(duì)于IPv6,該值為6各拷。
接下來(lái)的4bit字段用于記錄首部長(zhǎng)度刁绒,以字為單位。所以對(duì)于不含任何選項(xiàng)字段的IP報(bào)頭烤黍,則該長(zhǎng)度值為5膛锭,由于該字段最大值為15,所以其能描述的最大IP報(bào)頭長(zhǎng)度為15*4=60字節(jié)蚊荣。
再下來(lái)是一個(gè)8bit的服務(wù)類型字段,該字段主要用于描述該IP數(shù)據(jù)包急需的服務(wù)類型莫杈,如最小延時(shí)互例、最大吞吐量、最高可靠性筝闹、最小費(fèi)用等媳叨。這個(gè)字段在LWIP中沒(méi)啥用處腥光。
16位的總長(zhǎng)度字段描述了整個(gè)IP數(shù)據(jù)報(bào),包括IP數(shù)據(jù)報(bào)頭的總字節(jié)數(shù)糊秆。理論上說(shuō)武福,IP數(shù)據(jù)包總長(zhǎng)度最大可達(dá)65535字節(jié),但在實(shí)際應(yīng)用中痘番,底層鏈路可不允許這么大的數(shù)據(jù)包出現(xiàn)在鏈路上捉片,因?yàn)檫@會(huì)大大增加數(shù)據(jù)出錯(cuò)的可能性,所以在鏈路層往往會(huì)對(duì)大的IP數(shù)據(jù)包進(jìn)行分片汞舱,當(dāng)然這些都是后話伍纫。
接下來(lái)的16位標(biāo)識(shí)字段用于標(biāo)識(shí)IP層發(fā)送出去的每一份IP數(shù)據(jù)報(bào),每發(fā)送一份報(bào)文昂芜,則該值加1莹规。然后的3位標(biāo)志和13位片偏移字段用于在IP數(shù)據(jù)包分片時(shí)使用,這里先不討論泌神。LWIP的較高版本才支持IP分片功能良漱。
TTL字段描述該IP數(shù)據(jù)包最多能被轉(zhuǎn)發(fā)的次數(shù),每經(jīng)過(guò)一次轉(zhuǎn)發(fā)欢际,該值會(huì)減1母市,當(dāng)該值為0時(shí),一個(gè)ICMP報(bào)文會(huì)被返回至源主機(jī)幼苛。
8位協(xié)議字段用來(lái)描述該IP數(shù)據(jù)包是來(lái)自于上層的哪個(gè)協(xié)議窒篱,該值為1表示為ICMP協(xié)議,該值為2表示IGMP協(xié)議舶沿,該值為6表示TCP協(xié)議墙杯,該值為17表UDP協(xié)議。
16位首部校驗(yàn)和只針對(duì)IP首部做校驗(yàn)括荡,它并不關(guān)心其內(nèi)部數(shù)據(jù)在傳輸過(guò)程中出錯(cuò)與否高镐,對(duì)于數(shù)據(jù)的校驗(yàn)是上層協(xié)議負(fù)責(zé)的,如ICMP畸冲、IGMP嫉髓、TCP、UDP協(xié)議都會(huì)計(jì)算它們頭部以及整個(gè)數(shù)據(jù)區(qū)的長(zhǎng)度邑闲。這里再COPY一段這個(gè)校驗(yàn)和是怎樣生成以及在接收端是如何實(shí)驗(yàn)校驗(yàn)的算行。
在發(fā)送端為了計(jì)算一份數(shù)據(jù)報(bào)的IP檢驗(yàn)和,首先把檢驗(yàn)和字段置為0苫耸。然后州邢,對(duì)首部中每個(gè)16 bit進(jìn)行二進(jìn)制反碼求和(整個(gè)首部看成是由一串16 bit的字組成),結(jié)果存在檢驗(yàn)和字段中褪子。當(dāng)接收端收到一份I P數(shù)據(jù)報(bào)后量淌,同樣對(duì)首部中每個(gè)16 bit進(jìn)行二進(jìn)制反碼的求和骗村。由于接收方在計(jì)算過(guò)程中包含了發(fā)送方保存在首部中的檢驗(yàn)和字段,因此呀枢,如果首部在傳輸過(guò)程中沒(méi)有發(fā)生任何差錯(cuò)胚股,那么接收方計(jì)算的結(jié)果應(yīng)該為全1。如果結(jié)果不是全1(即檢驗(yàn)和錯(cuò)誤)裙秋,那么IP就丟棄收到的數(shù)據(jù)報(bào)琅拌。但是不生成差錯(cuò)報(bào)文,由上層去發(fā)現(xiàn)丟失的數(shù)據(jù)報(bào)并進(jìn)行重傳残吩。
接下來(lái)是兩個(gè)32位的IP地址财忽,不啰嗦了。最后一個(gè)字段是任選字段泣侮,不同的協(xié)議會(huì)選擇性的使用該字段即彪,這里也不討論。
現(xiàn)在來(lái)看看LWIP中是怎么樣來(lái)描述這個(gè)IP數(shù)據(jù)報(bào)頭的活尊,使用的結(jié)構(gòu)體叫ip_hdr:
struct ip_hdr {
PACK_STRUCT_FIELD(u16_t _v_hl_tos);//前三個(gè)字段:版本號(hào)隶校、首部長(zhǎng)度、服務(wù)類型
PACK_STRUCT_FIELD(u16_t _len);//總長(zhǎng)度
PACK_STRUCT_FIELD(u16_t _id);//標(biāo)識(shí)字段
PACK_STRUCT_FIELD(u16_t _offset); // 3位標(biāo)志和13位片偏移字段
#define IP_RF 0x8000//
#define IP_DF 0x4000//不分組標(biāo)識(shí)位掩碼
#define IP_MF 0x2000//后續(xù)有分組到來(lái)標(biāo)識(shí)位掩碼
#define IP_OFFMASK 0x1fff//獲取13位片偏移字段的掩碼
PACK_STRUCT_FIELD(u16_t _ttl_proto);// TTL字段和協(xié)議字段
PACK_STRUCT_FIELD(u16_t _chksum);//首部校驗(yàn)和字段
PACK_STRUCT_FIELD(struct ip_addr src);//源IP地址
PACK_STRUCT_FIELD(struct ip_addr dest);//目的IP地址
} PACK_STRUCT_STRUCT;
注意結(jié)構(gòu)體聲明的時(shí)候定義了幾個(gè)宏定義:IP_RF蛹锰、IP_DF深胳、IP_MF、IP_OFFMASK铜犬,它們是在求與分組相關(guān)兩個(gè)字段時(shí)要用到的掩碼舞终,也可以在結(jié)構(gòu)體的外面進(jìn)行定義,無(wú)影響癣猾。
前面講過(guò)敛劝,從以太網(wǎng)底層進(jìn)來(lái)的數(shù)據(jù)包經(jīng)過(guò)ethernet_input函數(shù)分發(fā)給IP模塊或者ARP模塊,分發(fā)給IP模塊是通過(guò)調(diào)用ip_input函數(shù)完成的纷宇,當(dāng)然在遞交前夸盟,ethernet_input需要將數(shù)據(jù)包去掉以太網(wǎng)頭。現(xiàn)在來(lái)看看數(shù)據(jù)包傳遞給ip_input后像捶,該函數(shù)進(jìn)行了哪些方面的工作上陕。這里我們先不涉及其內(nèi)部關(guān)于DHCP協(xié)議的相關(guān)處理。
第一件事是檢查IP頭部的版本號(hào)拓春,如果該值不為4释簿,則立即丟棄該數(shù)據(jù)包。更高版本的LWIP協(xié)議椗鹈В可以支持IPv6辕万,但這里我們只討論IPv4。接下來(lái)函數(shù)檢查IP數(shù)據(jù)報(bào)頭是否只保存于一個(gè)pbuf中,如果不是 渐尿,也直接丟棄該IP包,這是因?yàn)長(zhǎng)WIP不允許IP數(shù)據(jù)包頭被分裝在不同的pbuf里面矾瑰。同時(shí)砖茸,函數(shù)檢查IP報(bào)頭中的總長(zhǎng)度字段是否大于遞交上來(lái)的數(shù)據(jù)包總長(zhǎng)度,如果是殴穴,則說(shuō)明存在傳輸錯(cuò)誤凉夯,直接丟棄數(shù)據(jù)包。
然后是對(duì)IP數(shù)據(jù)報(bào)頭做校驗(yàn)采幌,該工作是函數(shù)inet_chksum完成的劲够,如果校驗(yàn)不通過(guò)則直接丟棄數(shù)據(jù)包。inet_chksum函數(shù)在后續(xù)有需要時(shí)會(huì)詳細(xì)講解休傍。
接著征绎,需要在這里對(duì)數(shù)據(jù)包進(jìn)行截?cái)嗖僮鳎凑誌P包頭記錄的總長(zhǎng)度字段截取數(shù)據(jù)包磨取,因?yàn)榻?jīng)過(guò)ethernet_input傳遞上來(lái)的數(shù)據(jù)包只被去除了以太網(wǎng)數(shù)據(jù)包頭部人柿,而對(duì)于可能存在的以太網(wǎng)填充字段和一定存在的以太網(wǎng)校驗(yàn)字段(最后一字節(jié))沒(méi)做處理,我們?cè)谶@里對(duì)它們進(jìn)行截?cái)嗝ρ幔玫酵暾麩o(wú)冗余的IP數(shù)據(jù)包凫岖。
然后,函數(shù)檢測(cè)IP數(shù)據(jù)包中的目的IP地址是否與本機(jī)的相符逢净,本機(jī)的IP地址是保存在netif結(jié)構(gòu)體變量中的哥放,一個(gè)系統(tǒng)可能有著多個(gè)網(wǎng)卡設(shè)備,這就意味著它有多個(gè)netif結(jié)構(gòu)體變量分別用于描述這些網(wǎng)卡設(shè)備爹土,也意味著本機(jī)有著多個(gè)IP地址甥雕,這些netif結(jié)構(gòu)體是被連接在netif_list鏈表上的。ip_input函數(shù)會(huì)遍歷netif_list鏈表上的netif結(jié)構(gòu)以找到匹配的IP地址着饥,并記錄該netif結(jié)構(gòu)體變量犀农,也即記錄該網(wǎng)卡。從這點(diǎn)看來(lái)宰掉,在ARP部分內(nèi)容中呵哨,對(duì)于某個(gè)接收到的ARP請(qǐng)求包,也應(yīng)該按照這種方式進(jìn)行遍歷后再給出ARP相應(yīng)更好轨奄,而源代碼并沒(méi)有這樣做孟害,當(dāng)然,這只是個(gè)人意見(jiàn)挪拟。當(dāng)遍歷完成后挨务,如果依舊沒(méi)有得到與匹配的netif結(jié)構(gòu)體變量,這說(shuō)明該數(shù)據(jù)包不是給本機(jī)的,此時(shí)需要對(duì)數(shù)據(jù)包進(jìn)行轉(zhuǎn)發(fā)或者丟棄工作谎柄,這是通過(guò)宏定義IP_FORWARD來(lái)完成的丁侄,這里注意不要對(duì)廣播數(shù)據(jù)包進(jìn)行轉(zhuǎn)發(fā)。
再接下來(lái)朝巫,根據(jù)目標(biāo)IP地址判斷數(shù)據(jù)包是否為廣播或多播IP數(shù)據(jù)包鸿摇,LWIP不對(duì)這些類型的數(shù)據(jù)包進(jìn)行相應(yīng)。
再接下來(lái)的工作可以說(shuō)是ip_input函數(shù)中最復(fù)雜最難理解的部分劈猿,這就是IP分片數(shù)據(jù)包的重裝拙吉,ip_input函數(shù)通過(guò)數(shù)據(jù)包的3位標(biāo)志和13位片偏移字段判斷發(fā)給自己的該IP包是不是分片包,如果是揪荣,則需要將該分片包暫存筷黔,等到接收完所有分片包后,統(tǒng)一將整個(gè)數(shù)據(jù)包遞交給上層應(yīng)用程序仗颈。這是萬(wàn)言難盡得過(guò)程佛舱,先在這里打住,我們?cè)谝院蟮膬?nèi)容里面細(xì)細(xì)討論揽乱。如果是分片包名眉,且不是最后一片,則函數(shù)到這里就返回了凰棉。
終于损拢,能到達(dá)這一步的數(shù)據(jù)包必然是未分片的或經(jīng)過(guò)分片完整重裝后的數(shù)據(jù)包。此時(shí)撒犀,ip_input函數(shù)根據(jù)IP數(shù)據(jù)包頭內(nèi)部的協(xié)議字段判斷該數(shù)據(jù)包應(yīng)該被遞交給哪個(gè)上層協(xié)議福压,并調(diào)用相應(yīng)的函數(shù)遞交數(shù)據(jù)包。是UDP協(xié)議或舞,則調(diào)用udp_input函數(shù)荆姆;是TCP協(xié)議,則調(diào)用tcp_input函數(shù)映凳;是ICMP協(xié)議胆筒,則調(diào)用icmp_input函數(shù);是IGMP協(xié)議诈豌,則調(diào)用igmp_input函數(shù)仆救;如果都不是,則調(diào)用函數(shù)icmp_dest_unreach返回一個(gè)協(xié)議不可達(dá)ICMP數(shù)據(jù)包給源主機(jī)矫渔,同時(shí)刪除數(shù)據(jù)包彤蔽。
寫完收工!