本页目录

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