recoding the ping command
Ping use ICMP (Internet Control Message Protocol)
Ping is the name of a command that allows you to test the accessibility of another
machine through the IP network.
The command also measures the time taken to receive a response, called the round-trip time
!!! You will take as reference the ping implementation from inetutils-2.0 (ping -V).
-- ping -V
ping from iputils 20240117
libcap: yes, IDN: yes, NLS: no, error.h: yes,
getrandom(): yes, __fpending(): yes
-
PC sends an ICMP Echo Request to a target IP.
-
If the target is reachable and allows ICMP, it responds with an ICMP Echo Reply (Type 0).
-
The time between request and reply = latency.
0 8 16 24 31
+---------------+---------------+---------------+---------------+
| Type | Code | Checksum |
+---------------+---------------+---------------+---------------+
| Identifier | Sequence Number |
+---------------+---------------+---------------+---------------+
| Data ... |
+---------------------------------------------------------------+type
- 8 → Echo Request
- 0 → Echo Reply
code
- Always 0 for Echo messages
ICMP (Internet Control Message Protocol) is used not only for ping (Echo Request/Reply) but also for error reporting and network diagnostics. When you use the -v (verbose) flag, you can see all ICMP messages, not just echo replies.
| Type | Name | Purpose | When You See It |
|---|---|---|---|
| 0 | Echo Reply | Response to ping | Normal successful ping |
| 3 | Destination Unreachable | Host/network/port can't be reached | Host is down, network doesn't exist |
| 8 | Echo Request | Ping request packet | Your own sent packet (visible with -v) |
| 11 | Time Exceeded | Packet TTL expired | Low TTL value, packet died in transit |
| 5 | Redirect | Route changed | Router suggests better path |
When a router or host cannot deliver your packet, it sends back a Type 3 message with a specific code explaining why:
| Code | Name | Meaning | Example Scenario |
|---|---|---|---|
| 0 | Network Unreachable | The destination network doesn't exist | ping 10.99.99.99 - no route to network |
| 1 | Host Unreachable | Network exists but host is down/unreachable | ping 192.168.1.254 - host doesn't respond |
| 2 | Protocol Unreachable | Network protocol not supported | ICMP blocked on destination |
| 3 | Port Unreachable | Destination port is closed | Used by traceroute |
| 4 | Fragmentation Needed | Packet too big, can't be fragmented | MTU issues |
| 13 | Communication Prohibited | Firewall/policy blocked packet | Security filtering |
Example with verbose:
$ ./ft_ping -v 192.168.1.254
PING 192.168.1.254 (192.168.1.254) 56(84) bytes of data
From 192.168.1.1 icmp_seq=1 Destination Host Unreachable
From 192.168.1.1 icmp_seq=2 Destination Host UnreachableYour router (192.168.1.1) is telling you it can't reach the host.
When a packet's TTL (Time To Live) reaches 0, the router drops the packet and sends back a Type 11 message.
| Code | Name | Meaning |
|---|---|---|
| 0 | TTL Exceeded in Transit | Packet died during routing |
| 1 | Fragment Reassembly Time Exceeded | Fragments couldn't be reassembled in time |
Example with low TTL:
$ ./ft_ping -v -t 1 google.com
PING google.com (142.251.142.14) 56(84) bytes of data
From 192.168.1.1 icmp_seq=1 Time to live exceeded
From 192.168.1.1 icmp_seq=2 Time to live exceededWith TTL=1, the packet dies at your router (first hop).
This is how traceroute works! It sends packets with increasing TTL values (1, 2, 3...) and collects the "Time Exceeded" responses to map the route.
With -v on localhost, you might see your own sent packets:
$ ./ft_ping -v 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data
From 127.0.0.1 icmp_seq=1 Echo Request (our own packet)
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.051 msThis happens because the loopback interface captures both sent and received packets.
Without -v: You only see successful replies. When ping fails, you just see "Request timeout" with no explanation.
With -v: You see why it failed:
- Is the network unreachable? (routing problem)
- Is the host unreachable? (host is down)
- Did the packet TTL expire? (too many hops or TTL too low)
- Is there a firewall blocking? (communication prohibited)
This diagnostic information helps network troubleshooting!
Checksum
- Initially set to 0, then computed over entire ICMP packet
Checksum is calculated using 16-bit chunks of data. This 16-bit word size is a protocol rule. If data length is odd, we pad with [0x00] (in code: sum += *(uint8_t *)ptr;)
Identifier
id - match request ↔ reply
Sequence number
- Starts at 1 and increments for each packet
Data
- can be anything but usually a timestamp
Stores command-line arguments parsed from user input.
Fields:
bool verbose- Flag indicating if-v(verbose mode) was specifiedchar *destination- Target hostname or IP address from command linebool help_requested- Flag indicating if-hor-?was specified
Usage: Created in main() and filled by parse_ping_argc()
Stores the resolved IPv4 address information for the target destination.
Fields:
struct sockaddr_in addr- Binary IPv4 address structure used by socket functionssin_family- AlwaysAF_INET(indicates IPv4)sin_addr- 32-bit IP address in network byte order (e.g., 0x08080808 for 8.8.8.8)sin_port- Port number (not used for ICMP)
char ip_string[INET_ADDRSTRLEN]- Human-readable IP string (e.g., "142.250.185.46")INET_ADDRSTRLEN= 16 bytes (enough for "255.255.255.255\0")
char *hostname- Pointer to original input string from user (can be IP or domain name)
Usage: Output structure filled by resolve_ipv4_sockaddr() after DNS resolution
ICMP packet header structure (8 bytes minimum + data).
Fields:
int8_t type- ICMP message type (0 = Echo Reply, 8 = Echo Request)int8_t code- ICMP message code (always 0 for Echo messages)int16_t checksum- 16-bit one's complement checksum of entire ICMP packetuint16_t id- Identifier to match request ↔ reply (usually process ID)uint16_t sequence- Sequence number (increments for each packet sent)int8_t data[]- Flexible array member for payload (timestamp, padding, etc.)
ICMP message types used in the program.
Values:
ICMP_UNKNOWN = -1- Unknown or unsupported typeICMP_ECHO_REPLY = 0- Echo Reply (response to ping)ICMP_ECHO_REQUEST = 8- Echo Request (ping packet)
int parse_ping_argc(int argc, char *argv[], struct s_ping_options *options)Purpose: Parse command-line arguments and store results in options struct
Parameters:
argc- Argument count from mainargv[]- Argument values from mainoptions- Pointer to struct where parsed options are stored
Returns:
0- Success, valid options parsed1- Error (invalid option or missing destination)2- Help requested (-h or -?), help message already printed
Behavior:
- Loops through all arguments
- Detects flags:
-v(verbose),-h/-?(help) - Stores destination (first non-flag argument)
- Prints help and returns 2 if help requested
- Prints error + help and returns 1 for invalid options
- Validates destination exists before returning
int resolve_ipv4_sockaddr(const char *dest, struct s_resolved_ipv4 *out)Purpose: Resolve hostname or IP string to binary IPv4 address
Parameters:
dest- Input: hostname (e.g., "google.com") or IP string (e.g., "8.8.8.8")out- Output: pointer to struct where resolved address info is stored
Returns:
0- Success,outis filled with valid IPv4 address-1- Failure (DNS lookup failed, invalid input, or conversion error)
What it does:
- Validates input parameters
- Sets up
getaddrinfo()hints for IPv4/ICMP/raw socket - Calls
getaddrinfo()to perform DNS resolution - Extracts binary IPv4 address to
out->addr - Converts binary address to string using
inet_ntop() - Stores original hostname pointer
- Frees resolver resources
Key functions used:
getaddrinfo()- Converts hostname/IP string → binary address (with DNS lookup)inet_ntop()- Converts binary address → human-readable stringfreeaddrinfo()- Frees memory allocated by getaddrinfo (must call!)
uint16_t calculate_icmp_checksum(void *data, int16_t len)Purpose: Calculate RFC 1071 Internet Checksum for ICMP packet integrity
Parameters:
data- Pointer to ICMP packet data (header + payload)len- Length of data in bytes
Returns:
- 16-bit checksum value (one's complement)
Algorithm:
- Sum all 16-bit words in the data
- If odd length, add the last byte as 8-bit value
- Fold any overflow (carry bits) from 32-bit sum into 16 bits
- Return one's complement (~sum)
Usage:
- Set checksum field to 0 before calling
- Calculate checksum over entire ICMP packet
- Store result in checksum field before sending
void copy_icmp_header(int8_t *dest, int8_t *src, int16_t len)Purpose: Copy ICMP header bytes from source to destination
Parameters:
dest- Destination buffersrc- Source bufferlen- Number of bytes to copy
Note: Simple byte-by-byte copy. Consider using memcpy() for better performance.
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res)Purpose: Protocol-independent hostname/IP resolution (DNS lookup)
What it does:
- Takes hostname or IP string
- Performs DNS lookup if needed
- Returns linked list of matching addresses
- Handles IPv4/IPv6 automatically based on hints
Must call: freeaddrinfo(res) when done to prevent memory leak
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)Purpose: Convert binary network address to presentation (string) format
Parameters:
af- Address family (AF_INET for IPv4)src- Pointer to binary address (e.g.,&sockaddr_in.sin_addr)dst- Destination buffer for stringsize- Size of destination buffer (INET_ADDRSTRLEN for IPv4)
Returns: Pointer to dst on success, NULL on error
Example: 0x08080808 → "8.8.8.8"