-v: Verbose output - Shows detailed socket information and packet identifiers in output-c <count>: Stop after sending count packets - Limits total packets sent, program exits after reaching this number-s <size>: Set packet payload size - Controls payload size (excludes ICMP/IP headers), affects total packet size-l <preload>: Send preload packets immediately - Sends multiple packets rapidly at start, then continues with normal 1-second intervals-W <timeout>: Set timeout per packet in seconds - How long to wait for each packet response before considering it lost-t <ttl>: Set Time-To-Live for packets - Maximum number of network hops before packet is discarded-h: Show help/usage - Displays usage information and exits<destination>: Target hostname or IP (required) - Target to ping, can be hostname (google.com) or IP address (8.8.8.8)
-
getopt(): Standard POSIX function that processes command-line arguments systematically. It takes the argument count, argument vector, and an option string ("vhc:s:l:W:t:") where letters represent valid options and colons indicate options that require arguments.getopt()returns each option character one by one, setsoptargto point to the option's argument (if any), and handles error cases like unknown options or missing required arguments. It automatically manages theoptindglobal variable to track position in the argument list. -
parse_int_range(): Custom validation function that safely converts string arguments to integers usingstrtol()and enforces min/max boundaries. It performs comprehensive error checking: ensures the entire string is a valid number, detects overflow/underflow conditions, and validates the result falls within acceptable ranges.
The resolveHost() Convert user-provided hostname or IP address into network operations format suitable for.
-
getaddrinfo(): DNS Resolution. Takes a hostname/IP string and service name, returning a linked list of address structures. -
inet_ntop(): Converts binary network addresses back to human-readable string format. Used to create the display string that shows the resolved IP address in program output.
The createSocket() establishes raw network sockets for ICMP communication. Creates both IPv4 and IPv6 sockets, sets non-blocking mode, and configures TTL.
socket(): Creates raw sockets for ICMP (IPv4) and ICMPv6 (IPv6) protocols.fcntl(): Sets sockets to non-blocking mode for asynchronous operation.setsockopt(): Configures TTL (IP_TTL/IPV6_UNICAST_HOPS) for packet hop limits.
[ IP Header (20 bytes) ][ ICMP Header (8 bytes) ][ Payload Data (variable) ]
Kernel ft_ping ft_ping
typedef struct s_ping_pkg {
struct icmphdr header; // ICMP header (8 bytes)
char msg[]; // Variable payload data
} t_ping_pkg;- Kernel: Automatically adds IP header (source/dest IP, protocol, TTL, checksum)
- Our Code: Constructs ICMP header + payload, calculates ICMP checksum
-
ICMP Header Setup:
type: ICMP_ECHO (IPv4) or ICMP6_ECHO_REQUEST (IPv6)id: Process ID for packet identificationsequence: Incremental sequence numberchecksum: RFC 792 Internet checksum
-
Payload Data:
- Timestamp:
struct timeval(16 bytes) at start for RTT calculation - Pattern Data: Repeating pattern
0x10 + (offset % 48)for packet validation
- Timestamp:
- Default Size: 56 bytes (ICMP header + payload)
- Size Range: 0-65507 bytes
- Timestamp Handling: Only included if payload ≥ 16 bytes (
sizeof(struct timeval)) - Pattern Generation:
- 0x10 (16): Base value ensuring printable ASCII range
- % 48: Cycles through 48 characters (0x10-0x3F, covering digits, letters, symbols)
- Minimum (0 bytes payload): 28 bytes total (20 IP + 8 ICMP), no timestamp or pattern
- Small (1-15 bytes payload): 29-43 bytes total, pattern data only, no RTT measurement possible
- Normal (16+ bytes payload): 44+ bytes total, timestamp + pattern data, full functionality
- Maximum (65507 bytes payload): 65535 bytes total, maximum IP packet size
gettimeofday(): Embeds send timestamp in packet payload for RTT measurementcalculate_checksum(): Computes RFC 792 checksum for packet integrityhtons(): Converts host byte order to network byte order for headers
struct pollfd fds[2]; - fds[0]: IPv4 ICMP socket (
state->conn.ipv4.sockfd) - fds[1]: IPv6 ICMP socket (
state->conn.ipv6.sockfd) - events:
POLLIN- Monitor for incoming data - revents: Reset to 0, filled by poll() with actual events
-
Active Transmission: Returns milliseconds until next 1-second send interval
-
Transmission Complete: Returns 100ms timeout for cleanup phase
-
Preload Phase: Returns 0 for immediate sending
The program runs until all packets are sent AND all responses received (or timed out):
- Send Phase: Attempt to send a ping packet if timing/preload allows
- Calculate Timeout: Determine how long to wait for network activity
- Poll for Events: Block until data arrives on either socket or timeout expires
- Handle Results:
- Data Available: Process incoming ICMP response
- Timeout: Clean up packets that exceeded response timeout
- Error: Break loop on unrecoverable errors
During each loop iteration, if timing and packet count allow, the program attempts to send a new ICMP Echo Request packet to the target.
- Packet Creation: A new packet is initialized with the correct ICMP header, sequence number, process ID, and payload (timestamp + pattern).
- Socket Selection: The appropriate socket (IPv4 or IPv6) is chosen based on the resolved address family.
- Sending: The packet is sent using
sendto(). The current timestamp is recorded for RTT calculation. - Tracking: The sent packet is added to a tracking list for later matching with replies or timeouts.
Key Points:
- Only sends if allowed by timing (interval/preload) and packet count.
- Updates statistics for packets sent.
- Handles errors gracefully (e.g., network unreachable, permission denied).
When data is available on a socket, the program processes incoming ICMP responses:
- Packet Reception: Reads the incoming packet using
recvfrom()into a buffer. - Validation: Checks that the response matches a sent packet (by process ID and sequence number).
- Type Handling: Distinguishes between echo replies (success) and ICMP errors (like time exceeded or unreachable).
- RTT Calculation: If a valid reply, extracts the timestamp from the payload and computes the round-trip time.
- Statistics Update: Updates counters for received packets, errors, and RTT statistics.
- Cleanup: Removes the matching packet from the tracking list.
We use
sendto()andrecvfrom()instead ofsend()andrecv()because ICMP operates over raw sockets without a connection-oriented protocol. These functions allow specifying the destination and source address explicitly, which is necessary since there is no established socket connection as with TCP.
When receiving ICMP packets, the buffer layout and size differ significantly depending on whether the packet is a success reply or an error message.
[ IP Header ][ ICMP Echo Reply Header ][ Payload (timestamp + pattern) ]
-
Size:
- Minimum: 28 bytes (20 IP + 8 ICMP, no payload)
- Typical: 64 bytes (20 IP + 8 ICMP + 36 payload)
The reply is a direct response to our echo request. The kernel fills in the IP header, and our code matches the reply by process ID and sequence number.
[ IP Header ][ ICMP Error Header ][ Embedded Original IP Header ][ Embedded Original ICMP Header ][ (Partial Original Payload) ]
-
Size:
- Minimum: 48 bytes (20 IP + 8 ICMP error + 20 embedded og IP + 8 embedded og ICMP)
- Can be larger if more of the original payload is included.
ICMP error messages (like "Time Exceeded" or "Destination Unreachable") must include the header and at least 8 bytes of the original packet that caused the error (per RFC 792). This allows us to identify which of our packets triggered the error by extracting the embedded headers and matching the process ID and sequence number.
For Success Replies:
- Check that the ICMP type is
ICMP_ECHOREPLY(IPv4) orICMP6_ECHO_REPLY(IPv6) - Check that the
idfield matches our process ID - Check that the sequence number exists in our sent packets list
For Error Messages:
- Check that the ICMP type is
ICMP_TIME_EXCEEDEDorICMP_DEST_UNREACH - Extract the embedded original packet from the error message payload
- Check that the embedded ICMP type is
ICMP_ECHO(our original request) - Check that the embedded
idmatches our process ID - Check that the embedded sequence number exists in our sent packets list
RTT (Round-Trip Time) is the time it takes for a packet to travel from your machine to the target and back. It is measured in milliseconds (ms) and is a key indicator of network latency.
- How is RTT measured?
- When sending a packet, the program embeds the current timestamp in the payload.
- When a reply is received, the program extracts the timestamp and subtracts it from the current time to get the RTT for that packet.
- min RTT: The lowest RTT observed during the session.
- max RTT: The highest RTT observed.
- avg RTT: The average RTT, calculated as the sum of all RTTs divided by the number of replies received.
- mdev: The mean absolute deviation of RTTs from the average RTT, representing the typical "jitter" or variability in round-trip times.
- Compute the average RTT (
avg_rtt). - For each RTT, calculate the absolute difference from the average.
- mdev is the mean (average) of these absolute differences.
Formula:
- mdev = (|rtt1 - avg| + |rtt2 - avg| + ... + |rttN - avg|) / N
- rtt min/avg/max/mdev = 0.035/0.046/0.060/0.007 ms
- min: 0.035 ms
- avg: 0.046 ms
- max: 0.060 ms
- mdev: 0.007 ms
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.064 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.063 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.060 ms
--- 127.0.0.1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3003ms rtt min/avg/max/mdev = 0.042/0.057/0.064/0.008 ms
With the -v flag, ft_ping prints additional diagnostic information:
- Socket file descriptors and types
- Address family and canonical name
- The ICMP identifier (
ident) for each reply (process id)
ping: sock4.fd: 3 (socktype: SOCK_RAW), sock6.fd: 4 (socktype: SOCK_RAW), hints.ai_family: AF_UNSPEC ai->ai_family: AF_INET, ai->ai_canonname: '127.0.0.1' PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ident=36192 ttl=64 time=0.042 ms
64 bytes from 127.0.0.1: icmp_seq=2 ident=36192 ttl=64 time=0.064 ms
64 bytes from 127.0.0.1: icmp_seq=3 ident=36192 ttl=64 time=0.063 ms
64 bytes from 127.0.0.1: icmp_seq=4 ident=36192 ttl=64 time=0.060 ms
--- 127.0.0.1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3003ms rtt min/avg/max/mdev = 0.042/0.057/0.064/0.008 ms
The -l flag controls how many packets are sent immediately at the start of the ping session, before switching to the normal 1-second interval between packets.
- Usage:
-l <preload> - Range: 1–3 (default: 1)
- Behavior:
- If
-l 3is specified, the first 3 packets are sent as quickly as possible, then the program continues sending at 1-second intervals. - Useful for quickly testing network burst handling or for simulating a short flood of packets.
- If
- Example:
This sends 3 packets instantly, then 2 more at 1-second intervals.
./ft_ping -l 3 -c 5 8.8.8.8
The -W flag sets the maximum time (in seconds) to wait for each individual ping reply before considering it lost.
- Usage:
-W <timeout> - Range: 1–2099999 (default: 4)
- Behavior:
- After sending each packet,
ft_pingwaits up to<timeout>seconds for a reply. - If no reply is received within this time, the packet is counted as lost and the program continues.
- This does not affect the interval between packet transmissions, only how long to wait for each reply.
- After sending each packet,
- Example:
Each packet will be considered lost if no reply is received within 2 seconds.
./ft_ping -W 2 -c 3 8.8.8.8
TTL (Time-To-Live) is a field in the IP header that limits the number of hops (routers) a packet can traverse before being discarded. Each router that forwards the packet decrements the TTL by 1. When TTL reaches zero, the packet is dropped and an ICMP "Time Exceeded" error is sent back to the sender.
-
Purpose: Prevents packets from circulating endlessly in routing loops.
-
IPv4: TTL is set using the
IP_TTLsocket option. -
IPv6: The equivalent is called Hop Limit, set using the
IPV6_UNICAST_HOPSsocket option. -
If a packet's TTL/hop limit reaches zero before reaching the destination, it is dropped by the router at that hop.
-
The router sends back an ICMP "Time Exceeded" message, which ft_ping reports as a timeout or error.
-
By adjusting TTL, you can trace the path to a host (like
traceroutedoes), or test how far packets get before being dropped.
The packet will only survive one hop. If the destination is farther, you'll receive a "Time Exceeded" error from the first router.
./ft_ping -c 2 -t 1 google.com polling ✔
PING google.com (142.250.185.14) 56(84) bytes of data.
From Linksys00831 (10.195.1.1): icmp_seq=1 Time to live exceeded
From Linksys00831 (10.195.1.1): icmp_seq=2 Time to live exceeded
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
+2 errors.
The packet makes it past your gateway, but dies at your ISP's first router.
./ft_ping -c 2 -t 5 google.com polling ✗
PING google.com (142.250.185.14) 56(84) bytes of data.
From lis2-cr1-be10-200.cprm.net (195.8.30.241): icmp_seq=1 Time to live exceeded
From lis2-cr1-be10-200.cprm.net (195.8.30.241): icmp_seq=2 Time to live exceeded
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
+2 errors.
Summary:
- TTL (IPv4) and Hop Limit (IPv6) control how far packets can travel.
- Low TTL values help diagnose routing and network reachability issues.