大家好,欢迎来到IT知识分享网。

文章索引:
1. ICMP协议定义和结构
2. ICMP协议作用
3. ICMP协议格式
4. ICMP协议/PING/TRACEROUTE原理
5. 代码示例
一、定义和结构:
1.定义:
ICMP:Internet Control Message Protocol,互联网控制报文协议,是TCP/IP协议族中的一个重要协议,主要用于IP主机和路由器之间传递控制信息和错误消息。
2.位置:
ICMP位于OSI参考模型的网络层(第三层),是IP协议的一部分。ICMP是一个面向消息的协议,它的消息嵌入在IP数据包中,由IP协议承载。

3.特点:
1) ICMP协议不直接传输用户数据,但它们在确保用户数据正确、高效地传输过程中起着至关重要的作用,为网络的操作和管理提供支持。
2) ICMP报文没有特定的端口号,因为它不涉及传输层。
3) ICMP的主要作用是帮助诊断网络问题、报告通信中的错误、提供网络状态信息等。ICMP协议通过发送和接收控制消息,如网络是否通畅、主机是否可达、路由是否可用等信息帮助网络管理员维护网络的正常运行。
二、作用:
CMP的核心作用是用于网络通信中的错误报告和网络诊断。以下是ICMP的主要功能和作用:
1. 错误报告
ICMP帮助网络设备(如路由器和主机)检测和报告通信中的问题。常见的错误报告功能包括:
- 目标不可达(Destination Unreachable):
当目标主机、网络或端口不可达时,发送ICMP Destination Unreachable消息。 - 超时(Time Exceeded):
当IP数据包在网络中超过其生存时间(TTL,Time-to-Live)时,发送ICMP超时消息。 - 重定向(Redirect):
当路由器发现有更好的路由时,发送ICMP Redirect消息,通知主机更新其路由表。
2.网络诊断
ICMP协议被广泛用于网络诊断工具。两个最常见的工具是:
- Ping:
Ping工具使用ICMP Echo Request和Echo Reply消息来测试目标主机是否可达,并测量往返时间(RTT)。 - Traceroute:
Traceroute工具利用ICMP Time Exceeded消息来追踪数据包从源到目的地的路径。
3.网络状态信息
ICMP可以向主机提供网络状态信息,帮助主机调整其行为。例如:
- 路由器可以发送ICMP Redirect消息,建议主机改变数据包的路由路径。
- 网络中的设备可以使用ICMP来检测网络连通性和性能。
4.支持IPv6(ICMPv6)
在IPv6中,ICMP协议的作用被进一步扩展,成为ICMPv6协议。ICMPv6不仅用于错误报告和诊断,还负责IPv6的关键功能,例如邻居发现(Neighbor Discovery Protocol, NDP)和路由器通告。
三、ICMP协议格式:
1. 抓包工具和方法:
在Linux操作系统中,可以使用工具如Wireshark、tcpdump或者是直接使用命令行工具如tcpdump来捕获和分析ICMP数据包。
sudo tcpdump -i [interface] icmp -w icmp.pcap
其中:
— [interface]是要监控的网络接口,可以使用ifconfig命令查看系统的网络接口名称;
—icmp: 抓取的协议类型,用于过滤;
—icmp.pcap 为要保存的文件;
2.ICMP报文通用格式

ICMP报文的格式可以分为两部分:ICMP头部和ICMP数据部分。不同类型的ICMP报文有不同的格式,但所有ICMP报文都包含一个基础的头部结构。
ICMP 消息的头部结构如下:
字段名称 |
长度 |
描述 |
Type |
1 字节 |
ICMP消息类型。 |
Code |
1 字节 |
ICMP消息代码。 |
Checksum |
2 字节 |
校验和,确保ICMP报文的完整性。 |
Rest of Header |
4 字节 |
不同类型的ICMP报文的其他信息。 |
—Type:指示 ICMP 消息的类型。不同类型的 ICMP 消息具有不同的功能;
—Code:表示该ICMP报文类型的具体子类型(每个ICMP类型都有若干个子类型)。例如,在类型3(Destination Unreachable)中,Code可以表示不同的子原因(如网络不可达、主机不可达等), ICMP协议主要通过 Type 和 Code 的组合,来标明具体的报文类型;
—Checksum:用于检测ICMP报文是否在传输过程中发生了错误。它是对ICMP报文头部和数据部分的校验和。
—Rest of Header:根据不同的ICMP类型,ICMP头部的结构有所不同。对于Echo Request/Echo Reply/ Timestamp Request/ Timestamp Reply消息,Rest of Header会包含标识符和序列号,其它类型不会包含标识符和序列号; 对于Destination Unreachable消息,Rest of Header包含原始IP数据报的前8字节。
2. ICMP报文类型分类:
ICMP报文根据其功能和用途可以分为两大类:差错报告报文和询问报文。

1). 查询报文类型:用于诊断的查询消息;
① 显请求(Echo Request)和回显应答(Echo Reply):
主机 / 路由器 询问特定主机 , 目的主机收到该报文后 , 必须给源主机 发送 ICMP 回答报文 ; 目的是 测试该 目的主机是否可达 ;
这些类型的ICMP报文用于ping操作(主机可达性测试)。Echo请求(Type 8)由发送方发送,Echo回复(Type 0)由接收方回复。

–类型: 回显请求:8; 回显应答:0
–代码:总是 0

–标识符:通常用于匹配请求和应答消息
a.作用:标识符字段用于区分不同的 ICMP 回显请求。它通常用于在发送多个请求时,帮助发送方识别对应的应答。
b.应用场景:当一个主机发送多个 ping 请求到同一目标时,每个请求都可以使用相同的标识符。接收到的应答中会包含相同的标识符,发送方可以通过这个字段来匹配请求和应答。
例如,如果一个主机同时对多个目标进行 ping 测试,标识符可以帮助它区分来自不同目标的响应。
–序列号:用于标识消息的顺序。
a. 作用:序列号字段用于标识发送的 ICMP 回显请求的顺序。它通常从 1 开始,每发送一个请求就递增 1。
b. 应用场景:通过序列号,发送方可以跟踪已发送的请求,检测丢包情况以及计算往返时间。比如,如果发送了 5 个请求,序列号分别为 1 到 5,接收到的应答可以通过序列号来确认哪些请求成功返回,哪些请求可能丢失。
以下抓包可以看出id相同,但seq在不断的累加。

其中标识符和序列号都有BE(大端)和LE(小端)两种不同的表示方法;

—数据:回显消息体中的数据内容。
② 时间戳请求(Timestamp Request)和时间戳应答(Timestamp Reply) :
请求 主机 / 路由器 当前的日期 和 时间 ; 用于进行时钟同步 和 时间测量 ;

ICMP时间戳响应的报文结构与时间戳请求报文相同,区别在于Type字段的值:
—Type:时间戳请求:13; 时间戳响应:14
—Code:固定为0,无特殊含义。
—Checksum:用于错误检测,计算整个ICMP报文的校验和。
—Identifier:用于标识请求和响应的唯一性,通常使用进程ID。
—Sequence Number:表示报文的序列号,通常从0开始递增。
—Originate Timestamp:发送请求报文的时间戳。
—Receive Timestamp:接收到请求报文的时间戳(在响应报文中设置)。
—Transmit Timestamp:响应报文发送的时间戳(在响应报文中设置)。
2). 差错报文类型:用于通知出错原因的错误消息;
常见的错误报文类型
类型值 |
名称 |
描述 |
3 |
Destination Unreachable(目标不可达) |
当目标主机或网络不可达时发送此报文。 |
4 |
Source Quench(源抑制,已弃用) |
通知源主机降低发送速率,避免网络拥塞(已被弃用)。 |
5 |
Redirect(重定向) |
通知源主机有更好的路由路径。 |
11 |
Time Exceeded(时间超时) |
当数据包的TTL(生存时间)字段减为0或分片重组超时时发送此报文。 |
12 |
Parameter Problem(参数问题) |
当IP头部字段出错(如参数非法)时发送此报文。 |
① 终点不可达报文Destination Unreachable (Type 3) :
路由器 / 主机 不能交付数据报时 , 就会向源点 发送 终点不可达报文 ;
字段名称 |
长度 |
描述 |
Type |
1 字节 |
3(Destination Unreachable) |
Code |
1 字节 |
0: 网络不可达 |
Checksum |
2 字节 |
校验和 |
Rest of Header |
4 字节 |
包含导致错误的原始IP数据报的前8字节 |
当路由器或目标主机无法到达目标时,会生成此报文。

② 时间超过报文Time Exceeded (Type 11) :
当一个数据包在网络中循环超过规定的TTL(生存时间)值时,路由器会返回Time Exceeded消息。
字段名称 |
长度 |
描述 |
Type |
1 字节 |
11(Time Exceeded) |
Code |
1 字节 |
0: 数据包的TTL减为0; |
Checksum |
2 字节 |
校验和 |
Rest of Header |
4 字节 |
包含导致错误的原始IP数据报的前8字节 |
常用于traceroute工具,通过逐步减少TTL值来发现数据包经过的路由器。
③ 参数问题报文 :
路由器 / 主机 收到的 数据报 首部 字段由错误值 , 丢弃该数据报 , 向源点发送 参数问题报文 ;
④ 改变路由报文(重定向,类型5) :
路由器 将 改变路由报文 发送给主机 , 让主机下次将数据报发送给另外的路由器 ; 又称为 “重定向报文”;
字段名称 |
长度 |
描述 |
Type |
1 字节 |
5(redirect) |
Code |
1 字节 |
0: 重定向到网络; |
Checksum |
2 字节 |
校验和 |
Rest of Header |
4 字节 |
包含导致错误的原始IP数据报的前8字节 |
通常由路由器发送,通知源主机有更好的路由路径。
四、ICMP协议/PING/TRACEROUTE原理:
1.ICMP协议原理
ICMP 主要的功能包括:
1) 确认IP 包是否成功送达目标地址
2) 报告发送过程中IP包被丢弃的原因
3) 改善网络设置等;
在 IP 通信中如果某个IP 包因为某种原因未能达到目标地址,那么这个具体的原因将由 ICMP 负表通知。

如上图例子,主机A向主机B发送了数据包,由于某种原因,途中的路由器2未能发现主机B的存在,这时,路由器2就会向主机A发送一个ICMP 目标不可达数据包,说明发往主机B的包未能成功。ICMP 的这种通知消息会使用IP 进行发送,
因此,从路由器2返回的ICMP 包会按照往常的路由控制先经过路由器1再转发给主机A。收到该 ICMP 包后,主机A则会解析 ICMP 的首部和数据域部分,从而得知具体发生问题的原因。
2.PING命令原理:
ping 是一个网络工具,用于测试网络连接的可达性。它通过发送 ICMP(Internet Control Message Protocol)回显请求消息到目标主机,并等待接收回显应答来工作。
1) ping 的工作流程如下:
a. 发送请求:当用户运行 ping 命令时,工具会创建一个 ICMP 回显请求数据包,并将其发送到指定的目标 IP 地址。
b. 接收应答:目标主机收到请求后,解析数据包并生成 ICMP 回显应答数据包,返回给源主机。
c. 计算延迟:源主机在收到应答后,计算从发送请求到接收应答所花费的时间,并显示给用户。
d. 统计信息:ping 还会统计成功发送和接收的请求数量、丢包率以及往返时间的最小、最大和平均值等信息。
以下为ping 过程中生成的抓包数据:

2)ping使用场景和作用
· 网络故障排查:ping 是网络故障排查的基本工具,可以帮助用户判断目标主机是否在线。
· 延迟测量:通过测量往返时间,用户可以了解网络的延迟情况。
· 丢包检测:通过发送多个请求,用户可以检测到网络中是否存在丢包现象。
3. TRACEROUTE原理:
1)解释:
traceroute 命令利用ICMP 协议定位您的计算机和目标计算机之间的所有路由器。TTL 值可以反映数据包经过的路由器或网关的数量,通过操纵独立ICMP 呼叫报文的TTL 值和观察该报文被抛弃的返回信息,traceroute命令能够遍历到数据包传输路径上的所有路由器。
2)原理:
Traceroute 的基本工作原理基于 ICMP 协议的 TTL(Time to Live) 字段和 ICMP 超时(Time Exceeded)消息。实现原理如下:
a. 初始发送:Traceroute 向目标主机发送一个带有初始 TTL 值(例如 1)的数据包。TTL 值为 1 的数据包会在第一个路由器处被丢弃,并返回一个 ICMP “Time Exceeded” 消息。这个消息的源 IP 地址就是第一个经过的路由器的地址。
b. 逐步增加 TTL:然后 Traceroute 会再次发送数据包,但这次将 TTL 增加 1(TTL = 2)。数据包会经过第一个路由器并到达第二个路由器,然后在第二个路由器处被丢弃,并返回一个 ICMP “Time Exceeded” 消息,显示第二个路由器的 IP 地址。
c. 继续递增 TTL:Traceroute 继续递增 TTL 的值,直到数据包到达目标主机。每次通过一个新的路由器,Traceroute 都能记录到该路由器的 IP 地址及响应时间。
d. 到达目标:当数据包的 TTL 值足够大,数据包最终到达目标主机时,目标主机会发送一个 ICMP “Echo Reply” 消息(与 ping 的工作原理类似)回源主机,标志着 Traceroute 结束。
在 traceroute 工具中,默认的 TTL(Time to Live)值通常为 30。这意味着,traceroute 在发送数据包时,初始的 TTL 值设置为 30,每经过一个路由器(跳数)会减 1。当 TTL 减至 0 时,数据包被丢弃,并返回一个 ICMP “Time Exceeded” 消息,traceroute 通过这个机制来识别经过的每一个路由器。
用法如下:
traceroute -m 10 http://www.baidu.com 当经过10个路由后就会丢弃此包。

五、代码示例:
以下是一个C语言程序示例,展示如何构建和发送ICMP时间戳请求报文,以及如何接收和解析响应报文。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <time.h> // 校验和计算函数 unsigned short checksum(void *b, int len) { unsigned short *buf = b; unsigned int sum = 0; unsigned short result; for (sum = 0; len > 1; len -= 2) { sum += *buf++; } if (len == 1) { sum += *(unsigned char *)buf; } sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); result = ~sum; return result; } // 获取当前时间戳(毫秒) unsigned int get_timestamp() { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <Target IP>\n", argv[0]); exit(EXIT_FAILURE); } const char *target_ip = argv[1]; int sock; struct sockaddr_in addr; struct icmphdr icmp_msg; unsigned char buffer[1024]; unsigned int originate_time, receive_time, transmit_time; // 创建原始套接字 sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sock < 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); } // 设置目标地址 memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(target_ip); // 构造ICMP时间戳请求消息 memset(&icmp_msg, 0, sizeof(icmp_msg)); icmp_msg.type = 13; // 时间戳请求 icmp_msg.code = 0; icmp_msg.un.echo.id = getpid(); // 使用进程ID作为标识符 icmp_msg.un.echo.sequence = 1; // 序列号 originate_time = get_timestamp(); // 获取当前时间戳 memcpy(buffer + sizeof(icmp_msg), &originate_time, sizeof(originate_time)); icmp_msg.checksum = checksum(&icmp_msg, sizeof(icmp_msg) + sizeof(originate_time)); // 发送ICMP报文 if (sendto(sock, &icmp_msg, sizeof(icmp_msg) + sizeof(originate_time), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("Send failed"); close(sock); exit(EXIT_FAILURE); } printf("ICMP Timestamp Request sent to %s\n", target_ip); // 接收ICMP响应 socklen_t addr_len = sizeof(addr); if (recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &addr_len) < 0) { perror("Receive failed"); close(sock); exit(EXIT_FAILURE); } // 解析ICMP响应 struct icmphdr *icmp_resp = (struct icmphdr *)(buffer + sizeof(struct iphdr)); if (icmp_resp->type == 14) { // 时间戳响应 memcpy(&originate_time, buffer + sizeof(struct iphdr) + sizeof(struct icmphdr), sizeof(originate_time)); memcpy(&receive_time, buffer + sizeof(struct iphdr) + sizeof(struct icmphdr) + sizeof(originate_time), sizeof(receive_time)); memcpy(&transmit_time, buffer + sizeof(struct iphdr) + sizeof(struct icmphdr) + sizeof(originate_time) + sizeof(receive_time), sizeof(transmit_time)); printf("ICMP Timestamp Response received:\n"); printf("Originate Timestamp: %u ms\n", ntohl(originate_time)); printf("Receive Timestamp: %u ms\n", ntohl(receive_time)); printf("Transmit Timestamp: %u ms\n", ntohl(transmit_time)); } else { printf("Unexpected ICMP response type: %d\n", icmp_resp->type); } close(sock); return 0; }
代码说明:
1. 发送请求:
- 设置ICMP类型为13,表示时间戳请求。
- 使用当前时间戳填充Originate Timestamp字段。
- 使用sendto()函数发送构造的ICMP时间戳请求报文。
2. 接收响应:
- 使用recvfrom()函数接收ICMP响应报文。
- 检查响应报文的类型是否为14(时间戳响应)。
- 解析Originate Timestamp、Receive Timestamp和Transmit Timestamp字段。
3. 时间戳格式:
- 时间戳以毫秒为单位,表示当天自午夜以来的时间。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/169569.html