当前位置: 首页 > news >正文

【网络协议 02】ICMP协议报文

1、CMP协议简介


ICMP(Internet Control Message Protocol)是一种网络协议,它用于在IP网络中传递控制信息和错误消息。它通常与IP协议一起使用,IP协议负责发送和路由数据包,而ICMP协议负责检查网络是否可达、路由是否正确、主机是否可达等网络状态的反馈信息。

2、ICMP协议的主要功能

发现网络错误:当一个数据包在传输过程中出现错误时,ICMP协议通过向发送方发送错误通知来发现网络错误。

检查网络是否可达:通过发送ICMP ECHO请求并接收ICMP ECHO回复消息,可以确定目标主机是否可达。

发现主机错误:当一个主机无法正常工作时,ICMP协议通过向发送方发送错误通知来发现主机错误。

发送路由信息:ICMP协议可以向其他主机发送路由信息,以帮助它们在网络中找到合适的路由。


3、CMP报文格式


3.1、 ICMP报文以太网数据帧格式 

 ICMP报文属于IP子协议,协议号为1。

3.2、 ICMP首部格式

其中各字段的含义如下:

类型(Type):指定 ICMP 报文的类型,占 1 个字节。常见类型有:回显应答(Echo Reply:0)、回显请求(Echo Request:8)等。

代码(Code):指定 ICMP 报文的代码,占 1 个字节。用于进一步描述 ICMP 报文,与 Type 字段组合使用。

校验和(Checksum):校验和,用于检查 ICMP 报文是否有损坏,占 2 个字节。

由类型决定的4字节:根据类型不一样,4字节表达的意思不一样。

数据(Data):数据,可变长度。可以是任意数据,长度由具体的 ICMP 报文类型和代码决定。
 

3.3、 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(地址掩码请求/应答):用于请求另一个主机的网络掩码,并将其返回给请求方。

4、ICMP校验和计算


ICMP校验和计算的校验数据为整个ICMP数据包。

4.1 、ICMP校验和计算


a.校验数据以16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。

b.如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。

c.循环步骤2,直至未产生进位为止。

d.累加和取反得到校验和。

4.2、 ICMP校验和验证


a.校验数据16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。

b.如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。

c.循环步骤2,直至未产生进位为止。

d.累加和和校验和相加得到0xffff,校验成功,否则失败。


5、ICMP编程示例

5.1、发送回显请求

#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 28uint16_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;
}

5.2、发送回显应答

#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;
}

http://www.xdnf.cn/news/847477.html

相关文章:

  • acl是什么
  • Qemu架构解析(二),涨知识
  • 字符串的全面解析
  • shiro(一):shiro基本概念及基本使用(认证、授权)
  • 手把手教你安装Kali Linux
  • PaddleOCR 的使用,极简介绍
  • 颜色代码对照表
  • 单点登录(SSO)看这一篇就够了
  • JAR 文件规范详解
  • NSFW检测 (色情检测)
  • 如何实现SGD的高效并行计算:性能提升
  • spring常用注解(六)@Valid和@Validated校验
  • 什么是单点登录(SSO)前端用 iframe 实现单点登录 超详细说明!!
  • 什么是Eureka?Eureka能干什么?Eureka怎么用?
  • AES加解密工具类
  • 手把手带你搞懂Modbus通信协议
  • NAT工作原理(细致易懂)
  • 【C语言】解决C语言报错:Stack Overflow
  • HDFS最基础使用
  • 以太坊的 ChainId 与 NetworkId
  • phoenix索引
  • HBase高阶(一)基础架构及存储原理
  • linux curl命令详解
  • 网络抓包工具Wireshark下载安装使用详细教程
  • 运输管理系统(TMS):一文扫盲,物流、制造业、零售电商都得用
  • 渗透测试-社会工程学与APT攻击
  • Sass:提升CSS开发效率的利器
  • TLS 详解
  • ansible的介绍,安装与部署
  • 数据结构——队列(Queue)