因特网控制报文协议ICMP(Internet Control Message Protocol) 是一个差错报告机制,是TCP/IP协议簇中的一个重要子协议,通常被IP层或更高层协议(TCP或UDP)使用,属于网络层协议,主要用于在 IP 主机和路由器之间传递控制消息,用于报告主机是否可达、路由是否可用等。这些控制消息虽然并不传输用户数据,但是对于收集各种网络信息、诊断和排除各种网络故障以及用户数据的传递具有至关重要的作用。
为什么需要ICMP?
在数据传输的过程中,IP 提供尽力而为的服务,指为了把数据包发送到目的地址尽最大努力。它并不对目的主机是否收到数据包进行验证,无法进行流量控制和差错控制。因此在数据包传输过程中,产生各种错误在所难免。为了更有效地转发IP数据包和提高数据包交付成功的机会,ICMP应运而生。使用ICMP,当网络中数据包传输出现问题时,主机或设备就会向上层协议报告差错情况和提供有关异常情况的报告,使得上层协议能够通过自己的差错控制程序来判断通信是否正确,以进行流量控制和差错控制,从而保证服务质量。
Ping程序是最常见的用于检测IPv4和IPv6网络设备是否可达的调试手段,它使用ICMP的echo信息来确定:
(1)远程设备是否可达;
(2)与远程主机通信的来回旅程(round-trip)的延迟;
(3)报文包的丢失情况。
Tracert程序主要用于查看数据包从源端到目的端的路径信息,从而检查网络连接是否可用。当网络出现故障时,用户可以使用该命令定位故障点。它利用 ICMP 超时信息和目的不可达信息 来确定从一个主机到网络上其他主机的路由,并显示IP网络中每一跳的延迟(这里的延迟是指:分组从信息源发送到目的地所需的时间,延迟也分为许多的种类——传播延迟、传输延迟、处理延迟、排队延迟等);
ICMP4 消息
ICMP 主要用作发送有关网络层(L3)错误和控制消息的机制,让你能够通过发送 ICMP 消息来获取有关通信环境中问题的反馈。
ICMPV4 消息分两类:错误消息和信息消息(查询消息)。
ICMPv4 被用于 ping 和 traceroute 等诊断工具。
ICMPv4 初始操作是在引导阶段调用 inet_init()
中完成,具体如下
ICMPv4 报头:由类型(8位)、代码(8位)、检验和(16位)和32位的可变部分组成,ICMPv4报头用结构 icmphdr
表示如下
ICMPv4 数据报不超过 576 字节的前提下,有效载荷应尽可能多地包含原始数据报的内容。长度为 576 字节是根据 RFC 791 确定的,它指出所有主机都必须能够接收长达 576 字节的数据报。
接收 ICMPv4 消息:方法 ip_local_deliver_finish()
处理目的地为当前机器的数据包。收到ICMP数据包后,此方法便将其交给注册 ICMPv4 协议的原始套接字。在方法 icmprcv()
中进行处理。
引入 ICMP 套接字后,ping 的发送方可以不是原始套接字。比如:我们也可以创建一个套接字socket(PF_INET, SOCK_DGRAM, PROT_ICMP)
,并使用它来发送 ping 数据包。
如果数据包为广播或组播方式,且为ICMP_ECHO
或ICMP_TIMESTAMP
消息,将读取变量来核实广播/组播回答请求是否被允许。收到 ping (消息类型为:回应请求即ICMP_ECHO
)时,将由方法icmp_echo()
进行处理。
发送 ICMPv4 消息:
用于发送 ICMPv4 消息的方法有两个:一是方法 icmp_reply()
,用于发送两种 ICMP 请求的响应(被动)(ICMP_ECHO
和 ICMP_TIMESTAMP
);二是方法 icmp_send()
,用于发送当前机器在特定条件下主动发送的 ICMPv4 消息。这两个方法最终都调用icmp_push_reply()
来执行实际发送数据包的工作。
ICMP_PROT_UNREACH
(协议不可达)
IP报头的协议字段(长8位)指定的协议不存在时,将向发送方发送一条 ICMP_DEST_UNREACH/ICMP_PROT_UNREACH
消息,因为没有针对指定协议的协议处理程序(协议处理程序数组将协议号用作索引,因此对于不存在的协议,没有相应的处理程序。
所谓不存在的协议,指的是下面两种情形之一:
(1)Pv4报头中的协议号是错误的,没有包含在协议号列表中(该列表可在include/uapi/linux/in.h
中找到);
(2)内核不支持该协议,因此该协议没有注册,协议处理程序数组中没有相应的条目。
由于这样的数据包无法处理,因此需要向发送方发回 ICMPv4 目的地不可达消息。这种应答中的代码 ICMP_PROT_UNREACH
指出了导致错误的原因——“协议不可达”。
ICMP_PORT_UNREACH
(端口不可达)
接收 UDPv4 数据包时,将查找匹配的 UDP 套接字。如果没有找到匹配的套接字,将检查校验和是否正确。如果不正确,就将数据包默默地丢弃;如果正确,就更新统计信息,并返回一条 ICMP “目的地不可达”/“端口不可达” 消息。
ICMP_FRAG_NEEDED
(需要分片)
转发数据包时,如果其长度超过了外出链路的 MTU,且在IPv4报头(IP_DF
)中没有设置分段( DF )位,将把数据包丢弃,并向发送方发回一条代码为 ICMP_FRAG_NEEDED
的 ICMP_DEST_UNREACH
消息。
ICMP_SR_FAILED
(目的不可达)
转发数据包时,如果其严格路由选择(strict routing)和网关(gatewaying)选项被设置,将把数据包丢弃,并发回一条代码为 ICMP_SR_FAILED
的“目的地不可达”消息。
方法 icmp_reply()
和 icmp_send()
都支持速率限制,它们调用 icmpv4_xrlim_allow()
如果速率限制检查允许发送当前数据包(icmpv4_xrlim_allow()
返回true
),它们就发送该数据包。
在如下情况不会进行速率限制检查:消息的类型未知、数据包为PMTU发现数据包、设备为环回设备、ICMP类型在速率掩码中未指定。
ICMP6 消息
ICMPv6 是 IPv6 的有机组成部分,每个结点都必须全面实现。
在 IPv6 中,ICMPv6 除用于错误处理和诊断外,还被用于领居发现(ND)协议以组播侦听者发现(MLD)协议。【IPv4 和 IPv6 中的 ICMP(IPv6中的 MLD 和 ND 协议分别对应于 IPv4 中的 IGMP 的 ARP 协议】
互联网组管理协议(英语:Internet Group Management Protocol,缩写:IGMP)是用于管理网路协议多播组成员的一种通信协议。 IP主机和相邻的路由器利用 IGMP 来建立多播组的组成员。 像ICMP用于单播连接一样,IGMP也是IP多播说明的一个完整部分。
ICMPv6 和 ICMPv4基本差不多,也为每个 CPU 都创建一个原始 ICMPv6 套接字,并将它们存储在一个数组中,要访问当前 sk
,可调用方法icmpv6_sk()
。
ICMPv6 初始化是由方法 icmpv6_init()
和 icmp_sk_init()
完成。
ICMPv6 报头:由类型字段(8位)、代码字段(8位)和检验和字段(16位)组成。
+0-------7-------15---------------31
| Type | Code | Checksum |
+----------------------------------
| Reserved |
+----------------------------------
| Target Address |
+----------------------------------
| Destination Address |
+----------------------------------
| Options... |
+---------------------------------+
ICMPv6 报头用结构 icmphdr 表示:
“类型”字段为最高位为0
(此字段取值范围0-127)时,表示为错误消息;为1
(此字段取值为128-255)时,表示为信息消息。
接收 ICMPv6 消息:收到的 ICMPv6 数据包被交给方法 icmpv6_rcv()
发送ICMPv6消息:发送 ICMPv6 消息的主方法为 icmpv6_send()
。
发送ICMPv6“跳数限制超时”消息:每台机器转发数据包时都将跳数限制计数器减1,跳数限制计数器是 IPv6 报头的一个成员,相当于 IPv4 中的存活时间。跳数限制计数器变成 0 后,将调用方法icmpv6_send()
发送一条代码为ICMPV6_EXC_HOPLIMIT
的ICMPv6_TIME_EXCEED
消息,之后再更新统计消息,并将数据包丢弃。
发送ICMPv6“分段重组超时”消息:分段超时时,将调用方法icmpv6_send()
发回一条代码为ICMPV6_EXC_FRAGTIME
的ICMPV6_TIME_EXCEED
消息。
发送ICMPv6“参数问题”消息:在分析扩展报头遇到问题时,将发回一条代码为ICMPV6_UNK_OPTION
的 ICMPV6_PARAMPROB
消息。
https://info.support.huawei.com/info-finder/encyclopedia/zh/ICMP.html