本页目录
ICMP 与 PMTUD
互联网的控制面——ICMP 不只用来 ping,它是 PMTUD(路径 MTU 发现)的引擎、是路由不可达的反馈信道。PMTUD 的"黑洞"故障——防火墙拦截 ICMP 导致 TCP 卡死——是 L2-L4 交界处最经典的排查案例。
概述
ICMP(Internet Control Message Protocol)是 IP 协议的诊断和控制辅助层。它不是传输协议——不传输应用数据——而是网络设备之间报告错误(不可达、超时)和交换信息(echo 请求/响应)的通道。ping 和 traceroute 是最常见的 ICMP 工具。PMTUD(Path MTU Discovery)利用 ICMP "Fragmentation Needed" 消息让发送端知道路径上最小的 MTU——这比盲目分片更高效,但当 ICMP 被防火墙过滤时会导致静默故障(MTU black hole)。
ICMPv4 消息结构
ICMP 是 IP 的附属协议 (protocol=1, 封装在 IP 包中):
[IP Header | ICMP Type (1B) | Code (1B) | Checksum (2B) | ICMP Body]
Type 大类:
0: Echo Reply
3: Destination Unreachable (codes: 0=Net, 1=Host, 3=Port, 4=Frag Needed, 13=Administratively Prohibited)
5: Redirect (codes: 0=Net, 1=Host)
8: Echo Request (ping)
11: Time Exceeded (codes: 0=TTL expired in transit, 1=Fragment reassembly timeout)
12: Parameter Problem (codes: 0=IP header bad, 1=Required option missing)
Destination Unreachable (type 3)
code 4 (Fragmentation Needed) 最关键 — 这是 PMTUD 的唯一信号:
RFC 1191 扩展了 code 4 的消息体:
[unused 2B][Next-Hop MTU 2B][original IP header + first 8B of payload]
→ Next-Hop MTU = 下一跳的 MTU (如 1492 for PPPoE)
→ 原始的 IP header + 前 8B → 发送方可以匹配到哪个连接
ping
Echo Request (type 8) → Echo Reply (type 0):
标识符 (2B): 通常 = 进程 PID (Unix ping) — 用于匹配 reply 和 request
序号 (2B): 递增 (每个 probe +1)
Data: 可变 (Unix: 默认 56B, 填充为 64B incl. ICMP header)
TTL 解读:
$ ping 1.1.1.1 → reply: ttl=57
→ 初始 TTL (default for this host) = 64 (Linux)
→ 64 - 57 = 7 hops away
$ ping 1.1.1.1 → reply: ttl=119
→ 发送者初始 TTL = 128 (Windows)
→ 可推断目标 OS: Linux/Unix = 64, Windows = 128, Solaris = 255
ping -M do (DF bit):
测试 PMTUD: 发送大包 + DF=1 → 如果路径上某处 MTU < 包大小 → Frag Needed or 超时
traceroute
利用 TTL 逐步增加 → 每 hop 返回 ICMP Time Exceeded (type 11, code 0):
traceroute 1.1.1.1:
Probe 1: TTL=1, UDP dst port 33434 → Router_1 丢弃 + 回复 ICMP Time Exceeded
Probe 2: TTL=2, UDP dst port 33435 → Router_2 丢弃 + 回复 ICMP Time Exceeded
Probe 3: TTL=3, UDP dst port 33436 → Destination (1.1.1.1): 端口不可达 (ICMP Port Unreachable)
→ 结束
Per-hop 3 probes: 检测每个 hop 的多路径 (可能 3 个不同的 IP 对应不同的路径)
IPv6 traceroute: 用 UDP 或 ICMPv6 Echo, 机制相同
PMTUD: 完整流程
发送方: TCP connection to server, MSS=1460, DF=1
1. Server 发送的数据包: size=1500B (MSS 1460 + TCP header 20 + IP header 20)
2. 中间路由器: interface MTU = 1492 (PPPoE) → 丢包 + ICMP Frag Needed (code 4, MTU=1492)
3. 发送方: 收到 ICMP → 更新路由缓存的 PMTU = 1492 → 重发小包 (MSS 1452 = 1492 - 40B TCP+IP)
4. 后续包: MSS=1452 → 不再触发 PMTUD
PMTU 缓存:
ip route get <dst> → cache mtu xxx
Linux: per-route PMTU, 10 min expiry → 过期后重探测
黑名单:
TCP MTU probing: tcp_mtu_probing=1 → 当 TCP retransmission 连续发生,
自动尝试减小 MSS (不用等 ICMP, 防止 ICMP 被过滤)
sysctl net.ipv4.tcp_mtu_probing=1
IPv6 PMTUD (RFC 8201):
原理相同, ICMPv6 Packet Too Big (type 2, code 0)
但 IPv6 header 固定 40B → MTU 计算更简单
参考
- RFC: 792, 4443, 1191, 8201
- 工具:
ping -M do,traceroute -T -p 443(TCP),tracepath,mtr - 源码:
net/ipv4/icmp.c,net/ipv6/icmp.c
Keywords: ICMP, Destination Unreachable, ping, traceroute, PMTUD, Frag Needed, MTU black hole, MSS clamping