網路協議 5 - ICMP 與 ping:投石問路的偵察兵
日常開發中,我們經常會碰到查詢網路是否暢通以及域名對應 IP 地址等小需求,這時候用的最多的應該就是 ping 命令了。 那你知道 ping 命令是怎麼工作的嗎?今天,我們就來一起認識下 ping 命令及其對應的 ICMP 協議。
ICMP 協議
ICMP 全稱 Internet Control Message Protocol,指網際網路控制報文協議。
網路本身是不可靠的,資料包在傳輸過程中,可能會發生很多突發事件並導致資料傳輸失敗。而網路層的 IP 協議是一個無連線的協議,它不會處理網路層的故障,因此,我們需要其它的協議,在資料包傳輸出現故障時,能將故障資訊傳回來,這樣才能對應處理相關問題。
就像在電視劇裡看到的古代戰爭一樣,打仗的時候需要通過斥候來傳遞戰局情況,進而更好的控制戰局。而 ICMP 報文在網路世界中就充當“斥候”這樣的角色。
ICMP 報文是封裝在 IP 包裡面的。因為傳輸指令的時候,肯定需要源地址和目標地址。它本身格式非常簡單,如下圖:
ICMP 報文有很多的型別,不同的型別有不同的程式碼,最常用的型別是主動請求,程式碼為 8,主動請求的應答,程式碼為 0。從大的方面看可以分為 查詢報文型別和差錯報文型別。
查詢報文型別
我們經常在電視劇裡聽到這樣的話:來人,前方戰事如何?斥候回來沒?一有情況,立刻通報。
類似這種主帥發起,主動檢視敵情的情況,就對應著 ICMP的查詢報文型別
對 ping 的主動請求,進行網路抓包,稱為 ICMP ECHO REQUEST。同理,主動請求的回覆,稱為ICMP ECHO REPLY。比起原生的 ICMP,這裡面多了兩個欄位,一個是識別符號,另一個是序號。這不難理解,大帥派出去兩隊斥候,一隊是找誰要的,一隊是偵查戰況的,要有個標識才能區分。
另一方面,派出去的斥候,都要編個號。如果派出去 10 個,回來 10 個,就說明前方戰況不錯。如果派出去 10 個,回來 2 個,就說明情況可能不妙。
在選項資料中,ping 還會存放傳送請求的時間值,來計算往返時間,說明路程的長短。
差錯報文型別
差錯報文主要是用來將傳送的出錯報文相關資訊返回到源裝置,以供源裝置確定如果更好的重發失敗的資料包。
還是拿我們的“大帥”舉例。
當主帥正在大帳中看地圖,思考戰事時,外面的小兵突然喊到:大帥,不好啦,張將軍遭遇埋伏,全軍覆沒了。
這種是異常情況發起的,來報告發生了不好的事情,對應 ICMP 的差錯報文。
差錯報文有以下常用的型別:
- 3:終點不可達
- 4:源抑制
- 5:重定向
- 11:超時
第一種情況終點不可達。小兵報告,大帥,送給張將軍的糧草沒有送到。
那大帥肯定會問,為啥沒有送到?這就對應 ICMP 中的以下程式碼了。
- 網路不可達程式碼:0
- 主機不可達程式碼:1
- 協議不可達:2
- 埠不可達:3
- 需要進行分片但設定了不分片:4
具體的場景就像這樣:
- 網路不可達:大帥,找不到地方
- 主機不可達:大帥,找到地方,沒找到張將軍
- 協議不可達:大帥,找到地方,也找到人了,但是口令沒對上。
- 埠不可達:大帥,找到地方,找到了人,也對上了口令,但事情沒對上。我去送糧草,人家說在等救兵。
- 需要進行分片但設定不分片:大帥,走到一半,山路狹窄,想換瞎扯,但是出發前你下令嚴禁換小車,就沒辦法送到了。
第二種是源站抑制。也就是讓源站放慢傳送速度(小兵:大帥,糧草送的太多了吃不完,你可以慢點送)。
第三種是時間超時。也就是超過網路包的生存時間還是沒到目的地(大帥,送糧草的人都把糧食吃完了,還沒到地方,已經餓死了)。
第四種是路由重定向。也就是下次發給另一個路由器(大帥,上次送糧草的人本來只要走大王村,一公里就到了,結果非要繞道張家界,多了五公里,下次記得走大王村)。
差錯報文的結構相對複雜一些。除了前面還是 IP,ICMP 的前 8 個位元組不變,後面則跟上出錯的那個 IP 包的 IP 頭和 IP 正文的前 8 個位元組。
而且這類斥候特別盡責,不但位元組返回來報信,還把一部分遺物帶回來。
- 斥候:大帥,張將軍已經戰死沙場,這是他的印信和佩劍。
- 大帥:張將軍是怎麼死的(可以檢視 ICMP 的前 8 位元組)?沒錯,這是張將軍的劍(IP 資料包的頭及正文前 8 位元組)。
ping:查詢報文型別的使用
接下來,我們重點來看 ping 命令的傳送和接收過程。
假定主機 A 的 IP 地址是 192.168.1.1,主機 B 的 IP 地址是 192.168.1.2,它們都在同一個子網。那麼,當在主機 A 上執行“ping 192.168.1.2” 後,會發生什麼呢?
- 源主機構建 ICMP 請求資料包。這個資料包內包含多個欄位。最重要的有兩個,一個是型別欄位,對應請求資料包而言,該欄位為 8。另一個是順序號,主要用於區分連續 ping 的時候發出的多個數據包。每發出一個請求資料包,順序號會自動加 1.為了能夠計算往返時間 RTT,它會在報文的資料部分插入傳送時間。
- IP 層構建 IP 資料包。ICMP 協議將資料包連同目標 IP 一起交給 IP 層,IP 層將以 192.168.1.2 作為目的地址,本機 IP 地址作為源地址,加上其他控制資訊,構建一個 IP 資料包。
- 加入 MAC 頭。找到 192.168.1.2 對應的 MAC 地址,附加上一些控制資訊,依據乙太網的介質訪問規則,將它們傳送出去。
主機 B 收到資料幀後,會進行如下步驟:
- 檢查 MAC 地址,丟棄或接收資料幀,提取 IP 資料包。檢查資料包目的 MAC 地址,並與本機 MAC 地址對比。如符合,就接收資料幀,否則就丟棄。接收後檢查資料幀,將 IP 資料包從幀中提取處理,交給本機的 IP 層。
- IP 層檢查IP。檢查完成後,提取有用的資訊交給 ICMP 協議。
- 構建 ICMP 應答包。應答資料包的型別欄位為 0,順序號為接收到的請求資料包中的順序號。
- 將應答資料包發給主機 A。
在規定的時間內,源主機如果沒有接到 ICMP 的應答包,則說明目標主機不可達。
如果接收到了應打包,則說明目標主機可達。此時,源主機會檢測時間延遲。就是用當前時刻減該資料包從源主機發出去的時刻。
當然,這只是最簡單的,同個區域網的情況。如果跨網段的話,還會涉及閘道器的轉發、路由器的轉發等。
可以看出,ping 命令是使用了 ICMP 裡面的 ECHO REQUEST 和 ECHO REPLY 型別。
那其它型別呢?是不是隻有真正遇到錯誤的時候,才能收到?答案是否定的。有一個 Traceroute 命令,它會使用 ICMP 的規則,故意製造一些能夠產生錯誤的場景。
Traceroute:差錯報文型別的使用
Traceroute 命令有兩個比較常用的功能。
第一個功能:
通過設定特殊的 TTL,追蹤去往目的地時經過的路由器
Traceroute 的引數執行某個目的 IP 地址,會發送一個 UDP 的資料包。
將 TTL 設定成 1 時,表示這個資料包的 MP 為 1,碰到第一個“攔路虎”(通常是路由器或一個其它型別的關卡)就會陣亡了,然後就會返回一個 ICMP 包,這個包就是 網路差錯包,型別是時間超時。
通過差錯包,我們就能得到資料包到第一個關卡時花費的時間及其每個關卡的 IP 地址(有的主機不會響應 ICMP,所以會出現請求時全是 * 的情況)。
那怎麼知道 UDP 有沒有到達目的主機呢?Traceroute 程式會發送一份 UDP 資料包給目的主機,但它會選擇一個不可能的值作為 UDP 埠號(大於30000)。當該資料報到達目的主機時,由於找不到對應埠號,所以會返回一個“埠不可達”的錯誤報文。這樣,我們就知道 UDP 是否到達主機了。
第二個功能:
設定資料包不分片,確定路徑的 MTU
傳送分組,並設定“不分片”標誌。傳送的第一個分組的長度正好與出口的 MTU 相等。如果中間遇到窄的關卡就會被卡主,返回 ICMP 網路差錯包,型別是“需要進行分片但設定了不分片”。就這樣,每次收到ICMP“不能分片”差錯時就減小分組的長度,從而確定整個路徑中的 MTU。
總結
- ICMP 相當於網路世界的偵察兵。常用的有兩種型別,主動探查的查詢報文和異常報告的差錯報文。
- ping 命令使用查詢報文,Traceroute 命令使用差錯報文。
參考:
- 劉超-趣談網路協議系列課;