Section 12 Internet Control Message Protocol ICMP

Introduction to ICMP Functions

ICMP is an acronym for "Internet Control Message Protocol". In the previous study, we also know that the IP protocol is an unreliable and connectionless protocol, which only delivers data between hosts, but the IP protocol does not care about the arrival of the data. In order to improve the accuracy of data delivery When delivering data, if the datagram cannot reach the target host due to network conditions, link failure, etc., ICMP will return an error message to let the source host know that the data failed to reach the target normally. The host can then resend or give up sending. For example, when the IP datagram lifetime TTL is 0, the router no longer forwards the datagram, but directly discards the datagram, and returns an ICMP message, telling the source host——your datagram lifetime When it arrives, it still hasn’t been sent to the target host, you can choose to resend or give up, it’s up to you.

Why ICMP is needed, because the IP protocol believes that it is reasonable to discard useless data, which can improve the efficiency of data processing, but the source host hopes to get a response when the data cannot be sent to the target, otherwise the target host will I don't know where the sent data has gone, and it is useless to receive it. It is like a stone sinking into the ocean. This is unacceptable. In addition, ICMP is also used by hosts and routers to communicate network layer information with each other. If the links are not connected, then sending data is meaningless. Although ICMP messages do not transmit user data, they play a role in the transmission of user data. important role.

The most typical use of ICMP is error reporting. For example, when running an FTP or HTTP application, you may encounter some error messages such as "destination unreachable". This message is generated in ICMP, and it may be in a certain position when forwarding , the router cannot find a suitable path to forward the data packet to the host specified by the FTP or HTTP application, the router will create and send an ICMP message of type 3 to the source host to indicate the error (destination unreachable ).

ICMP is generally considered to be a part of the IP layer protocol, but it is located on top of IP in terms of architecture, because ICMP messages are carried in IP datagrams. That is to say, the ICMP message is used as the data area of ​​the IP datagram (some books also call it the payload), just like the TCP and UDP message segments are used as the data area of ​​the IP datagram. Similarly, when a host receives an IP datagram indicating that the upper layer protocol is ICMP, it will decompose the content of the datagram to ICMP, just like decomposing the content of a datagram to TCP or UDP, but with TCP Or the UDP protocol is different. The purpose of ICMP is not to provide services for upper-layer applications, but to transmit error messages at the IP layer, relying on the IP protocol for transmission.

ICMP message structure

ICMP messages are encapsulated and sent using IP datagrams, so ICMP messages have no additional reliability and priority, and they will be discarded by other routers. At the same time, ICMP messages are encapsulated in IP datagrams. IP datagrams are encapsulated in Ethernet frames, so ICMP packets are encapsulated twice, see the figure for details.

insert image description here

Figure ICMP packet encapsulation

The ICMP message is the same as the IP datagram, which is composed of a header and a data area. The ICMP header is 8 bytes. For different types of ICMP messages, the format of the ICMP message header will be slightly different, but the first 4 bytes of the header The bytes are common:

The first byte (occupying 8bit space) is the type (Type) field, indicating the reason for generating this type of ICMP message.

The second byte is the code (Code) field, which further describes the specific reason for generating this type of ICMP message. Because there may be multiple packets of each type, such as destination unreachable packets, there may be multiple reasons such as host unreachable, protocol unreachable, and port unreachable.

The next checksum field (occupying 16 bits) is used to record the checksum of the entire ICMP datagram including the data part of the ICMP message, so as to check whether there is an error in the transmission of the message. The calculation method is the same as that in We introduced that the checksum calculation method in the IP data packet header is the same.

The remaining 4 bytes will be explained in detail when explaining the message type, because different types of messages have different definitions, and the length of the data part is also different. The ICMP message format is shown in the figure.

insert image description here
Figure ICMP message format

ICMP packet type

There are two types of ICMP messages, which can be divided into error report messages and query messages. The error report message is mainly used to return an error report message to the source host of the IP datagram, and the reason for this error report message is that the router Or the host cannot process the current datagram normally. Simply put, the datagram sent by the source host cannot reach the target host, or it cannot be delivered to the upper layer protocol after reaching the target host.

The query message is used for one host to initiate a request to another host. If the target host receives the query request, it will respond to the source host according to the format of the query message, such as the ping command we use , its essence is an ICMP query message.

Both error messages and query messages have their specific types. For details, see

ICMP packet type specific type describe
error report message 3 Unreachable
4 source suppression
5 redirect
11 time out
12 parameter error message
query message 0 or 8 echo request or echo reply
9 or 10 router inquiry or advertisement
13 or 14 Timestamp request or response
15 or 16 information request or information response
17 or 18 mask request or response

Although there are many ICMP messages, it cannot correct errors. It simply reports errors with the help of the IP protocol. The error messages are returned to the source host. Because of an error, the only available information in the datagram is the destination IP address and the source IP address. Address, after the source host receives the ICMP error report, it is passed to the upper layer protocol, and how to deal with it is not a matter of the ICMP protocol.

ICMP error report message

Unreachable

In daily life, mailed packages will go through multiple delivery links. If any link cannot be passed on, it will be returned to the sender with the reason why it cannot be mailed. Similarly, when a router receives an IP datagram that cannot be delivered, the router discards the datagram and returns an ICMP destination unreachable error report message (type 3) to the source sender of the IP message. There are many reasons for errors, such as network unreachable, host unreachable, protocol unreachable, port unreachable, etc. The code field (Code) in the ICMP message is used to record the cause of the error.

The value of the error code field is different for different error codes, but LwIP only implements the first 6 types, see the table for details.

The value and description of the code field whose purpose is unreachable in the form

code value illustrate code value illustrate
0 network unreachable 8 The source host is isolated (obsolete)
1 host unreachable 9 target network banned
2 protocol unreachable 10 Target host is banned
3 port unreachable 11 Network unreachable due to service type TOS
4 Fragmentation is required but no fragmentation is set 12 Host unreachable due to service type TOS
5 Origin site routing failed 13 Communication is forcibly prohibited due to filtering
6 target network unknown 14 host override
7 target host unknown 1 5 Priority suspension in effect

At the same time, the remaining 4 bytes of the header of the ICMP destination unreachable message are all unused, while the data area of ​​the ICMP message is loaded with the header of the IP datagram and the first 8 bytes of the data area of ​​the IP datagram. Why is it necessary to load the data of the IP datagram? What about the first 8 bytes of data in the region? Because the first 8 bytes of the data area of ​​the IP datagram just cover the port number field in the transport layer protocol, and the header of the IP datagram has the destination IP address and the source IP address, when the source host receives such an ICMP message Finally, it can judge which data packet has a problem according to the data area of ​​the ICMP message, and the IP layer can pass the message to the corresponding upper layer protocol for processing according to the port number. The format of the ICMP destination unreachable message is shown in the figure.

insert image description here

Figure ICMP destination unreachable message

source suppression

Because the IP protocol itself is a non-feedback protocol, when the IP layer sends data out, it will not receive any reply. Of course, it will not know the network status of the target host. The source host may send data to the target all the time. On the host side, but due to processing performance, network and other factors, it will lead to congestion, just like our roads, IP datagrams are vehicles driving on the road, if there are too many datagrams, the routing/target host is too late If you process these datagrams, you will be stuck here, just like a traffic jam, and the appearance of the ICMP source station suppression message is to tell the source host that you should not send so many datagrams.

This is not too important for us, just look at its format. In fact, the format of the ICMP Source Suppression message is the same as that of the ICMP Destination Unreachable message, but the code field is 0.

redirect

Generally speaking, a host has only one routing table (that is, the default route) when it is started, so the data it sends is sent to the default route, and let it help forward, and the router finds that the data should be sent to another router , then it will return an ICMP Redirect message to the source host, telling it to send it directly to another router. For example, if someone is looking for someone in your class, but the person he is looking for is not in your class, but in the next class, you will tell him that the person is in the next class, and let him look for it in the next class. The same is true for routers. .

Redirection is generally used to let the newly started host gradually build a more complete routing table, because there may be only one default route in the routing table when the host starts. Once the default router finds that it can forward to other routers, the default router will notify it to redirect and tell the host to make corresponding changes to the routing table, so that there is no need for the default route to handle these things, and the datagram transmission is more efficient Of course, redirection packets can only be generated by routers and not by hosts, but only hosts and not routers can use redirection packets, so we don’t care about the format of these packets for the time being. , and LwIP ignores such datagrams.

time out

There is a TTL field in the header of the IP datagram, which is to prevent the IP datagram from drifting in the network forever. When the datagram is forwarded once, the value of the TTL is reduced by one. If the TTL is 0, the router or the host will discard the datagram. , and returns an ICMP timeout message to the source host; in addition, ICMP messages are also used when datagram fragments are reloaded, when all IP fragmented datagrams cannot be reloaded within the specified time, then The host will also think that it has timed out, then these datagrams will be deleted, and an ICMP timeout message will be returned to the source host at the same time. The format of the ICMP timeout message is the same as that of the ICMP destination unreachable message, but the code field There are two values, see the table for details.

Form timeout message code field and description

code field value illustrate
0 Time-to-live timeout (TTL is 0)
1 Fragmented datagram reload timeout

Parameter error

When the IP datagram is transmitted in the network, it is identified according to its header. If there is an error in the header, it will cause serious problems. Therefore, if the IP datagram header has an error, the datagram will be discarded and the source host will be sent Return an ICMP parameter error message, of course, for datagrams carrying ICMP error messages, fragmented datagrams that are not the first fragment, and datagrams with special purpose addresses (such as loopback, multicast, broadcast) These types of datagrams will not return corresponding error messages even if an error occurs.

ICMP query message

We know that the ping command uses ICMP query messages. If the ping is successful, it means that the network card, IP layer, and ICMP layer can communicate normally, so it can prove that LwIP has been successfully ported. We usually test it when the porting is completed. Use the ping command to see if the migration is successful.

The name ping comes from the sonar positioning operation. The ping program was written by Mike Muuss to test whether another host is reachable. The program sends an ICMP echo request message to the target host, and waits for the target host to return an ICMP echo reply message.

ICMP Echo Request message and Echo Reply message are the only messages implemented in LwIP, and other messages are only used to determine the host’s own IP address, mask, routing status and other information when it starts up. Now It is basically useless, because the DHCP protocol has been implemented for us, and we only explain the ICMP echo request message and echo response message.

We call the ping program that sends the echo request the client, and the host being pinged the server. Most of the TCP/IP protocol stacks directly support the ping server in the kernel, but note that this ping server is not a user thread, but only processed in the kernel thread.

The ICMP echo request and echo response message formats are shown in the figure.

insert image description here
Figure ICMP Echo Request and Echo Reply message format

The remaining 4 bytes of the ICMP message header are divided into two fields. The identifier is used to identify multiple ping programs running on the same host at the same time, and the ping program can also identify the returned information. The serial number starts from 0 and increases by 1 every time a new echo request is sent. The ping program prints out the sequence number of each packet returned, allowing us to see if any packets are lost, out of sequence, or duplicated. The data option area indicates the data that can be included in the echo request message. The length is optional. The sender chooses the appropriate length and fills the data. On the receiver side, it will return an echo according to the echo request Response message, the data option area in the echo response message is exactly the same as the data option area in the echo request message.

We can use the wireshark packet capture tool to capture packets and view the format of the ICMP echo request message and echo response message. First, open the wireshark packet capture tool to start capturing packets, then open the cmd console, and enter "ping baidu.com", Then we can see the ICMP data packet in the wireshark packet capture tool, click on the corresponding data packet to view the ICMP echo request message and echo response message, as shown in the figure

insert image description here

Figure ICMP echo request message and echo response message

ICMP implementation in LwIP

ICMP message data structure

The data structure of an ICMP message is similar to that of an IP datagram. A structure is defined in LwIP to describe it. The name of the structure is icmp_echo_hdr. If you look at the name, are you surprised that it is actually the header of an ICMP echo message? This is indeed the case, because each type of ICMP message header is similar, so the ICMP echo message header can be used for other ICMP message headers, and the member variables in it correspond to the ICMP message header type, Code, checksum, identifier, serial number and other fields, see the code list for the icmp_echo_hdr structure.

Code list icmp_echo_hdr structure

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

In addition, LwIP also defines many variables of macro and enumeration type to describe the ICMP type and code field, see the code list for details.

Macros and enumeration variables implemented in the code list LwIP

#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 /* 信息应答*/
#define ICMP_AM 17 /* 地址掩码请求*/
#define ICMP_AMR 18 /* 地址掩码应答*/

/** 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
};

Seeing these macro definitions, did you find that many types of ICMP messages have not been implemented, and this is indeed the case in LwIP. We also know that LwIP is a relatively complete TCP/IP protocol stack, which only implements ICMP echo The processing of request messages and some error sending reports, such as destination unreachable report messages and timeout messages, and other ICMP messages, LwIP only recognizes them and ignores them.

In addition, in order to quickly operate the type and code fields of ICMP messages, LwIP also defines two sets of macro definitions to read and fill them, see the code list for details.

Code List Operation Macro Definition of ICMP Message Header

#define ICMPH_TYPE(hdr) ((hdr)->type) /** 读取类型字段 */
#define ICMPH_CODE(hdr) ((hdr)->code) /** 读取代码字段 */
#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) /** 填写类型字段*/
#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) /** 填写代码字段*/

Send ICMP error message

In any case, when the datagram cannot be submitted to the upper layer, the protocol stack will return an ICMP message of type destination unreachable to the source host (the specific type depends on the code field), such as IP datagram When it cannot be delivered to the transport layer, the icmp_dest_unreach() function will be called to return an ICMP protocol unreachable message; and if in the transport layer, the UDP protocol cannot deliver the datagram to the application layer, then this function will also be called to return an ICMP port unreachable message. The other is a timeout error message. If the TTL in the datagram is 0 when forwarding the datagram, or if the timeout occurs when the fragmented datagram is reloaded, LwIP will call the icmp_time_exceeded() function and send an ICMP timeout message to In the source host, let’s take a look at the sending of ICMP error messages, see the code list for details.

List of codes to send ICMP error messages

void
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) //发送目标不可达报文
{
    
    
	MIB2_STATS_INC(mib2.icmpoutdestunreachs);
	icmp_send_response(p, ICMP_DUR, t);
}

void
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) //发送ICMP 超时报文
{
    
    
	MIB2_STATS_INC(mib2.icmpouttimeexcds);
	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;
	ip4_addr_t iphdr_src;
	struct netif *netif;
	
	/* 申请pbuf 内存块,大小为ICMP 首部+ IP 首部+ 8 字节数据*/
	q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr)
				+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
				PBUF_RAM);
	if (q == NULL)
	{
    
    
		MIB2_STATS_INC(mib2.icmpouterrors);
		return;
	}
	
	//指向IP 数据报首部
	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"));
	
	//指针指向pbuf 数据区域,并且强制将该地址转化为ICMP 报文首部
	icmphdr = (struct icmp_echo_hdr *)q->payload;
	//填写类型字段
	icmphdr->type = type;
	//填写代码字段
	icmphdr->code = code;
	icmphdr->id = 0;
	icmphdr->seqno = 0;
	
	/* 从原始数据包中复制字段,IP 数据报首部+8 字节的数据区域*/
	SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr),
			(u8_t *)p->payload,
			IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
			
	//得到源IP 地址
	ip4_addr_copy(iphdr_src, iphdr->src);
	
	netif = ip4_route(&iphdr_src);
	
	if (netif != NULL)
	{
    
    
		icmphdr->chksum = 0;
		
		ICMP_STATS_INC(icmp.xmit);
		
		//发送出去
		ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0,
					IP_PROTO_ICMP, netif);
	}
	pbuf_free(q);
}

When sending, simply apply for pbuf space for ICMP, the size is ICMP message header + IP datagram header + 8 bytes, then directly fill in the ICMP message header area, and then copy the IP datagram header and the front of the IP datagram data area 8-byte content, and then directly call the send function to send the ICMP message.

Handle ICMP packets

The LwIP protocol is a lightweight TCP/IP protocol stack, so it does not process many types of messages in the ICMP message. LwIP will discard these unprocessed messages, but it will make an ICMP echo request message. Processing, so this is why we can ping the development board. From the figure in the previous chapter, we can see that when the IP layer receives an ICMP message, it will call the icmp_input() function to pass the message to the ICMP protocol for processing. The processing of this function is also relatively simple and clear. Only process the ICMP request message, and then return an ICMP response message, see the comments for details. For the icmp_input() function, see the code list for details.

Code Listing icmp_input() function

void
icmp_input(struct pbuf *p, struct netif *inp)
{
    
    
	u8_t type;
	struct icmp_echo_hdr *iecho;
	const struct ip_hdr *iphdr_in;
	u16_t hlen;
	const ip4_addr_t *src;
	
	ICMP_STATS_INC(icmp.recv);
	MIB2_STATS_INC(mib2.icmpinmsgs);
	
	iphdr_in = ip4_current_header();
	hlen = IPH_HL_BYTES(iphdr_in);
	if (hlen < IP_HLEN)
	{
    
    
		goto lenerr;
	}
	if (p->len < sizeof(u16_t) * 2)
	{
    
    
		goto lenerr;
	}
	
	type = *((u8_t *)p->payload);
	
	switch (type)
	{
    
    
	case ICMP_ER:
		MIB2_STATS_INC(mib2.icmpinechoreps);
		break;
	case ICMP_ECHO:
		MIB2_STATS_INC(mib2.icmpinechos);
		src = ip4_current_dest_addr();
		
		if (ip4_addr_ismulticast(ip4_current_dest_addr()))
		{
    
    
			goto icmperr;
		}
		if (ip4_addr_isbroadcast(ip4_current_dest_addr(),
						ip_current_netif()))
		{
    
    
			goto icmperr;
		}
		if (p->tot_len < sizeof(struct icmp_echo_hdr))
		{
    
    
			goto lenerr;
		}
		/* 调整回显报文请求中的相关字段以生成回显应答报文*/

		//强制将数据区域转换为ICMP 报文首部
		iecho = (struct icmp_echo_hdr *)p->payload;
		if (pbuf_add_header(p, hlen))
		{
    
    
			LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
						("Can't move over header in packet"));
		}
		else
		{
    
    
			err_t ret;
			struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
			//拷贝源IP 地址
			ip4_addr_copy(iphdr->src, *src);
			//拷贝目标IP 地址
			ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
			//填写报文类型
			ICMPH_TYPE_SET(iecho, ICMP_ER);
		
			iecho->chksum = 0;
		
			//填写生存时间
			IPH_TTL_SET(iphdr, ICMP_TTL);
			IPH_CHKSUM_SET(iphdr, 0);
		
			ICMP_STATS_INC(icmp.xmit);
			MIB2_STATS_INC(mib2.icmpoutmsgs);
			MIB2_STATS_INC(mib2.icmpoutechoreps);
		
			/* 发送ICMP 回显应答报文*/
			ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
								ICMP_TTL, 0, IP_PROTO_ICMP, inp);
			if (ret != ERR_OK)
			{
    
    
				LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
			}
		}
		break;
	default:
		//对于其他类型的报文,直接丢掉
		if (type == ICMP_DUR)
		{
    
    
			MIB2_STATS_INC(mib2.icmpindestunreachs);
		}
		else if (type == ICMP_TE)
		{
    
    
			MIB2_STATS_INC(mib2.icmpintimeexcds);
		}
		else if (type == ICMP_PP)
		{
    
    
			MIB2_STATS_INC(mib2.icmpinparmprobs);
		}
		else if (type == ICMP_SQ)
		{
    
    
			MIB2_STATS_INC(mib2.icmpinsrcquenchs);
		}
		else if (type == ICMP_RD)
		{
    
    
			MIB2_STATS_INC(mib2.icmpinredirects);
		}
		else if (type == ICMP_TS)
		{
    
    
			MIB2_STATS_INC(mib2.icmpintimestamps);
		}
		else if (type == ICMP_TSR)
		{
    
    
			MIB2_STATS_INC(mib2.icmpintimestampreps);
		}
		else if (type == ICMP_AM)
		{
    
    
			MIB2_STATS_INC(mib2.icmpinaddrmasks);
		}
		else if (type == ICMP_AMR)
		{
    
    
			MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
		}
		
			ICMP_STATS_INC(icmp.proterr);
			ICMP_STATS_INC(icmp.drop);
	}
	pbuf_free(p);
	return;
lenerr:
	pbuf_free(p);
	ICMP_STATS_INC(icmp.lenerr);
	MIB2_STATS_INC(mib2.icmpinerrors);
	return;
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING ||
!LWIP_BROADCAST_PING
icmperr:
	pbuf_free(p);
	ICMP_STATS_INC(icmp.err);
	MIB2_STATS_INC(mib2.icmpinerrors);
	return;
#endif
}

Reference: LwIP Application Development Practical Guide - Based on Wildfire STM32

Guess you like

Origin blog.csdn.net/picassocao/article/details/129256137