lwip ---(十二)ICMP處理

  目前,IP層的東西基本講解完,數據包的發送或分片發送沒有具體涉及到。數據包的發送,與上層協議密切相關,即傳輸層,後面的內容就是討論傳輸層的東西了。這裏先講解傳輸層協議中比較簡單的ICMP協議。ICMP(Internet Control Message Protocol)是Internet控制報文協議,用於在IP主機、路由器之間傳遞控制消息。控制消息是指網絡通不通、主機是否可達、路由是否可用等網絡本身的消息。這些控制消息雖然並不傳輸用戶數據,但是對於用戶數據的傳遞起着重要的作用。

  在以前講解IP層ip_input函數時,已經三次涉及到了ICMP的東西,第一次在數據包轉發過程中,需要將數據包的TTL值減1,若此時TTL值變爲0則用icmp_time_exceeded函數向源主機返回一份超時ICMP信息;還有兩次是ip_input函數通過IP報文頭部的協議字段值判斷該數據包是交給哪個上層協議的,若是ICMP協議,則調用icmp_input函數若沒有一個協議能接受這個數據包,則調用icmp_dest_unreach函數向源主機返回一個協議不可達ICMP差錯控制包。這裏先講解icmp_time_exceededicmp_dest_unreach函數是怎樣發送ICMP信息包的。

  先來看看ICMP報文的格式,所有ICMP報文的前四個字節都是一樣的,分別爲1字節的類型字段1字節的代碼字段2字節的校驗和字段。校驗和字段的計算覆蓋整個ICMP報文。類型字段和代碼字段唯一確定了該ICMP報文屬於那種類型:如回顯超時時間戮請求等等,儘管各種類型的ICMP報文結構通常是不相同的,但它們開始的四個字節是相同的。ICMP報文從大的方面來說可以分爲ICMP查詢報文和ICMP差錯報文。ICMP查詢報文包括ICMP回顯應答、回顯請求、時間戮請求、地址掩碼請求等等類型LWIP只實現了ICMP回顯應答;ICMP差錯報文有目的不可達、超時、重定向等等類型LWIP只實現了目的不可達、超時兩項ICMP處理功能。這裏先講解目的不可達和超時兩種類型的ICMP處理。

  目的不可達超時兩種ICMP報文均屬於ICMP差錯報文,協議中規定,ICMP差錯報文應始終包含產生ICMP差錯報文的IP數據報的IP首部和數據前8個字節。所以,目的不可達和超時這兩種ICMP報文均有下面的報文結構,這個結構與前面所述完全相符,不解釋了。
CMP處理《LwIP協議棧源碼詳解——TCP/IP協議的實現》

  先講與icmp_dest_unreach函數所描述的目的不可達差錯報文。該報文的類型字段值應爲3,代碼字段值應爲(0-15),目前LWIP只支持(0-5),分別表示網絡不可達、主機不可達、協議不可達、端口不可達、需進行分片但設置了不分片位、源站選擇失敗。很明顯,在ip_input函數找不到將該數據包交到哪個上層協議時,應該產生一個協議不可達的差錯報文,即代碼字段值爲2。函數原型如下,輸入參數爲接收到的目標不可達的IP數據包和應該用於填充ICMP頭部代碼字段的值。

void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)

  現在來看看這個函數做了哪些工作,首先爲要發送數去的ICMP數據包申請一個pbuf緩衝區,這個緩衝區的長度爲上圖所述結構的長度與一個IP數據報頭大小之和,之所以要多申請IP數據報頭大小的空間是爲了當該ICMP數據包被遞交給IP層發送時,IP層不需要再去申請一個數據報頭來封裝該ICMP數據包,而是直接在已經申請好的報頭中填入IP頭部數據,注意這裏申請好的pbuf的payload指針是指向ICMP數據報頭處的。接下來,函數填寫ICMP數據包的相關字段,將類型段填充爲3,代碼段爲輸入參數t,同時將不可達的IP數據包的IP報頭和數據前8個字節拷貝到ICMP數據包相應字段,最後計算校驗和字段的值,然後調用ip_output函數將組裝好的ICMP包發送出去。

  ip_output函數通過調用ip_output_if函數完成數據包的發送,它在調用ip_output_if函數前,要先根據發送數據包的目的IP地址找到相應的發送網絡接口結構,並將該結構作爲調用ip_output_if函數的參數ip_output_if函數主要是填充IP報頭各個字段的值,然後調用netif->output函數將封裝好的IP數據包發送出去。

  icmp_time_exceeded函數用於產生一個超時類型的ICMP報文。該類型的報文與上面所述的報文有完全相同的報文結構,但類型字段值應爲11,代碼字段應爲0或1,分別代表傳輸期間生存時間超時和數據組裝期間生存時間超時。對於後者,在討論數據包重組時,我們知道每個ip_reassdata結構體中都有個時間字段,當指定的時間到達而數據包還未被組裝完畢,則內核會將該ip_reassdata結構相關的所有分片數據全部刪除,並向源主機發送一個代碼字段爲1的超時ICMP報文。

  從代碼流程和內容來看,icmp_time_exceeded函數和icmp_dest_unreach函數完全一樣,只是在填充ICMP報文的類型和代碼字段使用了不同的值,這裏不再贅述。

  接下來該討論ICMP查詢報文了,這部分在整個產品的設計調試過程中顯示出非常重要的作用。即Ping命令,它與ICMP回顯應答、請求報文密切相關

  這小段來自於協議:“Ping”這個名字源於聲納定位操作目的是爲了測試另一臺主機是否可達該程序發送一份 ICMP回顯請求報文給主機,並等待返回ICMP回顯應答。一般來說,如果不能Ping到某臺主機,那麼就不能 Telnet或者FTP到那臺主機。反過來,如果不能Telnet到某臺主機,那麼通常可以用Ping程序來確定問題出在哪裏。Ping程序還能測出到這臺主機的往返時間,以表明該主機離我們有“多遠”。同時,Ping程序也能在數據包的路由過程中記錄下路由路徑。

  ICMP回顯請求和回顯應答報文格式如下圖,回顯應答的類型值爲0,回顯請求類型值爲8,二者代碼字段值均爲0,Unix系統在實現ping程序時是把ICMP報文中的標識符字段置成發送進程的ID號。這樣即使在同一臺主機上同時運行了多個ping程序實例,ping程序也可以識別出返回的信息。序列號從0開始,每發送一次新的回顯請求就加 1。

ICMP處理《LwIP協議棧源碼詳解——TCP/IP協議的實現》

  icmp_input函數處理接收到的ICMP數據包,並根據包類型做相應的處理。目前LWIP只能處理ICMP回顯請求包,對其他類型的ICMP包不做響應,這在嵌入式產品中是足夠用了的。對於ICMP回顯請求,icmp_input需要生成一個回顯應答報文返回給源主機

  來看看icmp_input函數做了哪些工作。首先將傳進來的數據包pbuf的payload指針調整爲指向ICMP頭部,並判斷ICMP頭部長度是否小於4個字節,若是,則說明是個錯誤的ICMP數據包,該包被丟棄。對於正確的ICMP包,函數根據其頭部類型字段的值判斷該做什麼樣的處理。當前版本只實現了對ICMP回顯請求的相關響應操作。

  當前的數據包爲ICMP回顯請求,則函數繼續判斷該數據包是否爲廣播或者多播包,對這兩種數據包不做處理;接下來判斷該數據包大小是否小於ICMP回顯請求頭部長度(如上圖所示),是則丟棄數據包;接下來函數爲這個數據包申請IP報頭和以太網幀頭內存空間,成功後將該ICMP包類型字段變爲0,從新計算校驗和,並將IP報頭的源IP地址和目的IP地址交換位置,最後將整個數據包利用ip_output_if函數將數據包發送出去。ICMP回顯應答將回顯請求中的數據原樣返回給源主機,源主機在收到回顯應答後,通過處理回顯應答中的數據可以得到相關信息,如計算往返時間等。

  ICMP就是這麼多了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章