Overview

The ping command is a very common networking tool that uses the ICMP protocol to probe the connectivity, latency, stability, and other performance metrics of a network from a local to a remote address. However, most people don’t know much about the implementation of the ping command because we rarely deal with the ICMP protocol in our daily development work. Recently I’ve been developing https://github.com/joyme123/gnt and the goal is to implement most of the network tools through a single binary. So I’ve come across some ping command implementations, and I’d like to make some brief notes and share them here.

ping command is based on ICMP protocol implementation

The ICMP protocol itself is not described here, there is a lot of good and detailed information on the web. For example, this article on Wikipedia: Internet_Control_Message_Protocol.

There is a big difference between protocols like ICMP and TCP/UDP. Transport layer protocols like TCP/UDP are process level, i.e., they can correspond to one or more processes via Port. Therefore, no special privileges are required to run applications that use TCP/UDP protocols. ICMP, on the other hand, is OS-level, so on early linux, if an application wanted to send ICMP packets, it had to do so via **socket(AF_INET, SOCK_RAW, int** *protocol***)**, and calling SOCK_RAW required root privileges, or via linux network capability.

Because of this special feature, later linux provides socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) to send ICMP, where SOCK_DGRAM is used by the UDP protocol. However, it is easy to misunderstand that it does not mean that the ICMP capability is wrapped or implemented with the UDP protocol, but the ICMP packets sent out this way are still ICMP packets, but no longer require root privileges or special network capability settings. This is often referred to as Unprivileged ICMP. linux also provides sysctl configuration to restrict the use of this capability.

1
2
# 999~59999 specifies the range of allowed user group IDs, and can be set to 1 0 if all user groups are not allowed
net.ipv4.ping_group_range = 999 59999

With the problem of how to send icmp packets solved, it’s time to consider implementing a few key ping command features.

Packet loss detection

Each ICMP packet has a sequence field, you can specify the value of this sequence when sending, and the destination will set the sequence value to the same when responding, indicating which request packet is being responded to. This way we can know the response of each ICMP packet sent out, and the sequence of no response is the discarded packet. In this way, we can detect the presence of packet loss in the network.

Delay detection

ICMP supports echo request and reply, i.e., the payload is sent out through ICMP protocol wrapper, and the destination will return the same. So we can write the time of sending in the request, and then retrieve the time when we receive the reply packet to know the delay.

Regarding the format of the time written, the implementation is free. However, it is recommended to use unix time, accurate to microseconds. Then keep 4 bytes of seconds + 4 bytes of microseconds and write it to the beginning of the payload. This is consistent with the implementation of linux ping, which can be identified by packet capture tools like wireshark.

Ref

  • https://www.myway5.com/index.php/2023/02/04/ping-%e4%b8%8e-icmp-%e5%8d%8f%e8%ae%ae/