Implementing the ping Command in C on Linux
ICMP Packet Structure and Transmission
The Internet Control Message Protocol (ICMP) operates at the network layer and functions as a support protocol for IP. ICMP messages are encapsulated within IP datagrams, where the IP header precedes the ICMP header and payload. An ICMP packet consists of an IP header (minimum 20 bytes), an ICMP header (minimum 8 bytes), and the ICMP data section.
ICMP types range from 0-127 for error messages and 128+ for informational messages. The code field further specifies the exact message type. The checksum field covers the entire ICMP packet including headers and data, ensuring data integrity during transmission. The payload typically contains 8 bytes of additional information.
Raw Socket Configuraton
To construct custom IP packets, raw sockets must be used with IP_HDRINCL enabled. This option allows manual construction of IP headers:
int enable = 1;
setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable));
Timer Management with Signals
The SIGALRM signal is used to implement timeouts. A simple approach uses alarm() and signal():
void timeout_handler(int sig) {
exit(0);
}
int main() {
signal(SIGALRM, timeout_handler);
alarm(10);
while(1);
}
Alternatively, setitimer() provides more control over timing intervals:
#include <sys/time.h>
void timer_handler(int sig) {
printf("Timer triggered\n");
}
int main() {
struct itimerval timer;
timer.it_value.tv_sec = 1;
timer.it_value.tv_usec = 0;
timer.it_interval.tv_sec = 10;
timer.it_interval.tv_usec = 0;
signal(SIGALRM, timer_handler);
setitimer(ITIMER_REAL, &timer, NULL);
while(1);
return 0;
}
Time Measurement
The gettimeofday() function retrieves current system time:
#include <sys/time.h>
struct timeval current_time;
g gettimeofday(¤t_time, NULL);
printf("Time: %ld.%06ld\n", current_time.tv_sec, current_time.tv_usec);
Checksum Calculation
For IPv4 headers, the checksum calculation involves:
- Setting the checksum field to zero
- Summing all 16-bit words
- Handling carry-over bits
- Taking bitwise complement
unsigned short calculate_checksum(unsigned short *buffer, int length) {
unsigned long sum = 0;
while (length > 1) {
sum += *buffer++;
length -= 2;
}
if (length) {
sum += *(unsigned char*)buffer;
}
while (sum >> 16) {
sum = (sum & 0xFFFF) + (sum >> 16);
}
return ~sum;
}
Core Implementation Components
The ping utility requires several key components:
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
struct icmphdr {
u8 type;
u8 code;
u16 checksum;
union {
struct {
u16 id;
u16 sequence;
} echo;
u32 gateway;
struct {
u16 unused;
u16 mtu;
} frag;
} un;
u8 data[0];
};
struct iphdr {
u8 hlen:4, ver:4;
u8 tos;
u16 tot_len;
u16 id;
u16 frag_off;
u8 ttl;
u8 protocol;
u16 check;
u32 saddr;
u32 daddr;
};
Key functions include:
- Sending ICMP echo requests using raw sockets
- Receiving and processing echo replies
- Calculating round-trip times
- Handling timeouts and signal interrupts
The program initializes a raw socket, configures it for manual IP header handling, sets up signal handlers for timeouts, and enters a loop to send and receive ICMP packets.