所謂 ICMP,不過將軍與士卒而已
本文轉(zhuǎn)載自微信公眾號「飛天小牛肉」,作者飛天小牛肉。轉(zhuǎn)載本文請聯(lián)系飛天小牛肉公眾號。
什么是 ICMP 協(xié)議
關(guān)于這點我們在 IP 協(xié)議那篇文章中提過一嘴,IP 協(xié)議作為一種提供不可靠數(shù)據(jù)交付的網(wǎng)絡(luò)層協(xié)議,在傳輸?shù)倪^程中,其 IP 數(shù)據(jù)報可能會發(fā)生丟失、重復(fù)、延遲和亂序等各種情況, 但是 IP 協(xié)議對這些糟糕的情況并不擁有有效的檢測和彌補措施,當(dāng)然更不會將這些結(jié)果通知收發(fā)雙方。
為此,鑒于上述原因,我們在構(gòu)建 IP 網(wǎng)絡(luò)時,就需要特別注意兩點:
- 確認(rèn)網(wǎng)絡(luò)是否能夠正常工作
- 即使診斷出現(xiàn)異常時的原因所在
于是,網(wǎng)際控制報文協(xié)議(Internet Control Message Protocol,ICMP)出現(xiàn)了。
形象來說,IP 協(xié)議就好像一個將軍,而 ICMP 協(xié)議就是他手下的情報員。將軍運籌帷幄于千里之外,而在前線浴血奮戰(zhàn)的士卒們傷亡當(dāng)然也在所難免。
那無法親臨前線的將軍最起碼要知道兩件事情:第一點,我的士卒們是不是按照我指引的方向在前進著,別一陣猛沖發(fā)現(xiàn)沖錯了地方;第二點,我的士卒們傷亡多少,被什么所傷,了解了己方傷亡的原因才好做下一步的戰(zhàn)略部署,總不能死了個不明白。不必多說,這就是情報員 ICMP 該做的事情了。
當(dāng)然了,上述只是打個比方,可能不是很嚴(yán)謹(jǐn),各位知道什么意思就行,不必過于吹毛求疵。
這里我們再用學(xué)術(shù)點的語言來總結(jié)下,ICMP 的主要功能有如下兩點:
1)確認(rèn) IP 數(shù)據(jù)報是否成功送達目標(biāo)地址
2)如果某個 IP 數(shù)據(jù)報因為某種原因未能正常到達目的地,則由 ICMP 負(fù)責(zé)通知具體的原因
ICMP 報文初探
具體的出錯原因是 ICMP 協(xié)議負(fù)責(zé)通知的,這個通知的學(xué)名就是 ICMP 報文,那么 ICMP 報文是由發(fā)送方發(fā)送方發(fā)出的還是由接收方發(fā)出的呢?
都不是。
ICMP 報文是由路由器發(fā)出來的。
舉個例子:主機 A 在不知情的情況下向主機 B 發(fā)送了數(shù)據(jù)包,而主機 B 正在呼呼大睡。主機 A 和主機 B 不在同一個局部網(wǎng)內(nèi),假設(shè)它倆之間會經(jīng)過兩個路由器,看下圖:
眾所周知,除了 IP 地址我們還需要 MAC 地址才能確保數(shù)據(jù)包精準(zhǔn)的找到傳送方向,因此,路由器 2 為了知道主機 B 的 MAC 地址,它會廣播一個 ARP 請求報文,希望獲取到主機 B 的 MAC 地址,而主機 B 都關(guān)機了自然也就無法應(yīng)答這個請求報文了。
為此,路由器 2 會一遍又一遍的重新發(fā)送著 ARP 請求報文,在多次無果后,路由器 2 就會返回一個 ICMP Destination Unreachable 的包給主機 A(關(guān)于 ICMP 報文類型下文會講),通知主機 A,非常遺憾,您發(fā)往主機 B 的包未能成功抵達。
那么,ICMP 報文具體是怎么傳輸給主機 A 的呢?
這個很簡單,TCP/UDP 報文是怎么傳輸?shù)模琁CMP 報文就怎么傳輸。
也就是說,真正的數(shù)據(jù)首先會被加上 ICMP 首部,封裝成 ICMP 報文,然后被 IP 協(xié)議封裝成 IP 數(shù)據(jù)報進行明文傳輸,由 IP 協(xié)議指定源 IP 地址和目的地址。主機 A 收到數(shù)據(jù)后會一層一層解封裝,從而獲得真正的數(shù)據(jù)得知發(fā)生異常的原因,遂大怒一聲:蠢貨主機 B。
ICMP 報文格式
至此,各位已經(jīng)知道了,ICMP 報文是被封裝在 IP 數(shù)據(jù)報里面的,我們來看看下圖:
額,這里好像沒啥好說的,上圖畫的很 Nice ,是我之前考研的時候看 B 站上的王道視頻截下來的,各位看明白上圖,了解 ICMP 報頭有哪些東西,知道類型和代碼這兩個字段很重要就好了,尤其是類型這個,接下來我們先重點講它。
ICMP 報文類型
上文提到了 ICMP Destination Unreachable,也就是目標(biāo)不可達的 ICMP 報文。
ICMP 報文類型大體上可以分為兩種,差錯報文和詢問報文,解釋一下:
所謂查詢報文就是,用于主機進行診斷的查詢消息。
這么學(xué)術(shù)性的文字可能不是很好理解,這樣,咱形象來說,查詢報文其實和通信異常沒啥關(guān)系,查詢報文就好比將軍率領(lǐng)著千軍萬馬來到了一片寂靜的峽谷,正是一個容易被埋伏的地方,將軍不敢貿(mào)然前進,于是派遣幾個情報員前去探明敵情,一有動靜立馬回報。
常見的 ICMP 查詢報文類型有以下幾種:
- 回送應(yīng)答(Echo Reply),對應(yīng) ICMP 報文首部類型字段的值:0
- 回送請求(Echo Request),對應(yīng) ICMP 報文首部類型字段的值:8
而差錯報文就是,用于通知主機出錯的原因。顯然,ICMP 差錯報告報文是伴隨著出錯數(shù)據(jù)產(chǎn)生的。一旦 IP 協(xié)議發(fā)現(xiàn)某個 IP 數(shù)據(jù)報出錯了,首先就會毅然地丟棄出錯的這個 IP 數(shù)據(jù)報,然后發(fā)送 ICMP 差錯報文。
常見的 ICMP 差錯報文類型有以下幾種:
- 目標(biāo)不可達(Destination Unreachable),對應(yīng) ICMP 報文首部類型字段的值:3
- 原點抑制(Source Quench),對應(yīng) ICMP 報文首部類型字段的值:4
- 重定向或改變路由(Redirect),對應(yīng) ICMP 報文首部類型字段的值:5
- 超時(Time Exceeded),對應(yīng) ICMP 報文首部類型字段的值:11
下面詳細(xì)解釋一下這幾個常見的 ICMP 報文類型。
ICMP 回送消息(類型 0、8)
用于進行通信的主機或路由器之間,判斷所發(fā)送的數(shù)據(jù)包是否已經(jīng)成功到達對端的一種消息。
可以向?qū)Χ酥鳈C發(fā)送 ICMP 回送請求的消息(Echo Request,類型 8),也可以接收對端主機發(fā)回來的 ICMP 回送應(yīng)答消息(Echo Reply,類型 0)
圖片
我們常用的 ping 命令就是基于 ICMP 回送消息實現(xiàn)的。
ping 這個單詞源自聲納定位,而這個命令的作用也確實如此,它發(fā)送類型為 0 的 ICMP Echo Request 消息,收到請求的主機則用類型為 8 的 ICMP Echo Reply 消息進行回應(yīng)。ping 就會計算發(fā)送 Requenst 和接收到 Reply 的消息間隔時間,并計算有多少個包被送達,丟失了多少個包等。用戶就可以據(jù)此判斷網(wǎng)絡(luò)大致的情況。
如下圖我們來 ping 一下 Github:
ping 也并不是啥事也沒做,它在 ICMP 報文格式中又添加了兩個字段:標(biāo)識符和序號。這倆其實很好理解:
1)標(biāo)識符用來區(qū)分是哪個應(yīng)用程序發(fā) ICMP 包。
形象來說,將軍派出了兩個情報員,一個用來是了解戰(zhàn)況的,一個是用來搬救兵的,那總得有個標(biāo)識區(qū)分這倆情報員吧。標(biāo)識符就是干這事的。最容易想到的能作為標(biāo)識符的東西,想來也不用我多嘴吧,就是進程的 PID。
2)序號用來確認(rèn)網(wǎng)絡(luò)包是否有丟失。
形象來說,將軍派出了 10 個情報員,給每個情報員都編個號。這樣,如果派出去 10 個,回來 10 個,就說明前方戰(zhàn)況不錯;如果派出去 10 個,一個也沒回來或者就回來 1 個,說明情況不妙啊。
ICMP 目標(biāo)不可達消息(類型 3)
路由器無法將 IP 數(shù)據(jù)報發(fā)送給目標(biāo)地址時,會給發(fā)送端主機返回一個目標(biāo)不可達(Destination Unreachable Message)的 ICMP 消息。
那目標(biāo)不可達有多種可能的原因,比如說網(wǎng)絡(luò)問題、目標(biāo)主機問題等等,所以這個目標(biāo)不可達消息還需要指明不可達的具體原因,這個具體原因就記錄在 ICMP 報頭的代碼字段。
那么這里我們?nèi)匀灰孕熊姶驊?zhàn)的例子來看看常見的目標(biāo)不可達類型的代碼有哪些:
1)前方戰(zhàn)事吃緊,將軍(主機 A)派了一隊士兵回京城找皇上(主機 B)搬救兵,中途情報員快馬加鞭趕到匯報:將軍,我們在途中迷失了方向,找不到京城在哪。這就是網(wǎng)絡(luò)不可達,其代碼為 0
2)假設(shè)士兵們成功回到京城,但是皇上出城了,不在京城,朝廷百官也不敢私自同意出兵救援。這就是主機不可達,到了地方卻沒找到人,其代碼為 1
3)假設(shè)士兵成功找到了京城,但是由于將士們常年在外征戰(zhàn),守城的年輕護衛(wèi)們已經(jīng)不認(rèn)識這些威名赫赫的將士們了,所以需要進城口令證明身份,但是久經(jīng)沙場的將士們一時半會想不起來這些東西,遂無法進城。這就是協(xié)議不可達,其代碼為 2
4)假設(shè)士兵們成功進了城,也成功面見了圣上,但是皇上卻說密偵司告訴他的情報和你們說的不一樣,你們說你們需要的是救兵,而我得到的消息是你們只需要糧草。這就是端口不可達,其代碼為 3
5)假設(shè)士兵們成功求得了救兵,并且獲得了火器十余箱,但是中途山路狹窄,裝火器的馬車太大過不去,為此需要換小一點的馬車,每個馬車裝一點,但是由于火器技術(shù)尚不成熟,考慮安全問題,將軍早就嚴(yán)令禁止把火器分裝。于是乎,浩浩蕩蕩的援兵阻塞在了狹窄的山路。這就是需要進行分片但設(shè)置了不分片位,其代碼為 4
ICMP 重定向消息(類型 5)
說到這個,我們先要明白 IP 協(xié)議或者網(wǎng)絡(luò)層的職責(zé)是什么,就是選擇合適的網(wǎng)間路由和交換結(jié)點, 確保數(shù)據(jù)的及時傳送。
為此,我們總是傾向于基于最短最優(yōu)的路徑進行傳輸。
那么如果路由器發(fā)現(xiàn)發(fā)送端主機使用了某個不是最優(yōu)的路徑發(fā)送數(shù)據(jù),他就會返回一個 ICMP 重定向消息(ICMP Redirect Message)給這個主機,并且,在這個消息中包含了最優(yōu)的路由信息和源數(shù)據(jù)。
寫上癮了哈哈,舉個例子:將軍得知 ICMP 的情報后震怒,派出去搬救兵的領(lǐng)隊竟然帶著十萬救兵在繞彎子,亂臣賊子,將軍趕緊下令誅殺這個領(lǐng)隊并立即走最近的路趕回來。
ICMP 超時消息(類型 11)
IP 包中有一個字段叫做 TTL (Time To Live,生存周期),它的值隨著每經(jīng)過一次路由器就會減 1,直到減到 0 時該 IP 包會被丟棄。
此時,IP 路由器將會發(fā)送一個 ICMP 超時消息(ICMP Time Exceeded Message)給發(fā)送端主機,并通知該包已被丟棄。
形象來說,就是將軍派出去的搬救兵的那隊人馬苦于找不到京城的方向,路途開始帶上的只夠三天的糧草斷盡,全隊飲恨而死。
設(shè)置 IP 包生存周期的主要目的,是為了在路由控制遇到問題發(fā)生循環(huán)狀況時,避免 IP 包無休止地在網(wǎng)絡(luò)上被轉(zhuǎn)發(fā)。
ICMP 的應(yīng)用
其中一個應(yīng)用 ping 命令我們已經(jīng)說過了,它是基于 ICMP 查詢報文的。
還有一個命令,是基于 ICMP 差錯報文的,在 Linux 下這條命令是 traceroute,在 Windows 是 tracert。
大家可能會覺得 ICMP 差錯報文是只有在通信異常的時候才會生成,其實不然,traceroute 命令就是一個例外,它會使用 ICMP 的規(guī)則,故意制造一些能夠產(chǎn)生異常的場景。
traceroute 命令有兩大作用:
1)故意設(shè)置特殊的 TTL,來追蹤去往目的主機上沿途經(jīng)過的路由器。
具體來說,就是發(fā)送端主機會不斷的向接收端主機發(fā)送 UDP 報文,UDP 報文被封裝成 IP 數(shù)據(jù)報,同時將 TTL 從 1 開始按照順序遞增。
比如說,將 TTL 設(shè)置 為 1,那么遇到第一個路由器的時候,這個 IP 數(shù)據(jù)報就會被丟棄,接著返回 ICMP 差錯報文,類型是 ICMP 超時消息。
接下來將 TTL 設(shè)置為 2,第一個路由器過了,遇到第二個路由器時這個 IP 數(shù)據(jù)報就會被丟棄,接著返回ICMP 差錯報文。
......
這樣,traceroute 就拿到了所有路由器 IP。
那到這里其實還有一個問題,怎么知道數(shù)據(jù)到底有沒有到達目的主機呢?
traceroute 是基于 UDP 傳輸?shù)模亲匀皇切枰付ㄒ粋€端口號的,traceroute 會選擇一個不可能的值作為 UDP 的端口號。
這樣,當(dāng)數(shù)據(jù)到達目的主機時,就會發(fā)現(xiàn)端口對不上,于是路由器會產(chǎn)生一份 ICMP 目標(biāo)不可達消息,其代碼是 3,即端口不可達。
當(dāng)發(fā)送端主機接收這份端口不可達的 ICMP 報文時,就知道目的主機成功收到了數(shù)據(jù)。
2)故意設(shè)置不分片,從而確定路徑的最大傳輸單元 MTU。
某些情況下我們并不知道路徑的 MTU 大小,所以我們需要某種手段去獲取 MTU,才能控制發(fā)送的數(shù)據(jù)包的大小。
發(fā)送端主機要做的工作很簡單,就是像往常一樣發(fā)送 IP 數(shù)據(jù)報,但是將 IP 首部的分片禁止標(biāo)志位置為 1。
這樣,如果 IP 數(shù)據(jù)報的長度超過了 MTU,該數(shù)據(jù)報會被路由器直接丟棄,并且給發(fā)送端主機發(fā)送 ICMP 目標(biāo)不可達消息,其代碼為 4,即需要進行分片但設(shè)置了不分片位
這樣,發(fā)送端主機每次收到 ICMP 需要進行分片但設(shè)置了不分片位消息時就減小 IP 數(shù)據(jù)報的長度,直到順利到達目標(biāo)主機。