ICMP协议(网际报文控制协议)详解


ICMP协议是一个网络层协议。 一个新搭建好的网络,往往需要先进行一个简单的测试,来验证网络是否畅通;但是IP协议并不提供可靠传输。如果丢包了,IP协议并不能通知传输层是否丢包以及丢包的原因。因此我们需要ICMP协议来完成这样的功能

ICMP协议的功能

ICMP协议的功能主要有:

  1. 确认IP包是否成功到达目标地址
  2. 通知在发送过程中IP包被丢弃的原因

如下图所示:
在这里插入图片描述

我们需要注意:ICMP是基于IP协议工作的,但是它并不是传输层的功能,因此仍然把它归结为网络层协议。

ICMP的报文格式

ICMP报文包含在IP数据报中,IP报头在ICMP报文的最前面。一个ICMP报文包括IP报头(至少20字节)、ICMP报头(至少八字节)和ICMP报文(属于ICMP报文的数据部分)。当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。

ICMP报头如下图所示:
在这里插入图片描述

  1. 类型:占1字节,标识ICMP报文的类型,从类型值来看ICMP报文可以分为两大类。第一类是取值为1~127的差错报文,第2类是取值128以上的信息报文
  2. 代码:占1字节,标识对应ICMP报文的代码。它与类型字段一起共同标识了ICMP报文的详细类型
  3. 校验和:占2字节,这是对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错
  4. 内容:占8字节。

常见的ICMP报文

常见的ICMP报文可以分为两大类,即差错报文查询报文

差错报文

常见的ICMP差错报文有以下几种:目的站不可达、数据报超时等。

目的站不可达

当路由器不能给数据报找到合适的路由路径,或者主机不能将数据报递交给上层协议时,相应的IP数据报就会被丢弃,然后一个目的站不可达差错控制报文将会被返回给源主机。目的站不可达差错可以由很多因素引起,例如网络不可达、主机不可达、协议不可达、端口不可达等,可以在报文首部中的代码字段指出具体原因。在ICMP目的不可达报文的数据区域,装载了引起目的站不可达的IP数据报首部及数据报数据区的前8个字节,当源主机收到这样一份ICMP报文后,它能根据报文数据区中的数据判断是哪个数据报出现了问题。装载引起差错的数据报数据区的前8个字节是因为这8个字节恰好覆盖了TCP报文或UDP报文中的端口号字段,IP层能够根据这个端口号把ICMP报文传递给具体的上层协议进行处理。

常用目的站不可达报文格式及代码字段常用取值如下:
在这里插入图片描述

在这里插入图片描述

数据报超时

数据报超时可以用来防止数据报在网络中被循环的路由,在IP首部中都有一个生存时间(TTL)字段,数据报每被转发一次,TTL的值便会减1,当TTL的值被减为0时,数据报会被网络丢弃,同时一个ICMP数据报超时报文会被返回给源主机。此外在分片重装时,也用到了ICMP超时报文,若某个数据报在重装过程中其重装时间超时,而数据分片还没有全部到达,此时与该数据报所有相关的分片将被删除,同时,一个ICMP超时报文将被返回给源主机。

常用数据报超时报文格式及代码字段常用取值如下:
在这里插入图片描述

在这里插入图片描述

查询报文

常见的ICMP查询报文有以下几种:回送请求或回答、时间戳请求或回答、路由器询问和通告、信息请求或回答、地址掩码请求或回答。

其中用得较多的是回送请求或回答、时间戳请求或回答,其他的三种查询报文目前几乎很少被用到,以前它们主要在主机启动时使用,通过使用这三种查询报文,主机能确定自身在网络环境中IP地址、地址掩码、路由器状况等信息,现在,DHCP协议已经能够完全实现这些功能。此外,在互联网中的两台主机能够使用时间戳请求或回答报文来确定数据报在彼此之间往返所需要的时间。

回送请求或回答

回送请求和回送回答报文是为诊断网络而设计的,我们使用这对报文来发现网络的问题,回送请求和回送回答报文组合起来就可以确定两个网络设备之间彼此是否能够通信。回收请求和回送回答报文可以直接确定两台主机的IP协议是否能够正常通信,这是因为ICMP报文是被封装在IP数据报中发送的,发送请求的主机能收到回答报文,证明两台主机之间能够使用IP协议进行通信,同时也能证明源主机与目的主机之间的所有路由器的接收、处理、转发功能正常。

回送请求或回答报文格式如下:
在这里插入图片描述

类型字段指出了是请求报文(8)还是回答报文(0);代码段无特殊取值,始终为0;首部中的标识符和序号两个字段在ICMP协议中没有定义其取值规范,因此发送方可以自由使用这两个字段,例如可以用序号来记录源主机发送出去的回送请求报文编号。可选数据区域表示回送请求报文中可包含的数据,其长度是可选的,发送方应该选择合适的长度并填充相应的数据。在接收方,它将根据这个回送请求产生一个回送回答,回送回答中的数据与回送请求中的数据应该完全相同。

  • 15
    点赞
  • 156
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个使用C语言实现的简单的ICMP协议分析代码,它可以发送和接收ICMP协议报文: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> #include <time.h> #include <netinet/ip_icmp.h> #define PACKET_SIZE 4096 #define MAX_WAIT_TIME 5 #define MAX_NO_PACKETS 3 char sendpacket[PACKET_SIZE]; char recvpacket[PACKET_SIZE]; int sockfd, datalen = 56; int nsend = 0, nreceived = 0; struct sockaddr_in dest_addr; pid_t pid; // 计算校验和 unsigned short cal_chksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } // 发送ICMP报文 void send_packet() { memset(sendpacket, 0, sizeof(sendpacket)); struct icmp *icmp_hdr = (struct icmp *)sendpacket; icmp_hdr->icmp_type = ICMP_ECHO; icmp_hdr->icmp_code = 0; icmp_hdr->icmp_cksum = 0; icmp_hdr->icmp_id = pid; icmp_hdr->icmp_seq = nsend++; memset(icmp_hdr->icmp_data, 0xa5, datalen); gettimeofday((struct timeval *)icmp_hdr->icmp_data, NULL); icmp_hdr->icmp_cksum = cal_chksum((unsigned short *)icmp_hdr, datalen + 8); sendto(sockfd, sendpacket, datalen + 8, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); } // 接收ICMP报文 void recv_packet() { int n; socklen_t fromlen; extern int errno; fromlen = sizeof(dest_addr); while (1) { if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&dest_addr, &fromlen)) < 0) { if (errno == EINTR) { continue; } perror("recvfrom error"); return; } struct iphdr *ip_hdr = (struct iphdr *)recvpacket; int ip_hdr_len = ip_hdr->ihl << 2; struct icmp *icmp_hdr = (struct icmp *)(recvpacket + ip_hdr_len); int icmp_len = n - ip_hdr_len; if (icmp_hdr->icmp_type == ICMP_ECHOREPLY && icmp_hdr->icmp_id == pid) { struct timeval *tvsend = (struct timeval *)icmp_hdr->icmp_data; struct timeval tvrecv; gettimeofday(&tvrecv, NULL); double rtt = (tvrecv.tv_sec - tvsend->tv_sec) * 1000.0 + (tvrecv.tv_usec - tvsend->tv_usec) / 1000.0; printf("%d bytes from %s: icmp_seq=%u ttl=%d time=%.1f ms\n", icmp_len, inet_ntoa(dest_addr.sin_addr), icmp_hdr->icmp_seq, ip_hdr->ttl, rtt); nreceived++; } } } // 主函数 int main(int argc, char *argv[]) { struct hostent *host; struct protoent *protocol; unsigned long inaddr = 0l; int waittime = MAX_WAIT_TIME; int size = 50 * 1024; if (argc < 2) { printf("usage:%s hostname/IP address\n", argv[0]); exit(1); } if ((protocol = getprotobyname("icmp")) == NULL) { perror("getprotobyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) { perror("socket error"); exit(1); } setuid(getuid()); setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); bzero(&dest_addr, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; if (inaddr = inet_addr(argv[1]) == INADDR_NONE) { if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname error"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length); } else { dest_addr.sin_addr.s_addr = inaddr; } pid = getpid(); printf("PING %s(%s): %d bytes data in ICMP packets.\n", argv[1], inet_ntoa(dest_addr.sin_addr), datalen); while (nsend < MAX_NO_PACKETS) { send_packet(); recv_packet(); sleep(waittime); } printf("--- %s ping statistics ---\n", argv[1]); printf("%d packets transmitted, %d received, %%%d lost\n", nsend, nreceived, (nsend - nreceived) / nsend * 100); close(sockfd); return 0; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值