網際網路控制訊息通訊協定

**網際網路控制訊息協定**（英語：**I**nternet **C**ontrol **M**essage **P**rotocol，縮寫：**ICMP**）是網際網路協定套組的核心協定之一。它用於網際網路協定（IP）中傳送控制訊息，提供可能發生在通訊環境中的各種問題回饋。通過這些資訊，使管理者可以對所發生的問題作出診斷，然後採取適當的措施解決。

ICMP 依靠 IP 來完成它的任務，它是 IP 的主要部分。它與傳輸協定（如 TCP 和 UDP）顯著不同：它一般不用於在兩點間傳輸資料。它通常不由網路程式直接使用，除了 ping 和 traceroute 這兩個特別的例子。
IPv4 中的 ICMP 被稱作 ICMPv4，IPv6 中的 ICMP 則被稱作 ICMPv6。

## 技術細節

ICMP 是在 RFC 792 中定義的網際網路協定套組之一。通常用於返回的錯誤資訊或是分析路由。ICMP 錯誤訊息總是包括了源資料並返回給傳送者。
ICMP 錯誤訊息的例子之一是 TTL 值過期。每個路由器在轉發資料報的時候都會把 IP 包頭中的 TTL 值減 1。如果 TTL 值為 0，「TTL 在傳輸中過期」的訊息將會回報給源位址。
每個 ICMP 訊息都是直接封裝在一個 IP 封包中的，因此，和 UDP 一樣，ICMP 是不可靠的。

雖然 ICMP 是包含在 IP 封包中的，但是對 ICMP 訊息通常會特殊處理，會和一般 IP 數據包的處理不同，而不是作為 IP 的一個子協定來處理。在很多時候，需要去檢視 ICMP 訊息的內容，然後發送適當的錯誤訊息到那個原來產生 IP 數據包的程式，即那個導致 ICMP 訊息被傳送的 IP 數據包。

很多常用的工具是基於 ICMP 訊息的。traceroute 是通過發送包含有特殊的 TTL 的包，然後接收 ICMP 超時訊息和目標不可達訊息來實現的。 ping 則是用 ICMP 的 "Echo request"（類別代碼：8）和 "Echo reply"（類別代碼：0）訊息來實現的。

## ICMP 封包結構

### 報頭

ICMP 報頭從 IP 報頭的第 160 位元開始（IP 首部 20 位元組）（除非使用了 IP 報頭的可選部分）。

* **Type** - ICMP 的類型，標識生成的錯誤封包；
* **Code** - 進一步劃分 ICMP 的類型，該欄位用來尋找產生錯誤的原因.；例如，ICMP 的目標不可達類型可以把這個位設為 1 至 15 等來表示不同的意思。
* **Checksum** - Internet 校驗和（RFC 1071），用於進行錯誤檢查，該校驗和是從 ICMP 頭和以該欄位替換為 0 的數據計算得出的。
* **Rest of Header** - 報頭的其餘部分，四位元組欄位，內容根據 ICMP 類型和代碼而有所不同。

### 填充數據

填充的數據緊接在 ICMP 報頭的後面（以 8 位元為一組）：

* Linux 的 "ping" 工具填充的 ICMP 除了 8 個 8 位元組的報頭以外，預設情況下還另外填充數據使得總大小為 64 位元組。
* Windows 的 "ping.exe" 填充的 ICMP 除了 8 個 8 位元組的報頭以外，預設情況下還另外填充數據使得總大小為 40 位元組。

## 封包類型

### 源站抑制

**源站抑制封包**旨在請求傳送方降低發往路由器或主機的封包傳送速率。在接收的過程中，當接收方沒有足夠的接收緩衝區來處理接收到的封包，或者接收這個封包會導致臨近其本身的緩衝區限制時，就會觸發源站抑制封包。

資料被從一個或一群主機高速地發往網路上的一個路由器，雖然路由器有緩衝機制，但是路由器的緩衝區大小通常（由於實體記憶體有限的原因）被限制。因此，如果路由器的通訊量過大，路由器最終會（由於記憶體耗盡，導致必須丟棄掉接收到的資料報）無法繼續處理超過輸入緩衝區限制的部分資料，直到路由器緩衝佇列有空餘空間可以存放新的資料報。但是由於網路層（Network Layer）缺乏確認訊息（ACK）機制，因此客戶端無法獲知資料是否成功抵達接收方。所以研究者提出了源站抑制這一補救措施來解決這一問題：當路由器發現流入資料速率遠遠高於流出資料速率時，會傳送 ICMP 源站抑制封包給源站，通知源站應該降低其資料傳輸速度或等待一定時間後再嘗試傳送更多資料。當源站接收到 ICMP 源站抑制封包時會減慢資料傳送的速度，或者在再次嘗試傳送資料前等待一定的時間，使得路由器能夠（在處理完當前接收到的資料之後）清空輸入緩衝佇列。

但是因為有研究表明「源站抑制是一種無效的（不公平的）補救措施「，所以路由的源站抑制封包已在 1995 年被 RFC 1812 （頁面存檔備份，存於網際網路檔案館）棄用。此外，（路由）轉發和回應任何形式的源站抑制封包已在 2012 年被 RFC 6633 （頁面存檔備份，存於網際網路檔案館）棄用

其中:

　
: **類型（Type）** 必須設定為 4

　
: **代碼（Code）** 必須設定為 0

　
: **IP 資料報頭部** 和其附加的資料用於傳送端根據回應封包匹配對應的請求封包

### 重新導向

**重新導向** 封包是閘道器發出的，用於要求主機或路由器改變資料報的傳輸路徑的 ICMP 封包。ICMP 重新導向是路由器將路由資訊傳達給主機的機制。這種類型的封包通知主機更新它的路由資訊（請求主機改變其路由）。如果一個主機在通訊時將資料報傳送給了路由器 R1，而 R1 將這個資料報轉發給了另一個路由器 R2，且主機到路由器 R2 之間有一條直連的路徑（也就是說，此主機和路由器 R2 處於同一乙太網路段上），那麼路由器 R1 會傳送一條重新導向封包給主機，來通知它到路由器 R2 可用路徑里有一條更短、更最佳化的路徑。這個主機在接收到這個重新導向封包之後應該改變其路由至這個最佳化版本的路由資訊，來將抵達這個目的地的資料報直接傳送到路由器 R2，並且路由器仍將原始資料報傳送到預期目的地。但是，如果一個資料報攜帶有路由資訊，那麼即使有更加最佳化的路徑，路由器也不會傳送重新導向封包。並且，RFC 1122 指出，重新導向封包應該只由閘道器傳送，而不應該由網際網路主機傳送。

其中:

　
: **類型（Type）** 必須設定為 5.

　
: **代碼（Code）** 指定重新導向的原因，見下表

　
: :

　
: **路由器的 IP 位址（IP address）** 是一個 32 位元的閘道器 IP 位址，該位址指明了該資料報應該被重新導向到的路由器位址。

　
: **激發重新導向封包的資料報 IP 首部及其資料的前 8 位元組（IP header）** 用於收到重新導向封包的主機根據回應封包匹配對應的請求封包，來確定該資料報的目的站位址。

### 逾時

**逾時** 封包是閘道器產生並行送給源站的 ICMP 封包，用於通知源站有資料報因為存活時間遞減至 0 而被此閘道器丟棄。當主機等待資料報分片的過程中逾時而無法重新組裝資料報分片時也會產生該封包。

逾時封包也用於 traceroute 工具來辨識兩個主機之間的路徑上的閘道器。

其中:

　
: **類型（Type）** 必須設定為 11

　
: **代碼（Code）** 指定重逾時的原因，見下表

　
: :

　
: **激發逾時封包的資料報 IP 首部及其資料的前 8 位元組** 這些資訊用於源站根據收到的逾時封包來確定具體哪個資料報已被丟棄。對於高層協定，比如使用者資料報協定 和 傳輸控制協定而言，額外的 8 位元組資料指明了已被丟棄的資料報中的源埠與目的埠。

### 時間戳請求

**時間戳請求** 封包主要用於網際網路機器（包括路由器和主機）之間同步時鐘。起始時間戳是傳送端最後一次改動該資料報的時間戳 (為世界標準時午夜開始計算的毫秒數)。在該類型的封包中，接收時間戳和傳輸時間戳未被使用。

其中:

　
: **類型（Type）** 必須設定為 13

　
: **代碼（Code）** 必須設定為 0

　
: **識別碼（Identifier）** and **序號（Sequence）** 用於在時間戳請求封包和時間戳回答封包之間建立關聯

　
: **起始時間戳（Originate timestamp）** 為世界標準時午夜開始計算的毫秒數。 如果沒有可用的世界標準時參考，則可以將最高有效位設定為 1 以指示這是一個非標準時間值。

### 時間戳回答

**時間戳回答** 封包是對時間戳請求封包的回答封包。 時間戳回答封包由接收到的時間戳請求封包其中的起始時間戳和接收時間戳（回應端主機接收到請求封包並建立時間戳回應封包的時間，單位為毫秒）、傳輸時間戳（時間戳回答封包被傳送出去的時間，單位為毫秒）組成。

其中:

　
: **類型（Type）** 必須設定為 14

　
: **代碼（Code）** 必須設定為 0

　
: **識別碼（Identifier）** 和 **序號（Sequence number）** 用於在時間戳請求封包和時間戳回答封包之間建立關聯。

　
: **起始時間戳（Originate timestamp）** 是傳送端最後一次改動該資料報的時間戳。

　
: **接收時間戳（Receive timestamp）** 是回應端主機接收到請求封包並建立時間戳回應封包的時間，單位為毫秒。

　
: **傳輸時間戳（Transmit timestamp）** 是最後一次修改回應封包並將其傳送出去的時間，單位為毫秒。

　
: 所有的時間戳都是世界標準時午夜起始的毫秒數。如果這個時間不能表示為毫秒或者沒有可用的世界標準時參考值，則可以使用任何格式的時間值並將最高有效位設定為 1 以指示這是一個非標準時間值。

### 位址遮罩請求

**位址遮罩請求** 封包是主機為了得到一個合適的子網路遮罩而傳送到路由器的 ICMP 請求封包。

接收到此請求封包的機器應當傳送一個位址遮罩回答封包給源站。

其中:

　
: **類型（Type）** 必須設定為 17

　
: **代碼（Code）** 必須設定為 0

　
: **位址遮罩（Address mask）** 可以為 0

由於 ICMP 位址遮罩請求可能會被用於嗅探攻擊來收集特定網路的資訊，因此該封包預設情況下被 Cisco IOS 禁用。

### 位址遮罩回答

**位址遮罩回答** 封包攜帶有位址遮罩資訊，用於回答位址遮罩請求封包。

其中:

　
: **類型（Type）** 必須設定為 18

　
: **代碼（Code）** 必須設定為 0

　
: **位址遮罩（Address mask）** 為待回答的位址遮罩

### 源站不可達

**源站不可達** 封包是由主機或入站閘道器用於通知客戶端出於目的站無法連接的封包。這些原因可能包括：物理連接失效（也即網路距離無限大），或指定的位址或埠處於非啟用狀態，或者資料報長度過長而導致必須分片但是 IP 首部指定了「不分片」選項導致無法分片。如果是 TCP 埠不可達，則會返回 TCP RST，而不會返回此封包。如果是 IP 多播的情況，也不會返回此封包。

其中:

　
: **類型（Type）** 必須設定為 3

　
: **代碼（Code）** 欄位用於指示具體導致源站不可達的原因。見下表。

　
: :

　
: **Next-hop MTU** 當需要分片但是 DF (Do not Fragment) 置位的錯誤發生時，包含了下一跳網路的 MTU 的值。

　
: **IP header** 用於源站根據收到的源站不可達封包來確定具體哪個資料報引起了源站不可達錯誤。

## 參考

## 外部連結

* RFC 792, _Internet Control Message Protocol_
* ICMP Sequence Diagram（頁面存檔備份，存於網際網路檔案館）
* RFC 1122, _Requirements for Internet Hosts -- Communication Layers_
* RFC 1716, _Towards Requirements for IP Router_
* RFC 1812, _Requirements for IP Version 4 Routers_
* RFC 6633, _Deprecation of ICMP Source Quench Messages_
* Filtering ICMP on firewalls
* IANA（頁面存檔備份，存於網際網路檔案館）