ICMP协议详解

news2024/11/26 14:32:55

目录

1.ICMP协议简介

2.ICMP报文格式

 2.1 ICMP报文以太网数据帧格式

2.2 ICMP首部格式

2.3 ICMP报文类型列表

3.ICMP故障排查工具

3.1 ping工具

3.2 traceroute工具

4.常见ICMP报文

4.1 ICMP请求和应答

4.2 ICMP差错报告报文

4.3 目标主机不可达

5.ICMP校验和计算

5.1 ICMP校验和计算

5.2 ICMP校验和验证

6.ICMP编程示例

6.1 发送回显请求

6.2 发送回显应答


1.ICMP协议简介

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

ICMP协议的主要功能如下:

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

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

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

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

2.ICMP报文格式

 2.1 ICMP报文以太网数据帧格式

 图 1 ICMP以太网数据帧

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

2.2 ICMP首部格式

图 2 ICMP首部格式

其中各字段的含义如下:

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

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

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

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

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

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

3.ICMP故障排查工具

3.1 ping工具

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:指定等待响应的最大时间

3.2 traceroute工具

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地址。

4.常见ICMP报文

4.1 ICMP请求和应答

执行ping命令,ping一个可以通信的IP地址,如下命令:

ping 223.5.5.5

ping通后会收到对端发来的ICMP应答报文。

ICMP回显请求报文

 图 3 ICMP回显请求报文

ICMP回显响应报文

 图 4 ICMP回显响应报文

4.2 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过期差错报告报文 

4.3 目标主机不可达

当我们使用ping命令向一个主机发送ICMP(Internet控制消息协议)数据包时,如果目标主机无法到达,我们将会得到“目标主机不可达”的错误提示。

这个错误通常是由以下几种原因引起的:

  1. 目标主机已经关闭或没有连接到网络。这种情况下,我们无法通过网络与目标主机通信。

  2. 网络连接故障。如果网络连接故障,例如连接断开或路由器故障,那么我们无法到达目标主机。

  3. 防火墙阻止了ping请求。如果目标主机上的防火墙设置了规则以阻止ping请求,那么我们无法与目标主机进行通信。

  4. ICMP协议被禁用。有些主机可能会禁用ICMP协议,这意味着它们不会回应ping请求。

ICMP目标主机不可达报文

ICMP type:3,code:1

5.ICMP校验和计算

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

5.1 ICMP校验和计算

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

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

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

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

5.2 ICMP校验和验证

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

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

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

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

6.ICMP编程示例

6.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 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;
}

6.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.coloradmin.cn/o/629616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ROS学习——通信机制(话题通信④—自定义msg)

052话题通信_自定义msg_实现_Chapter2-ROS通信机制_哔哩哔哩_bilibili 2.1.4 话题通信自定义msg Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 一、自定义msg实现 需求:创建自定义消息&#xff0c;该消息包含人的信息:姓名、身高、年龄等。 1.定义msg文件 &am…

python基础语法学习--字面量

1、字面量的定义 在代码中&#xff0c;被写下来的固定的值。 2、常见值类型 数字&#xff1a;Number&#xff0c;包含整数int、浮点数float、复数complex、布尔bool四类。 整数int&#xff1a;如10、-10等 浮点数float&#xff1a;如13.14、-13.14等 复数complex&#xff1a;如…

Cracking C++(6): 准确打印浮点数

文章目录 1. 目的2. 准确打印浮点数&#xff1a; 使用 fmt 库3. 准确算出被表示的值3.1 直观感受IEEE-754: float-toy3.2 获取浮点数二进制表示3.3 float 类型3.4 double 类型3.5 fp16 类型3.6 验证 4. 结论和讨论5. References 1. 目的 给 float 或 double 类型的变量赋值后&…

chatgpt赋能python:Python教程:如何倒序输出字典?

Python教程&#xff1a;如何倒序输出字典&#xff1f; Python是一种强大的编程语言&#xff0c;广泛用于各种类型的应用程序开发。在开发应用程序时&#xff0c;访问和操作数据是至关重要的一步&#xff0c;而字典是Python中最有用的数据结构之一。字典允许开发人员使用键值对…

Linux安装myql8.0操作步骤

1. 创建software目录&#xff0c;目录可以自定义 mkdir /usr/local/softwar 2. 进入目录software&#xff0c;获取安装包文件 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.32-linux-glibc2.12-x86_64.tar.xz 3. 解压文件 tar -vxf mysql-8.0.32-…

【MySQL数据库 | 第十六篇】存储引擎

目录 前言&#xff1a; MySQL体系结构图&#xff1a; 存储引擎简介&#xff1a; 1. InnoDB存储引擎&#xff1a; 2. MyISAM存储引擎&#xff1a; 3. MEMORY存储引擎&#xff1a; 4. NDB Cluster存储引擎&#xff1a; 5. ARCHIVE存储引擎&#xff1a; 存储引擎语法&#…

chatgpt赋能python:Python再次运行快捷键介绍及使用技巧

Python再次运行快捷键介绍及使用技巧 如果你是一名经验丰富的Python工程师&#xff0c;你一定知道如何使用Python快捷键加速代码调试和开发。然而&#xff0c;在实际开发中有许多情况下&#xff0c;你需要再次运行刚刚输入的代码块或函数&#xff0c;这个时候&#xff0c;你必…

JavaWeb开发聊天功能 聊天信息如何实现自动将其他消息上移 最新消息出现在界面下方

问题 JavaWeb开发聊天功能 聊天信息如何实现自动将其他消息上移 最新消息出现在界面下方 详细问题 笔者基于开发JavaWeb开发聊天功能&#xff0c;当用户处于聊天室中&#xff0c;若用户发送一条信息或用户接收到聊天对象的信息&#xff0c;若要查看信息&#xff0c;需要下滑…

Storm超实用教程详解-附示例

一、理论基础 Storm 是一个免费并开源的分布式实时计算系统。利用 Storm 可以很容易做到可靠地处理无限的 数据流&#xff0c;像 Hadoop 批量处理大数据一样&#xff0c;Storm 可以实时处理数据。在Storm中&#xff0c;topology的构建是一个有向无环图。结点就是Spout或者Bolt&…

CKA 04_部署 harbor 仓库 containerd 连接 harbor 仓库 kubeadm 引导集群

文章目录 1. 清空之前的策略1.1 kubeadm 重置1.2 刷新 IPtables 表 2. 查看 Kubernetes 集群使用的镜像3. 搭建 harbor 仓库3.1 部署 docker3.1.1 准备镜像源3.1.2 安装 docker3.1.3 开机自启 docker3.1.4 修改内核参数&#xff0c;打开桥接3.1.5 验证生效 3.2 准备 harbor 仓库…

chatgpt赋能python:Python如何写网站的SEO

Python如何写网站的SEO 在当今的数字时代&#xff0c;网站是公司或个人吸引更多目标受众和客户的重要媒介之一。然而&#xff0c;拥有优秀内容和设计不足以保证流量。搜寻引擎优化(SEO)是一项重要的工作&#xff0c;它帮助网站排名更高&#xff0c;被更多人看到。Python是一种…

chatgpt赋能python:Python技术分享:如何再建立一个文档的SEO

Python技术分享&#xff1a;如何再建立一个文档的SEO Python作为一种高级编程语言&#xff0c;被业内大量使用。它的易用性、跨平台性、语法简单易懂、代码可读性高等特性进一步增强了它的流行度。在使用Python编程时&#xff0c;经常会需要生成文档&#xff0c;使得我们的项目…

软考A计划-系统架构师-官方考试指定教程-(12/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

chatgpt赋能python:Python如何免费群发短信

Python如何免费群发短信 在数字化时代&#xff0c;短信成为了快速高效的沟通方式之一。针对群发短信需求&#xff0c;市场上存在着多种短信群发软件&#xff0c;而Python作为一个强大的编程工具&#xff0c;也可以轻松实现免费的短信群发功能。本篇文章将为大家介绍如何通过Py…

从QGIS图层中裁剪需要的区域

GiS数据裁切&#xff0c;创建一个临时图层&#xff0c;通过矢量裁切的方法&#xff0c;将Gis图层进行裁切&#xff1b;影像裁切&#xff0c;将影像图层放置在Gis中&#xff0c;截取影像图以及临时图层的轮廓&#xff0c;放入PS中进行对比&#xff0c;然后将影像图裁切下来。 1…

软件测试01:软件及分类和缺陷的定义

软件测试&#xff1a;软件及分类和缺陷的定义 软件 程序数据文档 软件分类 层次分类 系统软件应用软件组织分类 商业软件开源软件结构分类 单机软件分布式软件(两种&#xff1a;BS服务端架构模型和CS客户端架构模型) 软件缺陷 软件缺陷的由来 起源于上世纪70年代中期 《测…

javaScript蓝桥杯---新增地址

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 网购大家应该再熟悉不过&#xff0c;网购中有个很难让人忽略的功能就是地址管理&#xff0c;接下来就让我们通过完善代码来探索下地址管理中常用功能的实现吧&#xff5e; 本题需要在已提供的基础项目中使用 JS 知识…

chatgpt赋能python:Python输入:优秀的函数、方法和技巧

Python输入&#xff1a;优秀的函数、方法和技巧 引言 从文件读取数据&#xff0c;在终端上接收用户输入&#xff0c;在Web应用程序中处理表单数据&#xff0c;这些都是Python中经常需要进行的一些任务。Python内置了许多函数和方法来处理这些任务&#xff0c;但是从众多的选项…

CSS 谈谈你对重排和重绘的理解

一、前言 当我们给我们的DOM结构改变或者给DOM结构设置样式时&#xff0c;会触发回流和重绘&#xff0c;但不同的样式改变&#xff0c;是否触发重排和重绘是不确定的。我们有必要深度理解重排和重绘&#xff0c;通过减少重排可以提高性能。 了解浏览器的解析渲染机制: (1).解析…

C++中的数组理解与应用

数组的数据结构 数组是最基本的数据结构&#xff0c;关于数组的面试题也屡见不鲜&#xff0c;本文罗列了一些常见的面试题&#xff0c;仅供参考。目前有以下18道题目。 数组求和 求数组的最大值和最小值 求数组的最大值和次大值 求数组中出现次数超过一半的元素 求数组中元…