ICMP(网际控制报文协议),ICMP数据包是封装在IP数据包中的,由于IP不是为可靠传输服务设计的,ICMP的目的主要是用于在TCP/IP网络中发送和控制消息。主要应用有Ping、Traceroute和MTU测试。

ICMP报文的种类有三大种类,即ICMP差错报文、控制报文、请求/应答报文,各大类型报文又分为多种类型报文。

差错报文:

(1) 特点:

1.ICMP差错报文都是有路由器发送到源主机的。

2.ICMP报文只提供IP数据报的差错报告,并不采取处理措施,差错处理由应用程序处理。

3.传输过程中可能丢失、损坏,甚至被抛弃。

4.ICMP差错报文是伴随着抛弃出错的IP数据报而产生的。

5.为了防止广播风暴,以下情况不会产生ICMP差错报文。

1)ICMP差错报文    2)目的地址是广播或多播    3)链路层广播的数据报    4)不是IP分片的第一片    5)源地址是零地址、回送地址、广播地址或多播地址

(2)信息不可达报文:

1.目的机硬件故障或关机

2.目标地址不存在

3.网关不知道去往目的机的路径

(3)超时报文:

1.为了避免无限制的在网中循环,IP协议采用

1)在数据报头设置TTL域    2)对分片数据报采用定时器技术

2.当报文超时出现时,路由器或目的机立即丢弃该数据报,并向信源机发送超时报文

控制报文:

(1)拥塞控制与源站控制报文:

1.当路由器接收比处理快或者传入比传出快时就会产生拥塞

2.路由器通过发送源站抑制报文来抑制主机发送速率

3.源主机一段时间内没有收到抑制报文,便认为抑制解除,逐渐恢复原来的数据流量

(2)路由控制与重定向报文:

1.当源主机要向目标主机发送IP数据报时,则把IP数据报发送给默认路器1,再由路由器1经过选路发送给路由器2,路由器2发送给目标主机

2.如果路由器1和路由器2在同一网络,路由器1发现源主机可直接发送IP数据报给路由器2,就会向源主机发送重定向报文,以后源主机将直接发送IP报文给路由器2

请求/应答报文:

(1)回送请求与应答报文:

1.测试目标主机和路由器是否可以到达

(2)时戳请求与应答报文:

1.同步互联网中各个主机的时钟

(3)地址掩码请求和应答报文

1.用于无盘系统在引导过程中获取子网掩码。启动时广播地址掩码请求,路由器收到请求后回送一个包含32位地址掩码应答报文

报文格式,ICMP报文由8字节首部和可变长数据部分组成。不同类型的ICMP报文,首部格式有一定差异,但是首部前4字节的字段对所有类型通用。

类型:标识了ICMP具体类型

代码:进一步指出产生该类型ICMP的原因

校验和:整个ICMP的校验和

LWIP实现了ICMP的部分功能:目的不可达报文、超时报文、回送查询报文。

报文格式分别如下所示:

ICMP头部基本类似,信息不可达和超时报文的头部,相当于回送查询报文头部中标识符和序号设置为0的情况。因此,LwIP只以回送查询报文为基础提供了一种ICMP头部结构体

/* ICMP类型 */
#define ICMP_ER 0       /* 回送查询应答 */
#define ICMP_DUR 3      /* 目的不可达 */
#define ICMP_SQ 4       /* 源端被关闭 */
#define ICMP_RD 5       /* 重定向 */
#define ICMP_ECHO 8     /* 回送查询请求 */
#define ICMP_TE 11      /* 超时 */
#define ICMP_PP 12      /* 参数问题 */
#define ICMP_TS 13      /* 时间戳请求 */
#define ICMP_TSR 14     /* 时间戳应答 */
#define ICMP_IRQ 15     /* 信息请求 */
#define ICMP_IR 16      /* 信息应答 *//* ICMP目的不可达代码 */
enum icmp_dur_type {ICMP_DUR_NET = 0,      /* 网络不可达 */ICMP_DUR_HOST = 1,      /* 主机不可达 */ICMP_DUR_PROTO = 2,     /* 协议不可达 */ICMP_DUR_PORT = 3,      /* 端口不可达 */ICMP_DUR_FRAG = 4,      /* 需要分片但不分片位被置位 */ICMP_DUR_SR = 5          /* 源路由失败 */
};/* ICMP超时代码 */
enum icmp_te_type {ICMP_TE_TTL = 0,      /* 生存时间计时器超时 */ICMP_TE_FRAG = 1      /* 分片重装超时 */
};/* ICMP回送查询报文头部 */
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;
PACK_STRUCT_END

在前面分析ip_input函数时,当报文类型为ICMP报文时,调用icmp_input函数处理ICMP报文。当遇到不支持的协议时调用icmp_dest_unreach函数,返回目的不可达报文。

/* IP数据包输入处理 */
err_t ip_input(struct pbuf *p, struct netif *inp)
{struct ip_hdr *iphdr;....../* IP头部指针 */iphdr = p->payload;....../* 判断协议类型 */switch (IPH_PROTO(iphdr)) {/* UDP协议 */case IP_PROTO_UDP:udp_input(p, inp);break;/* TCP协议 */case IP_PROTO_TCP:tcp_input(p, inp);break;/* ICMP协议 */case IP_PROTO_ICMP:icmp_input(p, inp);break;/* 不支持的协议 */default:/* 目的IP不是广播地址且不是组播地址 */if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && !ip_addr_ismulticast(&(iphdr->dest))) {p->payload = iphdr;/* 发送ICMP目的不可达报文(协议不可达) */icmp_dest_unreach(p, ICMP_DUR_PROTO);}/* 释放数据包 */pbuf_free(p);}}......return ERR_OK;
}

下面先来看目的不可达报文的实现方式

/* 发送ICMP目的不可达报文 */
void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{icmp_send_response(p, ICMP_DUR, t);
}/* 发送ICMP响应报文 */
static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
{struct pbuf *q;struct ip_hdr *iphdr;struct icmp_echo_hdr *icmphdr;/* 为ICMP报文申请内存空间 */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"));return;}LWIP_ASSERT("check that first pbuf can hold icmp message", (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));/* IP头部指针 */iphdr = p->payload;LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));LWIP_DEBUGF(ICMP_DEBUG, (" to "));ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));LWIP_DEBUGF(ICMP_DEBUG, ("\n"));/* ICMP头部指针 */icmphdr = q->payload;/* ICMP头部类型 */icmphdr->type = type;/* ICMP头部代码 */icmphdr->code = code;/* ICMP头部剩余字节(标识符) */icmphdr->id = 0;/* ICMP头部剩余字节(序号) */icmphdr->seqno = 0;/* 将源数据报中的8字节 */SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);/* ICMP头部检验和 */icmphdr->chksum = 0;icmphdr->chksum = inet_chksum(icmphdr, q->len);ICMP_STATS_INC(icmp.xmit);snmp_inc_icmpoutmsgs();snmp_inc_icmpouttimeexcds();/* 调用IP输出函数,发送ICMP数据包 */ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);/* 释放数据包 */pbuf_free(q);
}

接下来再看 icmp_input函数

/* ICMP报文接收处理 */
void icmp_input(struct pbuf *p, struct netif *inp)
{u8_t type;struct icmp_echo_hdr *iecho;struct ip_hdr *iphdr;struct ip_addr tmpaddr;s16_t hlen;/* IP头部指针 */iphdr = p->payload;/* IP头部长度 */hlen = IPH_HL(iphdr) * 4;/* 向后调整剥离IP头部,保证ICMP头部不小于4字节 */if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t) * 2)) {goto lenerr;}/* ICMP类型 */type = *((u8_t *)p->payload);/* 判断ICMP类型 */switch (type) {/* 回送查询报文 */case ICMP_ECHO:{int accepted = 1;/* ICMP目的IP为组播地址 */if (ip_addr_ismulticast(&iphdr->dest)) {accepted = 0;    //不做处理}/* ICMP目的IP为广播地址 */if (ip_addr_isbroadcast(&iphdr->dest, inp)) {accepted = 0;    //不做处理}/* 不做处理,直接释放ICMP数据包 */if (!accepted) {pbuf_free(p);return;}}/* 检查ICMP数据包长度合不合理 */if (p->tot_len < sizeof(struct icmp_echo_hdr)) {goto lenerr;}/* 检查检验和是否正确 */if (inet_chksum_pbuf(p) != 0) {pbuf_free(p);return;}/* ICMP头部指针 */iecho = p->payload;/* 将IP头部中源地址和目的地址交换 */tmpaddr.addr = iphdr->src.addr;iphdr->src.addr = iphdr->dest.addr;iphdr->dest.addr = tmpaddr.addr;/* 设置ICMP报文类型为回送查询应答 */ICMPH_TYPE_SET(iecho, ICMP_ER);/* 重新设置ICMP头部校验和 */if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {iecho->chksum += htons(ICMP_ECHO << 8) + 1;} else {iecho->chksum += htons(ICMP_ECHO << 8);}/* 设置IP头部TTL字段 */IPH_TTL_SET(iphdr, ICMP_TTL);/* 计算IP头部校验和字段 */IPH_CHKSUM_SET(iphdr, 0);IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));/* 向前调整出IP头部空间 */if(pbuf_header(p, hlen)) {} else {/* 通过IP发送函数,发送回送查询应答报文 */ip_output_if(p, &(iphdr->src), IP_HDRINCL, ICMP_TTL, 0, IP_PROTO_ICMP, inp);}break;/* 不处理其它类型的ICMP报文 */default:break;}pbuf_free(p);return;lenerr:pbuf_free(p);return;
}

在前面分析IP数据报重组的时候, 一旦重组超时协议栈就会删除重组并向源主机返回ICMP分片重组超时报文

/* 重组IP数据报定时器回调函数(周期1秒) */
void ip_reass_tmr(void)
{struct ip_reassdata *r, *prev = NULL;/* 遍历所有重组IP数据报 */r = reassdatagrams;while (r != NULL) {/* 没有超时,将生存周期减一 */if (r->timer > 0) {r->timer--;prev = r;r = r->next;} /* 超时 */else {struct ip_reassdata *tmp;tmp = r;r = r->next;/* 释放重组IP数据报的所有分片,并将重组IP数据报从链表中移除 */ip_reass_free_complete_datagram(tmp, prev);}}
}/* 释放重组IP数据报的所有分片,并将重组IP数据报从链表中移除 */
static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
{......struct ip_reass_helper *iprh;/* 向源主机发送ICMP分片重组超时报文 */iprh = (struct ip_reass_helper *)ipr->p->payload;if (iprh->start == 0) {p = ipr->p;ipr->p = iprh->next_pbuf;SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);icmp_time_exceeded(p, ICMP_TE_FRAG);pbufs_freed += pbuf_clen(p);pbuf_free(p);}......
}

下面就来看一下ICMP超时报文发送函数

/* 发送ICMP超时报文 */
void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{icmp_send_response(p, ICMP_TE, t);
}

LwIP之ICMP协议相关推荐

  1. Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)

    4.ICMP协议 4.1 原理 IP协议提供的是一种不可靠无连接的数据服务,在IP数据报被交互到最终目的主机的过程中,网络中每一个路由器都是自主运行,它们根据数据报中的目的IP地址为数据报选择的最佳路 ...

  2. 4.3.9 ICMP协议

    4.3.9 ICMP协议

  3. 协议森林06 瑞士军刀 (ICMP协议)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁任何形式转载. 到现在为止,我们讲解了网络层中最重要的IP协议(参考协议森林).IP协议的一个重要补充是是ICMP ...

  4. PING的原理以及ICMP协议

    主要内容: 1.ping的原理以及工作过程 2.ICMP协议 3.ICMP的应用:ping,traceroute 1.ping的原理以及工作过程  ping的原理  ping 程序是用来探测主机到主机 ...

  5. linux c icmp协议 判断主机存活

    详细参考 <linux c ping 分析实现> https://blog.csdn.net/whatday/article/details/104192608 ICMP(Internet ...

  6. TCP/IP详解--学习笔记(4)-ICMP协议,ping和Traceroute

    1.IMCP协议介绍 前面讲到了,IP协议并不是一个可靠的协议,它不保证数据被送达,那么,自然的,保证数据送达的工作应该由其他的模块来完成.其中一个重要的模块就是ICMP(网络控制报文)协议. 当传送 ...

  7. 【计算机网络】网络层 : ICMP 协议 ( ICMP 差错报文 | 差错报文分类 | ICMP 询问报文 | ICMP 应用 | Ping | Traceroute )

    文章目录 一.ICMP 协议 二.ICMP 协议 简介 三.ICMP 五种差错报告报文 四.ICMP 差错报文形成 五.ICMP 差错报文 不发送 情形 六.ICMP 询问报文 七.ICMP 应用 一 ...

  8. 4、以太网基础知识——ICMP协议详解

    返回总目录 上一篇 下一篇 ICMP协议是一个网络层协议.  一个新搭建好的网络,往往需要先进行一个简单的测试,来验证网络是否畅通:但是IP协议并不提供可靠传输.如果丢包了,IP协议并不能通知传输层是 ...

  9. ping程序和tracert(traceroute)背后的故事--ICMP协议

    为路由器生,为交换机死,为了Ping通奋斗一辈子-----tracert.cn 上面是一个网络工程师的个人定位,很有意思,哈哈!那么我们来看看ping和tracert都是什么吧 PING (Packe ...

最新文章

  1. linux系统怎么查找 文件是否存在,在Linux上,如何知道一个可执行文件是否包含调试信息呢?...
  2. Linux awk内部变量
  3. 图解http协议头实例分析
  4. 官方宣布:谷歌开发者中国网站正式发布!
  5. 【CodeForces - 227A】Where do I Turn? (计算几何,叉积判断直线拐向)
  6. 大喇叭疫情防控广播解决方案
  7. 网络编程在线英英词典之查询模块(五)
  8. 如何给页面添加背景音乐
  9. 联想服务器安装系统 F11,教你联想thinkpad系统还原f11一键恢复方法
  10. HDwiki 源代码 - 互动百科开源
  11. HTML的style属性(替代font等标签)
  12. android磁场传感器页面布局在哪,教程:Android传感器—传感器查询demo
  13. 使用route查看路由表,添加/删除默认路由网关
  14. git-linux终端命令详解
  15. opensips3.0之新工具opensips-cli
  16. h264中的pps和sps
  17. 天天链n1 与电脑连接Samba win10 教程
  18. matlab专业代做hslogic,MATLAB代做|FPGA代做--OLA算法的仿真与分析
  19. 28335之GPIO输出
  20. gof23 设计模式 各个模式代码demo

热门文章

  1. 系统架构师学习笔记-基于构件的开发
  2. linux指令解压rpm,dpkg rpm apt yum 的常用指令 +linux 各种解压缩方法.docx
  3. linux pidof用法,科技常识:Linux pidof命令使用总结
  4. php里isset的属性,PHP魔术方法__isset()
  5. 蛋白对接_JCIM | 金属蛋白分子对接程序哪家强?七种对接程序的基准测试
  6. python编辑器背景设置为黑色_VScode 配置为Python编辑器
  7. 损失函数一直不变_MIT 18.03 微分方程笔记 3.4 狄拉克δ函数
  8. 操作系统之文件管理:6、文件的基本操作(创建文件、打开文件、删除文件、关闭文件、读文件、写文件)
  9. (王道408考研操作系统)第二章进程管理-第一节3:进程控制(配合Linux讲解)
  10. 二叉树经典题之线索二叉树(中序)