From f45a755633b68b2fb7c70b6f941887cfd1fe9ff3 Mon Sep 17 00:00:00 2001 From: vauden Date: Wed, 25 Sep 2024 02:28:32 +0200 Subject: [PATCH] Major refactor --- .editorconfig | 0 ft_ping.h | 21 +++++++ main.c | 149 ++++++++++++++++++++++++++------------------------ utils.c | 91 ++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 71 deletions(-) create mode 100644 .editorconfig create mode 100644 utils.c diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e69de29 diff --git a/ft_ping.h b/ft_ping.h index 88b5bf6..d208e02 100644 --- a/ft_ping.h +++ b/ft_ping.h @@ -1,9 +1,12 @@ #pragma once + #include #include #include #include +#include +#include #include #include @@ -13,6 +16,8 @@ #include #include +#define PACKET_SIZE 64 + struct icmp_header { uint8_t type; @@ -21,3 +26,19 @@ struct icmp_header uint16_t id; uint16_t seq; }; + +struct packet_stats +{ + uint16_t n_packet_sent; + uint16_t n_packet_recv; + double timestamp_array[65536]; +}; + +uint16_t calculate_checksum(uint16_t *data, int len); +double get_timestamp(); +struct in_addr get_addr_by_hostname(char *hostname); +double get_stddev(double *timestamp_array); +void fill_timestamp_array(struct packet_stats *sockstats, double time); +double get_avg(double *timestamp_array); +double get_min(double *timestamp_array); +double get_max(double *timestamp_array); diff --git a/main.c b/main.c index 86c8826..9aca163 100644 --- a/main.c +++ b/main.c @@ -1,42 +1,14 @@ #include "ft_ping.h" -int n_packet_sent = 0; -int n_packet_recv = 0; -char *ip; -char loop = 1; +struct packet_stats stats; +bool loop = true; -uint16_t calculate_checksum(uint16_t *data, int len) -{ - uint32_t checksum = 0; - - while (len > 1) - { - checksum += *data++; - len -= 2; - } - if (len == 1) - checksum += *(uint8_t *)data; - - while (checksum >> 16) - checksum = (checksum & 0xFFFF) + (checksum >> 16); - return (~checksum); -} - -double get_timestamp() -{ - struct timeval timestamp; - - gettimeofday(×tamp, NULL); - return (timestamp.tv_sec + (double)timestamp.tv_usec / 1000000); -} - -int ft_ping(int sock, int seq, struct sockaddr_in dst) +int ft_ping(int sock, uint16_t seq, struct sockaddr_in *dst) { unsigned char data[64]; struct icmp_header *icmp_hdr = (struct icmp_header *)data; memset(data, 0, sizeof(data)); - memset(icmp_hdr, 0, sizeof(*icmp_hdr)); icmp_hdr->type = ICMP_ECHO; icmp_hdr->code = 0; @@ -44,16 +16,16 @@ int ft_ping(int sock, int seq, struct sockaddr_in dst) icmp_hdr->seq = seq; icmp_hdr->checksum = calculate_checksum((uint16_t *)icmp_hdr, sizeof(icmp_hdr)); - if (sendto(sock, data, sizeof(data), 0, (struct sockaddr *)&dst, sizeof(dst)) == -1) + if (sendto(sock, data, sizeof(data), 0, (struct sockaddr *)dst, sizeof(struct sockaddr_in)) == -1) { - fprintf(stderr, "ERROR : sendto() failed\n"); + fprintf(stderr, "ERROR : Network is unreachable\n"); return (0); } - n_packet_sent++; + stats.n_packet_sent++; return (1); } -int ft_recv(int sock, int seq, char *ip, double start) +void ft_recv(int sock, uint16_t seq, char *ip, double start) { unsigned char data[64]; struct icmp_header *icmp_hdr = (struct icmp_header *)(data + 20); @@ -61,26 +33,21 @@ int ft_recv(int sock, int seq, char *ip, double start) struct sockaddr_in addr; int len = sizeof(addr); double time; + uint16_t checksum; + memset(data, 0, sizeof(data)); n_bytes = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, (socklen_t *)&len); - while (icmp_hdr->type != 0 && n_bytes > 0) - n_bytes = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, (socklen_t *)&len); - time = (get_timestamp() - start) * 1000000; - if (icmp_hdr->seq != seq || calculate_checksum((uint16_t *)data, sizeof(data))) - return (-1); - n_packet_recv++; - printf("%d bytes from %s: icmp_seq:%d time:%5.3fms\n", n_bytes, ip, icmp_hdr->seq, time); - return (0); -} - -char *get_ip_by_hostname(char *hostname) -{ - struct hostent *he; - struct in_addr **addr_list; - - he = gethostbyname(hostname); - addr_list = (struct in_addr **)he->h_addr_list; - return (inet_ntoa(*addr_list[0])); + if (n_bytes < 1) + return; + time = (get_timestamp() - start) * 1000; + checksum = icmp_hdr->checksum; + icmp_hdr->checksum = 0; + if (icmp_hdr->seq != seq || calculate_checksum((uint16_t *)icmp_hdr, sizeof(*icmp_hdr)) != checksum) + return; + fill_timestamp_array(&stats, time); + stats.n_packet_recv++; + printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%5.3fms\n", n_bytes, ip, icmp_hdr->seq, (uint8_t)data[8], time); + return; } void handler(int code) @@ -94,43 +61,83 @@ void init_signal() signal(SIGINT, handler); } +bool init_socket(int *sock, struct sockaddr_in *dst, char *host) +{ + struct timeval timeout; + + memset(dst, 0, sizeof(*dst)); + memset(&stats.timestamp_array, 0, sizeof(stats.timestamp_array)); + + dst->sin_family = AF_INET; + dst->sin_port = 0; + if (gethostbyname(host) == NULL && inet_aton(host, &dst->sin_addr) == 0) + { + fprintf(stderr, "ERROR : %s is an unknown host\n", host); + return (false); + } + dst->sin_addr = get_addr_by_hostname(host); + if ((*sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) // create a RAW socket for issuing ICMP Requests + { + fprintf(stderr, "ERROR : socket() failed, are you sudo ?\n"); // creating a RAW socket will fail if we are not superuser + return (false); + } + timeout.tv_sec = 1; + timeout.tv_usec = 0; + setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); // use the timeval struct to set a timeout to our socket + return (true); +} + +void print_recap(char *ip) +{ + printf("--- %s ping statistics ---\n", ip); + printf("%d packed transmitted, %d received, %0.0f%% packet loss\n", stats.n_packet_sent, stats.n_packet_recv, (double)(100 - (stats.n_packet_recv / stats.n_packet_sent) * 100)); + printf("round-trip min/avg/max/stddev = %5.3f/%5.3f/%5.3f/%5.3f ms\n", get_min(stats.timestamp_array), get_avg(stats.timestamp_array), get_max(stats.timestamp_array), get_stddev(stats.timestamp_array)); +} int main(int argc, char **argv) { int sock; struct sockaddr_in dst; - int seq = 1; + char *ip; + uint16_t seq = 1; double start; + int flags; + bool verbose = false; - if (argc != 2 || argv[1] == NULL || argv[1][0] == 0) + while ((flags = getopt(argc, argv, "v?")) != -1) { - fprintf(stderr, "ERROR : usage : ping {-v?} [ADRESS]\n"); - return (0); + switch (flags) + { + case 'v': + verbose = true; + break; + case '?': + fprintf(stdout, "usage : %s {-v?} [ADRESS]\n", argv[0]); + return (1); + } } - if (inet_aton(argv[1], (struct in_addr *)&dst.sin_addr.s_addr) == 0 && gethostbyname(argv[1]) == NULL) + if (argc < 2 || argv[optind] == NULL || argv[optind][0] == 0) { - fprintf(stderr, "ERROR : %s is an invalid adress\n", argv[1]); + fprintf(stderr, "ERROR : usage : %s {-v?} [ADRESS]\n", argv[0]); return (0); } init_signal(); - dst.sin_family = AF_INET; - dst.sin_port = 0; - sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); - ip = get_ip_by_hostname(argv[1]); - if (sock < 0) - { - fprintf(stderr, "ERROR : socket() failed\n"); - return (0); - } + if (!init_socket(&sock, &dst, argv[optind])) + return (-1); + ip = inet_ntoa(get_addr_by_hostname(argv[optind])); + if (verbose) + fprintf(stdout, "PING %s (%s) : %d data bytes, id 0x%04x = %d\n", argv[optind], ip, PACKET_SIZE, getpid(), getpid()); + else + fprintf(stdout, "PING %s (%s) : %d data bytes\n", argv[optind], ip, PACKET_SIZE); while (loop) { start = get_timestamp(); - ft_ping(sock, seq, dst); + if (ft_ping(sock, seq, &dst) == 0) + break; ft_recv(sock, seq, ip, start); seq++; sleep(1); } - printf("--- %s ping statistics ---\n", ip); - printf("%d packed transmitted, %d received, %2.1f%% packet loss\n", n_packet_sent, n_packet_recv, (double)((n_packet_sent / n_packet_sent) - 1) * 100); + print_recap(argv[optind]); close(sock); return (0); } diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..7bfb413 --- /dev/null +++ b/utils.c @@ -0,0 +1,91 @@ +#include "ft_ping.h" + +uint16_t calculate_checksum(uint16_t *data, int len) +{ + uint32_t checksum = 0; + int i = 0; + + while (len > 1) + { + checksum += data[i++]; + len -= 2; + } + if (len == 1) + checksum += ((uint8_t *)data)[i]; + + while (checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + return (~checksum); +} + +double get_timestamp() +{ + struct timeval timestamp; + + gettimeofday(×tamp, NULL); + return (timestamp.tv_sec + (double)timestamp.tv_usec / 1000000); +} + +struct in_addr get_addr_by_hostname(char *hostname) +{ + struct hostent *he; + struct in_addr **addr_list; + + he = gethostbyname(hostname); + addr_list = (struct in_addr **)he->h_addr_list; + return (*addr_list[0]); +} + +void fill_timestamp_array(struct packet_stats *stats, double time) +{ + stats->timestamp_array[stats->n_packet_recv] = time; +} + +double get_min(double *timestamp_array) +{ + double min = timestamp_array[0]; + for (int i = 1; timestamp_array[i]; i++) + { + if (min > timestamp_array[i]) + min = timestamp_array[i]; + } + return (min); +} + +double get_max(double *timestamp_array) +{ + double max = timestamp_array[0]; + for (int i = 1; timestamp_array[i]; i++) + { + if (max < timestamp_array[i]) + max = timestamp_array[i]; + } + return (max); +} + +double get_avg(double *timestamp_array) +{ + double avg = 0; + int i = 0; + + while (timestamp_array[i]) + { + avg += timestamp_array[i]; + i++; + } + return (avg /= i); +} + +double get_stddev(double *timestamp_array) +{ + float avg = get_avg(timestamp_array); + float variance = 0; + float variance_tmp; + + for (int i = 0; timestamp_array[i]; i++) + { + variance_tmp = timestamp_array[i] - avg; + variance += variance_tmp * variance_tmp; + } + return (sqrt(variance)); +} \ No newline at end of file