標籤:
本章介紹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結構中的各成員的名字
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVE9ERDkxMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVE9ERDkxMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >
標準的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 得到的結果是全一。正確。
《TCP/IP具體解釋卷2:實現》筆記--IP:網際協議