网际控制报文协议

  • 背景
  • 相关概念
    • 报文类型
    • 报文格式
    • 差错报文
    • 查询报文
  • 代码实现
    • 数据结构
    • 发送差错报文代码实现
    • 回送报文请求

背景

IP协议并不完美,在传递数据时提供的是一种无连接的不可靠数据报交付,协议本身不提供任何错误检验和恢复机制。为弥补这个缺陷,于是引入网际控制报文协议ICMP。
ICMP作用于IP主机与路由器之间,控制的消息很多种:数据错误信息、网络状态信息、主机状况信息等,这些控制消息虽不传输用户信息,但对用户传输信息有重要影响。从协议栈分层上观察,ICMP属于网络层,配合IP完成数据传输,提高数据传输的有效性,虽ICMP有自己报文结构,但是被封装在IP报文中发送。

相关概念

一方面,IP本身不提供差错控制来保证数据传输的有效性。在路由器无法传输一个数据报或数据报生存时间为0时,路由器会直接丢弃这个数据报。很多情况下,源主机希望数据报传输出现异常时能得到相关失败信息,以便重传或其他处理。另一方面,IP缺少一个辅助机制,即主机的管理类和查询机制。在一些情况下,源主机要确定另一主机或路由器是否活跃,对于不活跃的主机没必要再发送数据报,因为发了也没用。还有一些情况,主机管理员希望获取另一个主机或路由的信息,根据这些信息进行自身的配置、数据报发送控制等。

报文类型

ICMP报文分为两大类:差错报告报文和查询报文。
ICMP报文封装如下:

差错报告报文主要向IP数据报源主机返回一个差错报告信息,这个信息产生的原因是路由器或主机不能对当前数据报进行正常的处理,例如数据无法传给上层协议、生存时间TTL为0而被删除等。
查询报文用于一台主机向另一台主机查询特定的信息,通常查询报文都是成对出现的,即源主机发起一个查询报文,目的主机会按照约定的格式向源主机返回一个应答报文。
常见类型如下表:

报文格式

ICMP报文由8字节首部和可变长度的数据部分组成,如下图所示:

不同类型的ICMP报文,首部格式有一定差异,但前4个字节是相同的。代码部分是标识该种类型ICMP报文的具体原因。这里校验和包括了整个ICMP报文。

差错报文

LWIP协议栈可根据数据报的处理异常情况发送目的站不可达报文和数据报超时报文,当收到任何类型差错报文时,直接丢弃报文,不做任何处理。

  1. 目的站不可达
    当路由器不能找到合适路由路径或主机不能向上层协议传递数据时,相应IP数据报被丢弃,然后一个目的站不可达差错报文被返回给源主机。代码段记录了差错原因,常见值如下表:

    目的站不可达报文如下:

  2. 数据报超时
    数据报超时用来防止数据报再网络中被循环的路由,IP首部有个生存计数器TTL,数据报每转发一次,TTL减1,当TTL被减为0时,数据报被网络丢弃,同时ICMP超时报文返回给源主机。常见值如下表:

    数据报超时报文如下:

  3. 源站抑制
    各个主机间采用稳定的流量控制机制提高数据交互的有效性。当路由器或主机因拥塞而丢弃数据报时,她可向源站发送源站抑制报文。

  4. 重定向
    当一台主句刚运行时,会将所有数据报发送给网络中一个默认路由器。若默认路由器收到数据报后,它就发送一个ICMP重定位报文告诉主机改变它的路由表了。

  5. 数据报参数错误
    数据报再网络传输时,首部出现的任何二义性都会产生严重问题,如果主机或路由发现这种二义性或数据报某个字段丢失,路由会直接丢弃数据报并返回一个数据报参数错误。

查询报文

常见ICMP查询报文有以下几种:回送请求、路由器查询和通告、时间戳请求、信息请求、地址掩码请求。使用较多的时回送请求和时间戳请求,而其他三个使用很少,他们三种主要主机启动时使用,现在DHCP协议已经完全实现这些功能。
LWIP协议栈能接收主机外部的回送请求,并根据报文返回一个ICMP回答报文。回送请求和回送回答时为诊断目的设计的,网络管理员和用户都可以使用这对报文来发现网络问题,两者组合起来可以确定两个网络设备之间彼此是否能够通信。
回送请求和回答报文格式如下:

类型字段的8表示请求报文,0表示回答报文。

代码实现

数据结构

//常见报文类型
#define ICMP_ER   0    /* echo reply */
#define ICMP_DUR  3    /* destination unreachable */
#define ICMP_SQ   4    /* source quench */
#define ICMP_RD   5    /* redirect */
#define ICMP_ECHO 8    /* echo */
#define ICMP_TE  11    /* time exceeded */
#define ICMP_PP  12    /* parameter problem */
#define ICMP_TS  13    /* timestamp */
#define ICMP_TSR 14    /* timestamp reply */
#define ICMP_IRQ 15    /* information request */
#define ICMP_IR  16    /* information reply */
//目的站不可达报文中代码字段常用取值
enum icmp_dur_type {ICMP_DUR_NET   = 0,  /* net unreachable */ICMP_DUR_HOST  = 1,  /* host unreachable */ICMP_DUR_PROTO = 2,  /* protocol unreachable */ICMP_DUR_PORT  = 3,  /* port unreachable */ICMP_DUR_FRAG  = 4,  /* fragmentation needed and DF set */ICMP_DUR_SR    = 5   /* source route failed */
};
//数据报超时报文中的代码字段取值
enum icmp_te_type {ICMP_TE_TTL  = 0,    /* time to live exceeded in transit */ICMP_TE_FRAG = 1     /* fragment reassembly time exceeded */
};
//回送请求报文首部结构
struct icmp_echo_hdr {PACK_STRUCT_FIELD(u8_t type);PACK_STRUCT_FIELD(u8_t code);PACK_STRUCT_FIELD(u16_t chksum);PACK_STRUCT_FIELD(u16_t id);PACK_STRUCT_FIELD(u16_t seqno);
} PACK_STRUCT_STRUCT;
//用于读取首部字段
#define ICMPH_TYPE(hdr) ((hdr)->type)
#define ICMPH_CODE(hdr) ((hdr)->code)
//用于首部字段写入相关值
/** Combines type and code to an u16_t */
#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t))
#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c))

源码中只定义了目的不可达报文、数据超时报文、回送请求报文相关数据结构。

发送差错报文代码实现

数据报不能递交任何一个上层协议时,函数icmp_dest_unreach会被调用,然后发送一个目的不可达差错报文给源主机。
发送超时报文的函数时icmp_time_exceeded,数据报转发和分片重装过程中,都可能调用这个函数,原因可能是:1.TTL为0;2.分片重装时间超时。

void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{icmp_send_response(p, ICMP_DUR, t);
}
void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{icmp_send_response(p, ICMP_TE, t);
}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;/* 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) {return;}iphdr = (struct ip_hdr *)p->payload;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);/* calculate checksum */icmphdr->chksum = 0;icmphdr->chksum = inet_chksum(icmphdr, q->len);ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);pbuf_free(q);
}

代码逻辑很简单,就是给报文申请空间,然后根据类型和代码字段填写数据,然后计算校验和,最后发送。

回送报文请求

目前LWIP只支持回送请求报文处理,其他类型的报文会直接丢弃,不做任何响应,这对于嵌入式产品已经够用了。回送请求中,icmp_input需要生成一个回送回答报文并返回给源主机。

void icmp_input(struct pbuf *p, struct netif *inp)
{u8_t type;struct icmp_echo_hdr *iecho;struct ip_hdr *iphdr;s16_t hlen;iphdr = (struct ip_hdr *)p->payload;hlen = IPH_HL(iphdr) * 4;if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {goto lenerr;}type = *((u8_t *)p->payload);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). */break; case ICMP_ECHO:{int accepted = 1;/* multicast destination address? */if (ip_addr_ismulticast(&current_iphdr_dest)) {accepted = 0;}/* broadcast destination address? */if (ip_addr_isbroadcast(&current_iphdr_dest, inp)) {accepted = 0;}/* broadcast or multicast destination address not acceptd? */if (!accepted) {pbuf_free(p);return;}}if (p->tot_len < sizeof(struct icmp_echo_hdr)) {goto lenerr;}if (inet_chksum_pbuf(p) != 0) {pbuf_free(p);return;}/* 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. */iecho = (struct icmp_echo_hdr *)p->payload;ip_addr_copy(iphdr->src, *ip_current_dest_addr());ip_addr_copy(iphdr->dest, *ip_current_src_addr());ICMPH_TYPE_SET(iecho, ICMP_ER);/* adjust the checksum */if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;} else {iecho->chksum += PP_HTONS(ICMP_ECHO << 8);}/* Set the correct TTL and recalculate the header checksum. */IPH_TTL_SET(iphdr, ICMP_TTL);IPH_CHKSUM_SET(iphdr, 0);IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));if(pbuf_header(p, hlen)) {} else {err_t ret;/* send an ICMP packet, src addr is the dest addr of the curren packet */ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,ICMP_TTL, 0, IP_PROTO_ICMP, inp);}break;default:pbuf_free(p);return;
lenerr:pbuf_free(p);return;
}

上面源码首先一上来,就开始做一系列的校验工作,判断ICMP头部长度是否小于4个字节,若是就丢弃,否则继续根据判断头部类型,分情况干活;若是回送请求,则检查报文目的地址是否合法,如果是多播地址和广播地址,不回应;再检查报文长度是否合法;然后判断校验和是否正确。
校验完后,直接调整回送请求报文的相关字段。生成回送回答报文:交换数据报中的源IP和目的IP,填写报文类型字段,重新计算ICMP报文校验和。

LWIP学习笔记---网际控制报文协议ICMP相关推荐

  1. 【计算机网络】湖科大微课堂笔记 p54-56 IPv4数据报的首部格式、网际控制报文协议ICMP、虚拟专用网VPN与网络地址转换NAT

    文章目录 IPv4数据报的首部格式 小结 一些例题 网际控制报文协议ICMP 小结 一些例题 虚拟专用网VPN与网络地址转换NAT 小结 IPv4数据报的首部格式 版本 首部长度.可选字段.填充字段 ...

  2. 网络层:网际控制报文协议ICMP

    网络层:网际控制报文协议ICMP 笔记来源: 湖科大教书匠:网际控制报文协议ICMP 声明:该学习笔记来自湖科大教书匠,笔记仅做学习参考 主机或路由器使用ICMP来发送差错报告报文和询问报文 ICMP ...

  3. 计算机网络-基本概念(2)【网络层】-网际控制报文协议ICMP

    为了更有效的转发IP数据报和提高交付成功的机会,在网络层使用了网际控制报文协议ICMP.ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告. 差错报告报文 终点不可达:使用tracerout ...

  4. 计算机网络 网际控制报文协议 ICMP

    介绍 为了更有效地转发IP数据报和提高交付成功的机会,在网际层使用了网际控制报文协议 ICMP(Internet Control Message Protocol) ICMP是互联网的标准协议 ICM ...

  5. 网际控制报文协议ICMP的应用——traceroute

    为了更有效地转发IP数据报和提高交付成功的机会,在网际层使用了网际控制报文协议ICMP(Internet Control Message Protocol). ICMP的应用举例 ICMP的一个重要应 ...

  6. 网络层(网际控制报文协议ICMP)

    ICMP是(Internet Control Message Protocol)Internet控制报文协议.它是TCP/IP协议族的一个子协议,用于在IP主机.路由器之间传递控制消息.控制消息是指网 ...

  7. 网际控制报文协议ICMP(Internet Control Message Protocol)(详解)

    一.回顾下TCP/IP协议栈 二.ICMP(Internet Control Message Protocol)协议概述 三.ICMP协议的组成 四.ICMP差错报告报文(5种): 终点不可达:当路由 ...

  8. 网际控制报文协议---ICMP

    我们知道,在网际层,我们是不进行错误检查的,在网际层,我们主要进行快速的数据传输,但是,互联网在设计时候,也是要尽可能的提高交付成功的机会: 我们知道,在网际层传输的是IP数据报,IP数据报的协议我们 ...

  9. 网际控制报文协议icmp_网络中的ICMP(Internet控制消息协议)

    网际控制报文协议icmp ICMP(Internet控制消息协议)简介 (Introduction to ICMP (Internet Control Message Protocol)) IP (I ...

最新文章

  1. Linux中的基础和小工具
  2. Nginx uWsgi Django环境搭建
  3. 实用 | PyCharm常用快捷键整理
  4. Android调试秘钥证书指纹获取方式
  5. 有关系统环境变量的设置问题
  6. 查看远程Redis服务器的版本
  7. 第三篇:C++ 中的几种初始化
  8. GitHub C 和 C++ 开源库的清单(含示例代码)
  9. sql server 2012 数据库还原方法
  10. 如何更好地控制input输入框的高度
  11. LINUX下载编译libreadline
  12. python爬取高德poi数据_高德地图之python爬取POI数据及其边界经纬度
  13. 数据分析项目整理之用户消费行为分析
  14. 软考中级软件设计师——数据库系统
  15. 谷歌眼镜公开接受预订,全新宣传视频帅爆了_-Chaz-_新浪博客
  16. traditional 和conventional区别
  17. “浅尝”JavaScript设计模式
  18. 小米科学计算机开立方,小米计算器安卓版_小米计算器怎么开立方_小米科学计算器_易玩网...
  19. KiCAD元件库快速制作
  20. 生成模型经典网络之CGAN剖析

热门文章

  1. 【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo...
  2. 李宏毅 深度学习【持续更新】
  3. 傍上区块链大腿,e签宝恐仍难高枕无忧?
  4. 考研预报名、正式报名有区别?
  5. [转] 理解各种熵最大熵模型
  6. Servlet[DispatcherServlet]的Servlet.init()引发异常
  7. 腰椎间盘突出,睡硬床垫还是软床垫好?听骨科医生答疑
  8. 【车载】ABS/BAS/BA防抱死制动系统
  9. linux的crontab定时任务完整配置和定时任务不执行分析
  10. 前程无忧达成私有化协议:遭遇大砍价 作价降至43亿美元