4. ICMP协议:报文类型与结构、Ping与Traceroute原理、ICMP重定向与差错报告
ICMP,全称 Internet Control Message Protocol,互联网控制报文协议。很多人觉得它只是 IP 协议的附属品,没什么好学的。但说实话,网络出问题的时候,ICMP 往往是第一个帮你定位故障的帮手。
我刚开始做网络运维那会儿,遇到一个跨网段丢包的问题。抓包看了半天,TCP 重传一大堆,就是找不到根因。后来一个老同事提醒我:“你看看 ICMP 的差错报文。” 我这才发现,中间路由器一直在发 “Destination Unreachable — Fragmentation Needed”。嗯,从那以后,我再也不敢小看 ICMP 了。
4.1 ICMP 报文类型与结构
ICMP 报文是直接封装在 IP 数据报里的。它的头部很简单,只有 8 个字节,但类型字段决定了它的“性格”。
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 类型(Type) | 1 | 标识报文类型,如 0、3、8、11 |
| 代码(Code) | 1 | 对同一类型的细分,比如 Type 3 下 Code 0 是网络不可达,Code 1 是主机不可达 |
| 校验和(Checksum) | 2 | 覆盖整个 ICMP 报文 |
| 剩余字段 | 4 | 根据类型不同,内容不同。比如 Echo Request/Reply 里放标识符和序列号 |
常见的 ICMP 类型,我整理了一张表,你抓包时对照着看,一眼就能认出报文在干什么。
| Type | Code | 含义 | 典型场景 |
|---|---|---|---|
| 0 | 0 | Echo Reply | Ping 的响应 |
| 3 | 0~15 | Destination Unreachable | 路由不可达、端口不可达、分片需要但 DF 置位等 |
| 5 | 0~3 | Redirect | 路由器告诉主机“你走错路了,应该走那条路” |
| 8 | 0 | Echo Request | Ping 的请求 |
| 11 | 0~1 | Time Exceeded | TTL 超时(Traceroute 的核心) |
icmp 就能看到所有 ICMP 报文。如果想看特定类型,用 icmp.type == 8 或 icmp.type == 3。我个人习惯把 Type 和 Code 一起过滤,比如 icmp.type == 3 && icmp.code == 4 专门抓“分片需要但 DF 置位”的报文。
4.2 Ping 的原理与抓包分析
Ping 可能是大家最熟悉的网络工具了。它的原理其实很简单:发送 ICMP Echo Request,等待 ICMP Echo Reply。
但你真的看过 Ping 的报文细节吗?我建议你亲自抓一次包。你会发现,Ping 报文的 Identifier 和 Sequence Number 字段是用来匹配请求和响应的。操作系统发出去一个请求,收到回复时通过这两个字段确认“这是刚才那个请求的回复”。
举个例子,我在 Windows 上执行 ping 8.8.8.8,抓包结果大致是这样的流程:
- 主机发送 ICMP Type 8, Code 0,Identifier=1, Sequence=1
- 目标回复 ICMP Type 0, Code 0,Identifier=1, Sequence=1
- 主机发送 ICMP Type 8, Code 0,Identifier=1, Sequence=2
- 目标回复 ICMP Type 0, Code 0,Identifier=1, Sequence=2
你看,Identifier 在整个 Ping 会话中保持不变,Sequence 逐次递增。这样即使多个 Ping 进程同时运行,也不会搞混。
4.3 Traceroute 的原理与抓包分析
Traceroute 的原理比 Ping 稍微绕一点,但说白了就是利用了 ICMP 的 Time Exceeded 报文。
它的工作方式是这样的:
- 发送一个 UDP 报文(Linux 默认)或 ICMP Echo Request(Windows 默认),TTL 设为 1。
- 第一跳路由器收到后,TTL 减为 0,丢弃报文,并回复一个 ICMP Time Exceeded(Type 11, Code 0)。
- 源主机收到这个 ICMP 报文,就知道第一跳路由器的 IP 了。
- 接着发送 TTL=2 的报文,第二跳路由器回复 Time Exceeded。
- 以此类推,直到报文到达目标主机。目标主机收到 TTL=1 的报文后,会回复一个 ICMP Port Unreachable(Type 3, Code 3),因为 Traceroute 用的目标端口通常是一个高位随机端口,目标主机上没有对应的服务。
我画了一张流程图,帮你把 Traceroute 的完整路径理清楚。
你想想看,Traceroute 其实就是在“试探”路径上的每一台路由器。每次 TTL 超时,路由器都会“暴露”自己的 IP。这就是为什么 Traceroute 能画出网络路径。
* * *。一开始我以为网络断了,后来发现是中间某台路由器配置了“不回复 TTL 超时”的策略。这种情况下,Traceroute 就失效了。所以,看到星号别慌,先确认一下是不是路由器故意不回复。
4.4 ICMP 重定向与差错报告
ICMP 重定向,说白了就是路由器在告诉主机:“你刚才发报文的路由不是最优的,下次走这条路。” 这个机制在早期网络里很常见,但现在用得少了,因为很多主机直接配了默认网关,不需要动态学习。
重定向的流程是这样的:
- 主机发送一个报文到路由器 A。
- 路由器 A 查路由表,发现报文应该走路由器 B 才能到目标。
- 路由器 A 把报文转发给路由器 B,同时给主机发一个 ICMP Redirect(Type 5)。
- 主机收到后,更新自己的路由表,下次直接发给路由器 B。
但这里有个安全风险。你想想看,如果攻击者伪造一个 ICMP Redirect 报文,告诉主机“去 8.8.8.8 走我这里”,那主机的流量就被劫持了。这就是所谓的“ICMP 重定向攻击”。
sysctl -w net.ipv4.conf.all.accept_redirects=0 来关闭。Windows 下也可以在注册表里关掉。别给攻击者留机会。
再来说说 ICMP 的差错报告。当 IP 数据报在传输过程中遇到问题,比如目标不可达、TTL 超时、参数错误等,路由器或目标主机会生成一个 ICMP 差错报文,告诉源主机“你的报文出问题了”。
差错报文有一个特点:它会把出问题的 IP 数据报的头部(至少 8 个字节)原封不动地放在 ICMP 报文的数据部分。这样源主机收到后,就能知道是哪个报文出了问题。
举个例子,你发了一个 TCP SYN 报文到某个端口,但目标主机上没有服务监听。目标主机会回复一个 ICMP Destination Unreachable(Type 3, Code 3 — Port Unreachable)。你抓包一看,ICMP 报文的数据部分就是那个 TCP SYN 报文的 IP 头和 TCP 头。嗯,这样你就能精确地定位到是哪个连接出了问题。
- ICMP 差错报文必须包含原始报文的 IP 头 + 至少 8 字节载荷
- 差错报文本身不会再触发新的 ICMP 差错报文(防止风暴)
- 多播和广播报文不会触发 ICMP 差错报告
好了,ICMP 的内容就讲到这里。你只要记住:Ping 是 ICMP 的“请求-应答”模型,Traceroute 是 ICMP 的“TTL 超时”模型,而重定向和差错报告是 ICMP 的“网络诊断”模型。抓包的时候,多看看 Type 和 Code 字段,你就能读懂网络在说什么。