標籤:style blog http 資料 os 2014
本章介紹IP分組的結構和基本的IP處理過程,包括輸入,轉寄和輸出。顯示了IP層常見的組織形式。
在之前的文章中,我們看到了網路介面如何把到達的IP分組放到IP輸入隊列ipintrq中去,並如何調用一個軟體中斷,如所示:
因為硬體中斷的優先順序比軟體中斷的要高,所以在發生一次軟體中斷之前,有的分組可能會被放到隊列中。在軟體中斷中,ipintr
函數不斷從ipintrq中移走和處理分組,直到對壘為空白。在最終的目的地,IP把分組重裝為資料包,並通過函數調用把該資料包直接
傳給適當的運輸層協議。如果分組沒有到達最後的目的地,並且如果主機被配置成一個路由器,則IP把分組傳給ip_forward。傳輸
協議和ip_forward把要輸出的分組傳給ip_output,由ip_output完成ip首部,選擇輸出介面以及在必要時對分組分區。最終的分組
被傳給合適的網路介面輸出函數。
當產生差錯時,IP丟棄該分組,並在某些條件下向分組的來源站點發出一個差錯報文,這些報文是ICMP的一部分。
1.IP分組
我們把傳輸層協議交給IP的資料稱為報文。典型的報文包含一個傳輸層首部和應用程式資料。所示的傳輸協議時UDP。IP在報
文的首部前加上它自己的首部形成一個資料包。如果在選定的網路中,資料報長度太大,IP就把資料包分裂成幾個分區,每個分區
包含它自己的IP首部和一段原來的資料報的資料。
顯示了IP首部的結構。
包含了IP結構中的各成員的名字
標準的IP首部長度是20個位元組,所以ip_hl必須大於等於5.大於5表示IP選項緊跟在標準首部後,如ip_hl的最大值為15,允許最多
40各位元組的選項。ip_hl是以4位元組為單位計算的。
2.輸入處理:ipintr函數
當介面把分組放到ipintrq上排隊後,通過schednetisr調用一個非強制中斷。當該非強制中斷發生時,如果IP處理過程已經由schednetisr
調度,則核心調用ipintr。在調用ipintr之前,cpu的優先順序被改成splnet。
ipintr是一個大函數,主要分4部分討論:
1.對到達分組驗證
2.選項處理及轉寄
3.分組重裝
4.分用
其中選項處理和分用重裝比較複雜,會在以後作為單獨的章節進行說明。
2.1.驗證把分組從ipintrq中取出,驗證他們的內容,損壞和有差錯的分組被自動丟棄。IP版本:如果網路介面沒有指派IP地址,則必須丟掉所有的IP分組。在ipintr訪問任何IP首部欄位之前,它必須證實ip_v是4.IP首部檢驗和:ipintr把由in_cksum計算出來的檢驗和儲存首部的ip_sum欄位中。一個未被破壞的首部應該具有0檢驗和。如果為非0,則該分組被自動丟棄。下面的章節具體介紹in_cksum。3.位元組順序NTOHS宏將IP首部中的所有16bit的值從網路位元組序轉換成主機位元組序:分組長度(ip_len)、資料報標識符(ip_id)和分區位移(ip_off)。4.分組長度如果分組的邏輯長度(ip_len)比儲存在mbuf中的資料量(m_pkthdr.len)大,並且有些位元組被丟失了,就必須丟棄該分組,如果mbuf比分組大,則去掉多餘的位元組。丟失位元組的一個常見原因是因為資料到達某個沒有或只有很少緩衝的串口裝置,裝置丟棄到達的位元組。多餘位元組可能產生,如在某個乙太網路裝置上,當一個IP分組的大小比乙太網路要求的最小長度還要小時。發送該幀是加上的多餘位元組就在這裡被丟棄。2.2.選項處理和轉寄
接下來調用ip_dooptions來處理IP選項,然後決定分組是否到達它最後的目的地。如果分組沒有到達最後目的地,則Net/3會
嘗試轉寄該分組;如果分組到達最後目的地,就被交付給合適的傳輸層協議。
1.選項處理
ip_dooptions處理選項,如果ip_dooptions返回0,ipintr將繼續處理該分組;否則ip_dooptions通過轉寄或丟棄分組完成對該
分組的處理,ipintr可以處理輸入隊列中的下一個分組。
處理完選項後,ipintr通過把ip首部內的ip_dst與配置的所有本地介面的ip地址比較,以決定分組是否已經到達最終目的地。ipintr
必須考慮與介面相關的幾個廣播位址、一個或多個單播地址以及任意多個多播地址。
2.轉寄
如果ip_dst與所有地址都不匹配,分組還沒有達到最終目的地。如果不準備轉寄,則丟棄分組,否則,ip_forward嘗試把分組路由
到它的最終目的地。
2.3.分組重裝和分用
ipintr函數最後進行分組的重組和分組,在後面的章節會詳細進行介紹。
最後ipintr調用選定protosw結構中的pr_input函數來處理資料報包含的運輸報文,當pr_input返回時,ipintr繼續處理ipintrq中的
下一個分組。
3.IP頭部檢驗:in_cksum函數IP首部校正和的計算方法: 1.把校正和欄位清零。 2.然後對每16位(2位元組)進行二進位反碼求和,反碼求和的意思是先對每16位求和,再將得到的和轉為反碼。 接下來詳細描述反碼求和的步驟:看下面的代碼 基本演算法:
SHORT checksum(USHORT* buffer, int size){ unsigned long cksum = 0; while(size>1) { cksum += *buffer++; size -= sizeof(USHORT); } if(size) { cksum += *(UCHAR*)buffer; } cksum = (cksum>>16) + (cksum&0xffff); cksum += (cksum>>16); return (USHORT)(~cksum);}
參數buffer是指向16位整數的指標,剛開始指向的是IP首部的起始地址,參數size是IP首部的大小。while迴圈是將IP首部的內容以16位為單元加在一起,如果沒有整除(即size還有餘下的不足16位的部分),則加上餘下的部分,此時的cksum就是相加後的結果,這個結果往往超出了16位,因為校正和是16位的,所以要將高16位和計算得到的cksum再加工。 再加工第一步:cksum = (cksum>>16) + (cksum&0xffff); sum>>16是將高16位移位到低16位,sum&0xffff是取出低16位,相加得到新的cksum。 再加工第二步:cksum += (cksum>>16); 第一步相加時很可能會產生進位,因此要再次把進位移到低16位進行相加。 這樣就加工好了,接下來就是取反,並強制轉換為16位,這樣就得到了最終的校正和。 校正和計算出來了,接下來就是該如何校正: 接收方進行校正時,也是對每16位進行二進位反碼求和。接收方計算校正和時的首部與發送方計算校正和時的首部相比,多了一個發送方計算出來的校正和。因此,如果首部在傳輸過程中沒有發生差錯,那麼接收方計算的結果應該為全一,因為接收方計算除校正和以外的部分得到值是校正和的反碼,再加多出來的校正和當然是全一了。 最後對上述過程舉個例子: IP頭: 45 00 00 31 89 F5 00 00 6E 06 00 00(校正欄位) DE B7 45 5D -> 222.183.69.93 C0 A8 00 DC -> 192.168.0.220 計算: 4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4 0003 + 22C4 = 22C7 ~22C7 = DD38 ->即為應填充的校正和 當接受到IP資料包時,要檢查IP頭是否正確,則對IP頭進行檢驗,方法同上: 計算: 4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC 0003 + FFFC = FFFF 得到的結果是全一,正確。