icmp报文及用go实现

目录

一、概述

二、ICMP报文格式详解

2.1 什么是ICMP

2.2 ICMP报文格式

2.3 ICMP报文类型

2.4 实际报文举例

三、使用go实现icmp请求以及接收响应内容


一、概述

本文主要旨在学习icmp报文格式,以及通过go语言来实现ICMP发包。

二、ICMP报文格式详解

2.1 什么是ICMP

因特网控制报文协议ICMP(Internet Control Message Protocol)是一个差错报告机制,是TCP/IP协议簇中的一个重要子协议,通常被IP层或更高层协议(TCP或UDP)使用,属于网络层协议,主要用于在IP主机和路由器之间传递控制消息,用于报告主机是否可达、路由是否可用等。这些控制消息虽然并不传输用户数据,但是对于收集各种网络信息、诊断和排除各种网络故障以及用户数据的传递具有至关重要的作用。ICMP的功能是检错而不是纠错。

2.2 ICMP报文格式

CMP报文包含在IP数据报中,属于IP的一个用户,IP头部就在ICMP报文的前面,所以一个ICMP报文包括IP头部、ICMP头部和ICMP报文,IP头部的Protocol值为1就说明这是一个ICMP报文,ICMP头部中的类型(Type)域用于说明ICMP报文的作用及格式,此外还有一个代码(Code)域用于详细说明某种ICMP报文的类型,所有数据都在ICMP头部后面

  • type:类型,1字节,报文类型,用来标识报文
  • code:代码,1字节,提供报文类型的进一步信息
  • checksum:校验和,2字节,使用和IP相同的加法校验和算法,但是icmp校验仅覆盖ICMP报文
  • Message body:数据部分,长度可变,字段的长度的和内容,取决于消息的类型和代码

2.3 ICMP报文类型

1、类型比对表

typecode描述查询/差错
0--Echo(应答)响应0Echo Reply -- 回显应答(Ping应答)        查询
3--目的不可达0

Network Unreachable -- 网络不可达

差错
1Host Unreachable -- 主机不可达差错
2Protocol Unreachable --协议不可达差错
3Port Unreachable --端口不可达差错
4Fragmentation needed but no frag. bit set--要求分段并设置DF flag标志报文差错
5Source routing failed --源路由失败报文差错
6Destination network unknown --目的网络未知差错
7Destination host unknown --目的主机未知差错
8Source host isolated(obsolete)--源主机被隔离(作废不用)差错
9Destination network administratively prohibited -- 目的网络被强制禁止差错
10Destination host administratively prohibited --目的主机被强制禁止差错
11Network unreachable for TOS --对特定的TOS网络不可达报文差错
12Host unreachable for  TOS --对特定的TOS主机不可达报文差错
13Communiation administratively prohibited by filtering --由于过滤 网络流量被禁止报文差错
14Host precedence violation --主机越权报文差错
15Precedence cutoff ineffect --优先权终止生效报文差错
4--流量控制0Source quench --源端被关闭(基本流控制)差错
5--重定向0Redirect for network --对网络重定向差错
1Redirect for host --主机重定向差错
2Redirect for TOS and network --对服务类型和网络重定向差错
3Redirect for TOS and host --对服务类型和主机重定向差错
8--Echo请求0Echo request -- 回显请求(ping请求)查询
9-路由器通告0Router advertisement --路由器通告查询
10--路由器请求0Route solicitation --路由器的发现/选择/请求报文查询
11--ICMP超时0TTL equals 0 during transit --传输期间生存时间为0差错
1TTL equals 0 during reassembly --在数据报组装期间生存时间为0差错
12--参数问题0IP header bad(catchall error) --坏的IP首部(包括各种差错)差错
1Required options missing --缺少必须的选项差错
2不支持的长度报文差错
13--时间戳请求0Timestamp request(obsolete) --时间戳请求(作废不用)查询
14--时间戳应答Timestamp reply(obsolete) --时间戳应答(作废不用)查询
15--信息请求Information request(obsolete) --信息请求(作废不用)查询
16--信息应答0Information reply(obsolete) --信息应答(作废不用)查询
17--掩码请求0Address mask request --地址掩码请求查询
18--掩码应答0Address mask reply --地址掩码应答查询

2、ICMP分类

  • 差错报文

①目的不可达

目的不可达的类型字段值为3,代码字段有为0-15,也就是说若将目的不可达的ICMP报文再做一个细分,会将目的不可达的原因分为16种,并用不同ICMP差错报文进行表示。

  • code=0;代表着网络不可达,出现这个ICMP差错报文,就代表着报文在路由过程的时候出现了问题,比如报文的目的网络在路由器上没有相应的条目,于是该路由器就回送网络不可达的报文
  • code=1;代表主机不可达,这个报文的来源一般是目的主机所处的网关发送的,因为目的主机所处的网关没有找到对应的目的主机的IP地址,而无法转交该数据报文,所以将数据报文丢弃并回送该ICMP差错报文。
  • code=2;代表着协议不可达,这就说明数据交互的双方在协议上的出现了问题。
  • code=3;代表着端口不可达,这就说明数据包上指定的目的端口在目的主机上可能没有监听
  • code=4;代表一个原本需要分片的数据包,但是IP头部上的表示是不进行分片,由此就出现了错误。比如我们可以设置自己的网卡的MTU大小比网关的MTU大,那么我们发送过去的数据在被网关接收后可能会出现错误,因为网关网卡的最大接收MTU数比发送过来的数据包小,而且这个数据包还标识不进行分片,这就会出现错误。

②参数问题

参数问题的类型字段值为12,它主要是因为对IP头部中的字段值出现了问题,从而导致收到这些问题报文的主机返送一个参数问题的ICMP差错报文

  • ICMP控制报文
  • ①源站抑制

type=4,code=0

源站抑制是拥塞控制的一种方式,虽然TCP在端到端上使用了窗口机制和慢开始,拥塞避免和快重传对流量进行了控制,网关通过对链路上的链路情况进行监控,对信源发送源站抑制里面包含着目的网络的信息,当接收方接收该信息后根据目的网络信息知道去往该网络的链路发生拥塞,于是减少信息的发送。

  • ②路由重定向

type=5;code=0-3

路由重定向是指当主机发送给某个路由器的时候,这个路由器会判断自己是否是最佳的转发设备,如果根据它的路由信息发现其他的转发设备对于该主机来说最好,也就是能够更快的将数转发到目的对象,那么它就将发送路由重定向给这个主机让它将路由修改为更佳的路由。更佳路由的信息存储在ICMP的后4个字节上,

  • ICMP查询报文
  • ①请求和回应报文

type=8,code=0

需要注意的是请求和回应的ICMP报文使用到了ICMP头部的后4个字节,分为两个字段,即标识(和序列号,标识一般是发送该报文的进程号,标识和序列号是标识一对请求和回应报文,只有与某请求报文对应的回应报文,它们的标识与序列号才是相同的。

需要注意的是,请求和回应的ICMP数据包中的数据部分都是相同的。

  • ②路由询问或通告

路由询问的类型字段值为10,通告的类型字段值为9,只有一个代码0

该类型报文主要用于无盘工作站,没有办法保存网关的情况,它就只能靠发送路由询问,来询问网关信息。路由询问报文只用了ICMP头部的前面4个字节,但是路由通告使用了全部的8个字节。、后4个字节有三个字段,分别为“地址数,地址项长度,生存时间”,它们占用的长度是1B,1B,2B这三个字段记载着数据部分包含的路由条目数量,路由条目的长度(即IP地址的长度),以及路由条目在路由器上面的有效生存时间。

在该ICMP报文中,每个路由信息分为路由地址和优先级,各自占用4个字节,优先级越高越有可能成为该主机的默认网关。

  • ③时间戳请求与应答

时间戳的请求的类型字段为13,应答为14,只有一个代码0

它的头部与请求与回应的ICMP报文一致,但是数据部分它使用了12个字节,每4个字节记录一段时间信息,总共有三段,分别是“发送时间戳 ,接收时间戳,回送时间戳”,发送时间戳的信息由时间戳请求者记录,后面两个字段由回送者记录。字段里面记录的是有关当前时间的毫秒数的表示,发送者只要根据回送者发送的时间信息就可以很容易的求出往返时长。

  • ④地址掩码请求和应答

请求的类型字段值为17,应答的类型字段为18,只有一个代码0

它的ICMP头部与请求的ICMP包的头部相同,数据字段存储的是请求的子网掩码

 PS:

  1. Identifier(标识符):Identifier 是一个16位的字段,通常用于标识 ICMP Echo 请求和响应之间的匹配。当发送 ICMP Echo 请求时,Identifier 字段的值会被设置为一个特定的标识符(通常是随机生成的),然后在接收到 ICMP Echo 响应时,接收端会将相同的标识符字段包含在响应中,以便发送端能够识别与响应相关联的请求。

  2. Sequence Number(序列号):Sequence Number 是一个16位的字段,它通常用于按顺序对 ICMP Echo 请求和响应进行排序。每个 ICMP Echo 请求都会包含一个唯一的序列号,然后在接收到 ICMP Echo 响应时,接收端会将相同的序列号字段包含在响应中,以便发送端能够识别响应与哪个请求相对应。

这两个字段的组合(标识符和序列号)允许发送端将 ICMP Echo 请求与响应正确匹配,从而可以测量网络的延迟和连通性。当发送多个 ICMP Echo 请求时,这些字段的组合确保了每个响应都与特定的请求关联,并且可以按顺序排列。

需要注意的是,Identifier 和 Sequence Number 的确切含义可能因 ICMP 报文的类型和用途而有所不同。上述解释是针对 ICMP Echo 请求和响应的常见用法。其他类型的 ICMP 报文可能会使用这些字段以不同的方式

2.4 实际报文举例

1、请求响应

请求包

响应包

2、网络、主机、协议、端口不可达

三、使用go实现icmp请求以及接收响应内容

package mainimport ("bytes""container/list""encoding/binary""flag""fmt""log""net""os""time"
)type ICMP struct {Type        uint8Code        uint8Checksum    uint16Identifier  uint16SequenceNum uint16
}var (timeout int64size    intcount   inttyp     uint8 = 8code    uint8 = 0data    string
)func main() {//获取命令输入内容getCommandArgs()if len(os.Args) < 2 {log.Fatal("Usage: programname target_ip")}raddr, _ := net.ResolveIPAddr("ip", os.Args[len(os.Args)-1])desIP := raddr.String()//建立连接,以及最后释放连接conn, err := net.DialTimeout("ip:icmp", desIP, time.Duration(timeout)*time.Millisecond)if err != nil {log.Fatal(err)}defer conn.Close()//发送报文展示格式// statistic := list.New()icmp := *&ICMP{Type:        typ,Code:        code,Checksum:    0,Identifier:  0,SequenceNum: 0,}icmpdata := []byte(data)recv := make([]byte, 1024)statistic := list.New()sended_packets := 0var buffer bytes.Bufferbinary.Write(&buffer, binary.BigEndian, icmp)buffer.Write(icmpdata)icmp.Checksum = CheckSum(buffer.Bytes())buffer.Reset()binary.Write(&buffer, binary.BigEndian, icmp)buffer.Write(icmpdata)fmt.Printf("正在Ping %s [%s] 具有 %d 字节的数据:\n", raddr, conn.RemoteAddr(), len(data))for i := 0; i < count; i++ {t_start := time.Now()_, err := conn.Write(buffer.Bytes())if err != nil {log.Fatalln(err)continue}sended_packets++conn.SetReadDeadline((time.Now().Add(time.Second * time.Duration(timeout))))_, err = conn.Read(recv)if err != nil {fmt.Println("请求超时")continue}t_end := time.Now()dur := t_end.Sub(t_start).Nanoseconds() / 1e6 //计算请求时间// fmt.Printf("来自 %s 的回复: 字节 = %s 时间 = %dms\n", desIP, len(data), dur)fmt.Printf("来自 %s 的回复: 字节 = %d 时间 = %dms TTL = %d\n", conn.RemoteAddr(), len(data), dur, recv[8])statistic.PushBack(dur)defer buffer.Reset()}//最后统计请求包defer func() {fmt.Println("")var min, max, sum int64if statistic.Len() == 0 {min, max, sum = 0, 0, 0} else {min, max, sum = statistic.Front().Value.(int64), statistic.Front().Value.(int64), int64(0)}for v := statistic.Front(); v != nil; v = v.Next() {val := v.Value.(int64)switch {case val < min:min = valcase val > max:max = val}sum = sum + val}recved, losted := statistic.Len(), sended_packets-statistic.Len()fmt.Printf(" %s 的Ping统计信息\n  数据包: 已发送 = %d, 已接收 = %d, 丢失 = %d (%.1f%% 丢失),\n往返行程的估计时间(以毫秒为单位):\n  最短 = %dms, 最长 = %dms, 平均 = %.0fms\n",desIP,sended_packets, recved, losted, float32(losted)/float32(sended_packets)*100,min, max, float32(sum)/float32(recved),)}()}func getCommandArgs() {flag.Int64Var(&timeout, "w", 5, "请求超时时长, 单位秒")flag.StringVar(&data, "d", "this is icmp databody", "请求的数据内容")flag.IntVar(&count, "n", 4, "发送请求数")flag.Parse()
}func CheckSum(data []byte) uint16 {length := len(data)index := 0var sum uint32 = 0for length > 1 {sum += uint32(data[index])<<8 + uint32(data[index+1])length -= 2index += 2}if length != 0 {sum += uint32(data[index])}hi16 := sum >> 16for hi16 != 0 {sum = hi16 + uint32(uint16(sum))hi16 = sum >> 16}return uint16(^sum)}

使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80080.shtml

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

相关文章

练习:使用servlet显示试卷页面

试卷页面代码 在浏览器输入如下地址&#xff1a; http://localhost/examPageServlet 效果如下&#xff1a;

SV-6002T-P 网络对讲求助终端,立柱式智慧城市网络对讲求助终端,停车场出入口一键求助终端

SV-6002T-P 网络对讲求助终端&#xff0c;立柱式智慧城市网络对讲求助终端&#xff0c;停车场出入口一键求助终端 描述&#xff1a;SV-6002T是深圳锐科达电子有限公司的一款新型立柱型室外防水一键求助对讲终端&#xff0c;具有10/100M以太网接口&#xff0c;其接收网络的音频数…

基于Java+freemarker实现动态赋值以及生成Word文档

前言 有一个需求就是给定一个正确格式的 Word 文档模板&#xff0c;要求通过动态赋值方式&#xff0c;写入数据并新生成 该模板格式的 Word 文档。这很明显使用 Javafreemarker 方式来实现颇为简单。 一、导入依赖 <!-- freemarker --> <dependency><groupId…

Qt点亮I.MX6U开发板的一个LED

本篇开始将会介绍与开发版相关的Qt项目&#xff0c;首先从点亮一个LED开始。I.MX6U和STM32MP157的相关信息都会用到&#xff0c;但是后期还是将I.MX6U的学习作为重点。当然其他开发版的开发也可以参考本博文。 文章目录 1. Qt是如何操控开发板上的一个LED2. 出厂内核设备树中注…

Spring-Cloud GateWay+Vue 跨域方案汇总

文章目录 一、简介背景和概述 二、前端跨域解决方案Axios跨域CORS跨域 三、后端跨域解决方案反向代理服务器 四、Spring Cloud中的跨域解决方案Gateway网关的跨域配置 五、基于Vue和Spring Cloud的跨域整合实践**这两种配置只需配置一种即可生效&#xff08;前端or后端&#xf…

CPU的三级缓存

CPU缓存&#xff08;Cache Memory&#xff09;是位于CPU与内存之间的临时存储器&#xff0c;它的容量比内存小的多但是交换速度却比内存要快得多。高速缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾&#xff0c;因为CPU运算速度要比内存读写速度快很多&#…

使用Python CV2融合人脸到新图片--优化版

优化说明 上一版本人脸跟奥特曼图片合并后边界感很严重&#xff0c;于是查找资料发现CV2还有一个泊松函数很适合融合图像。具体代码如下&#xff1a; import numpy as np import cv2usrFilePath "newpic22.jpg" atmFilePath "atm2.jpg" src cv2.imrea…

Gmail邮箱注册情况及最新动态

在中国大陆地区&#xff0c;对于是否可以注册Gmail邮箱一直存在一定的限制和讨论。准确来说&#xff0c;中国大陆地区的用户目前无法直接访问和注册Gmail邮箱。由于某些政策和技术原因&#xff0c;中国政府对于一些外国的网站和服务实施了网络封锁与限制。因此&#xff0c;中国…

JMeter基础 —— 使用Badboy录制JMeter脚本!

1、使用Badboy录制JMeter脚本 打开Badboy工具开始进行脚本录制&#xff1a; &#xff08;1&#xff09;当我们打开Badboy工具时&#xff0c;默认就进入录制状态。 如下图&#xff1a; 当然我们也可以点击录制按钮进行切换。 &#xff08;2&#xff09;在地址栏中输入被测地…

Java学习——基本语法笔记

1. 基本框架 Java中的程序是以类为单位&#xff0c;所以所有的程序都必须在class定义范畴之内&#xff0c; 类的定义有两种形式&#xff1a; class 类名称{程序代码 } public class 类名称{程序代码 } ⭐public class定义类&#xff0c;要求文件名称与类名称一致 ⭐如果现在没有…

Linux学习之MySQL连接查询

接上一篇 连接查询 连接查询也中多表查询&#xff0c;常用于查询来自于多张表的数据&#xff0c;通过不同的连接方式把多张表组成一张新的临时表&#xff0c;再对临时表做数据处理。 #表基础信息&#xff0c;内容可从上一篇博客中查看 mysql> desc departments; ---------…

gRpc_go_dart-1.编写第一个服务

​ 通俗的讲下grpc 简化掉所有复杂的实现,它要求服务端和客户端之间按照protobuf的规范进行数据交换,所以服务端和客户端都不用关心彼此的代码实现,只关心按照protobuf的形式提供数据 为什么是go和dart 技术栈,已经是google的形状了 同时,go客户端和Flutter间本身通过http…

Java(运算符+循环)万字超详细介绍 (囊括了按位,异或,for,while等基础和疑难知识)

【本节目标1】熟练掌握运算符 【本章目标2】熟练掌握循环 万字讲解&#xff0c;十分详细&#xff0c;有大量&#xff08;简单&#xff09;代码帮助理解和大量的&#xff08;简单&#xff09;举例与总结。 1.运算符 1.什么是运算符 计算机最基本的用途之一就是执行数学运算…

如何实现一个简单的Promise/A+规范的Promise库?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Promise/A规范的Promise⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚…

ROS2-IRON Ubuntu-22.0 源码下载失败解决方法 vcs import --input

ROS2 一.ROS2 IRON环境搭建1.设置系统字符集为UTF-82.将RO2 apt 库添加到系统中3.添加ROS2 GPG key4.添加ROS 2 的软件源安装开发工具 二.下载ROS2sh源代码编译 一.ROS2 IRON环境搭建 虚拟机系统&#xff1a;Ubuntu22.04 虚拟机&#xff1a;VMware-player-full-16.2.5-2090451…

身份和访问管理解决方案:混合型IAM

对于依赖于本地 IT 基础结构和传统安全模型的组织&#xff0c;可以更轻松地验证和授权企业网络内的所有内容&#xff0c;包括设备、用户、应用程序和服务器。尝试从公司网络外部获取访问权限的用户使用虚拟专用网络 &#xff08;VPN&#xff09; 和网络访问控制 &#xff08;NA…

【Spring Boot系列】- Spring Boot侦听器Listener

【Spring Boot系列】- Spring Boot侦听器Listener 文章目录 【Spring Boot系列】- Spring Boot侦听器Listener一、概述二、监听器Listener分类2.1 监听ServletContext的事件监听器2.2 监听HttpSeesion的事件监听器2.3 监听ServletRequest的事件监听器 三、SpringMVC中的监听器3…

匿名管道-

因为父子进程是共享文件描述符的环形队列&#xff0c;只能读一次 会被后面覆盖 /*#include <unistd.h>int pipe(int pipefd[2]);功能&#xff1a;创建一个匿名管道&#xff0c;用于进程间通信参数&#xff1a;int 类型数组 &#xff0c;是传出参数pipefd[0]是管道读端 p…

Git多人开发解决冲突案例

准备工作&#xff1a; 1.创建一个gitee远程仓库https://gitee.com/xxxxxxx.git 2.初始化两个本地git仓库用户&#xff0c;目的是模拟多人协作开发时提交代码发生冲突的场景 3.解决冲突并提交。 进入正题&#xff1a; lisi 通过vim指令修改readme.md文件内容&#xff0c;推送到…

Centos8系统中安装docker-compose报错(已解决)

1.报错内容&#xff1a; ModuleNotFoundError: No module named setuptools_rust Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-jrzp2ukw/bcrypt/2.报错原因&#xff1a; 在CentOS8中安装“加密”程序包时出现问题。当包所需的…