ICMP 原理与报文 — 互联网控制报文协议

ICMP 报文格式、Type/Code 全表、ping 与 traceroute 原理、PMTUD 与重定向、ICMPv6 差异 / 网络层 / Protocol=1 / RFC 792

先建立直觉

先别管字段和协议号,用一个画面记住 ICMP 是干嘛的。

想象 IP 是一个闷头送货、从不说话的快递员:包裹送到了他不报喜,送不到他也不吭声,收件人永远不知道"为什么没收到"。ICMP 就是贴在包裹上的那张退件通知单——通知单上会写清楚"查无此人 / 电话空号 / 地址不存在",让你一眼看出问题出在哪一类。

再补一刀:ICMP 还顺手提供了 ping(“在吗?在。")和 traceroute(“你到我家要经过哪几站?")这两把全网工程师最常用的"体检刀”。所以一句话:没有 ICMP,IP 网络就是一个黑灯瞎火、坏了也不报修的机器

它解决什么问题

IP 是尽力而为的:包被丢弃时不通知任何人。这在实际网络中不可接受,会带来三类问题:

  1. 故障不可见:主机不知道包为什么没到——是目标关机?路由缺失?防火墙拦截?还是端口没人监听?ICMP 用 Type 3 Destination Unreachable 的不同 Code 精确区分这些原因。
  2. 无法探测与诊断:需要一种轻量方式验证"某个 IP 是否可达"“路径经过哪些跳”。ICMP 提供 Echo Request/Reply(Type 8/0)Time Exceeded(Type 11),直接催生了 pingtraceroute
  3. 参数无法协商:路径中最小 MTU 是多少?下一跳应该走谁更优?ICMP 用 Type 3 Code 4(分片需求) 支撑 PMTUD,用 Type 5(重定向) 提示更优下一跳。

注意 ICMP 的边界:它只做报告,不做纠正。ICMP 告诉你"包丢了”,但不会替你重传——那是 TCP 的职责。ICMP 本身也是不可靠的,其报文同样可能丢失。

工作流程(简化版)

两类报文,两种触发方式

类别触发方式代表 Type特点
差错报告类被动产生——路由器/主机在处理 IP 包时遇到问题,向源地址回送3(不可达)、4(源抑制,已废弃)、5(重定向)、11(超时)、12(参数问题)携带原始 IP 头 + 前 8 字节数据作为"案发现场"
查询/信息类主动发起——诊断工具或主机主动询问0/8(Echo Reply/Request)、13/14(时间戳)、9/10(路由器通告/请求)成对出现,用 Identifier + Sequence 配对

差错报文是怎么产生并送回去的

  1. 路由器 R 收到一个 IP 包,处理时发现问题(无路由、TTL 归零、包过大且 DF=1…);
  2. R 丢弃该包;
  3. R 构造一个 ICMP 差错报文,源 IP = R 的接口地址,目的 IP = 原包的源地址
  4. 报文体中复制原始 IP 头 + 原始数据的前 8 字节
  5. 沿反向路径送回源主机;
  6. 源主机内核解析后,根据那 8 字节中的端口号定位到具体的套接字,向应用层报错(如 connect() 返回 EHOSTUNREACH)。

为什么恰好是 8 字节? 因为 TCP/UDP 头部的前 8 字节包含源端口(2)+目的端口(2)+(UDP 的长度/校验和,或 TCP 的序号),足以唯一定位一条连接。RFC 1812 建议路由器尽可能多带(至少 576 字节的 IP 包),RFC 4884 进一步定义了多部分扩展。

抑制规则(RFC 1122 §3.2.2)——防止"报错风暴"

为避免 ICMP 风暴与放大攻击,以下情况绝不产生 ICMP 差错报文:

  1. ICMP 差错报文本身(否则两台设备会无限互相报错);
  2. 对目的地址为广播/组播的数据报;
  3. 链路层广播帧承载的数据报;
  4. 非第一个分片(Fragment Offset ≠ 0);
  5. 源地址非单播(如 0.0.0.0、环回、广播、组播)的数据报。

此外,多数实现对 ICMP 差错报文做速率限制(Linux 通过 net.ipv4.icmp_ratelimiticmp_ratemask 控制)——这正是 traceroute 中间跳常出现 * * * 的原因之一。

报文/头部长什么样

通用格式

看这张表前先记住:ICMP 报文紧跟在 IP 头之后,最前面 4 字节是固定结构;三个核心字段的中文全称是——Type(类型)、Code(代码)、Checksum(校验和)。“类型"定大类,“代码"定细因,校验和覆盖整个 ICMP 报文。

1
2
3
4
5
6
7
8
9
 0 1 2 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 其余 4 字节(随 Type 变化) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据部分 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段长度含义
Type1 B报文大类
Code1 B同一 Type 下的细分原因
Checksum2 B覆盖整个 ICMP 报文(头 + 数据)的反码和。注意:与 TCP/UDP 不同,ICMPv4 校验和不含 IP 伪首部ICMPv6 则包含伪首部
其余 4 字节4 BEcho 类:Identifier(2) + Sequence(2);Type 3 Code 4:未用(2) + Next-Hop MTU(2);Type 5:Gateway Address(4);Type 11:全 0
数据部分可变查询类:任意载荷(ping 常填时间戳与填充模式);差错类:原始 IP 头 + 前 8 字节数据

Echo Request / Reply(Type 8 / 0)——ping 的核心

看这张表前先记住:这是 ping 用的报文。Identifier(标识符)用来区分"是哪个 ping 进程发的”,Sequence Number(序列号)每发一个包就 +1、用来算丢包和乱序,Data(数据)里常塞一个时间戳用来算往返时延。

字段说明
TypeRequest = 8,Reply = 0
Code恒为 0
Identifier区分不同的 ping 进程(Linux 通常用 PID),使多个 ping 并行不串扰
Sequence Number每发一个包递增,用于计算丢包与乱序
Data载荷。Linux ping 默认 56 字节(+8 字节 ICMP 头 = 64 字节 ICMP 报文,+20 IP 头 = 84 字节 IP 包),前 8 字节常放发送时间戳用于算 RTT

RTT 是怎么算出来的:发送方把时间戳写进载荷,对端原样回显,收到 Reply 后用当前时间减去载荷里的时间戳即得往返时延。因此不依赖两端时钟同步。

ICMPv4 Type / Code 完整对照表

看这张表前先记住:读懂这张表只要抓两列——Type(类型)是"大类别”,Code(代码)是"这个类别下的具体原因"。排错时遇到一个 ICMP 差错,先找 Type 再找 Code,就能锁定故障大类。加粗的行是排错最高频的几种。

Type名称Code含义常见场景
0Echo Reply0回显应答ping 成功
3Destination Unreachable0Net Unreachable路由器无到达该网段的路由
1Host Unreachable最后一跳路由器ARP 解析不到目标主机
2Protocol Unreachable目标主机不支持该上层协议
3Port Unreachable目标主机该 UDP 端口无监听(traceroute 判定终点、UDP 探活的关键)
4Fragmentation Needed and DF Set包过大且禁止分片,携带 Next-Hop MTU → PMTUD 核心
5Source Route Failed源路由不可用
6/7Dest Network/Host Unknown目的网络/主机未知
9/10Comm. with Dest Net/Host Administratively Prohibited管理策略禁止
13Communication Administratively Prohibited被防火墙/ACL 明确拒绝(相比静默丢弃,这是"礼貌拒绝")
4Source Quench0源抑制(拥塞)已废弃(RFC 6633),由 ECN 与 TCP 拥塞控制取代
5Redirect0/1/2/3网络/主机/TOS网络/TOS主机重定向同网段内提示更优下一跳;安全风险高,常关闭
8Echo Request0回显请求ping 发出的包
9 / 10Router Advertisement / Solicitation0路由器发现(RFC 1256)少用,已被 DHCP/IPv6 RA 取代
11Time Exceeded0TTL Exceeded in Transittraceroute 原理;也指示路由环路
1Fragment Reassembly Time Exceeded分片重组超时(默认 30 秒未收齐)
12Parameter Problem0/1/2IP 头字段错误 / 缺少必需选项 / 长度错误头部格式非法
13 / 14Timestamp Request / Reply0时间戳查询已少用,且可泄露系统时间,常被禁
17 / 18Address Mask Request / Reply0掩码查询已废弃,可被用于网络侦察

ICMPv6 关键 Type(RFC 4443 及相关)

看这张表前先记住:ICMPv6 用最高位区分类别——Type 0–127 是差错报文,128–255 是信息报文。另外它一人身兼三职:把 IPv4 里 ARP、IGMP、ICMP 的活儿全揽了。

Type名称说明
1Destination UnreachableCode:0 无路由,1 管理禁止,3 地址不可达,4 端口不可达
2Packet Too BigIPv6 PMTUD 的唯一依靠(IPv6 中间不分片)
3Time ExceededCode 0 跳数超限,1 分片重组超时
4Parameter Problem头部/扩展头字段问题
128 / 129Echo Request / Replyping6
130 / 131 / 132MLD Query / Report / Done取代 IGMP
133 / 134Router Solicitation / Advertisement路由器发现、SLAAC 前缀通告
135 / 136Neighbor Solicitation / Advertisement取代 ARP,兼做 DAD
137Redirect重定向
143MLDv2 ReportMLDv2 组播成员报告

关键结论:ICMPv6 承担了 IPv4 中由 ARP、IGMP、ICMP 三个协议分担的职责。在 IPv6 网络中绝不能全量丢弃 ICMPv6,否则地址解析、地址自动配置、组播、PMTUD 全部失效(RFC 4890 给出了推荐的过滤策略)。

交互时序

ping 与三种失败路径

一句话看懂这张图:上面绿色是"正常 ping 通"——请求方发 Echo Request、目标原样回显、请求方用载荷里的时间戳算 RTT;下面三块红色分别是"路由器无路由"“最后一跳 ARP 不到"“目标静默丢弃"三种失败,注意前两种会收到明确 ICMP 差错、第三种则什么都没有。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sequenceDiagram
    autonumber
    participant H as 主机A
    participant R as 路由器
    participant B as 主机B

    rect rgb(235,245,235)
    Note over H,B: 成功路径
    H->>R: ICMP Echo Request(Type=8, ID=1234, Seq=1, 载荷含时间戳)
    R->>B: 转发(TTL−1)
    B-->>R: ICMP Echo Reply(Type=0, ID=1234, Seq=1, 原样回显载荷)
    R-->>H: 转发
    Note over H: RTT = 当前时间 − 载荷中的时间戳
    end

    rect rgb(250,238,238)
    Note over H,B: 失败路径①:路由器无路由
    H->>R: Echo Request(目的 203.0.113.9)
    R-->>H: ICMP Type 3 Code 0(Net Unreachable)<br/>携带原始IP头+8字节
    end

    rect rgb(250,238,238)
    Note over H,B: 失败路径②:最后一跳 ARP 不到目标
    H->>R: Echo Request
    Note over R: ARP 请求 B 无应答
    R-->>H: ICMP Type 3 Code 1(Host Unreachable)
    end

    rect rgb(250,238,238)
    Note over H,B: 失败路径③:目标存在但静默丢弃
    H->>R: Echo Request
    R->>B: 转发
    Note over B: 防火墙 DROP(无任何回应)
    Note over H: 超时 → "Request timed out"<br/>(区别于收到明确差错报文)
    end

traceroute 逐跳探测原理

一句话看懂这张图:traceroute 的核心招数是"每轮把 TTL 加 1”——TTL 归零的那台路由器就会回 ICMP Type 11(Time Exceeded),从而暴露自己是第几跳;到了最后一跳时,UDP 高端口无人监听→Type 3 Code 3、ICMP 模式→Echo Reply、TCP 模式→SYN-ACK/RST,三种方式都用来"判定终点”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
sequenceDiagram
    autonumber
    participant H as 主机A
    participant R1 as 路由器R1
    participant R2 as 路由器R2
    participant D as 目标D

    Note over H: 第1轮:TTL=1
    H->>R1: 探测包(UDP 高端口 / ICMP Echo / TCP SYN)TTL=1
    Note over R1: TTL 1→0
    R1-->>H: ICMP Type 11 Code 0(Time Exceeded)<br/>源IP = R1 → 得到第1跳

    Note over H: 第2轮:TTL=2
    H->>R1: TTL=2
    R1->>R2: TTL 2→1,转发
    Note over R2: TTL 1→0
    R2-->>H: ICMP Type 11 Code 0,源IP = R2 → 得到第2跳

    Note over H: 第3轮:TTL=3
    H->>R1: TTL=3
    R1->>R2: 转发
    R2->>D: 到达目标
    alt UDP 模式(Linux 默认,探测高端口 33434+)
        D-->>H: ICMP Type 3 Code 3(Port Unreachable)→ 判定终点
    else ICMP 模式(Windows tracert 默认)
        D-->>H: ICMP Type 0(Echo Reply)→ 判定终点
    else TCP 模式(-T,最贴近真实业务路径)
        D-->>H: TCP SYN-ACK 或 RST → 判定终点
    end
    Note over H: 每跳默认发 3 个探测包,输出 3 个 RTT

PMTUD 路径 MTU 发现

一句话看懂这张图:发送方发一个"禁止分片(DF=1)“的大包,中间某路由器出口 MTU 更小、装不下又不敢分片,只能丢包并回一条 ICMP Type 3 Code 4(带上 Next-Hop MTU);发送方据此把包改小重传。最右边红框提醒:如果这条 ICMP 被防火墙丢了,就变成"小包通、大包永久超时"的 MTU 黑洞。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
sequenceDiagram
    participant H as 发送方
    participant R1 as R1(MTU 1500)
    participant R2 as R2(出口 MTU 1400)
    participant D as 目标

    H->>R1: IP包 1500字节,DF=1
    R1->>R2: 转发
    Note over R2: 出接口 MTU=1400 < 1500 且 DF=1<br/>→ 丢弃
    R2-->>H: ICMP Type 3 Code 4<br/>Next-Hop MTU = 1400
    Note over H: 更新路由缓存 PMTU=1400<br/>TCP 相应下调 MSS 并重传
    H->>R1: IP包 1400字节,DF=1
    R1->>R2: 转发
    R2->>D: 送达 ✓

    rect rgb(250,235,235)
    Note over R2,H: 若中间防火墙丢弃了这条 ICMP<br/>→ 发送方永远收不到通知<br/>→ MTU 黑洞:小包通、大包永久超时
    end

关键机制/变体

这是干什么的——ICMP 重定向(Type 5)

当路由器发现"某主机发来的包,其最优下一跳与该主机在同一网段”,会回送 Redirect 提示主机直接发给更优网关,同时仍然转发该包(不丢弃)。

四种 Code:0 网络重定向、1 主机重定向、2 基于 TOS 的网络重定向、3 基于 TOS 的主机重定向。

安全风险:攻击者伪造 Redirect 可劫持主机流量(把网关改成自己),实现中间人。因此现代系统普遍关闭:

1
2
3
4
5
# 主机侧:不接受重定向
sysctl -w net.ipv4.conf.all.accept_redirects=0
sysctl -w net.ipv4.conf.all.secure_redirects=0 # 只接受来自已知网关的重定向
# 路由器侧:不发送重定向
sysctl -w net.ipv4.conf.all.send_redirects=0

这是干什么的——区分三种"不通"

排错时必须严格区分,它们指向完全不同的方向:

现象收到什么含义定位方向
Destination Host Unreachable本机内核产生(未收到任何 ICMP)本机 ARP 解析不到下一跳同网段目标不在线 / VLAN 不通 / 网关 MAC 学不到
From x.x.x.x icmp_seq=1 Destination Host Unreachable收到路由器发的 Type 3 Code 1路径中某路由器无法把包交给目标目标关机 / 目标网段二层故障
From x.x.x.x Destination Net UnreachableType 3 Code 0路由器无路由补路由 / 检查路由协议邻居
From x.x.x.x Communication prohibitedType 3 Code 13被 ACL/防火墙明确拒绝调整安全策略
Request timed out / 100% packet loss什么都没收到静默丢弃或回程不通防火墙 DROP / 回程路由缺失 / 目标禁 ICMP

关键区分技巧:注意 ping 输出里有没有 From <IP> 前缀。说明收到了明确的 ICMP 差错报文,且该 IP 就是报错者,可直接定位到具体设备;没有则是纯超时,说明包石沉大海。

这是干什么的——ICMP 与安全

风险说明缓解
ping sweep / 网络侦察批量 Echo Request 探测存活主机边界限速、内网可放行以保留可诊断性
Smurf 攻击伪造源 IP 向广播地址发 Echo,全网回包淹没受害者禁用定向广播(no ip directed-broadcast)、icmp_echo_ignore_broadcasts=1
ICMP 隧道 / 数据外泄把数据藏在 Echo 载荷中穿越防火墙检测异常大载荷、异常高频的 ICMP 流量
伪造 Redirect劫持主机路由accept_redirects=0
伪造 Type 3 Code 4声称极小 MTU,迫使对端发小包降低吞吐Linux min_pmtu(默认 552)设下限
信息泄露Timestamp / Address Mask 请求泄露系统信息禁用 Type 13/17

最佳实践不要一刀切禁 ICMP。推荐放行:Type 0/8(可限速)、Type 3 Code 4(必须放行,否则 MTU 黑洞)、Type 11(保留 traceroute 能力);禁止:Type 5、13、17。IPv6 环境务必参考 RFC 4890 的过滤建议。

这是干什么的——Linux 相关内核参数

1
2
3
4
5
6
7
net.ipv4.icmp_echo_ignore_all = 0 # 1 = 完全不响应 ping
net.ipv4.icmp_echo_ignore_broadcasts = 1 # 防 Smurf,默认已开
net.ipv4.icmp_ratelimit = 1000 # 差错报文速率限制(毫秒/个)
net.ipv4.icmp_ratemask = 6168 # 哪些 Type 参与限速
net.ipv4.icmp_errors_use_inbound_ifaddr = 0 # 差错报文源地址选择策略
net.ipv4.ip_no_pmtu_disc = 0 # 0 = 启用 PMTUD
net.ipv4.route.min_pmtu = 552 # PMTU 下限,防伪造攻击

常见误区

  • 误区一:ping 不通 = 网络断了。 其实 ping 被禁(icmp_echo_ignore_all=1)、被限速、被防火墙 DROP,或回程路由缺失,都会表现为"不通",但三层/四层业务可能好好的。验证要用 nc -zvcurltraceroute -T
  • 误区二:ICMP 会重传。 不会。ICMP 本身不可靠,报文丢了就丢了,没有重传机制——这正是 traceroute 中间跳偶发 * * * 不代表故障的原因之一。
  • 误区三:所有 “Host Unreachable” 是一回事。 区别很大:Destination Host Unreachable(无 From)是本机 ARP 不到下一跳;From x.x.x.x ... Host Unreachable 是路由器回的 Type 3 Code 1(最后一跳 ARP 不到目标)。一个指向本机/同网段,一个指向路径中的下游设备。
  • 误区四:ICMP 重定向是路由协议。 不是。Redirect 只在同网段内提示"更优下一跳",不传播、不维护路由表,也不参与路由协议的计算。
  • 误区五:ICMPv6 能像 ICMPv4 一样全禁。 绝对不行。ICMPv6 同时承担 NDP(地址解析)、RA(地址自动配置)、MLD(组播)、PMTUD,全禁等于把 IPv6 网络的"网线拔了"。

速记口诀

  • 差错查询两大家:Type 3/5/11/12 是差错类,Type 0/8/13/14 是查询类——先分大类再看 Code。
  • 八字节带案发现场:差错报文必带"原始 IP 头 + 前 8 字节",五元组据此定位到具体套接字。
  • 三零四黑洞、幺幺环路查:Type 3 Code 4 是 MTU 黑洞铁证,Type 11 Code 0 是 TTL 超时(环路或 traceroute)。
  • v6 不能一刀切:ICMPv6 揽了 ARP+IGMP+PMTUD 的活,全禁直接瘫——放行策略参考 RFC 4890。

知识框架

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
mindmap
  root((ICMP 控制报文协议))
    定位
      网络层
      封装于IP Protocol=1
      ICMPv6 Next Header=58
      RFC 792 / RFC 4443
      无端口用Type+Code
    报文结构
      Type 1字节
      Code 1字节
      Checksum 覆盖整个ICMP
      Identifier与Sequence
      Next-Hop MTU
      差错报文带原始IP头加8字节
    两大类别
      差错报告类
        Type3 目的不可达
        Type5 重定向
        Type11 超时
        Type12 参数问题
        Type4 源抑制已废弃
      查询信息类
        Type8 Echo Request
        Type0 Echo Reply
        Type13/14 时间戳
        Type9/10 路由器发现
    关键Code
      3-0 网络不可达
      3-1 主机不可达ARP失败
      3-3 端口不可达UDP
      3-4 需分片但DF置位
      3-13 管理禁止
      11-0 TTL超时
      11-1 重组超时
    经典应用
      ping 回显请求应答
        Identifier区分进程
        Sequence算丢包
        载荷时间戳算RTT
      traceroute 逐跳TTL递增
        UDP高端口模式
        ICMP模式
        TCP模式最真实
      PMTUD 路径MTU发现
        DF置1
        依靠Type3 Code4
        MTU黑洞成因
    抑制规则
      不对ICMP差错报错
      不对广播组播报错
      不对非首片报错
      不对非单播源报错
      速率限制
    安全
      ping sweep侦察
      Smurf放大攻击
      ICMP隧道外泄
      伪造Redirect劫持
      禁ICMP破坏PMTUD
      RFC 4890过滤建议
    ICMPv6扩展
      0到127差错 128到255信息
      Type2 Packet Too Big
      Type128/129 ping6
      Type133到137 NDP
      Type130到132/143 MLD
      不可全禁否则瘫痪