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

【LwIP源码学习7】ICMP部分源码分析

ICMP功能简介

当IP数据报由于网络状况、链路不通等问题无法到达目标主机时,ICMP会返回一个差错报文。

ICMP查询报文(ping)

标识符用于标识在同一台主机上同时运行了多个ping程序,序列号从0开始,每发送一次新的回显请求就进行加1。
在这里插入图片描述

源码分析

PACK_STRUCT_BEGIN
struct icmp_echo_hdr {PACK_STRUCT_FLD_8(u8_t type);PACK_STRUCT_FLD_8(u8_t code);PACK_STRUCT_FIELD(u16_t chksum);PACK_STRUCT_FIELD(u16_t id);PACK_STRUCT_FIELD(u16_t seqno);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

对应着ICMP报文格式。
echo:回声,表示验证链路可达性。
hdr:header,头部。
seqnono表示number,No.1

ICMP目标不可达代码枚举为:

enum icmp_dur_type {/** net unreachable */ICMP_DUR_NET   = 0,/** host unreachable */ICMP_DUR_HOST  = 1,/** protocol unreachable */ICMP_DUR_PROTO = 2,/** port unreachable */ICMP_DUR_PORT  = 3,/** fragmentation needed and DF set */ICMP_DUR_FRAG  = 4,/** source route failed */ICMP_DUR_SR    = 5
};

dur :destination unreachable ,目标不可达
ICMP超时代码枚举为:

enum icmp_te_type {/** time to live exceeded in transit */ICMP_TE_TTL  = 0,/** fragment reassembly time exceeded */ICMP_TE_FRAG = 1
};

te:time exceeded,超时

当ip数据报无法到达传输层或者应用层时,调用icmp_dest_unreach()函数返回一个ICMP协议报文不可达。

void
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{MIB2_STATS_INC(mib2.icmpoutdestunreachs);icmp_send_response(p, ICMP_DUR, t);
}

dest_unreach:目标不可达

根据

#define MIB2_STATS_INC(x) STATS_INC(x)
#define STATS_INC(x) ++lwip_stats.x

两个宏可知,
MIB2_STATS_INC(mib2.icmpoutdestunreachs);相当于

++lwip_stats.mib2.icmpoutdestunreachs;

lwip_stats是全局统计数据容器,在stats.c文件中定义如下:

struct stats_ lwip_stats;

结构体struct stats_stats.h文件中定义:

/** lwIP stats container */
struct stats_ {
#if LINK_STATS/** Link level */struct stats_proto link;
#endif
。。。
#if MIB2_STATS/** SNMP MIB2 */struct stats_mib2 mib2;
#endif
};

mib2 : management information base,2表示版本号是2,base有库和集合的意思,所以含义是“管理数据集合”

stats.h中有如下两个宏

#define STATS_INC(x) ++lwip_stats.x
#define STATS_DEC(x) --lwip_stats.x

inc:increment,递增
dec:decrement,递减

如果数据报超时,lwip会调用icmp_time_exceeded()函数发送一个ICMP超时报文

void
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{MIB2_STATS_INC(mib2.icmpouttimeexcds);icmp_send_response(p, ICMP_TE, t);
}

time_exceeded:超时

icmp_send_response分析
源码为:

static void
icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
{struct pbuf *q;struct ip_hdr *iphdr;/* we can use the echo header here */struct icmp_echo_hdr *icmphdr;ip4_addr_t iphdr_src;struct netif *netif;/* increase number of messages attempted to send */MIB2_STATS_INC(mib2.icmpoutmsgs);/* ICMP header + IP header + 8 bytes of data */q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,PBUF_RAM);if (q == NULL) {LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));MIB2_STATS_INC(mib2.icmpouterrors);return;}LWIP_ASSERT("check that first pbuf can hold icmp message",(q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));iphdr = (struct ip_hdr *)p->payload;LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);LWIP_DEBUGF(ICMP_DEBUG, (" to "));ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);LWIP_DEBUGF(ICMP_DEBUG, ("\n"));icmphdr = (struct icmp_echo_hdr *)q->payload;icmphdr->type = type;icmphdr->code = code;icmphdr->id = 0;icmphdr->seqno = 0;/* copy fields from original packet */SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);ip4_addr_copy(iphdr_src, iphdr->src);
#ifdef LWIP_HOOK_IP4_ROUTE_SRC{ip4_addr_t iphdr_dst;ip4_addr_copy(iphdr_dst, iphdr->dest);netif = ip4_route_src(&iphdr_dst, &iphdr_src);}
#elsenetif = ip4_route(&iphdr_src);
#endifif (netif != NULL) {/* calculate checksum */icmphdr->chksum = 0;
#if CHECKSUM_GEN_ICMPIF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {icmphdr->chksum = inet_chksum(icmphdr, q->len);}
#endifICMP_STATS_INC(icmp.xmit);ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);}pbuf_free(q);
}

输入的三个变量中,
struct pbuf *p用于查找输出接口对象netif,和目标ip地址iphdr_src
u8_t type, u8_t code用于填充输出内容。

接下来是函数体,可以看到声明的几个变量:

  struct pbuf *q;struct ip_hdr *iphdr;/* we can use the echo header here */struct icmp_echo_hdr *icmphdr;ip4_addr_t iphdr_src;struct netif *netif;

与他们第一次被使用的顺序是相同的。

struct netif *
ip4_route(const ip4_addr_t *dest)

route:路由
该函数用于根据ip地址查找对应接口的netif,所有的netif都挂在netif_list链表中。

err_t
ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,u8_t ttl, u8_t tos,u8_t proto, struct netif *netif)

用IPv4协议发送数据。其上层还有一个函数:

err_t
ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,u8_t ttl, u8_t tos, u8_t proto)

接下来是icmp_input(struct pbuf *p, struct netif *inp)函数,用于处理输入的ICMP数据,其中只对回显请求报文做了处理,所以ping运行lwip的设备可以ping通。

其中有一段:

const ip4_addr_t *src;src = ip4_current_dest_addr();

其中:

#define ip4_current_dest_addr()    (&ip_data.current_iphdr_dest)
struct ip_globals ip_data;
struct ip_globals
{/** The interface that accepted the packet for the current callback invocation. */struct netif *current_netif;/** The interface that received the packet for the current callback invocation. */struct netif *current_input_netif;
#if LWIP_IPV4/** Header of the input packet currently being processed. */const struct ip_hdr *current_ip4_header;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6/** Header of the input IPv6 packet currently being processed. */struct ip6_hdr *current_ip6_header;
#endif /* LWIP_IPV6 *//** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */u16_t current_ip_header_tot_len;/** Source IP address of current_header */ip_addr_t current_iphdr_src;/** Destination IP address of current_header */ip_addr_t current_iphdr_dest;
};

ip_globals:表示ip层相关的一些全局变量。声明的变量为ip_data
其中“当前的”用current来表示。

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

相关文章:

  • 【工具篇2】Gitee导入github repo作为持续的镜像站,自建 GitHub 镜像仓库详细步骤
  • Web转uni-app
  • 如何使用 Xshell 8 连接到一台 CentOS 7 电脑(服务器)
  • CellCharter | 入门了解
  • Linux 服务器故障全解析:常见问题及处理方法大全
  • imx6ull-驱动开发篇44——Linux I2C 驱动实验
  • PP工单状态JEST表
  • 浅聊达梦数据库物理热备的概念及原理
  • Ubuntu 切换 SOCKS5代理 和 HTTP 代理并下载 Hugging Face 模型
  • 三方相机问题分析八:【返帧异常导致性能卡顿】Snapchat后置使用特效预览出现卡顿
  • OpenTelemetry 在 Spring Boot 项目中的3种集成方式
  • 互联网大厂Java面试深度解析:从基础到微服务云原生的全场景模拟
  • 嵌入式linux相机(1)
  • CPU、IO、网络与内核参数调优
  • 【目标检测】论文阅读5
  • 6.8 学习ui组件方法和Element Plus介绍
  • 【C++】类型系统:内置类型与自定义类型的对比
  • FlashAttention算法原理
  • 元宇宙与医疗健康:重构诊疗体验与健康管理模式
  • 【开题答辩全过程】以 微信小程序的老年活动中心为例,包含答辩的问题和答案
  • LabVIEW 音频信号处理
  • 火焰传感器讲解
  • laravel学习并连接mysql数据库
  • 煤矸石检测数据集VOC+YOLO格式3090张2类别
  • Python爬虫获取1688商品列表与图片信息
  • AGDO-BP+NSGAII梯度下降优化算法优化BP神经网络+NSGAII多目标优化算法,三目标和四目标案例
  • 【Oracle篇】伪列之ROWID:行数据的物理地址(基于物理地址对行数据最快速度的查询、更新、删除)(第四篇,总共六篇)
  • Python 前后端框架实战:从选型到搭建简易全栈应用
  • 使用MP4视频格式链接地址的自适应视频弹窗实现方案HTML代码
  • 共享云服务器替代传统电脑做三维设计会卡顿吗