I. Implementing ICMP in Rust
Tags: icmp, rust, networkprogramming
Published: 2021-07-18 14:38:01

---
title: I. Implementing ICMP in Rust
published: true
description: Implementing ICMP in Rust
tags: icmp, rust, icmp, networkprogramming
//cover_image: https://direct_url_to_image.jpg
---

This post is inspired by [Jon Gjengset](https://twitter.com/jonhoo) series titled ["Implementing TCP in Rust"] (https://www.youtube.com/watch?v=bzja9fQWzdA).

This should be an interesting read for anyone who wants to get a better understanding of networking protocols down to bits and doesn't mind diving into RFCs.

So what is ICMP anyway? It's the protocol that makes `ping` and `traceroute` possible. If you're a gamer or ever joined an online call, then you know how important those ping/latency numbers are.

Unlike TCP/UDP, which are Layer 4 protocols in OSI Model, ICMP actually belongs to Layer 3 which is the same level as IP, itself the **main** protocol of the Internet.

![osi-model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ew5c1p4s7frl9ecfp90m.png)
 

## 1. IP Packet

First, I'll show you what an IPv4 ([RFC-791](https://datatracker.ietf.org/doc/html/rfc791#section-3.1)) header looks like, remember all your connections on the internet start with one of these:
```
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                  Example Internet Datagram Header
```

To capture a raw IP frame, ping `localhost` in another terminal and listen with:
```
$ tcpdump -i lo -p icmp -X
15:23:33.885666 IP localhost > localhost: ICMP echo request, id 21780, seq 1, length 64
	0x0000:  4500 0054 9210 4000 4001 aa96 7f00 0001  E..T..@.@.......
	0x0010:  7f00 0001 0800 492b 5514 0001 2d08 f460  ......I+U...-..`
	0x0020:  0000 0000 6c83 0d00 0000 0000 1011 1213  ....l...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567
15:23:33.885695 IP localhost > localhost: ICMP echo reply, id 21780, seq 1, length 64
	0x0000:  4500 0054 9211 0000 4001 ea95 7f00 0001  E..T....@.......
	0x0010:  7f00 0001 0000 512b 5514 0001 2d08 f460  ......Q+U...-..`
	0x0020:  0000 0000 6c83 0d00 0000 0000 1011 1213  ....l...........
	0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
	0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
	0x0050:  3435 3637                                4567

```

Let's start with the first IP and decode the header — the first 20 bytes of `4500 0054 9210 4000 4001 aa96 7f00 0001 7f00 0001 ...`:
```
▼
4			Version					= IPv4 version
5			IHL						= 20 bytes
00			ToS
0054		Total Length			= 84 bytes
9210		Identification
4			Flags
000			Fragment Offset
40			Time to Live			= 64 TTL
01			Protocol				= ICMP
aa96		Header Checksum
7f00 0001	Source IP Address		= 127.0.0.1
7f00 0001 	Destination IP Address	= 127.0.0.1
```

## 2. ICMP Packet

From [RFC-792](https://datatracker.ietf.org/doc/html/rfc792) we can see the layout of *Echo or Echo reply message*:

```
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Identifier          |        Sequence Number        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Data ...
+-+-+-+-+-
```

And by replacing `data` bytes, the remaining bytes after the header, we get our ICMP header:

```
08			Type					= Echo message
00			Code					 
492b		Checksum
5514		Identifier				= id 21780 (in raw capture)
0001		Sequence Number			= seq 1
```

If you compare this ICMP packet with the one from second IP frame, two fields have changed:
1. `Type` which has become `00` or Echo **reply** message
2. `Checksum`, naturally because content has changed

## 3. Implementation in Rust

Now that we know what an ICMP packet looks like, we are ready to implement it in Rust. 

Unlike TCP/UDP, we have no concept of ports here and only use IPs to find our path. (Now you understand why ping has no port?).

Furthermore, astute reader may have noticed that the only thing that changes in the ICMP echo reply is the `Type` which goes from `08` to `00`. 

**We've got all we need. Check out:** [II. Implementing ICMP in Rust](https://dev.to/xphoniex/ii-implementing-icmp-in-rust-3bk5)