1. ICMP協議概述
- ICMP(Internet Control Message Protocol),因特網控制報文協議。ICMP協議屬於網絡層協議,用於在源主機與路由器之間傳遞控制消息。控制消息對數據報文的傳遞有着重要作用,如:網絡不通、通信超時等消息。
- ICMP協議也是一種無連接的不可靠數據報交付協議,協議本身不提供任何的錯誤檢查與恢復機制。
- ICMP協議的主要功能:
1)差錯通知:反饋數據傳遞過程出現的錯誤的信息至源主機;
2)信息查詢:源主機向目標主機查詢相關信息。
P.S.:ICMP只能搭配IPv4使用,ICMPv6搭配IPv6使用。
2. ICMP報文
2.1 報文格式
(1)ICMP報文格式
類型:報文的類型,表示產生報文的原因,占8bit;
代碼(code):報文的代碼,表示產生報文的具體原因,占8bit;
校驗和:整個ICMP報文的校驗和
(2)ICMP報文封裝
ICMP數據報封裝在IP數據報中,IP數據報封裝在以太網幀中。
2.2 報文類型
ICMP報文分為兩大類:差錯報告報文、查詢報文。
- 差錯報告報文:當目標主機或路由器不能正常處理當前的數據報時,目標主機或路由器會反饋一個差錯報告報文到源主機,向源主機表明出差錯的具體原因。
- 查詢報文:源主機向目標主機發送信息查詢請求,目標主機向源主機作出應答。
2.3 差錯報告報文
差錯報告報文有6中類型:目的不可達、源站抑制、重定向、超時、參數錯誤。
(1)目的不可達
網絡數據報在傳遞過程中出錯,不能到達目標主機,或到達目標主機后無法傳遞至上層協議;此時,路由器或目標主機會反饋一個ICMP目的不可達差錯報文,通知源主機數據發送失敗。
目的不可達報文如下圖所示。
- ICMP首部剩余的4Byte未使用,為全0。
- IP數據報首部包含了源IP地址和目標IP地址,源主機可通過該信息判斷是哪個數據報出現差錯。
- IP數據報數據區域前8Byte包含了傳輸層協議的“port”字段的值,源主機可以將ICMP報文傳遞給對應的上層協議處理。
導致目的不可達的原因有多個,其“code”字段值如下圖所示。LWIP協議棧只實現了前6種。
(2)源站抑制
當出現網絡擁堵,路由器或目標主機會向源主機發送源站抑制報文,同時源主機降低數據報的發送頻率。
源站抑制報文的格式和目的不可達報文相同,但其“code”字段為0。
(3)重定向
源主機剛啟動時,其路由表中只有一個默認路由,所有數據都會發送至默認路由。當默認路由器發現數據是發送給其他路由器時,默認路由器會反饋一個ICMP重定向報文給 源主機,通知源主機更改路由表。下次發送數據時,通過新路由進行發送,從而提高數據發送效率。
P.S.:LWIP協議棧中未對ICMP重定向報文進行處理。
(4)超時
IP數據報首部的“TTL”字段記錄着該數據報的生命值,該數據報每被轉發一次,TTL值減1。當路由器轉發一個TTL為0的數據報時,會丟棄數據報並向源主機反饋一個ICMP超時報文。另外,IP分片重裝未在規定時間完成也會認為它超時。
超時報文的格式和目的不可達報文相同,但其“code”字段有兩個,“code = 0”表示生存時間超時,“code = 1”表示IP數據報分片重裝超時。
(5)參數錯誤
網絡數據報傳輸過程中,會對IP數據報首部進行校驗。若IP首部校驗出錯,則該IP數據報會被丟棄,並向源主機發送ICMP參數錯誤報文。
P.S.:對於攜帶 ICMP 差錯報文的數據報、非第一個分片的分片數據報、具有特殊目的地址的數據報(如環回、多播、廣播),即使是出現了差錯也不會返回對應的差錯報文。
2.4 查詢報文
LWIP協議棧中只實現了ICMP回顯請求報文、回顯應答報文。ping命令使用的是ICMP回顯請求報文、回顯應答報文。ping是一個應用程序,該程序發送一份 ICMP 回顯請求報文給目標主機,並等待目標主機返回 ICMP 回顯應答報文。ping命令執行成功,說明鏈路層、網絡層、傳輸層都能通信正常。
- “標識符”表示ping程序的編號,同一台主機可同時運行多個ping程序。
- “序號”表示回顯請求的編號,每發送一個回顯請求,其值加1。

2.5 報文數據結構
LWIP協議棧中定義了“icmp_echo_hdr”結構體來描述ICMP報文首部的數據結構。同時定義了ICMP報文的“type”、“code”字段的宏,以及對其進行讀取和操作的宏。
P.S.:“icmp_echo_hdr”結構體實際上描述的回顯報文的首部,但由於ICMP各個類型的報文類似,因此可以用其描述其余報文的數據結構。
PACK_STRUCT_BEGIN struct icmp_echo_hdr { PACK_STRUCT_FLD_8(u8_t type); //類型 PACK_STRUCT_FLD_8(u8_t code); //代碼 PACK_STRUCT_FIELD(u16_t chksum); //校驗和 PACK_STRUCT_FIELD(u16_t id); //標志符 PACK_STRUCT_FIELD(u16_t seqno); //序號 } PACK_STRUCT_STRUCT; PACK_STRUCT_END
3. ICMP報文的發送與接收
3.1 ICMP報文的發送
LWIP協議棧中,只實現了“目的不可達報文、超時報文”的發送。實現代碼位置:lwip_2_1_2/src/core/ipv4/icmp.c
(1)發送“目的不可達報文”
調用“icmp_dest_unreach()”函數發送目的不可達報文。當IP層無法向傳輸層傳遞數據報,ip_input()函數將會調用此函數發送“目的不可達報文”;當UDP協議無法向應用層傳遞數據報,udp_input()也將會調用此函數發送“目的不可達報文”。
/** * 功能:發送目的不可達報文。 * 參數: * struct pbuf *p:指向引起ICMP報文的IP數據報; * enum icmp_dur_type t:報文“code”字段的值。 * */ void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
(2)發送“超時報文”
調用“icmp_time_exceeded()”函數發送超時報文。當IP數據報TTL值為0時,ip_forward()函數在轉發數據報時會調用此函數;或者IP數據報分片重裝超時,也將調用此函數發送超時報文。
/** * 功能:發送超時報文。 * 參數: * struct pbuf *p:指向引起ICMP報文的IP數據報; * enum icmp_dur_type t:報文“code”字段的值。 * */ void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
icmp_dest_unreach()、icmp_time_exceeded()函數實際調用此函數完成ICMP報文的發送。
/** * 功能:發送ICMP報文。 * * 參數: * struct pbuf *p:指向引起ICMP報文的IP數據報; * u8_t type:報文“type”字段的值 * u8_t code:報文“code”字段的值。 * */ void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
3.2 ICMP報文的接收
LWIP協議棧中,只對回顯報文進行處理。當IP層收到一個ICMP報文,“ip_input()”函數將調用“icmp_input()”函數對其進行處理(只處理回顯報文)。
實現代碼位置:lwip_2_1_2/src/core/ipv4/icmp.c
/** * 功能:處理接收到的回顯報文,並向源主機發送回顯報文應答。 * * 參數: * struct pbuf *p:指向引起ICMP報文的IP數據報; * struct netif *inp:接收ICMP報文的netif。 * */ void icmp_input(struct pbuf *p, struct netif *inp);
4. 參考資料
[1] 野火《LwIP應用開發實戰指南》。