├── fping.1 ├── README.md └── fping.c /fping.1: -------------------------------------------------------------------------------- 1 | .TH PING 1 "2024" "Custom Ping Utility" "User Commands" 2 | .SH NAME 3 | ping \- send ICMP ECHO_REQUEST packets to network hosts 4 | .SH SYNOPSIS 5 | .B ping 6 | [\fB\-v\fR] 7 | [\fB\-q\fR] 8 | [\fB\-d\fR] 9 | [\fB\-s\fR \fIpacket_size\fR] 10 | [\fB\-t\fR \fItimeout\fR] 11 | \fIhostname\fR [\fIhostname2\fR ...] 12 | .SH DESCRIPTION 13 | .B ping 14 | uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. 15 | .PP 16 | This enhanced version supports multiple targets, configurable packet sizes, and various display options. 17 | .SH OPTIONS 18 | .TP 19 | .BR \-v 20 | Verbose output. Shows detailed IP and ICMP header information for each packet. 21 | .TP 22 | .BR \-q 23 | Quiet output. Shows nothing except final statistics when finished. 24 | .TP 25 | .BR \-d 26 | Show DNS resolution. Displays both hostname and IP address when pinging. 27 | .TP 28 | .BI \-s " packet_size" 29 | Specifies the number of data bytes to be sent. Default is 56, which translates to 64 ICMP data bytes when combined with the 8 bytes of ICMP header data. 30 | The minimum size is 8 bytes and maximum is 65527 bytes. 31 | .TP 32 | .BI \-t " timeout" 33 | Specify a timeout, in milliseconds, before ping exits regardless of how many packets have been sent or received. 34 | Value must be between 100 and 60000 milliseconds. 35 | .SH OUTPUT 36 | For each ECHO_REQUEST sent, one of the following will be displayed: 37 | .PP 38 | .nf 39 | Normal output: 40 | 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.123 ms 41 | 42 | Verbose output (-v): 43 | IP Header: 44 | Version: 4, IHL: 5, TOS: 0 45 | Total Length: 84, ID: 12345 46 | TTL: 64, Protocol: 1 47 | ICMP Header: 48 | Type: 0 (Echo Reply), Code: 0 49 | Checksum: 0x1234 50 | 51 | DNS info (-d): 52 | Pinging example.com [93.184.216.34] 53 | .fi 54 | .SH STATISTICS 55 | When finished, statistics are displayed: 56 | .PP 57 | .nf 58 | --- Ping statistics --- 59 | 5 packets transmitted, 4 received, 20% packet loss 60 | rtt min/avg/max/mdev = 15.632/17.291/20.910/1.629 ms 61 | .fi 62 | .PP 63 | Statistics include: 64 | .IP \[bu] 2 65 | Packet loss percentage 66 | .IP \[bu] 67 | Minimum round-trip time 68 | .IP \[bu] 69 | Average round-trip time 70 | .IP \[bu] 71 | Maximum round-trip time 72 | .IP \[bu] 73 | Mean deviation (jitter) 74 | .SH EXAMPLES 75 | .TP 76 | Simple ping to a single host: 77 | .B ping google.com 78 | .TP 79 | Verbose ping with custom packet size: 80 | .B ping -v -s 100 google.com 81 | .TP 82 | Quiet ping to multiple hosts: 83 | .B ping -q google.com facebook.com 84 | .TP 85 | Ping with DNS info and custom timeout: 86 | .B ping -d -t 2000 google.com 87 | .SH EXIT STATUS 88 | Returns 0 on normal operation, non-zero on error. 89 | .SH NOTES 90 | This program must be run with root privileges to create raw sockets. 91 | .SH SEE ALSO 92 | .BR ping (8), 93 | .BR ping6 (8), 94 | .BR traceroute (8) 95 | .SH BUGS 96 | Report bugs to your system administrator. 97 | .SH AUTHOR 98 | Custom implementation of the ping utility. 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enhanced Ping Utility 2 | 3 | A feature-rich ping utility with support for multiple targets, statistics, and various display options. 4 | 5 | ## Features 6 | 7 | - Multiple target support 8 | - Configurable packet sizes 9 | - Detailed statistics (packet loss, RTT, jitter) 10 | - Verbose mode with header information 11 | - DNS resolution display 12 | - Quiet mode 13 | - Custom timeout settings 14 | 15 | ## Building from Source 16 | 17 | ### Linux 18 | 19 | 1. Install required dependencies: 20 | ```bash 21 | sudo apt-get update 22 | sudo apt-get install gcc make libc6-dev 23 | ``` 24 | 25 | 2. Compile the program: 26 | ```bash 27 | gcc fping.c -o fping -lm 28 | ``` 29 | 30 | 3. Install the man page: 31 | ```bash 32 | sudo mkdir -p /usr/local/share/man/man1 33 | sudo cp fping.1 /usr/local/share/man/man1/ 34 | sudo mandb 35 | ``` 36 | 37 | ### Windows 38 | 39 | 1. Install MinGW or Cygwin: 40 | - MinGW: Download and install from [MinGW website](https://www.mingw-w64.org/) 41 | - Cygwin: Download and install from [Cygwin website](https://www.cygwin.com/) 42 | 43 | 2. Using MinGW: 44 | ```batch 45 | gcc fping.c -o fping.exe -lws2_32 -lm 46 | ``` 47 | 48 | 3. Using Cygwin: 49 | ```bash 50 | gcc fping.c -o fping.exe -lm 51 | ``` 52 | 53 | Note: The man page is not typically used on Windows systems. 54 | 55 | ## Usage 56 | 57 | ### Linux 58 | ```bash 59 | # Basic usage 60 | sudo ./fping google.com 61 | 62 | # Multiple targets with verbose output 63 | sudo ./fping -v google.com facebook.com 64 | 65 | # Quiet mode with custom packet size 66 | sudo ./fping -q -s 100 google.com 67 | 68 | # Show DNS resolution with custom timeout 69 | sudo ./fping -d -t 2000 google.com 70 | ``` 71 | 72 | ### Windows 73 | ```batch 74 | # Run as Administrator 75 | fping.exe google.com 76 | 77 | # Multiple targets 78 | fping.exe -v google.com facebook.com 79 | ``` 80 | 81 | ## Command Line Options 82 | 83 | - `-v`: Verbose output 84 | - `-q`: Quiet output (statistics only) 85 | - `-d`: Show DNS resolution 86 | - `-s size`: Set packet size (8-65527 bytes) 87 | - `-t ms`: Set timeout (100-60000 ms) 88 | 89 | ## Viewing the Manual 90 | 91 | ### Linux 92 | ```bash 93 | # After installation 94 | man fping 95 | 96 | # Direct from file 97 | man ./fping.1 98 | ``` 99 | 100 | ### Windows 101 | - The manual content can be viewed in the source file `fping.1` 102 | - Or access this README for quick reference 103 | 104 | ## Requirements 105 | 106 | ### Linux 107 | - GCC compiler 108 | - Root privileges for raw sockets 109 | - Math library (libm) 110 | 111 | ### Windows 112 | - MinGW or Cygwin 113 | - Administrator privileges 114 | - WinSock2 library (ws2_32) 115 | 116 | ## Common Issues 117 | 118 | 1. "Operation not permitted": 119 | - Run with sudo/administrator privileges 120 | 121 | 2. Compilation errors: 122 | - Ensure all required libraries are installed 123 | - Check if math library is linked (-lm) 124 | 125 | 3. Man page not found: 126 | - Run `sudo mandb` after installation 127 | - Check if man page is in correct directory 128 | -------------------------------------------------------------------------------- /fping.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define MAX_PACKET_SIZE 65527 23 | #define DEFAULT_PACKET_SIZE 56 24 | #define MAX_HOSTS 10 25 | 26 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 27 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 28 | 29 | struct ping_stats 30 | { 31 | unsigned long packets_sent; 32 | unsigned long packets_received; 33 | double min_rtt; 34 | double max_rtt; 35 | double sum_rtt; 36 | double sum_rtt_square; // For jitter calculation 37 | struct timeval last_rtt; 38 | }; 39 | 40 | struct ping_target 41 | { 42 | char *hostname; 43 | struct sockaddr_in addr; 44 | struct ping_stats stats; 45 | }; 46 | 47 | struct ping_config 48 | { 49 | bool verbose; 50 | bool quiet; 51 | int timeout_ms; 52 | bool show_dns; 53 | }; 54 | 55 | volatile bool running = true; 56 | struct ping_target targets[MAX_HOSTS]; 57 | int num_targets = 0; 58 | int packet_size = DEFAULT_PACKET_SIZE; 59 | 60 | const char *icmp_type_str[] = { 61 | [ICMP_ECHOREPLY] = "Echo Reply", 62 | [ICMP_DEST_UNREACH] = "Destination Unreachable", 63 | [ICMP_SOURCE_QUENCH] = "Source Quench", 64 | [ICMP_REDIRECT] = "Redirect", 65 | [ICMP_ECHO] = "Echo Request", 66 | [ICMP_TIME_EXCEEDED] = "Time Exceeded", 67 | [ICMP_PARAMETERPROB] = "Parameter Problem"}; 68 | 69 | struct ping_config config = { 70 | .verbose = false, 71 | .quiet = false, 72 | .timeout_ms = 1000, 73 | .show_dns = false}; 74 | 75 | void signal_handler(int signo) 76 | { 77 | if (!config.quiet) 78 | { 79 | printf("\nReceived SIGINT, stopping...\n"); 80 | } 81 | running = false; 82 | } 83 | 84 | void init_stats(struct ping_stats *stats) 85 | { 86 | memset(stats, 0, sizeof(*stats)); 87 | stats->min_rtt = 999999.0; 88 | } 89 | 90 | void print_statistics(struct ping_stats *stats) 91 | { 92 | // Always print statistics even in quiet mode since it's the final summary 93 | double loss = 100.0 * (stats->packets_sent - stats->packets_received) / stats->packets_sent; 94 | double avg_rtt = stats->sum_rtt / stats->packets_received; 95 | double variance = (stats->sum_rtt_square / stats->packets_received) - (avg_rtt * avg_rtt); 96 | double jitter = sqrt(variance); 97 | 98 | printf("\n--- Ping statistics ---\n"); 99 | printf("%lu packets transmitted, %lu received, %.1f%% packet loss\n", 100 | stats->packets_sent, stats->packets_received, loss); 101 | printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n", 102 | stats->min_rtt, avg_rtt, stats->max_rtt, jitter); 103 | } 104 | 105 | uint16_t compute_checksum(uint16_t *addr, int len); 106 | 107 | void print_verbose_header(struct iphdr *iph, struct icmphdr *icmph) 108 | { 109 | printf("IP Header:\n"); 110 | printf(" Version: %d, IHL: %d, TOS: %d\n", iph->version, iph->ihl, iph->tos); 111 | printf(" Total Length: %d, ID: %d\n", ntohs(iph->tot_len), ntohs(iph->id)); 112 | printf(" TTL: %d, Protocol: %d\n", iph->ttl, iph->protocol); 113 | 114 | printf("ICMP Header:\n"); 115 | printf(" Type: %d (%s), Code: %d\n", 116 | icmph->type, 117 | icmp_type_str[icmph->type] ? icmp_type_str[icmph->type] : "Unknown", 118 | icmph->code); 119 | printf(" Checksum: 0x%04x\n", ntohs(icmph->checksum)); 120 | } 121 | 122 | void send_ping(int sockfd, struct ping_target *target) 123 | { 124 | char packet[MAX_PACKET_SIZE]; 125 | struct icmphdr *icmp = (struct icmphdr *)packet; 126 | struct timeval *tv = (struct timeval *)(packet + sizeof(struct icmphdr)); 127 | 128 | memset(packet, 0, packet_size); 129 | icmp->type = ICMP_ECHO; 130 | icmp->code = 0; 131 | icmp->checksum = 0; 132 | icmp->un.echo.id = getpid(); 133 | icmp->un.echo.sequence = ++target->stats.packets_sent; 134 | 135 | gettimeofday(tv, NULL); 136 | icmp->checksum = compute_checksum((uint16_t *)icmp, packet_size); 137 | 138 | if (!config.quiet && config.show_dns) 139 | { 140 | printf("Pinging %s [%s]\n", target->hostname, 141 | inet_ntoa(target->addr.sin_addr)); 142 | } 143 | 144 | if (sendto(sockfd, packet, packet_size, 0, 145 | (struct sockaddr *)&target->addr, sizeof(target->addr)) == -1) 146 | { 147 | perror("sendto"); 148 | } 149 | } 150 | 151 | uint16_t compute_checksum(uint16_t *addr, int len) 152 | { 153 | uint32_t sum = 0; 154 | uint16_t *w = addr; 155 | 156 | // Add up 16-bit words 157 | while (len > 1) 158 | { 159 | sum += *w++; 160 | len -= 2; 161 | } 162 | 163 | // Add left-over byte, if any 164 | if (len > 0) 165 | { 166 | sum += *(uint8_t *)w; 167 | } 168 | 169 | // Fold 32-bit sum to 16 bits 170 | while (sum >> 16) 171 | { 172 | sum = (sum & 0xFFFF) + (sum >> 16); 173 | } 174 | 175 | return ~sum; 176 | } 177 | 178 | void receive_ping(int sockfd) 179 | { 180 | char buffer[MAX_PACKET_SIZE]; 181 | struct sockaddr_in sender; 182 | socklen_t sender_len = sizeof(sender); 183 | struct timeval tv_now; 184 | 185 | // Use non-blocking receive 186 | struct timeval timeout; 187 | timeout.tv_sec = config.timeout_ms / 1000; 188 | timeout.tv_usec = (config.timeout_ms % 1000) * 1000; 189 | 190 | setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 191 | 192 | int result = recvfrom(sockfd, buffer, sizeof(buffer), MSG_DONTWAIT, 193 | (struct sockaddr *)&sender, &sender_len); 194 | 195 | if (result < 0) 196 | { 197 | if (errno != EAGAIN && errno != EWOULDBLOCK && !config.quiet) 198 | { 199 | perror("recvfrom"); 200 | } 201 | return; 202 | } 203 | 204 | struct iphdr *iph = (struct iphdr *)buffer; 205 | struct icmphdr *icmph = (struct icmphdr *)(buffer + (iph->ihl << 2)); 206 | 207 | if (config.verbose && !config.quiet) 208 | { 209 | print_verbose_header(iph, icmph); 210 | } 211 | 212 | if (icmph->type == ICMP_ECHOREPLY) 213 | { 214 | for (int i = 0; i < num_targets; i++) 215 | { 216 | if (sender.sin_addr.s_addr == targets[i].addr.sin_addr.s_addr) 217 | { 218 | struct timeval *tv_sent = (struct timeval *)(buffer + (iph->ihl << 2) + sizeof(struct icmphdr)); 219 | gettimeofday(&tv_now, NULL); 220 | double rtt = (tv_now.tv_sec - tv_sent->tv_sec) * 1000.0 + 221 | (tv_now.tv_usec - tv_sent->tv_usec) / 1000.0; 222 | 223 | // Update statistics even in quiet mode 224 | targets[i].stats.packets_received++; 225 | targets[i].stats.min_rtt = MIN(targets[i].stats.min_rtt, rtt); 226 | targets[i].stats.max_rtt = MAX(targets[i].stats.max_rtt, rtt); 227 | targets[i].stats.sum_rtt += rtt; 228 | targets[i].stats.sum_rtt_square += rtt * rtt; 229 | 230 | // Only print if not in quiet mode 231 | if (!config.quiet) 232 | { 233 | printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.1f ms\n", 234 | packet_size, inet_ntoa(sender.sin_addr), 235 | icmph->un.echo.sequence, iph->ttl, rtt); 236 | } 237 | break; 238 | } 239 | } 240 | } 241 | else if (!config.quiet) 242 | { 243 | printf("Received ICMP %s message (Type: %d, Code: %d) from %s\n", 244 | icmp_type_str[icmph->type] ? icmp_type_str[icmph->type] : "Unknown", 245 | icmph->type, icmph->code, inet_ntoa(sender.sin_addr)); 246 | } 247 | } 248 | 249 | int main(int argc, char *argv[]) 250 | { 251 | if (argc < 2) 252 | { 253 | printf("Usage: %s [-s size] hostname [hostname2 ...]\n", argv[0]); 254 | exit(1); 255 | } 256 | 257 | int opt; 258 | while ((opt = getopt(argc, argv, "s:vqt:d")) != -1) 259 | { 260 | switch (opt) 261 | { 262 | case 's': 263 | packet_size = atoi(optarg); 264 | if (packet_size < sizeof(struct icmphdr) + sizeof(struct timeval) || 265 | packet_size > MAX_PACKET_SIZE) 266 | { 267 | fprintf(stderr, "Invalid packet size\n"); 268 | exit(1); 269 | } 270 | break; 271 | case 'v': 272 | config.verbose = true; 273 | break; 274 | case 'q': 275 | config.quiet = true; 276 | break; 277 | case 't': 278 | config.timeout_ms = atoi(optarg); 279 | if (config.timeout_ms < 100 || config.timeout_ms > 60000) 280 | { 281 | fprintf(stderr, "Invalid timeout (100-60000 ms)\n"); 282 | exit(1); 283 | } 284 | break; 285 | case 'd': 286 | config.show_dns = true; 287 | break; 288 | } 289 | } 290 | 291 | if (config.verbose && config.quiet) 292 | { 293 | fprintf(stderr, "Cannot use both verbose and quiet modes\n"); 294 | exit(1); 295 | } 296 | 297 | // Improve signal handling 298 | struct sigaction sa; 299 | memset(&sa, 0, sizeof(sa)); 300 | sa.sa_handler = signal_handler; 301 | sigaction(SIGINT, &sa, NULL); 302 | 303 | for (int i = optind; i < argc && num_targets < MAX_HOSTS; i++) 304 | { 305 | struct hostent *host = gethostbyname(argv[i]); 306 | if (!host) 307 | { 308 | fprintf(stderr, "Could not resolve hostname '%s'\n", argv[i]); 309 | continue; 310 | } 311 | 312 | targets[num_targets].hostname = argv[i]; 313 | targets[num_targets].addr.sin_family = AF_INET; 314 | targets[num_targets].addr.sin_port = 0; 315 | memcpy(&targets[num_targets].addr.sin_addr, host->h_addr_list[0], host->h_length); 316 | init_stats(&targets[num_targets].stats); 317 | num_targets++; 318 | } 319 | 320 | if (num_targets == 0) 321 | { 322 | fprintf(stderr, "No valid hosts specified\n"); 323 | exit(1); 324 | } 325 | 326 | int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 327 | if (sockfd == -1) 328 | { 329 | perror("socket"); 330 | exit(1); 331 | } 332 | 333 | fd_set read_fds; 334 | struct timeval timeout; 335 | 336 | while (running) 337 | { 338 | for (int i = 0; i < num_targets; i++) 339 | { 340 | send_ping(sockfd, &targets[i]); 341 | } 342 | 343 | FD_ZERO(&read_fds); 344 | FD_SET(sockfd, &read_fds); 345 | timeout.tv_sec = 0; 346 | timeout.tv_usec = 100000; // 100ms timeout 347 | 348 | if (select(sockfd + 1, &read_fds, NULL, NULL, &timeout) > 0) 349 | { 350 | receive_ping(sockfd); 351 | } 352 | 353 | // Use nanosleep for more precise timing 354 | struct timespec sleep_time = {0, 1000000000}; // 1 second 355 | nanosleep(&sleep_time, NULL); 356 | } 357 | 358 | for (int i = 0; i < num_targets; i++) 359 | { 360 | printf("\nStatistics for %s:\n", targets[i].hostname); 361 | print_statistics(&targets[i].stats); 362 | } 363 | 364 | close(sockfd); 365 | return 0; 366 | } --------------------------------------------------------------------------------