#include #include #include #include #include #include #include #include #include #include #include #include // Parse a string into an integer. // Returns 0 if it fails. int parse_int(char *s, int *result) { if (s == NULL || result == NULL) return 0; return sscanf(s, "%d", result) == 1; } // Compute ICMP checksum. // It uses a standard 16-bit one's complement sum algorithm. unsigned short checksum(void *b, int len) { // points to the first 2-byte length of b unsigned short *buf = b; // set sum to zero unsigned int sum = 0; // split the packet into 2-byte chunks and sum it for (sum = 0; len > 1; len -= 2) sum += *buf++; // if length is odd, last byte is treated as a 16-bit word by padding it with a zero if (len > 0) sum += *(unsigned char *)buf; // any bits beyond 16 bits are shift and added to the lower 16 bits sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return (unsigned short) ~sum; } int main(int argc, char *argv[]) { if (argc < 2) { printf("usage: %s \n", argv[0]); return 1; } // parse packets number optional arg char **args = argv; int count = 4; // default packet number to send for(int argpos = 1; argpos < argc; argpos++) { if (!strcmp(*args++, "-c")) { if (!parse_int(*args, &count)) { printf("unable to parse ping count number\n"); printf("usage: -c \n"); return 1; } continue; } args++; } // DNS resolution struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_ICMP; int status = getaddrinfo(argv[1], NULL, &hints, &res); if (status != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return 1; } // extract IP address char ip_str[INET_ADDRSTRLEN]; struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr; inet_ntop(AF_INET, &(addr->sin_addr), ip_str, INET_ADDRSTRLEN); printf("PING %s (%s): 56 data bytes\n", argv[1], ip_str); // create raw socket for ICMP int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sock < 0) { perror("socket"); freeaddrinfo(res); return 1; } // set destination address struct sockaddr_in dest = *addr; // ICMP packet (header + 56-byte payload) char packet[64]; struct icmp *icmp = (struct icmp *)packet; icmp->icmp_type = ICMP_ECHO; // type 8 (Echo Request) icmp->icmp_code = 0; icmp->icmp_id = getpid() & 0xFFFF; // use process ID as identifier fitting into 16 bits icmp->icmp_seq = 0; icmp->icmp_cksum = 0; memset(packet + 8, 0, 56); // zero payload struct timeval timeout; timeout.tv_sec = 2; timeout.tv_usec = 0; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); int sent = 0, received = 0; double min_rtt = 1e9, max_rtt = 0, sum_rtt = 0, sum_sq_rtt = 0; for (int i = 0; i < count; i++) { icmp->icmp_seq = i + 1; icmp->icmp_cksum = 0; icmp->icmp_cksum = checksum(packet, 64); // record send time struct timeval send_time, recv_time; gettimeofday(&send_time, NULL); sent++; // send ICMP packet if (sendto(sock, packet, 64, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) { perror("sendto"); continue; } // receive reply char recv_buf[128]; struct sockaddr_in from; socklen_t from_len = sizeof(from); int n = recvfrom(sock, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&from, &from_len); gettimeofday(&recv_time, NULL); if (n < 0) { printf("Request timed out\n"); continue; } // extract IP and ICMP headers struct iphdr *ip = (struct iphdr *)recv_buf; struct icmp *recv_icmp = (struct icmp *)(recv_buf + (ip->ihl << 2)); // verify reply if (n >= (int)(ip->ihl << 2) + 8 && recv_icmp->icmp_type == ICMP_ECHOREPLY && recv_icmp->icmp_id == icmp->icmp_id && recv_icmp->icmp_seq == icmp->icmp_seq) { double rtt = (recv_time.tv_sec - send_time.tv_sec) * 1000.0 + (recv_time.tv_usec - send_time.tv_usec) / 1000.0; received++; sum_rtt += rtt; sum_sq_rtt += rtt * rtt; if (rtt < min_rtt) min_rtt = rtt; if (rtt > max_rtt) max_rtt = rtt; printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.1f ms\n", n - (ip->ihl << 2), ip_str, recv_icmp->icmp_seq, ip->ttl, rtt); } else { printf("Invalid reply\n"); } sleep(1); } printf("\n--- %s ping statistics ---\n", argv[1]); printf("%d packets transmitted, %d received, %.0f%% packet loss\n", sent, received, (sent - received) * 100.0 / sent); // see for stddev computation: https://en.wikipedia.org/wiki/Standard_deviation if (received > 0) { double avg_rtt = sum_rtt / received; double stddev = received > 1 ? sqrt((sum_sq_rtt - sum_rtt * sum_rtt / received) / (received - 1)) : 0; printf("rtt min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", min_rtt, avg_rtt, max_rtt, stddev); } close(sock); freeaddrinfo(res); return 0; }