《lwip学习8》-- 网际控制报文协议ICMP_lwip_broadcast_ping g工-程序员宅基地

技术标签: lwip  

IP 协议是一种不可靠、无连接的协议,只在各个主机间交付数据,但是对于数据的到达与否, IP 协议并不关心,为了提高数据交付的准确性, ICMP 就随之出现,在交付数据的时候,如果由于网络状况、链路不通等情况数据报无法到达目标主机,ICMP 就会返回一个差错报文,让源主机知道数据没能正常到达目标主机,接着进行重发或者放弃发送都可以。
ICMP 通常被认为是 IP 层协议的一部分,但从体系结构上讲它是位于 IP 之上的,因为ICMP 报文是承载在 IP 数据报中的。这就是说, ICMP 报文是作为 IP 数据报数据区域的(有一些书籍也称之为有效载荷) ,就像 TCP 与 UDP 报文段作为 IP 数据报数据区域那样。类似地,当一台主机收到一个指明上层协议为 ICMP 的 IP 数据报时,它将分解出该数据报的内容给 ICMP,就像分解出一个数据报的内容给 TCP 或 UDP 一样,但与 TCP 或 UDP 协议又有所不同, ICMP 出现的目的不是为上层应用程序提供服务,只是在 IP 层传递差错的报文,依赖于 IP 协议进行传输。

ICMP报文结构

ICMP 报文是使用 IP 数据报来封装发送的, 所以 ICMP 报文也是没有额外的可靠性与优先级,它一样会被别的路由器丢弃,与此同时, ICMP 报文封装在 IP 数据报中, IP 数据报封装在以太网帧中,因此 ICMP 报文是经过了两次的封装。
在这里插入图片描述
ICMP 报文与 IP 数据报一样,都是由首部与数据区域组成, ICMP 首部是 8 个字节,对于不同类型的 ICMP 报文, ICMP 报文首部的格式也会有点差异,但是首部的前 4 个字节都是通用的:
第一个字节(占据 8bit 空间)是类型(Type) 字段,表示产生这种类型 ICMP 报文的原因。
第二个字节是代码(Code) 字段,它进一步描述了产生这种类型 ICMP 报文的具体原因。因为每种类型的报文都可能有多个,比如目的不可达报文,产生这种原因可能有主机不可达、协议不可达、端口不可达等多个原因。
接下来的校验和字段(占据 16bit) 用于记录包括 ICMP 报文数据部分在内的整个ICMP 数据报的校验和,以检验报文在传输过程中是否出现了差错, 其计算方法与IP 数据包首部中的校验和计算方法是一样的。
剩下的 4 个字节部分在讲解报文类型的时候详细讲解,因为不同类型的报文都有不一样的定义,并且数据部分的长度也存在差异, ICMP 报文格式
在这里插入图片描述

ICMP报文类型

ICMP 报文有两大类型, 可以划分为差错报告报文和查询报文, 差错报告报文主要是用来向 IP 数据报源主机返回一个差错报告信息, 而这个差错报告信息产生的原因是路由器或者主机不能对当前数据报进行正常的处理,简单来说就是源主机发送的数据报没法到目标主机中,或者到达了目标主机而无法递交给上层协议。
查询报文是用于一台主机向另一台主机发起一个请求,如果目标主机收到这个查询的请求后,就会按照查询报文的格式向源主机做出应答,比如我们使用的 ping 命令,它的本质就是一个 ICMP 查询报文。
在这里插入图片描述

ICMP差错报文

目的不可达(类型 3)

LwIP 实现的只有前 6 种,代码取值如下
在这里插入图片描述
同时 ICMP 目的不可达报文首部剩下的 4 字节全部未用,而 ICMP 报文数据区域则装载 IP 数据报首部及 IP 数据报的数据区域前 8 字节,为什么需要装载 IP 数据报的数据区域中前 8 个字节的数据呢?因为 IP 数据报的数据区域前 8 个字节刚好覆盖了传输层协议中的端口号字段,而 IP 数据报首部就拥有目标 IP 地址与源 IP 地址,当源主机收到这样子的ICMP 报文后,它能根据 ICMP 报文的数据区域判断出是哪个数据包出现问题,并且 IP 层能够根据端口号将报文传递给对应的上层协议处理, ICMP 目的不可达报文的格式具体见图
在这里插入图片描述

ICMP查询报文

ICMP 回显请求报文和回显应答报文是 LwIP 中唯一实现的报文,即 ping

处理ICMP报文

LwIP 协议是轻量级 TCP/IP 协议栈,所以对 ICMP 报文中很多类型的报文都不做处理,LwIP 会将这些不处理的报文丢掉,但是对 ICMP 回显请求报文就做出处理,所以这也是为什么我们能 ping 通开发板的原因。当 IP 层收到一个 ICMP 报文的时候, 会调用 icmp_input()函数将报文传递到 ICMP 协议去处理,关于这个函数的处理也是比较简单明了的,只处理 ICMP 请求报文,然后返回一个 ICMP 应答报文,具体看注释即可, icmp_input()函数具体见代码清单

void
icmp_input(struct pbuf *p, struct netif *inp)
{
    
  u8_t type;
#ifdef LWIP_DEBUG
  u8_t code;
#endif /* LWIP_DEBUG */
  struct icmp_echo_hdr *iecho;
  const struct ip_hdr *iphdr_in;
  u16_t hlen;
  const ip4_addr_t *src;

  ICMP_STATS_INC(icmp.recv);
  MIB2_STATS_INC(mib2.icmpinmsgs);

  iphdr_in = ip4_current_header();
  hlen = IPH_HL_BYTES(iphdr_in);
  if (hlen < IP_HLEN) {
    
    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
    goto lenerr;
  }
  if (p->len < sizeof(u16_t) * 2) {
    
    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
    goto lenerr;
  }

  type = *((u8_t *)p->payload);
#ifdef LWIP_DEBUG
  code = *(((u8_t *)p->payload) + 1);
  /* if debug is enabled but debug statement below is somehow disabled: */
  LWIP_UNUSED_ARG(code);
#endif /* LWIP_DEBUG */
  switch (type) {
    
    case ICMP_ER:
      /* This is OK, echo reply might have been parsed by a raw PCB
         (as obviously, an echo request has been sent, too). */
      MIB2_STATS_INC(mib2.icmpinechoreps);
      break;
    case ICMP_ECHO: //对回显报文进行处理
      MIB2_STATS_INC(mib2.icmpinechos);
      src = ip4_current_dest_addr();
      /* multicast destination address? */
      if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
    
        goto icmperr;
      }
      /* broadcast destination address? */
      if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
    
#if LWIP_BROADCAST_PING
        /* For broadcast, use address of receiving interface as source address */
        src = netif_ip4_addr(inp);
#else /* LWIP_BROADCAST_PING */
        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
        goto icmperr;
#endif /* LWIP_BROADCAST_PING */
      }
      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
      if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
    
        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
        goto lenerr;
      }
      /* At this point, all checks are OK. */
      /* We generate an answer by switching the dest and src ip addresses,
       * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
      /* 调整回显报文请求中的相关字段以生成回显应答报文 */
      //强制将数据区域转换为 ICMP 报文首部
      iecho = (struct icmp_echo_hdr *)p->payload;
      if (pbuf_add_header(p, hlen)) {
    
        LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
      } else {
    
        err_t ret;
        struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
        ip4_addr_copy(iphdr->src, *src); //拷贝源 IP 地址
        ip4_addr_copy(iphdr->dest, *ip4_current_src_addr()); //拷贝目标 IP 地址
        ICMPH_TYPE_SET(iecho, ICMP_ER); //填写报文类型
#if CHECKSUM_GEN_ICMP
        IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
    
          /* adjust the checksum */
          if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
    
            iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
          } else {
    
            iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));
          }
        }
#if LWIP_CHECKSUM_CTRL_PER_NETIF
        else {
    
          iecho->chksum = 0;
        }
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
#else /* CHECKSUM_GEN_ICMP */
        iecho->chksum = 0;
#endif /* CHECKSUM_GEN_ICMP */

        /* Set the correct TTL and recalculate the header checksum. */
        IPH_TTL_SET(iphdr, ICMP_TTL); //填写生存时间
        IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP
        IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
    
          IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
        }
#endif /* CHECKSUM_GEN_IP */

        ICMP_STATS_INC(icmp.xmit);
        /* increase number of messages attempted to send */
        MIB2_STATS_INC(mib2.icmpoutmsgs);
        /* increase number of echo replies attempted to send */
        MIB2_STATS_INC(mib2.icmpoutechoreps);

        /* send an ICMP packet */
        ret = ip4_output_if(p, src, LWIP_IP_HDRINCL, /* 发送 ICMP 回显应答报文 */
                            ICMP_TTL, 0, IP_PROTO_ICMP, inp);
        if (ret != ERR_OK) {
    
          LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
        }
      }
      break;
    default: //对于其他类型的报文,直接丢掉
      if (type == ICMP_DUR) {
    
        MIB2_STATS_INC(mib2.icmpindestunreachs);
      } else if (type == ICMP_TE) {
    
        MIB2_STATS_INC(mib2.icmpintimeexcds);
      } else if (type == ICMP_PP) {
    
        MIB2_STATS_INC(mib2.icmpinparmprobs);
      } else if (type == ICMP_SQ) {
    
        MIB2_STATS_INC(mib2.icmpinsrcquenchs);
      } else if (type == ICMP_RD) {
    
        MIB2_STATS_INC(mib2.icmpinredirects);
      } else if (type == ICMP_TS) {
    
        MIB2_STATS_INC(mib2.icmpintimestamps);
      } else if (type == ICMP_TSR) {
    
        MIB2_STATS_INC(mib2.icmpintimestampreps);
      } else if (type == ICMP_AM) {
    
        MIB2_STATS_INC(mib2.icmpinaddrmasks);
      } else if (type == ICMP_AMR) {
    
        MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
      }
      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
                               (s16_t)type, (s16_t)code));
      ICMP_STATS_INC(icmp.proterr);
      ICMP_STATS_INC(icmp.drop);
  }
  pbuf_free(p);
  return;
lenerr:
  pbuf_free(p);
  ICMP_STATS_INC(icmp.lenerr);
  MIB2_STATS_INC(mib2.icmpinerrors);
  return;
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
icmperr:
  pbuf_free(p);
  ICMP_STATS_INC(icmp.err);
  MIB2_STATS_INC(mib2.icmpinerrors);
  return;
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_40831436/article/details/127205843

智能推荐

基于STM32串口通信的ESP8266WiFi模块使用_cipmux and cipserver must be 0-程序员宅基地

文章浏览阅读8.8w次,点赞196次,收藏1.9k次。掌握esp8266的使用可以实现真正的万物物联。esp8266wifi通信对于MCU而言归结到底还是串口或spi通信。因此,掌握RS232通信协议、SPI通信协议以及esp8266的配置就可以基本搞定WiFi模块的使用。参考文章:[1] ESP8266WiFi模块AT指令入门指南[2] ESP8266 WIFI串口通信模块使用详解(实例:附STM32详细代码)[3] STM32串口接收..._cipmux and cipserver must be 0

win7系统远程桌面 无法连接到服务器,win7系统远程连接提示“由于以下原因无法连接”的解决方法...-程序员宅基地

文章浏览阅读1.4k次。很多小伙伴都遇到过win7系统远程连接提示“由于以下原因无法连接”的困惑吧,一些朋友看过网上零散的win7系统远程连接提示“由于以下原因无法连接”的处理方法,并没有完完全全明白win7系统远程连接提示“由于以下原因无法连接”是如何解决的,今天小编准备了简单的解决办法,只需要按照 1)未启用对服务器的远程访问 2)远程计算机已关闭的顺序即可轻松解决,具体的win7系统远程连接提示“由于以下原因无法连..._win7 无法远程 到服务器

vue_element-admin整合SpringBoot实现登录_vue-element-admin 整合springboot登录-程序员宅基地

文章浏览阅读3.4k次。SpringBoot实现vue-admin-template登录接口vue-admin-templatevue-admin-template是一个简化版的vue-element-admin的模板,适合vue用来做项目的搭建,虽然官方文档以及花裤衩前端大佬在博客中已经将大部分的疑难杂症解决了,但当完全自己搭建的时候才发现多折腾vue-admin-template登录接口请求详解要想搞懂一个接口,最好的方式就是1.去看接口的定义声明的参数2.发送请求查看返回的数据格式开始1.先配置跨域等基_vue-element-admin 整合springboot登录

百度离线地图瓦片下载_百度瓦片地图下载-程序员宅基地

文章浏览阅读2.2k次。瓦片简介百度离线地图是由多层级的多张瓦片组成的,存放在titles文件夹下,层级越高,瓦片越多,显示的地图越详细下载的瓦片有png和jpg等格式,我下载的png格式没法用,后面全改成jpg格式就可以了将下载的瓦片按照下载级别放到对应的文件夹下瓦片获取从别的博主那找了两种获取方式1、链接: http://www.xiaoguo123.com/p/baidumap_offline_v2/.从这个链接里边的网盘下载,按照博主的介绍一步步做,一定要看使用前必读,需要按照用前必读改一下百度的密钥这个瓦_百度瓦片地图下载

调Q技术的基本原理-程序员宅基地

文章浏览阅读1.7w次,点赞19次,收藏68次。几分钟复习一下调Q技术~ 基本原理及常见方法详解。复习面试必备!_调q

维护网络服务器安全的七个小技巧-服务器安全资源网-程序员宅基地

文章浏览阅读127次。技巧一:从基本做起***开始对你的网络发起***的时候,他们首先会检查是否存在一般的安全漏洞。因此,当你服务器上的数据都存在一个FAT的磁盘分区的时候,即使安装上世界上所有的安全软件也不会对你有多大帮助的。因此,你需要从基本做起。将服务器上所有包含了敏感数据的磁盘分区都转换成NTFS格式的。同时,可以为Exchange Server安装反病毒软件,将被感染的邮件在到达用户以前..._简述你利用互联网进行资源检索时遇到的问题或是小技巧,列举并上传至活动心得

随便推点

CentOS 7 构造GCC 4.8.2 32位编译环境_libpthread库 yum安装-程序员宅基地

文章浏览阅读1.2w次。CentOS 7 构造GCC 4.8.2 32位编译环境_libpthread库 yum安装

第三次作业——《原型设计》-程序员宅基地

文章浏览阅读424次。原型设计这个作业属于哪个课程这个作业要求在哪里https://www.cnblogs.com/harry240/p/11524127.html这个作业的目标1,熟悉三种原型设计工具并学会使用他们; 2,对给出的题目进行需求分析; 3,设计题目所需要的原型背景简介:什么是原型设计 ?原型设计是产品经理确认需求、设计产品最重要的沟通工具。一,对..._供不完全的原型设计文件-“数据集实例(不全).rp”, 完成如下任务

MT7628无法连接_mt7628有些mac地址导致ap无法被连接-程序员宅基地

文章浏览阅读2.8k次。MT7628驱动版本:4.0.13现象:手机或者pad无法连接MT7628 APlog:[ 3633.276000] Qidx(0), not enough space in MgmtRing, MgmtRingFullCount=957![ 3633.288000] Qidx(0), not enough space in MgmtRing, MgmtRingFullCount=_mt7628有些mac地址导致ap无法被连接

NoSQL学习笔记 – Dynamo-程序员宅基地

文章浏览阅读146次。要想入门NoSQL,先读圣经Dynamo。 Amazon的这篇论文《Dynamo: Amazon's Highly Available Key-value Store》网上随处可以下得到,据说搞NoSQL的人都是从这里爬出去的。短短16页,不大好看,但非常精彩。不好看不是说写的不好,而是里面提到了很多分布式系统的概念和算法,要引经据典不容易弄懂(好吧,其实是个人基础太差)..._dynamo的物理架构

基于maven的spring-struts2-mybatis(注解版)环境搭建 对spring中常用注解进行详细说明 包含图片上传 ajax发送异步请求 select标签详细案例_spring-struts maven-程序员宅基地

文章浏览阅读264次。引言所用版本:jdk1.8 tomcat7.88 所用数据库:mysql所用技术:maven-spring4.3.2-struts2-mybatis-ajax 基于注解版//主要实现 两张表 增删改查 包含图片上传 ajax发送异步请求 主要对 select标签 有详细案例本案例中用到的常用注解总结如下:1.实体类上:@Component此注解表示 创建简单对象 默认构建的简单对..._spring-struts maven

IPP库的安装以及源码分析方法_图像处理ipp库源码-程序员宅基地

文章浏览阅读4.2k次,点赞5次,收藏23次。目录第一部分 IPP环境搭建(基于win10的WSL)1.1 WSL的几种安装方法1.1.1 开启wsl支持1.1.2 (可选)使用Windows store直接安装(1)下载(2)安装1.1.3 (可选)使用LxRunOffline自定义安装wsl1. 安装LxRunOffline2. 下载wsl离线文件3. 解压wsl offline文件4. 安装wsl5. 打开wsl6. wsl初始化1.2 (可选)使用VScode连接wsl1.2.1 插件下载1.2.2 连接wsl1.3 在ubuntu上安装IPP_图像处理ipp库源码