赞
踩
目录
ICMP(Internet Control Message Protocol)是一种网络协议,它用于在IP网络中传递控制信息和错误消息。它通常与IP协议一起使用,IP协议负责发送和路由数据包,而ICMP协议负责检查网络是否可达、路由是否正确、主机是否可达等网络状态的反馈信息。
ICMP协议的主要功能如下:
发现网络错误:当一个数据包在传输过程中出现错误时,ICMP协议通过向发送方发送错误通知来发现网络错误。
检查网络是否可达:通过发送ICMP ECHO请求并接收ICMP ECHO回复消息,可以确定目标主机是否可达。
发现主机错误:当一个主机无法正常工作时,ICMP协议通过向发送方发送错误通知来发现主机错误。
发送路由信息:ICMP协议可以向其他主机发送路由信息,以帮助它们在网络中找到合适的路由。
图 1 ICMP以太网数据帧
ICMP报文属于IP子协议,协议号为1。
图 2 ICMP首部格式
其中各字段的含义如下:
类型(Type):指定 ICMP 报文的类型,占 1 个字节。常见类型有:回显应答(Echo Reply:0)、回显请求(Echo Request:8)等。
代码(Code):指定 ICMP 报文的代码,占 1 个字节。用于进一步描述 ICMP 报文,与 Type 字段组合使用。
校验和(Checksum):校验和,用于检查 ICMP 报文是否有损坏,占 2 个字节。
由类型决定的4字节:根据类型不一样,4字节表达的意思不一样。
数据(Data):数据,可变长度。可以是任意数据,长度由具体的 ICMP 报文类型和代码决定。
表 1 ICMP报文类型表
常见的ICMP报文类型:
Echo Reply(回显应答):用于回复Echo Request(回显请求)报文,通常用于测试网络连接是否正常。
Destination Unreachable(目的地不可达):用于指示主机或路由器无法到达目的地或某个网络服务不可用。
Source Quench(源站抑制):当接收方无法处理所有传入的数据报时,源站抑制报文会发送到发送方,以通知其减慢数据传输速度。
Redirect(重定向):用于通知发送方,其正在使用的路由不再是最佳路由,建议使用另一条路由。
Echo Request(回显请求):用于测试测试网络连接是否正常。
Time Exceeded(时间超时):用于指示一个数据包在传输过程中被丢弃,原因是数据包在经过路由器时超过了其生存时间。
Parameter Problem(参数问题):用于指示数据包头部中存在错误的参数或选项,导致数据包无法被识别或处理。
Timestamp Request/Reply(时间戳请求/应答):用于向另一个主机请求当前时间戳,并将其返回给请求方。
Information Request/Reply(信息请求/应答):用于向另一个主机请求特定信息,并将其返回给请求方。
Address Mask Request/Reply(地址掩码请求/应答):用于请求另一个主机的网络掩码,并将其返回给请求方。
Ping命令是一种常用的网络诊断工具,用于测试网络连接性和响应时间。它发送一个ICMP数据包(Internet控制消息协议),并在目标主机收到数据包后返回一个响应,以确定目标主机是否可达,以及响应时间。
Ping命令的语法如下:
ping [-t] [-a] [-n count] [-l size] [-f] [-i TTL] [-v TOS] [-r count] [-s count] [[-j host-list] | [-k host-list]] [-w timeout] destination-list
其中,常用的参数包括:
-t:持续发送数据包,直到手动停止
-a:解析IP地址为主机名
-n count:指定要发送的数据包数
-l size:指定要发送的数据包大小
-f:在数据包中设置“不分片”标志
-i TTL:设置数据包的存活时间
-w timeout:指定等待响应的最大时间
traceroute命令用于检测网络连接的路径和延迟时间,以及确定网络上的故障点。它通过向目标主机发送一系列的数据包,并记录每个包从源主机到目标主机的路由路径上所经过的中间节点(路由器)。
traceroute命令会输出每个中间节点的IP地址、主机名(如果可用)、延迟时间和TTL值。TTL(Time to Live)值是每个数据包的生命周期,当数据包经过一个路由器时,TTL值就会减少1,如果TTL值降到0,则该数据包就会被丢弃并返回一个ICMP超时消息,这样我们就可以知道数据包到达了哪个中间节点。
例如,我们可以使用以下命令来traceroute到百度的IP地址(202.108.22.5):
traceroute 202.108.22.5
输出结果可能类似于以下内容:
- traceroute to 202.108.22.5 (202.108.22.5), 30 hops max, 60 byte packets
- 1 router (192.168.1.1) 2.025 ms 1.326 ms 1.115 ms
- 2 100.64.0.1 (100.64.0.1) 4.505 ms 4.591 ms 4.659 ms
- 3 218.240.40.121 (218.240.40.121) 7.131 ms 7.217 ms 7.291 ms
- 4 218.240.40.146 (218.240.40.146) 25.398 ms 25.397 ms 25.393 ms
- 5 202.96.12.26 (202.96.12.26) 25.373 ms 202.96.12.34 (202.96.12.34) 25.357 ms 25.344 ms
- 6 202.96.12.110 (202.96.12.110) 25.314 ms 25.301 ms 25.289 ms
- 7 202.97.94.118 (202.97.94.118) 25.492 ms 202.97.94.114 (202.97.94.114) 25.478 ms 25.463 ms
- 8 202.97.58.237 (202.97.58.237) 25.434 ms 25.409 ms 25.394 ms
- 9 202.97.58.233 (202.97.58.233) 25.372 ms 25.363 ms 25.349 ms
- 10 * * *
- 11 * * *
- 12 202.108.22.5 (202.108.22.5) 25.633 ms 25.618 ms 25.603 ms
从输出结果中可以看到,traceroute命令首先会输出目标主机的IP地址和最大跳数(30),然后每一行显示一个中间节点的信息。例如,第一行显示第一个中间节点的IP地址(192.168.1.1)、主机名(如果可用)、三次ping的延迟时间。最后一行显示目标主机的IP地址和延迟时间。在第10和11行中,我们看到了两个星号,这表示该数据包在到达该中间节点时已经超时并被丢弃了,因此我们无法确定该节点的IP地址。
执行ping命令,ping一个可以通信的IP地址,如下命令:
ping 223.5.5.5
ping通后会收到对端发来的ICMP应答报文。
ICMP回显请求报文
图 3 ICMP回显请求报文
ICMP回显响应报文
图 4 ICMP回显响应报文
TTL过期差错报告报文
图 5 TTL过期原理
ping命令通过-i指定TTL值,如下命令:
ping 223.5.5.5 -i 5 -t
命令指定TTL值为5,也就是通过5个路由器后,TTL会变成0,数据包丢弃,路由器发送ICMP TTL过期报文给源主机。
ICMP TTL过期报文
图 6 TTL过期差错报告报文
当我们使用ping命令向一个主机发送ICMP(Internet控制消息协议)数据包时,如果目标主机无法到达,我们将会得到“目标主机不可达”的错误提示。
这个错误通常是由以下几种原因引起的:
目标主机已经关闭或没有连接到网络。这种情况下,我们无法通过网络与目标主机通信。
网络连接故障。如果网络连接故障,例如连接断开或路由器故障,那么我们无法到达目标主机。
防火墙阻止了ping请求。如果目标主机上的防火墙设置了规则以阻止ping请求,那么我们无法与目标主机进行通信。
ICMP协议被禁用。有些主机可能会禁用ICMP协议,这意味着它们不会回应ping请求。
ICMP目标主机不可达报文
ICMP type:3,code:1
ICMP校验和计算的校验数据为整个ICMP数据包。
a.校验数据以16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。
b.如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。
c.循环步骤2,直至未产生进位为止。
d.累加和取反得到校验和。
a.校验数据16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。
b.如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。
c.循环步骤2,直至未产生进位为止。
d.累加和和校验和相加得到0xffff,校验成功,否则失败。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdint.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/ip_icmp.h>
- #include <arpa/inet.h>
-
- #define PACKET_SIZE 4096
- #define ICMP_PACKET_SIZE 28
-
- uint16_t checksum(uint16_t *buf, int len)
- {
- unsigned long sum = 0;
- while (len > 1) {
- sum += *buf++;
- len -= 2;
- }
- if (len == 1) {
- sum += *(unsigned char *)buf;
- }
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
- return ~sum;
- }
-
- int main(int argc, char *argv[])
- {
- if (argc != 2) {
- printf("Usage: %s <destination_ip>\n", argv[0]);
- return -1;
- }
-
- char buf[PACKET_SIZE] = {0};
- memset(buf, 0, sizeof(buf));
-
- int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- if (sockfd < 0) {
- perror("socket error");
- return -1;
- }
-
- struct sockaddr_in dest_addr;
- memset(&dest_addr, 0, sizeof(dest_addr));
- dest_addr.sin_family = AF_INET;
- dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
-
- uint16_t seq = 0;
- while(1) {
- memset(buf, 0, PACKET_SIZE);
- struct icmp *icmp_packet = (struct icmp *)buf;
- icmp_packet->icmp_type = ICMP_ECHO;
- icmp_packet->icmp_code = 0;
- icmp_packet->icmp_id = 0;
- icmp_packet->icmp_seq = seq++;
- memset(icmp_packet->icmp_data, 0, ICMP_PACKET_SIZE);
- icmp_packet->icmp_cksum = 0;
- icmp_packet->icmp_cksum = checksum((uint16_t *)icmp_packet, ICMP_PACKET_SIZE);
- printf("icmp_packet size:%lu\n", sizeof(struct icmp));
- int sent_bytes = sendto(sockfd, buf, sizeof(struct icmp), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
- if (sent_bytes <= 0) {
- perror("sendto error");
- break;
- }
-
- printf("sent icmp request:%d bytes to:%s\n", sent_bytes, argv[1]);
-
- int recv_bytes = recv(sockfd, buf, PACKET_SIZE, 0);
- if (recv_bytes <= 0) {
- perror("recv");
- break;
- }
- struct iphdr *ip_packet = (struct iphdr *)buf;
- struct icmp *icmp_reply = (struct icmp *)(buf + (ip_packet->ihl << 2));
- printf("recv icmp reply:%d from:%s\n", recv_bytes, inet_ntoa(dest_addr.sin_addr));
- printf("icmp type:%d,code:%d\n", icmp_reply->icmp_type, icmp_reply->icmp_code);
-
- sleep(1);
- }
-
- close(sockfd);
- return 0;
- }
- #include <stdio.h>
- #include <string.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <unistd.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/ip.h>
- #include <netinet/ip_icmp.h>
- #include <linux/in.h>
- #include <arpa/inet.h>
-
- #define IP_HDRLEN (20)
- #define PACKET_SIZE (4096)
-
- uint16_t checksum(uint16_t *buf, int len)
- {
- unsigned long sum = 0;
- while (len > 1) {
- sum += *buf++;
- len -= 2;
- }
- if (len == 1) {
- sum += *(unsigned char *)buf;
- }
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
- return ~sum;
- }
-
- uint16_t checksum_nofold(uint16_t *buf, int len)
- {
- unsigned long sum = 0;
- while (len > 1) {
- sum += *buf++;
- len -= 2;
- }
- if (len == 1) {
- sum += *(unsigned char *)buf;
- }
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
- return sum;
- }
-
- bool parse_pack(char *buf, uint32_t len) {
- struct icmp *icmp_packet = (struct icmp *)buf;
- uint16_t csum = checksum_nofold((uint16_t *)buf, len);
- printf("icmp csum:0x%04x\n", csum);
- return csum == 0xffff;
- }
-
- int main(int argc , char *argv[]) {
- int sockfd;
- int ret;
- char send_buf[PACKET_SIZE] = {0};
- char recv_buf[PACKET_SIZE] = {0};
-
- sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- if (sockfd == -1) {
- perror("socket error");
- return -1;
- }
-
- while(1) {
- struct sockaddr_in peer;
- socklen_t peerlen = sizeof(peer);
- memset(recv_buf, 0, PACKET_SIZE);
- ret = recvfrom(sockfd, recv_buf, PACKET_SIZE, 0, (struct sockaddr *)&peer, &peerlen);
- if (ret <= 0) {
- printf("ret:%d, errno:%d(%s)\n", ret, errno, strerror(errno));
- } else {
- printf("recv len:%d, peer src:port->%s:%d\n", ret, inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
- bool bret = parse_pack(recv_buf, ret);
- if (bret) {
- struct icmp *recv_icmp = (struct icmp *)(recv_buf + sizeof(struct iphdr));
- memset(send_buf, 0, PACKET_SIZE);
- struct icmp *icmp_packet = (struct icmp *)send_buf;
- icmp_packet->icmp_type = ICMP_ECHOREPLY;
- icmp_packet->icmp_code = 0;
- icmp_packet->icmp_id = 0;
- icmp_packet->icmp_seq = recv_icmp->icmp_seq;
- memset(icmp_packet->icmp_data, 0, sizeof(struct icmp));
- icmp_packet->icmp_cksum = 0;
- icmp_packet->icmp_cksum = checksum((uint16_t *)icmp_packet, sizeof(struct icmp));
- int sent_bytes = sendto(sockfd, send_buf, sizeof(struct icmp), 0, (struct sockaddr *)&peer, sizeof(peer));
- if (sent_bytes <= 0) {
- perror("sendto error");
- break;
- }
- }
- }
- }
-
- close(sockfd);
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。