ICMP and Ping
ICMP, or Internet Control Message Protocol, is one of the core protocols in the internet protocol family. It serves as a crucial tool primarily used by operating systems in networked computers to send error messages. These error messages can convey important information, such as indicating that the destination computer is unreachable.
Now, ICMP has a different purpose compared to TCP and UDP. Unlike TCP and UDP, which are commonly used by user-owned network applications, ICMP isn't directly utilized by such applications. However, there's an exception—a popular application called Ping.
Essentially, Ping is a command used by both techies and non-techies to check if a specific IP address is active and ready to receive requests. It acts as a diagnostic tool to ensure that the computer we're trying to reach is indeed up and running.
The interesting thing is that Ping got its name from sonar technology used in submarines. Just like the "ping" sound you hear when sonar detects an object through echoes, Ping sends an ICMP Echo Request and waits for an ICMP Echo Response. It's like a virtual sonar, helping us detect the presence and responsiveness of a target host.
Here's an analogy to help you grasp it better: You know when you send a message on WhatsApp saying "Ping" or simply "P" to grab someone's attention or see if they're online? Well, in the context of computer networking, Ping operates in a similar fashion. It sends a small message, known as an ICMP Echo Request, to a specific network interface and patiently waits for a response. If it receives a ICMP Echo Reply, it indicates that the host is reachable and good to go.
Alright, let's check out how the Ping and ICMP works by pinging its IP address.
So, we're going to run the ping command with the IP address 126.96.36.199. Take a look at my terminal below:
$ ping 188.8.131.52 PING 184.108.40.206 (220.127.116.11) 56(84) bytes of data. 64 bytes from 18.104.22.168: icmp_seq=1 ttl=60 time=4.50 ms 64 bytes from 22.214.171.124: icmp_seq=2 ttl=60 time=3.34 ms 64 bytes from 126.96.36.199: icmp_seq=3 ttl=60 time=9.42 ms 64 bytes from 188.8.131.52: icmp_seq=4 ttl=60 time=7.73 ms 64 bytes from 184.108.40.206: icmp_seq=5 ttl=60 time=6.72 ms 64 bytes from 220.127.116.11: icmp_seq=6 ttl=60 time=3.97 ms 64 bytes from 18.104.22.168: icmp_seq=7 ttl=60 time=8.41 ms 64 bytes from 22.214.171.124: icmp_seq=8 ttl=60 time=5.68 ms 64 bytes from 126.96.36.199: icmp_seq=9 ttl=60 time=15.4 ms 64 bytes from 188.8.131.52: icmp_seq=10 ttl=60 time=17.7 ms 64 bytes from 184.108.40.206: icmp_seq=11 ttl=60 time=13.3 ms
We can see that we received a reply from the host. Now, let's notice a few more things:
The default size of the payload sent by the source machine is 64 bytes. Keep in mind that this is for a Linux machine; for Windows, it's usually 32 bytes.
The size of the payload received by the source machine from the destination machine (the reply) is also 64 bytes.
Seqeunce number (icmp_seq). Most Linux systems use a unique identifier for every ping process, and sequence number is an increasing number within that process (we can see that the sequence number is keep increasing).
TTL (Time to Live) is 120. This value represents the maximum number of hops (routers) the ping packet can pass through before being discarded. It ensures that the packet doesn't loop indefinitely in the network.
Time. This value represents the round-trip time (RTT) between a source and a destination.
Analyze ICMP Packet
So, when I pinged 220.127.116.11, I decided to run tcpdump to capture some network traffic. I stumbled upon an interesting packet with some cool details. Check it out:
ICMP Echo Request packet:
ICMP Echo Reply Packet:
ICMP Packet Structure
To know the details of the packet above, we must understand the ICMP Packet Structure. An ICMP packet follows a very simple and well-defined format. Let’s look at and understand what can learn from each component.
Because ICMP is built on the core IP protocol, every packet has 20 bytes of standard IP header. There’s very little value in the information from the header because many security tools already pull out the source IP, destination IP, and TTL information.
This tells us what kind of ICMP message it is. For example, it could be an Echo Request, indicating that we're asking, "Hey, are you there?" Or it could be an Echo Reply, meaning the device is responding with a "Yep, I'm here!"
ICMP codes are like tags or stickers that provide additional information within ICMP messages. They help us understand the specific nature or reason behind a certain ICMP message type.
This is like a secret code that helps ensure the integrity of the message. It's like a checksum on your pizza delivery to make sure no one took a sneaky bite on the way.
The next four bytes
Were originally unused in this piece, but they have been repurposed several times over the years. Now, the four bytes may contain padding data. They may also be an identifier and a sequence number of the packet used to help manage ICMP over NAT.
Identifier: This field is used to help match echo requests to the associated reply.
Sequence number: Same function as Identifier.
Data block is variable size and content depending on the type and code. It’s also the most interesting part (after Type and Code) because it was designed to be extensible. This means anyone using ICMP for any purpose has to support a variable size/purpose data block. Ultimately, supporting a variable size/purpose data block opens up pathways for all sorts of malicious network shenanigans.
FYI: What is BE and LE which we can see in the capture?
BE and LE The BE is big endian and LE is called little endian. The sequence number field is simply being displayed in both big endian (BE) and little endian (LE) formats to make it easier to follow when those sequence numbers are incrementing from one ICMP echo request/reply to the next. The reason both formats are shown is because sometimes those fields are populated in big endian format and sometimes they are populated in little endian format, and there is no definitive way to tell which format it's in from the contents of the packet .
Read more about BE and LE:
When analyzing an ICMP frame with a size of 9000 bytes, it is observed that the packet is fragmented according to the smallest Maximum Transmission Unit (MTU) size of the network devices it traverses. This fragmentation process is handled automatically by the IP protocol, so we don't need to worry about it. The IP protocol is responsible for breaking down the large packet into smaller fragments that can fit within the MTU limitations of each network device along the path. The receiving device then reassembles these fragments back into the original packet.
If you want to know more you can read the following article:
Length of default ICMP frame is 98 bytes as explained in the below table:
Maximum Size of ICMP Data
The maximum allowed size of an IPv4 network packet is 65535 bytes. This includes the IP header (20 bytes) and ICMP header (8 bytes), leaving up to 65507 bytes for the ICMP Data. But there is usually network policy specifies the maximum size of ICMP packets that are allowed to traverse from the source to the destination.
When an ICMP packet is sent from the source to the destination, the network policy examines the packet size. If the packet size exceeds the specified limit, the policy may take different actions depending on the configured rules. It can either drop the packet, allow it to pass through but with modifications (e.g., fragmentation), or apply other specific actions defined in the policy.
For example when I try to ping google dns with a size of 1-68 bytes the packet is accepted and the size of the reply message is the same. When I try more than 68 bytes, the size of the ICMP echo reply will be different from the echo request, causing truncated status. If I try more than 1400 bytes, packets will be dropped immediately.
But the ICMP message packet size limit is different depending on the source and destination networks. For example, if I try to ping my server with the maximum packet size, the packet is accepted and sends an echo reply normally.
Let's give it a try. Use the -s option to set the packet size.
ping to google dns :
// Packets are accepted $ ping 18.104.22.168 -s 50 PING 22.214.171.124 (126.96.36.199) 50(78) bytes of data. 58 bytes from 188.8.131.52: icmp_seq=1 ttl=55 time=39.8 ms 58 bytes from 184.108.40.206: icmp_seq=2 ttl=55 time=55.6 ms 58 bytes from 220.127.116.11: icmp_seq=3 ttl=55 time=36.8 ms ^C --- 18.104.22.168 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 36.783/44.080/55.634/8.263 ms // Packets are truncated $ ping 22.214.171.124 -s 69 PING 126.96.36.199 (188.8.131.52) 69(97) bytes of data. 76 bytes from 184.108.40.206: icmp_seq=1 ttl=55 (truncated) 76 bytes from 220.127.116.11: icmp_seq=2 ttl=55 (truncated) 76 bytes from 18.104.22.168: icmp_seq=3 ttl=55 (truncated) 76 bytes from 22.214.171.124: icmp_seq=4 ttl=55 (truncated) ^C --- 126.96.36.199 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3002ms rtt min/avg/max/mdev = 44.137/62.431/106.170/25.619 ms $ ping 188.8.131.52 -s 1400 PING 184.108.40.206 (220.127.116.11) 1400(1428) bytes of data. 76 bytes from 18.104.22.168: icmp_seq=1 ttl=55 (truncated) 76 bytes from 22.214.171.124: icmp_seq=2 ttl=55 (truncated) ^C --- 126.96.36.199 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 51.808/80.160/108.512/28.352 ms // Packets are dropped $ ping 188.8.131.52 -s 1450 PING 184.108.40.206 (220.127.116.11) 1450(1478) bytes of data. ^C --- 18.104.22.168 ping statistics --- 6 packets transmitted, 0 received, 100% packet loss, time 5109ms
Ping to my server:
// Max packet size allowed $ ping 22.214.171.124 -s 65507 PING 126.96.36.199 (188.8.131.52) 65507(65535) bytes of data. 65515 bytes from 184.108.40.206: icmp_seq=1 ttl=56 time=232 ms 65515 bytes from 220.127.116.11: icmp_seq=2 ttl=56 time=150 ms 65515 bytes from 18.104.22.168: icmp_seq=3 ttl=56 time=189 ms 65515 bytes from 22.214.171.124: icmp_seq=4 ttl=56 time=198 ms 65515 bytes from 126.96.36.199: icmp_seq=5 ttl=56 time=221 ms ^C --- 188.8.131.52 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4001ms rtt min/avg/max/mdev = 149.830/197.934/232.122/28.608 ms
File Transfer over ICMP Messages
So, the inspiration for this article came from a LinkedIn feed where Aldin Setiawan shared his personal project about file transfer through ICMP. It got me curious about how this was possible, so I decided to delve into it and explore how to create a similar program.
Aldin's project sparked my interest because transferring files through ICMP sounded pretty cool and unconventional. I wanted to understand the mechanics behind it and see if I could come up with my own implementation.
After understanding the point we discussed earlier I started to understand how it works:
1. Since there is a maximum size on the ICMP Packet, we have to break the file into smaller picies, such as small chunks or packets. These chunks carry a portion of the file's data.
2. Now, here comes the smart part. We take these chunks and embed them in the data or payload portion of the ICMP message. The ICMP message is like a small carrier that can carry our file data. It's like hiding pieces of the file inside the ICMP message. By doing this, we can send these ICMP packets across the network, moving from one device to another until they reach their destination. Each device along the way, such as a router or computer, will process these ICMP packets and forward them to the destination.
3. At the receiving end, the destination device receives this ICMP packet and starts reconstructing the original file. It takes all the pieces it receives and puts them together, like solving a puzzle. Once all the packets are received and assembled correctly, voila! You have a complete file on the receiving device.
The ICMP Payload
I split the payload data into three parts. The first part, the password which is 50 bytes, is used to identify the packet from the sender. The second is 100 bytes, serves as the file name and is used to identify where the chunk should be constructed. The third part, or the remaining part, is used for the chunk file itself.
Trying Out the Program
I wrote this program in Golang, and you can try it out or contribute to it on this GitHub repository: https://github.com/radendi/pongo. Here's an example of how to use this program. Make sure you have the latest version of Golang installed.
1. First clone the project at the sending and receiving nodes
git clone https://github.com/radendi/go-icmp
2. Run recv.go on the reciever node, -l to listen, -p to specify the password
go run recv.go -l 0.0.0.0 -p pass12345
3. Run send.go on the sender node, -d to specify the destination, -p to specify the same password as the sender, and -s to specify the maximum icmp packet size.
go run send.go -d 184.108.40.206 -s 1500 -p pass12345 -f image.jpg
4. Wait for the transfer process to complete
5. Compare files md5 checksum:
Here are some points to note from:
- As we discussed earlier, it can be seen that packets that exceed the MTU size will be fragmented.
- After reassembling the fragmented packets, the length of data per echo request will remain the same as what we initially specified. For example, if we set the payload size to 5000 bytes, that will be the size of each echo request containing the file data.
- And the point is that only the ICMP protocol communicates to transfer files.
File transfer over ICMP introduces a captivating concept that breaks away from the conventional norms. It offers a unique and offbeat method for transferring files. However, it's crucial to be aware of its limitations, such as restrictions on payload size and the potential hurdles posed by administrative and firewall constraints. When it comes to reliable and efficient file transfer, TCP and UDP remain the go-to choices. They have garnered extensive support and boast purpose-built features.
Well, this concept might not be the go-to choice for real-world file transfer due to the numerous drawbacks associated with using ICMP. But hey, that doesn't mean we can't extract some valuable lessons from this experimental adventure called "File transfer over ICMP."
Exploring this unconventional approach opens up a world of possibilities and teaches us a great deal about the intricacies of networking. It pushes us to think outside the box, challenge the norms, and experiment with new ideas. Even though the practicality may be limited, the knowledge gained from this experiment can be invaluable.
By diving into the realm of File transfer over ICMP, we gain insights into the inner workings of protocols, understand the nuances of packet fragmentation, and learn how different components of the network infrastructure come into play. This hands-on experience fuels our curiosity and expands our understanding of the vast networking landscape.
So, while we may not see File transfer over ICMP being widely used in real-world scenarios, it's the journey of exploration and experimentation that truly counts. We can take away valuable lessons, broaden our knowledge, and apply the insights gained from this experiment to other areas of networking.
In the end, it's the spirit of exploration and the lessons learned that make File transfer over ICMP a worthwhile endeavor. So let's embrace the experimental nature, embrace the limitations, and embark on this fascinating journey of discovery. Who knows what other exciting insights we might stumble upon along the way?