LWIP协议栈:ICMP协议

1. ICMP协议概述

  • ICMP(Internet Control Message Protocol),因特网控制报文协议。ICMP协议属于网络层协议,用于在源主机与路由器之间传递控制消息。控制消息对数据报文的传递有着重要作用,如:网络不通、通信超时等消息。
  • ICMP协议也是一种无连接的不可靠数据报交付协议,协议本身不提供任何的错误检查与恢复机制。
  • ICMP协议的主要功能:

    1)差错通知:反馈数据传递过程出现的错误的信息至源主机;

    2)信息查询:源主机向目标主机查询相关信息。

P.S.:ICMP只能搭配IPv4使用,ICMPv6搭配IPv6使用。

2. ICMP报文

2.1 报文格式

(1)ICMP报文格式

类型:报文的类型,表示产生报文的原因,占8bit;

代码(code):报文的代码,表示产生报文的具体原因,占8bit;

校验和:整个ICMP报文的校验和

(2)ICMP报文封装

ICMP数据报封装在IP数据报中,IP数据报封装在以太网帧中。

2.2 报文类型

ICMP报文分为两大类:差错报告报文、查询报文。

  • 差错报告报文:当目标主机或路由器不能正常处理当前的数据报时,目标主机或路由器会反馈一个差错报告报文到源主机,向源主机表明出差错的具体原因。
  • 查询报文:源主机向目标主机发送信息查询请求,目标主机向源主机作出应答。

2.3 差错报告报文

差错报告报文有6中类型:目的不可达、源站抑制、重定向、超时、参数错误。

(1)目的不可达

网络数据报在传递过程中出错,不能到达目标主机,或到达目标主机后无法传递至上层协议;此时,路由器或目标主机会反馈一个ICMP目的不可达差错报文,通知源主机数据发送失败。

目的不可达报文如下图所示。

  • ICMP首部剩余的4Byte未使用,为全0。
  • IP数据报首部包含了源IP地址和目标IP地址,源主机可通过该信息判断是哪个数据报出现差错。
  • IP数据报数据区域前8Byte包含了传输层协议的“port”字段的值,源主机可以将ICMP报文传递给对应的上层协议处理。

导致目的不可达的原因有多个,其“code”字段值如下图所示。LWIP协议栈只实现了前6种。

(2)源站抑制

当出现网络拥堵,路由器或目标主机会向源主机发送源站抑制报文,同时源主机降低数据报的发送频率。

源站抑制报文的格式和目的不可达报文相同,但其“code”字段为0。

(3)重定向

源主机刚启动时,其路由表中只有一个默认路由,所有数据都会发送至默认路由。当默认路由器发现数据是发送给其他路由器时,默认路由器会反馈一个ICMP重定向报文给 源主机,通知源主机更改路由表。下次发送数据时,通过新路由进行发送,从而提高数据发送效率。

P.S.:LWIP协议栈中未对ICMP重定向报文进行处理。

(4)超时

IP数据报首部的“TTL”字段记录着该数据报的生命值,该数据报每被转发一次,TTL值减1。当路由器转发一个TTL为0的数据报时,会丢弃数据报并向源主机反馈一个ICMP超时报文。另外,IP分片重装未在规定时间完成也会认为它超时。

超时报文的格式和目的不可达报文相同,但其“code”字段有两个,“code = 0”表示生存时间超时,“code = 1”表示IP数据报分片重装超时。

(5)参数错误

网络数据报传输过程中,会对IP数据报首部进行校验。若IP首部校验出错,则该IP数据报会被丢弃,并向源主机发送ICMP参数错误报文。

P.S.:对于携带 ICMP 差错报文的数据报、非第一个分片的分片数据报、具有特殊目的地址的数据报(如环回、多播、广播),即使是出现了差错也不会返回对应的差错报文。

2.4 查询报文

LWIP协议栈中只实现了ICMP回显请求报文、回显应答报文。ping命令使用的是ICMP回显请求报文、回显应答报文。ping是一个应用程序,该程序发送一份 ICMP 回显请求报文给目标主机,并等待目标主机返回 ICMP 回显应答报文。ping命令执行成功,说明链路层、网络层、传输层都能通信正常。

ICMP回显请求报文、回显应答报文格式,如下图所示。
  • “标识符”表示ping程序的编号,同一台主机可同时运行多个ping程序。
  • “序号”表示回显请求的编号,每发送一个回显请求,其值加1。

ping程序执行后,会打印出每个回显请求的序列号及其回显应答,从而可以判断数据报是否丢失、失序、重复。

2.5 报文数据结构

LWIP协议栈中定义了“icmp_echo_hdr”结构体来描述ICMP报文首部的数据结构。同时定义了ICMP报文的“type”、“code”字段的宏,以及对其进行读取和操作的宏。

P.S.:“icmp_echo_hdr”结构体实际上描述的回显报文的首部,但由于ICMP各个类型的报文类似,因此可以用其描述其余报文的数据结构。

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

3. ICMP报文的发送与接收

3.1 ICMP报文的发送

LWIP协议栈中,只实现了“目的不可达报文、超时报文”的发送。实现代码位置:lwip_2_1_2/src/core/ipv4/icmp.c

(1)发送“目的不可达报文”

调用“icmp_dest_unreach()”函数发送目的不可达报文。当IP层无法向传输层传递数据报,ip_input()函数将会调用此函数发送“目的不可达报文”;当UDP协议无法向应用层传递数据报,udp_input()也将会调用此函数发送“目的不可达报文”。

/**
 *   功能:发送目的不可达报文。
 *    参数:
 *        struct pbuf *p:指向引起ICMP报文的IP数据报;
 *        enum icmp_dur_type t:报文“code”字段的值。
 *
 */
void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);

(2)发送“超时报文”

调用“icmp_time_exceeded()”函数发送超时报文。当IP数据报TTL值为0时,ip_forward()函数在转发数据报时会调用此函数;或者IP数据报分片重装超时,也将调用此函数发送超时报文。

/**
 *    功能:发送超时报文。
 *    参数:
 *        struct pbuf *p:指向引起ICMP报文的IP数据报;
 *        enum icmp_dur_type t:报文“code”字段的值。
 *
 */
void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);

icmp_dest_unreach()、icmp_time_exceeded()函数实际调用此函数完成ICMP报文的发送。

/**
 * 功能:发送ICMP报文。
 *
 *    参数:
 *        struct pbuf *p:指向引起ICMP报文的IP数据报;
 *        u8_t type:报文“type”字段的值
 *        u8_t code:报文“code”字段的值。
 *
 */
void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);

3.2 ICMP报文的接收

LWIP协议栈中,只对回显报文进行处理。当IP层收到一个ICMP报文,“ip_input()”函数将调用“icmp_input()”函数对其进行处理(只处理回显报文)。

实现代码位置:lwip_2_1_2/src/core/ipv4/icmp.c

/**
 *    功能:处理接收到的回显报文,并向源主机发送回显报文应答。
 *
 *    参数:
 *        struct pbuf *p:指向引起ICMP报文的IP数据报;
 *        struct netif *inp:接收ICMP报文的netif。
 *
 */
void    icmp_input(struct pbuf *p, struct netif *inp);

4. 参考资料

 [1] 野火《LwIP应用开发实战指南》。

posted @ 2020-03-19 20:23  LinFeng-Learning  阅读(2229)  评论(0编辑  收藏  举报