什么是ICMP
IP 层的”先天缺陷”,催生了 ICMP
1970 年代末定 IP 协议(RFC 791)时,设计者给 IP 定了两个核心调性:
- 无连接:每个包独立路由,不保序
- 不可靠:不保证送达,丢了就丢了,IP 自己不重传
这两个特性让 IP 极简、跑得快、能跨各种网络,但带来一个问题——
包转发出问题的时候,谁来告诉发送方”怎么回事”?
比如:
- 目标主机在线,但端口没开 → 这包该不该继续跑?
- 目标网络根本不存在 → 别白费劲了
- TTL 跳到 0 了,包在环路上打转 → 得有人把它掐掉
- MTU 不够,包又不允许分片 → 得告诉发送方”你切小点再发”
这些问题不能让 TCP 管(TCP 在 IP 上一层,包还没到传输层就可能已经被路由器丢了),也不能让 IP 自己管死(IP 的定位就是”尽力而为递送”,塞太多逻辑就重了)。
于是 RFC 792(1981)单独拆出一个协议 —— ICMP,定位就一句话:
IP 的”信令旁路”:不参与数据传送,专管”递送过程中的控制与差错”。
ICMP 的设计哲学
1. 跟 IP 平级,但不独立存在
ICMP 报文是直接封装在 IP 包里的(IP header 里 protocol 字段 = 1),所以它跟 TCP/UDP 平级,都属于”IP 的上层协议”。但它又不像 TCP/UDP 那样给用户程序传数据,而是只给网络栈自己和运维工具用。
2. “只报信,不补救”
ICMP 的职责边界很硬:通知发生了什么,但不负责解决。
- 收到 “Destination Unreachable” → ICMP 只负责报,TCP 层决定要不要重传、要不要通知应用
- 收到 “Time Exceeded” → ICMP 只负责说”TTL 超了”,路径探测工具(
traceroute)拿这个信息画图,IP 层继续扔包
3. 防风暴:ICMP 不再生成 ICMP
这是个关键设计点。如果:
- 一个 ICMP 差错报文本身又出错了 → 路由器再回一个 ICMP 报那个 ICMP 的错 → 无限套娃
所以 RFC 规定:ICMP 差错报文只对”非 ICMP 差错”的原始数据包生成,且只对首 8 字节做封装回传(够 TCP/UDP 头认出是哪个连接就行),从源头掐断风暴。
4. 分”差错”和”查询”两路
很多人以为 ICMP 全是报错,其实它分两类,设计目的完全不同:
| 类别 | 目的 | 例子 |
|---|---|---|
| 差错报文(Error) | 被动回:IP 包转发/交付出问题时,由中间节点或目标回给源 | Destination Unreachable, Time Exceeded, Source Quench, Redirect |
| 查询报文(Query) | 主动发:主机主动探网络状态 | Echo Request/Reply (ping), Timestamp, Address Mask(已废弃) |
查询类是后来”借 ICMP 这个现成通道”加的诊断能力,设计上跟差错类是两回事。
回看几个熟悉工具的”设计本意”
ping
- 本来不是 ICMP 的主业,是 Mike Muuss 1983 年随手写的工具名(声纳 ping 的梗)
- 借用了 ICMP Echo 这对查询报文:不发 TCP 不走三次握手,纯 IP + ICMP 就能测可达性和 RTT
- 好处:哪怕目标机器没开任何端口、防火墙只放 ICMP,也能探到”主机活没活”——这就是 nmap -sn 在局域网外也发 ICMP Echo 的原因
traceroute
- 设计巧思:故意发 TTL=1 的 UDP(或 ICMP)包
- 第 1 跳路由器丢包 → 回 Time Exceeded (Type 11) → 拿到第 1 跳 IP
- TTL=2 → 第 2 跳……一路递进去
- 这里 ICMP 的”Time Exceeded”本来是为”包环路自动掐断”设计的(防路由环),
traceroute只是反向利用了它”被迫报信”的特性来画路径
Path MTU Discovery
- 另一处借力:发送方发 DF(Don’t Fragment)位的大包
- 中间路由器 MTU 不够 → 回 Destination Unreachable / Fragmentation Needed (Type 3, Code 4),里面带”我能接受的最大 MTU”
- 发送方据此调整 MSS —— 这套机制完全跑在 ICMP 上,TCP 才能做到不分片直连
留下评论