Internet Control Message Protocol

From OSDev Wiki
Jump to navigation Jump to search

Internet Control Message Protocol, commonly referred to as ICMP, is one of the fundamental protocols in a typical IPv4-capable network stack. The protocol gives nodes on a network the ability to share information and errors without utilising an upper-layer protocol which may have been compromised. For example, if a TCP connection is having packets dropped due to their time-to-live being too low, there is no way for either host to determine the fault. The only way either host can resolve the problem is if an out-of-band information message is transmitted to them, informing them that their time-to-live is too low and a router is dropping the packets. This is the purpose of ICMP.

Most people will have used ICMP at least once indirectly, by utilising the "ping" tool of their favourite operating system.

RFC

ICMP is defined in RFC792. RFC1122 defines the relationship between IPv4 and ICMP.

Packet Layout

Note that all structures in this article are intended to show the layout of a packet in memory rather than be usable as C code (although most of them should be able to be copied verbatim).

An example C structure for an ICMP packet is:

struct icmp_packet {
    uint8_t icmp_type;
    uint8_t icmp_code;
    uint16_t checksum;
};

Checksum

An ICMP checksum is performed over the header and data in the same manner as an IPv4 checksum. There is no IPv4 psuedo-header required.

Essentially, the algorithm is as follows:

1. Add all 16-bit words in the data to calculate a checksum for. This should be done in a 32-bit counter.
2. Add any stray bytes in the case of an uneven length.
3. Fold the sum into one 16-bit word (add the top 16 bits to the bottom 16 bits until the top 16 bits are zero).
4. Return the 16-bit 1's complement of the result.

When verifying an incoming packet, you should perform the checksum over the header and data without modifying any data in the packet. Your calculated checksum will be zero if the checksum is valid, and non-zero if the checksum is invalid.

ICMP Types

There are a variety of message types defined for ICMP.

Echo Request & Reply

These packets have an additional header after the ICMP header with information related to echo replies and requests:

struct icmp_echo_packet {
    uint16_t echo_id;
    uint16_t echo_seq;
};

Following this header is an arbitrary length of data.

The ICMP code for an echo request or reply is always zero.

Echo Request (type 8)

An echo request is sent to a host in order to request an "Echo Reply" response. These are typically used to determine if a host on the network is reachable, or to determine latency in a link or route.

The ID and Sequence values can be set to an arbitrary value, but should be meaningful to the implementation sending the echo request.

The packet data should be a number of bytes (typically enough to pad the packet to 64 or so bytes) that you expect to receive back from the host. Windows seems to just use the lower-case alphabet from 'a' to 'z' - it really is up to you what data goes here!

Echo Reply (type 0)

An echo reply is sent to a host in response to an "Echo Request" message. All features of the reply are the same as the echo request, except for the destination and source addresses. The ID and Sequence values must not be changed. These are how a received response is tied to a previous request.

Destination Unreachables (type 3)

These packets are sent by a host or router to notify another host that the destination it is attempting to send packets to is unreachable, for a variety of possible reasons.

The packet format is as follows:

struct dest_unreachable_packet {
    uint16_t empty;
    uint16_t next_mtu;
    iphdr original_header;
    uint8_t original_payload[8]; // First eight bytes of the original IPv4 payload.
};

Note that it may be better to not define your structure containing an IP header structure and these extra bytes depending on your operating system and individual needs.

The next_mtu field contains the MTU for the next hop if this message is a Fragmentation Required message.

A host uses the original IP header transmitted in the destination unreachable packet to determine which packet failed. This is a good reason to use valid and tracked IDs in your IPv4 packets.

Destination Unreachable messages use the ICMP code field rather than the ICMP type field to define individual message types.

Framentation Required (code 4)

A host or router sends this packet to a host when the packet is larger than the MTU for a link, and the "Don't Fragment" (DF) bit in the IPv4 header is set. Were the DF bit to be unset, the router or host would fragment the packet automatically in order to transmit it.

One common use of this message is in Path MTU Discovery, which determines the MTU for a complete route from one host to another by sending out packets with decreasing size (with DF set) until a Fragmentation Required message does not get sent for the packet. Rather than "guess-and-check", a host should use the "Next MTU" field of the destination unreachable header to determine the next MTU to attempt. Keep this in mind when writing code to route packets; you may need to implement the infrastructure required to handle this (very important) message.