lwip ---(十二)ICMP处理

  目前,IP层的东西基本讲解完,数据包的发送或分片发送没有具体涉及到。数据包的发送,与上层协议密切相关,即传输层,后面的内容就是讨论传输层的东西了。这里先讲解传输层协议中比较简单的ICMP协议。ICMP(Internet Control Message Protocol)是Internet控制报文协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

  在以前讲解IP层ip_input函数时,已经三次涉及到了ICMP的东西,第一次在数据包转发过程中,需要将数据包的TTL值减1,若此时TTL值变为0则用icmp_time_exceeded函数向源主机返回一份超时ICMP信息;还有两次是ip_input函数通过IP报文头部的协议字段值判断该数据包是交给哪个上层协议的,若是ICMP协议,则调用icmp_input函数若没有一个协议能接受这个数据包,则调用icmp_dest_unreach函数向源主机返回一个协议不可达ICMP差错控制包。这里先讲解icmp_time_exceededicmp_dest_unreach函数是怎样发送ICMP信息包的。

  先来看看ICMP报文的格式,所有ICMP报文的前四个字节都是一样的,分别为1字节的类型字段1字节的代码字段2字节的校验和字段。校验和字段的计算覆盖整个ICMP报文。类型字段和代码字段唯一确定了该ICMP报文属于那种类型:如回显超时时间戮请求等等,尽管各种类型的ICMP报文结构通常是不相同的,但它们开始的四个字节是相同的。ICMP报文从大的方面来说可以分为ICMP查询报文和ICMP差错报文。ICMP查询报文包括ICMP回显应答、回显请求、时间戮请求、地址掩码请求等等类型LWIP只实现了ICMP回显应答;ICMP差错报文有目的不可达、超时、重定向等等类型LWIP只实现了目的不可达、超时两项ICMP处理功能。这里先讲解目的不可达和超时两种类型的ICMP处理。

  目的不可达超时两种ICMP报文均属于ICMP差错报文,协议中规定,ICMP差错报文应始终包含产生ICMP差错报文的IP数据报的IP首部和数据前8个字节。所以,目的不可达和超时这两种ICMP报文均有下面的报文结构,这个结构与前面所述完全相符,不解释了。
CMP处理《LwIP协议栈源码详解——TCP/IP协议的实现》

  先讲与icmp_dest_unreach函数所描述的目的不可达差错报文。该报文的类型字段值应为3,代码字段值应为(0-15),目前LWIP只支持(0-5),分别表示网络不可达、主机不可达、协议不可达、端口不可达、需进行分片但设置了不分片位、源站选择失败。很明显,在ip_input函数找不到将该数据包交到哪个上层协议时,应该产生一个协议不可达的差错报文,即代码字段值为2。函数原型如下,输入参数为接收到的目标不可达的IP数据包和应该用于填充ICMP头部代码字段的值。

void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)

  现在来看看这个函数做了哪些工作,首先为要发送数去的ICMP数据包申请一个pbuf缓冲区,这个缓冲区的长度为上图所述结构的长度与一个IP数据报头大小之和,之所以要多申请IP数据报头大小的空间是为了当该ICMP数据包被递交给IP层发送时,IP层不需要再去申请一个数据报头来封装该ICMP数据包,而是直接在已经申请好的报头中填入IP头部数据,注意这里申请好的pbuf的payload指针是指向ICMP数据报头处的。接下来,函数填写ICMP数据包的相关字段,将类型段填充为3,代码段为输入参数t,同时将不可达的IP数据包的IP报头和数据前8个字节拷贝到ICMP数据包相应字段,最后计算校验和字段的值,然后调用ip_output函数将组装好的ICMP包发送出去。

  ip_output函数通过调用ip_output_if函数完成数据包的发送,它在调用ip_output_if函数前,要先根据发送数据包的目的IP地址找到相应的发送网络接口结构,并将该结构作为调用ip_output_if函数的参数ip_output_if函数主要是填充IP报头各个字段的值,然后调用netif->output函数将封装好的IP数据包发送出去。

  icmp_time_exceeded函数用于产生一个超时类型的ICMP报文。该类型的报文与上面所述的报文有完全相同的报文结构,但类型字段值应为11,代码字段应为0或1,分别代表传输期间生存时间超时和数据组装期间生存时间超时。对于后者,在讨论数据包重组时,我们知道每个ip_reassdata结构体中都有个时间字段,当指定的时间到达而数据包还未被组装完毕,则内核会将该ip_reassdata结构相关的所有分片数据全部删除,并向源主机发送一个代码字段为1的超时ICMP报文。

  从代码流程和内容来看,icmp_time_exceeded函数和icmp_dest_unreach函数完全一样,只是在填充ICMP报文的类型和代码字段使用了不同的值,这里不再赘述。

  接下来该讨论ICMP查询报文了,这部分在整个产品的设计调试过程中显示出非常重要的作用。即Ping命令,它与ICMP回显应答、请求报文密切相关

  这小段来自于协议:“Ping”这个名字源于声纳定位操作目的是为了测试另一台主机是否可达该程序发送一份 ICMP回显请求报文给主机,并等待返回ICMP回显应答。一般来说,如果不能Ping到某台主机,那么就不能 Telnet或者FTP到那台主机。反过来,如果不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。Ping程序还能测出到这台主机的往返时间,以表明该主机离我们有“多远”。同时,Ping程序也能在数据包的路由过程中记录下路由路径。

  ICMP回显请求和回显应答报文格式如下图,回显应答的类型值为0,回显请求类型值为8,二者代码字段值均为0,Unix系统在实现ping程序时是把ICMP报文中的标识符字段置成发送进程的ID号。这样即使在同一台主机上同时运行了多个ping程序实例,ping程序也可以识别出返回的信息。序列号从0开始,每发送一次新的回显请求就加 1。

ICMP处理《LwIP协议栈源码详解——TCP/IP协议的实现》

  icmp_input函数处理接收到的ICMP数据包,并根据包类型做相应的处理。目前LWIP只能处理ICMP回显请求包,对其他类型的ICMP包不做响应,这在嵌入式产品中是足够用了的。对于ICMP回显请求,icmp_input需要生成一个回显应答报文返回给源主机

  来看看icmp_input函数做了哪些工作。首先将传进来的数据包pbuf的payload指针调整为指向ICMP头部,并判断ICMP头部长度是否小于4个字节,若是,则说明是个错误的ICMP数据包,该包被丢弃。对于正确的ICMP包,函数根据其头部类型字段的值判断该做什么样的处理。当前版本只实现了对ICMP回显请求的相关响应操作。

  当前的数据包为ICMP回显请求,则函数继续判断该数据包是否为广播或者多播包,对这两种数据包不做处理;接下来判断该数据包大小是否小于ICMP回显请求头部长度(如上图所示),是则丢弃数据包;接下来函数为这个数据包申请IP报头和以太网帧头内存空间,成功后将该ICMP包类型字段变为0,从新计算校验和,并将IP报头的源IP地址和目的IP地址交换位置,最后将整个数据包利用ip_output_if函数将数据包发送出去。ICMP回显应答将回显请求中的数据原样返回给源主机,源主机在收到回显应答后,通过处理回显应答中的数据可以得到相关信息,如计算往返时间等。

  ICMP就是这么多了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章