
ICMP 这协议太容易被忽略了。平时大家说网络调试第一反应是 ping第二反应是 ping 通没有但很少有人注意到 ping 背后那个真正干活的协议。它就是 ICMPInternet Control Message Protocol互联网控制报文协议。这篇文章我会从协议设计、报文细节、实战抓包再到防火墙策略把 ICMP 讲透。适合刚入行的网络工程师、运维同学也适合所有想搞懂“为什么有时候 ping 不通应用却能用”这种怪现象的开发者。1. ICMP 到底是个什么协议1.1 从“网络报错器”说起把 IP 网络比作邮政系统可能最合适你寄出一封信填好收件地址剩下的就只能祈祷它能顺利到达。传统 IP 协议就是这种“尽力而为”的设计它只负责转发数据包但从不向你汇报结果。无论包裹是被丢进垃圾桶还是因为地址格式不对被打回寄件人都一无所知。ICMP 就是用来解决这个问题的它相当于邮政系统里那位拿着小纸条跑回来告诉你“查无此人”“派件超时”的信使。ICMP 和 TCP、UDP 可不在同一层它直接跑在 IP 之上是被 IP 协议IPv4 和 IPv6 都支持封装传输的。IP 头部里协议字段写着 1后面跟的就是 ICMP 报文。所以从层次上讲它属于网络层的一部分更像是 IP 的配套工具而不是像 HTTP、SSH 那样直接给应用程序使用的数据传输通道。不过也正是因为有了它网络设备才能互相报告错误、传递控制信息大家才能用上 ping 和 traceroute 这些最基础、最常用的排查工具。1.2 为什么需要 ICMP最早的 IP 协议设计得相当“省事”没有收发确认没有路径反馈。于是很多互联网服务在发生故障时唯一的线索就是“没反应”。能不能让网络自己把问题说出来呢于是 ICMP 被设计出来专门用来承载各种错误与控制消息。具体它干了哪些事情第一差错报告。比如一个数据包在路由器上找不到下一跳路由器就能回一个“目标不可达”的 ICMP 包给源主机。第二诊断查询。最典型的是 Echo 请求和应答也就是 ping用来确认主机是否“活着”。第三路径发现。traceroute 依赖路由器返回“超时”和“端口不可达”消息来一一还原整条路径。第四路径 MTU 发现PMTUD。一台主机发出一个不允许分片的大包若中间链路只能承载更小的包路由器会退回“需要分片但 DF 置位”的错误源端据此调整包大小避免大包卡死在链路里。注意ICMP 并不保证可靠传输它本身也是被尽力传递的甚至有时 ICMP 报文丢失了也没有人管。所以实际排障时我们常常需要结合多个信号一起判断。2. ICMP 报文结构拆解一眼看懂协议细节2.1 报文的固定头部ICMP 的报文格式并不复杂但每个字节都得抠清楚。通用部分由四个字段组成类型Type、代码Code、校验和Checksum以及根据类型不同而变化的内容区。前三个字段加起来正好 8 字节。类型和代码是最关键的类型决定这条消息的大类代码负责在大类里再细分。比如类型 3 代表“目标不可达”其中代码 0 表示网络不可达代码 1 表示主机不可达代码 3 表示端口不可达。校验和的计算方式与 IP 头校验和类似是覆盖整个 ICMP 报文的发送方计算好后填入接收方校验如果发现不一致就直接丢弃避免错误信息继续在网络中扩散。2.2 常见类型与代码速查为了帮你快速核对我把最常见的一批 ICMPv4 类型整理成了表格类型值名称常见代码一句话说明0Echo Reply0用于 ping 的响应3Destination Unreachable0/1/3/4网络/主机/端口不可达需要分片但 DF 置位4Source Quench0抑制发送速率已废弃5Redirect0~3路由器告诉主机走更优的下一跳8Echo Request0用于 ping 的请求9Router Advertisement0路由器定期通告10Router Solicitation0主机请求路由器通告11Time Exceeded0/1TTL 超时/分片重组超时12Parameter Problem0~2IP 头参数错误实际抓包时你通常只会看到 type 0、3、8、11算是绝大多数 ICMP 流量的来源。重定向 type 5 在有些网络里因为安全原因会被关掉而 type 4 因为当年设计有问题后来被认为不建议使用并废弃了。2.3 不可达消息是怎么区分的在不可达消息里“主机不可达”和“端口不可达”这两个代码特别容易让人迷糊。代码 1 的意思是根据路由表目标网络或者目标主机根本找不到或者目标主机不回应请求代码 3 的意思是目标主机已经给出了响应但对应端口上没有任何服务在监听。可以这样理解代码 1 是“找不到人”代码 3 是“人在但你敲的门不存在”。还有一个特别的代码 4网络领域的工程师对它应该又爱又恨需要分片但 DF 置位。它是 PMTUD 的关键信号。当发送端设置 IP 头 DFDont Fragment为 1而中间链路 MTU 小于报文长度无法拆分时路由器会丢弃报文同时回一个 type 3 code 4 的 ICMP并带上它自己那条链路的 MTU 值。发送端收到以后就能把包改小一点再发整套机制这才闭环。我在 4.2 节会专门讲这个报文被误删以后引发的“MTU 黑洞”。3. 从 ping 到 tracerouteICMP 的实际应用3.1 ping 的原理与细节ping 应该是大家最先接触到的 ICMP 应用。执行 ping本质上就是发送一个 type 8 Echo Request 到目标 IP目标主机的 IP 栈在收到后会返回一个 type 0 Echo Reply。如果请求丢在半路或者目标主机根本不响应 ICMP你就会看到请求超时。Echo 报文的“数据部分”不是随便填的大多数实现会在里面写入发送时刻的时间戳和一段序列号收到回复之后用当前时间减去时间戳就能算出往返时延。序列号则用来匹配请求和回复避免乱序导致误判。你可以在 Linux 上用ping -s 1400指定数据包大小故意把它调大来测 MTU也可以加-f做 Flood 模式在受控环境里测极限但这在生产环境要非常小心别把别人带宽打爆。一个常见误区是ping 通了就觉得主机没问题。ICMP 的回显应答是由操作系统内核协议栈完成的它不依赖用户态的应用服务。也就是说Web 服务挂了、数据库连不上只要主机还活着内核照样能回你这声“在呢”。所以 ping 只能说明“主机在线、网络可达”不能说明“业务正常”。3.2 traceroute 如何“画”出网络路径traceroute 的机制同样离不开 ICMP只是用得很巧妙。发送端向目标地址发送一系列 UDP 数据包默认使用 33434 及其之后的端口并把第一组包的 TTL 设为 1。第一个路由器收到后把 TTL 减 1结果变成 0按规定要丢弃并回一个 ICMP 超时消息type 11 code 0。发送端看到这个 ICMP 包的源 IP 是第一个路由器的出接口地址就记下“第 1 跳在这”。第二组包把 TTL 设为 2就能看到第二个路由器……依此类推。当 UDP 包终于到达目标主机时由于 traceroute 选择的端口号通常没有对应服务目标主机会回一个“目标不可达端口不可达”type 3 code 3的 ICMP 包发送端识别到这一信号就确定路径探测结束。整个过程就像是拿着一个倒计时的灯泡沿着管道往前递每一次爆炸露出的出口就是路径上的一跳。新版 traceroute 还支持用 TCP SYN 或 ICMP Echo 来探测Windows 里的tracert默认就是发 ICMP EchoLinux 里的traceroute默认发 UDP。排障时如果默认的 UDP 被中间设备过滤了可以换成-I参数用 ICMP 模式或者-T用 TCP 模式绕开限制。3.3 实操用 Wireshark 抓一次完整交互纸上谈兵没用我们亲手抓一次包。打开 Wireshark抓取环回口或物理网卡流量然后在终端里 ping 一个内网 IP。抓包界面的过滤栏输入icmp就能只看到 ICMP 报文。你会看到成对的报文一个 type 8 的 Echo Request紧接着一个 type 0 的 Echo Reply。展开 Request 的详情能看到 Type 和 Code 都是 0下面是标识符和序列号再往下是数据部分里的时间戳。Reply 报文的标识符和序列号和 Request 是对应的这是内核用来匹配的关键。如果 Reply 一直没回来说明发出的请求要么被丢弃了要么目标设备不响应 ICMP 回显。再试试 traceroute把过滤条件改成icmp || udp.port33434 udp.port33464。你会看到一串 TTL 超时的 ICMP 包源 IP 地址由近及远逐渐变化最后出现一个 type 3 code 3 的不可达包表示到达终点。抓过这次包你对 traceroute 的理解会扎实很多。4. ICMP 在真实网络里的坑与排查实战4.1 ping 得通但业务连不上的怪现象这类问题我遇到过太多次了。某天用户反馈应用访问失败你满心欢喜地 ping 了一下发现目标主机明明在线于是觉得骨干链路没问题。结果调了半天也没找到原因后来拿测端口工具试了一下发现端口根本没有响应。根因往往很简单目标机器上的服务进程已经崩溃或者防火墙只放行了 ICMP 而把业务端口给挡了。这个现象的底层机制我在 3.1 节提过——ICMP 回显应答和业务进程是否健康没有直接关系。所以排障铁律是先 ping 探测通断如果通了但业务不行立刻做端口级别测试用nc -vz IP 端口或telnet IP 端口试一把。如果端口测试也通应用仍异常再看应用日志如果端口测试不通多半是防火墙规则或服务监听问题。不能因为 ping 通就轻易放过链路。4.2 MTU 黑洞 ICMP 被误杀的典型案例这是我个人认为 ICMP 最关键的实战知识点。想象一条链路 MTU 只有 1400 字节一个目标主机发出的包有 1480 字节且设置了 DF。路由器无法分片就只能丢弃并回发一个 type 3 code 4 的消息告诉源端“需要分片但你不让分”。源端收到后会相应减小发送包的大小。如果中间的防火墙或设备把这种 ICMP 错误消息过滤掉了源端就永远不知道要减小包重传还会继续发同样的大包最终连接卡死。实际表现往往很诡异ping 默认 56 字节小包通ping -s 1472加大包不通浏览器访问网页时好时坏因为小请求能过一旦需要传输大响应就卡死。SSH 能连上但刷屏特别慢也可能就是这个问题。排查方法很简单先确认两端接口的 MTU 值再用ping -M do -s分档测试最大可通包长。如果确认是中间设备吞掉了 type 3 code 4就得在防火墙策略里专门放行这个类型的 ICMP或者把客户端和服务器之间的 MSS 钳制MSS Clamping做好。4.3 TTL 引发的路由判断误区traceroute 输出的每一跳并不像表面看那么可信。中间某跳频繁显示* * *先别急着判断链路断连因为很多路由器默认不发 TTL 超时报文或者做了限速导致探测包被静默丢弃。只要后续跳数还能探测到那丢失的中间跳可能只是“不配合回应”而不是实际断连。另外通过收到的 TTL 来猜对方的操作系统也不是绝对可靠。Windows 默认初始 TTL 是 128Linux 是 64但设备可以配置改掉运营商还可能做 NAT 转换使源地址不再体现真实设备地址。判断路径质量不要单看 TTL 这一个字段要综合时延、丢包率和各跳的往返情况。5. 防火墙与 ICMP过滤策略该怎么写5.1 该不该一刀切封掉 ICMP很多安全加固文档会把 ICMP 列进“高危协议”建议全部丢弃理由是 ICMP 可以被用来探测网络拓扑、发起放大攻击或实施重定向欺骗。这思路不是没道理但完全不切实际。如果把所有 ICMP 都封了你会亲手制造出我前面说的 MTU 黑洞、traceroute 失效、路径诊断困难等一系列问题业务反而更不稳定。正确思路是精细化管理必须保留必要类型的 ICMP但对不需要的或者高频类型做速率限制。比如 Echo Request 可以作为运维探测手段但应限制每秒允许几个Echo Reply 可以让它进来但可以限速Time Exceeded、Destination Unreachable 等错误报文应该放行否则影响 PMTUD。ICMP Redirect 在没有特殊需求的情况下可以丢弃减少被利用的风险。5.2 常用防火墙规则参考以 Linux iptables 为例我通常会从下面的策略起步都只是常规网络加固示例# 允许本机发出的数据包对应的相关错误回包 iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT iptables -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT # 对本机发起的回显请求做速率限制避免被 flood iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 10/second --limit-burst 20 -j ACCEPT iptables -A INPUT -p icmp -j DROP注意顺序很重要允许规则放在前面默认策略最后再设置为 DROP否则会把该放行的包也拒了。实际生产里如果你使用的是硬件防火墙或者云安全组界面不同但思路一致。关键点永远是你要给 PMTUD 留一条活路也就是 type 3 code 4。5.3 ICMP 攻击与防护要点恶意流量里常见的有大流量 ping flood通过大量 Echo Request 耗尽目标主机的带宽和 CPU。防护手段可以对 ICMP 做速率限制也可以依靠云厂商的抗 DDoS 能力。还有一种古老的 Smurf 攻击攻击者伪造受害者源 IP向网络广播地址发送 Echo Request让网络上所有主机都同时向受害者回包形成流量放大。现在的路由器默认禁止定向广播这类攻击已经少见但老设备上仍然可能遇到。另外恶意主机还能发送伪造的 ICMP Redirect 报文试图把受害者的流量引到攻击者控制的设备上。应对办法是在主机和路由器上禁用或严格校验重定向消息。这里多说一句无状态防火墙如果单纯放行所有 Echo Reply可能被某些隧道技术利用让非 ICMP 的业务数据藏在 Echo 报文里避开审查。你不需要过度担心普通用户会这么做但在安全策略评审时至少要知道全放行 Echo Reply 并不是没有代价的。6. 从 IPv4 到 IPv6ICMPv6 的变化与不可绕开的新职责6.1 ICMPv6 成了“基础设施”在 IPv6 网络中ICMP 不再只是个报错器它承当了更多基础性职责。最典型的是邻居发现Neighbor Discovery ProtocolNDP被划入 ICMPv6替代了 IPv4 时代的 ARP。NDP 使用 ICMPv6 类型 135邻居请求和 136邻居通告来解析 MAC 地址还利用类型 133、134 做路由器发现利用类型 137 做重定向。除此之外无状态地址自动配置SLAAC依赖路由器周期性发送的路由器通告RA报文主机才能获取前缀、默认路由等信息。这也就是说在 IPv6 里随便封掉 ICMPv6可能连最基本的内网通信和地址获取都会失败。所以业界有一句经验IPv4 你可以考虑少量过滤 ICMPIPv6 你必须谨慎再谨慎。尤其在纯 IPv6 或双栈环境中需要确保 NDP 和 RA 报文不被阻断否则整个网段都会“失联”。6.2 ICMPv6 的类型设计差异ICMPv6 没有沿用 IPv4 的哪些类型号重新定义了类型。最常用的几个类型 128 和 129 对应 Echo Request 和 Echo Reply相当于 IPv4 的 type 8 和 0类型 1 表示目标不可达代码 4 对应“端口不可达”和 ICMPv4 的语义不完全一样类型 2 表示数据包太大Packet Too Big它在 IPv6 里的地位等同于 IPv4 的“需要分片但 DF 置位”因为 IPv6 中间设备完全不分片发端必须根据类型 2 消息来调整包大小类型 3 是超时代码 0 是跳数超限代码 1 是分片重组超时。另外IPv6 的校验和计算还依赖伪头需要结合源/目的地址因此不能像 IPv4 那样直接在中间节点上重新计算校验和。好在这些细节对大多数网络管理员来说一般是抓包或者开发协议栈时才需要用到。6.3 安全策略要跟着协议版本走在一个同时存在 IPv4 和 IPv6 的网络里如果你只在 IPv4 防火墙上做了 ICMP 过滤而忘了 IPv6 对应的防火墙规则攻击流量完全可能从 IPv6 通道绕过防护这经常被安全评估当作典型漏洞。双栈环境下必须把两套规则分开梳理IPv4 按“可裁剪、需限速”的思路配置IPv6 则重点保障 NDP、RA、数据包过大消息和跳数超限消息的可达性同时仍然对 Echo Request 做限速。用表格总结一下两个版本核心类型的对应关系方便你参考用途ICMPv4ICMPv6Echo Request类型 8类型 128Echo Reply类型 0类型 129目标不可达含端口不可达类型 3类型 1包过大/需要分片但 DF 置位类型 3 代码 4类型 2跳数超时类型 11 代码 0类型 3 代码 0这张表平时排障直接用省得临时查文档。我自己在这些年的排障里踩过最深的坑就是“防火墙顺手把 ICMP 全封了结果应用大包走不了”。后来养成了一个习惯无论改哪台设备的策略都会先确认 ICMP 里Packet Too Big或者Fragmentation Needed这类消息没被拦住。协议本身不复杂但它的影响面比想象中大得多。建议新入行的朋友别只背命令遇到一次 MTU 或 TTL 问题亲手抓包分析一遍以后就再也不会忘了。ICMP 这张“小纸条”值得认真对待。