Internet Control Message Protocol
Internet控制消息协议(Internet Control Message Protocol 通常称为ICMP)是典型的支持IPv4的网络栈中的基本协议之一。 该协议使网络上的节点能够共享信息和错误,而无需使用可能已被破坏的上层协议。 例如,如果TCP连接由于数据包的生存时间太短而导致数据包被丢弃,则两台主机都无法确定故障。 想要任何一个主机都可以解决这个问题的唯一方法是向它们发送带外(out-of-band)信息,通知它们生存时间太短,路由器正在丢弃数据包。 这就是ICMP的目的。
许多人会通过使用他们的操作系统的“ping”工具,也算是至少间接使用过一次ICMP。
RFC
ICMP定义于RFC792。 RFC1122 定义IPv4和ICMP之间的关系。
数据包布局
请注意,本文中的所有结构都旨在显示内存中数据包的布局,而不是用作C代码 (尽管其中大多数应该能够被逐字复制)。
ICMP数据包的示例C结构体为:
struct icmp_packet {
uint8_t icmp_type;
uint8_t icmp_code;
uint16_t checksum;
};
校验和(Checksum)
ICMP校验和以与IPv4校验和相同的方式在报头和数据上执行。 There is no IPv4 psuedo-header required.
基本上,算法如下所示:
1. 将数据中的所有16位字相加,计算其校验和。 这应该在32位计数器中完成。 2. 在长度不齐的情况下添加任何零散字节。 3. 将总和折叠成一个16位字(将前16位与后16位相加,直到前16位为零)。 4. 返回结果的16位1的补码。
验证传入数据包时,应在不修改数据包中任何数据的情况下对报头和数据执行校验和。 如果校验和有效,则计算的校验和为零,如果校验和无效,则非零。
ICMP类型
ICMP定义了多种消息类型。
Echo Request & Reply
这些数据包在ICMP报头之后有一个额外的报头,其中包含与echo replies和requests相关的信息:
struct icmp_echo_packet {
uint16_t echo_id;
uint16_t echo_seq;
};
此报头后面是任意长度的数据。
echo request或reply的ICMP代码始终为零。
Echo Request (type 8)
向主机发送echo request,以请求“Echo Reply”响应。 它们通常用于确定网络上的主机是否可访问,或确定链路或路由中的延迟。
ID和Sequence值可以设置为任意值,但对于发送echo request的具体实现来说应该是有意义的。
数据包数据应该是你期望从主机接收回的字节数(通常足以将数据包填充到64字节左右)。 Windows似乎只是使用从“a”到“z”的小写字母表 — 这里的数据完全由实现者决定!
Echo Reply (type 0)
echo reply被发送到主机以响应“Echo Request”消息。 除了目的和源地址之外,回复的所有功能都与echo request相同。 不能更改ID和Sequence”。 通过它们将收到的响应与以前的请求联系起来。
Destination Unreachables (type 3)
这些数据包由主机或路由器发送,以通知另一台主机,由于各种可能的原因,它试图向其发送数据包的目的地无法到达。
数据包格式如下:
struct dest_unreachable_packet {
uint16_t empty;
uint16_t next_mtu;
iphdr original_header;
uint8_t original_payload[8]; // First eight bytes of the original IPv4 payload.
};
请注意,最好不要定义包含IP头结构和这些额外字节的结构,具体取决于操作系统和个人需求。
next_mtu字段包含下一跳的MTU,如果该消息是Fragmentation Required消息。
主机使用在Destination unreachable数据包中传输的原始IP报头来确定哪个数据包失败。 这也是在IPv4数据包中使用有效且可跟踪的ID的一个很好的理由。
Destination Unreachable消息使用ICMP代码字段而不是ICMP类型字段来定义单个消息类型。
Framentation Required (code 4)
当数据包大于链路的MTU时,主机或路由器将该数据包发送给主机,IPv4报头中的“不分段”(DF-Don't Fragment)位会被设置。 如果DF位未设置,路由器或主机将自动对数据包进行分段,以便传输数据包。
该消息的一个常见用途是Path MTU Discovery,它通过发送大小逐渐减小的数据包(已设置DF)来确定从一台主机到另一台主机的完整路由的MTU,直到该数据包没有发送需要分段的消息。 主机应该使用destination unreachable报头的“Next MTU”字段来确定下一个要尝试的MTU,而不是不断“猜测再检查”。 在编写路由数据包的代码时,请记住这一点;你可能遇到对此方面消息处理非常看重的基础设施。