From 6cd5b0618d3e7e017a07de31f8272cc94f2c2cf7 Mon Sep 17 00:00:00 2001 From: vauden Date: Tue, 19 Nov 2024 18:51:04 +0100 Subject: [PATCH] Add: error manager Added function to print errors corresponding to the packet type and code values --- Makefile | 5 +-- includes/ft_ping.h | 16 ++++++-- includes/icmp_codes.h | 87 +++++++++++++++++++++++++++++++++++++++++++ src/flags.c | 18 +++++++-- src/main.c | 67 ++++++++++++++++++--------------- src/packets.c | 29 +++++++++++++++ 6 files changed, 183 insertions(+), 39 deletions(-) create mode 100644 includes/icmp_codes.h create mode 100644 src/packets.c diff --git a/Makefile b/Makefile index 08880dd..14e6a1b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ NAME = ./ft_ping SRCS = src/main.c \ src/utils.c \ - src/flags.c + src/flags.c \ + src/packets.c OBJ_DIR = objs @@ -32,8 +33,6 @@ $(OBJ_DIR)/%.o: %.c all: $(NAME) - - $(NAME): $(OBJ_DIR) $(OBJS) @echo "\e[1;32m[linking "$(MODE)" {"$(CC)"}...]\e[1;00m "$@ @$(CC) -o $(NAME) $(OBJS) -lm diff --git a/includes/ft_ping.h b/includes/ft_ping.h index 61364a1..7db5c6d 100644 --- a/includes/ft_ping.h +++ b/includes/ft_ping.h @@ -8,18 +8,15 @@ #include #include -#include #include #include -#include #include #include -#include #define PACKET_SIZE 64 -struct icmp_header +struct net_icmp_header { uint8_t type; uint8_t code; @@ -28,6 +25,14 @@ struct icmp_header uint16_t seq; }; +struct net_packet +{ + uint8_t data[PACKET_SIZE]; + int n_bytes; + struct sockaddr_in addr; + struct net_icmp_header *icmp_hdr; +}; + struct packet_stats { uint16_t n_packet_sent; @@ -43,11 +48,13 @@ struct flags double interval; int preload_count; int count; + int ttl; }; // PACKET UTILS uint16_t make_checksum(uint16_t *data, int len); struct in_addr get_addr_by_hostname(char *hostname); +bool parse_packet(struct net_packet packet, uint16_t seq, uint16_t checksum); // MATH double get_stddev(const double *timestamp_array); @@ -59,6 +66,7 @@ long ft_atoi(const char *nptr); // VERBOSE void print_help(void); bool parse_opt(int argc, char **argv, struct flags *flags); +void print_packet_error(struct net_packet packet); // STATS void print_recap(char *ip, const struct packet_stats stats); diff --git a/includes/icmp_codes.h b/includes/icmp_codes.h new file mode 100644 index 0000000..7d8954b --- /dev/null +++ b/includes/icmp_codes.h @@ -0,0 +1,87 @@ +#pragma once + +typedef enum +{ + ICMP_ECHOREPLY = 0, /* Echo Reply */ + ICMP_DEST_UNREACH = 3, /* Destination Unreachable */ + ICMP_SOURCE_QUENCH = 4, /* Source Quench */ + ICMP_REDIRECT = 5, /* Redirect (change route) */ + ICMP_ECHO = 8, /* Echo Request */ + ICMP_TIME_EXCEEDED = 11, /* Time Exceeded */ + ICMP_PARAMETERPROB = 12, /* Parameter Problem */ + ICMP_TIMESTAMP = 13, /* Timestamp Request */ + ICMP_TIMESTAMPREPLY = 14, /* Timestamp Reply */ + ICMP_INFO_REQUEST = 15, /* Information Request */ + ICMP_INFO_REPLY = 16, /* Information Reply */ + ICMP_ADDRESS = 17, /* Address Mask Request */ + ICMP_ADDRESSREPLY = 18 /* Address Mask Reply */ +} net_icmp_types; + + +typedef enum +{ + ICMP_NET_UNREACH = 0, /* Network Unreachable */ + ICMP_HOST_UNREACH = 1, /* Host Unreachable */ + ICMP_PROT_UNREACH = 2, /* Protocol Unreachable */ + ICMP_PORT_UNREACH = 3, /* Port Unreachable */ + ICMP_FRAG_NEEDED = 4, /* Fragmentation Needed/DF set */ + ICMP_SR_FAILED = 5, /* Source Route failed */ + ICMP_NET_UNKNOWN = 6, + ICMP_HOST_UNKNOWN = 7, + ICMP_HOST_ISOLATED = 8, + ICMP_NET_ANO = 9, + ICMP_HOST_ANO = 10, + ICMP_NET_UNR_TOS = 11, + ICMP_HOST_UNR_TOS = 12, + ICMP_PKT_FILTERED = 13, /* Packet filtered */ + ICMP_PREC_VIOLATION = 14, /* Precedence violation */ + ICMP_PREC_CUTOFF = 15 /* Precedence cut off */ +} net_icmp_unreach_codes; /* Codes for UNREACH*/ + +typedef enum +{ + ICMP_REDIR_NET = 0, + ICMP_REDIR_HOST = 1, + ICMP_REDIR_NETTOS = 2, + ICMP_REDIR_HOSTTOS = 3 +} net_icmp_redirect_code; /* Codes for REDIRECT*/ + +typedef enum +{ + ICMP_EXC_TTL = 0, + ICMP_EXC_FRAGTIME = 1 +} net_icmp_time_code; /* Codes for TIME_EXCEEDED. */ + +static const char *net_icmp_unreach_messages[] = +{ + [ICMP_NET_UNREACH] = "Network Unreachable", + [ICMP_HOST_UNREACH] = "Host Unreachable", + [ICMP_PROT_UNREACH] = "Protocol Unreachable", + [ICMP_PORT_UNREACH] = "Port Unreachable", + [ICMP_FRAG_NEEDED] = "Fragmentation Needed/DF set", + [ICMP_SR_FAILED] = "Source Route failed", + [ICMP_NET_UNKNOWN] = "Network Unknown", + [ICMP_HOST_UNKNOWN] = "Host Unknown", + [ICMP_HOST_ISOLATED] = "Host Isolated", + [ICMP_NET_ANO] = "Network Administratively Prohibited", + [ICMP_HOST_ANO] = "Host Administratively Prohibited", + [ICMP_NET_UNR_TOS] = "Network Unreachable for TOS", + [ICMP_HOST_UNR_TOS] = "Host Unreachable for TOS", + [ICMP_PKT_FILTERED] = "Packet filtered", + [ICMP_PREC_VIOLATION] = "Precedence violation", + [ICMP_PREC_CUTOFF] = "Precedence cut off" +}; + +static const char *net_icmp_redirect_messages[] = +{ + [ICMP_REDIR_NET] = "Redirect for Network", + [ICMP_REDIR_HOST] = "Redirect for Host", + [ICMP_REDIR_NETTOS] = "Redirect for Network with TOS", + [ICMP_REDIR_HOSTTOS] = "Redirect for Host with TOS" +}; + +static const char *net_icmp_time_messages[] = +{ + [ICMP_EXC_TTL] = "Time-to-live exceeded", + [ICMP_EXC_FRAGTIME] = "Fragment Reassembly Timeout" +}; \ No newline at end of file diff --git a/src/flags.c b/src/flags.c index 157118a..d186406 100644 --- a/src/flags.c +++ b/src/flags.c @@ -3,14 +3,16 @@ const char *flag_list[] = { "usage : ping [OPTIONS ...] [ADDRESS]", + "Send ICMP ECHO_REQUEST packets to network hosts.", " -c [COUNT] stop after COUNT packets have been sent", " -f flood mode", " -i [NUMBER] send a ping at NUMBER seconds intervals", " -l [NUMBER] send NUMBER ping as fast as possible then continue in normal mode", " -q quiet mode", + " -t [NUMBER] set the packet TTL (time to live) to NUMBER", " -v, verbose output", " -?, print this help page", - + NULL, }; void print_help(void) @@ -22,7 +24,8 @@ void print_help(void) bool parse_opt(int argc, char **argv, struct flags *flags) { int flag; - while ((flag = getopt(argc, argv, "c:fi:l:qv?")) != -1) + static bool interval = false; + while ((flag = getopt(argc, argv, "c:fi:l:qt:v?")) != -1) { switch (flag) { @@ -50,6 +53,7 @@ bool parse_opt(int argc, char **argv, struct flags *flags) return (false); } flags->interval = atof(optarg); + interval = true; break; case 'c': @@ -62,7 +66,7 @@ bool parse_opt(int argc, char **argv, struct flags *flags) break; case 'f': - if (flags->interval != -1) + if (interval) { fprintf(stderr, "%s: -f and -i flags are incompatible\n%s\n", argv[0], flag_list[0]); return (false); @@ -80,6 +84,14 @@ bool parse_opt(int argc, char **argv, struct flags *flags) } flags->preload_count = ft_atoi(optarg); break; + case 't': + if (ft_atoi(optarg) == -1) + { + fprintf(stderr, "%s: invalid value '%s'\n%s\n", argv[0], optarg, flag_list[0]); + return (false); + } + flags->ttl = ft_atoi(optarg); + break; } } if (argc < 2 || argv[optind] == NULL || argv[optind][0] == 0) // optind is a value given by getopt() that's equal to the first argument that isn't a flag diff --git a/src/main.c b/src/main.c index e9deb7f..4f2183d 100644 --- a/src/main.c +++ b/src/main.c @@ -5,49 +5,56 @@ bool loop = true; static int ft_ping(const int sock, const uint16_t seq, const struct sockaddr_in *dst) { - unsigned char data[PACKET_SIZE]; - struct icmp_header *icmp_hdr = (struct icmp_header *)data; + struct net_packet packet; - memset(data, 0, sizeof(data)); + memset(packet.data, 0, sizeof(packet.data)); - icmp_hdr->type = ICMP_ECHO; - icmp_hdr->code = 0; - icmp_hdr->id = getpid(); - icmp_hdr->seq = seq; - icmp_hdr->checksum = make_checksum((uint16_t *)icmp_hdr, sizeof(icmp_hdr)); + packet.icmp_hdr = (struct net_icmp_header *)packet.data; + packet.icmp_hdr->type = 8; + packet.icmp_hdr->code = 0; + packet.icmp_hdr->id = getpid(); + packet.icmp_hdr->seq = seq; + packet.icmp_hdr->checksum = make_checksum((uint16_t *)packet.icmp_hdr, sizeof(packet.icmp_hdr)); - if (sendto(sock, data, sizeof(data), 0, (struct sockaddr *)dst, sizeof(struct sockaddr_in)) == -1) + if (sendto(sock, packet.data, sizeof(packet.data), 0, (struct sockaddr *)dst, sizeof(struct sockaddr_in)) == -1) { - fprintf(stderr, "ERROR : Network is unreachable\n"); + fprintf(stderr, "error : sending packet : Network is unreachable\n"); return (0); } + #ifdef DEBUG + printf("\e[1;31m[DEBUG]\e[1;00m sendto() packet header: type:%d code:%d checksum:%x id:%d icmp_seq:%d\n", packet.icmp_hdr->type, packet.icmp_hdr->code, packet.icmp_hdr->checksum, packet.icmp_hdr->id, packet.icmp_hdr->seq); + #endif stats.n_packet_sent++; return (1); } static int ft_recv(const int sock, const uint16_t seq, const double start, const bool quiet) { - unsigned char data[PACKET_SIZE]; - struct icmp_header *icmp_hdr = (struct icmp_header *)(data + 20); - int n_bytes; - struct sockaddr_in addr; - int len = sizeof(addr); - double time; - uint16_t checksum; + struct net_packet packet; + int len = sizeof(packet.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); - if (n_bytes < 1) + packet.icmp_hdr = (struct net_icmp_header *)(packet.data + 20); + memset(packet.data, 0, sizeof(packet.data)); + packet.n_bytes = recvfrom(sock, packet.data, sizeof(packet.data), 0, (struct sockaddr *)&packet.addr, (socklen_t *)&len); + if (packet.n_bytes < 1) return (1); time = (get_timestamp() - start) * 1000; - checksum = icmp_hdr->checksum; - icmp_hdr->checksum = 0; - if (icmp_hdr->seq != seq || make_checksum((uint16_t *)icmp_hdr, sizeof(*icmp_hdr)) != checksum) - return (0); + checksum = packet.icmp_hdr->checksum; + packet.icmp_hdr->checksum = 0; + if (parse_packet(packet, seq, checksum) == false) + { + print_packet_error(packet); + return (1); + } fill_timestamp_array(&stats, time); stats.n_packet_recv++; if (!quiet) - printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%5.3fms\n", n_bytes, inet_ntoa(addr.sin_addr), icmp_hdr->seq, (uint8_t)data[8], time); + printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%5.3fms\n", packet.n_bytes, inet_ntoa(packet.addr.sin_addr), packet.icmp_hdr->seq, (uint8_t)packet.data[8], time); + #ifdef DEBUG + printf("\e[1;31m[DEBUG]\e[1;00m recv() packet header: type:%d code:%d checksum:%x id:%d icmp_seq:%d\n", packet.icmp_hdr->type, packet.icmp_hdr->code, checksum, packet.icmp_hdr->id, packet.icmp_hdr->seq); + #endif return (1); } @@ -62,7 +69,7 @@ static void init_signal() signal(SIGINT, signal_handler); } -static bool init_socket(int *sock, struct sockaddr_in *dst, char *host) +static bool init_socket(int *sock, struct sockaddr_in *dst, char *host, int ttl) { struct timeval timeout; @@ -85,6 +92,8 @@ static bool init_socket(int *sock, struct sockaddr_in *dst, char *host) 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 + if (ttl != -1) + setsockopt(*sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); return (true); } @@ -95,18 +104,18 @@ int main(int argc, char **argv) char *ip; uint16_t seq = 1; double start; - struct flags flags = {false, false, false, 1, -1, -1}; + struct flags flags = {false, false, false, 1, -1, -1, -1}; if (parse_opt(argc, argv, &flags) == false) return (0); - init_signal(); - if (!init_socket(&socket_fd, &dst_addr, argv[optind])) + if (!init_socket(&socket_fd, &dst_addr, argv[optind], flags.ttl)) return (-1); ip = inet_ntoa(get_addr_by_hostname(argv[optind])); if (flags.verbose) printf("PING %s (%s) : %d data bytes, id 0x%04x = %d\n", argv[optind], ip, (PACKET_SIZE - 8), getpid(), getpid()); else printf("PING %s (%s) : %d data bytes\n", argv[optind], ip, (PACKET_SIZE - 8)); + init_signal(); while (loop) { start = get_timestamp(); diff --git a/src/packets.c b/src/packets.c new file mode 100644 index 0000000..add1448 --- /dev/null +++ b/src/packets.c @@ -0,0 +1,29 @@ +#include "ft_ping.h" +#include "icmp_codes.h" + +static const char *get_error_message(uint8_t type, uint8_t code) +{ + switch(type) + { + case ICMP_DEST_UNREACH: + return (net_icmp_unreach_messages[code]); + case ICMP_REDIRECT: + return (net_icmp_redirect_messages[code]); + case ICMP_TIME_EXCEEDED: + return (net_icmp_time_messages[code]); + } + return (NULL); +} +bool parse_packet(struct net_packet packet, uint16_t seq, uint16_t checksum) +{ + if (packet.icmp_hdr->seq != seq || make_checksum((uint16_t *)packet.icmp_hdr, sizeof(*packet.icmp_hdr)) != checksum) + return (false); + if (packet.icmp_hdr->type != ICMP_ECHOREPLY) + return (false); + return (true); +} + +void print_packet_error(struct net_packet packet) +{ + fprintf(stderr, "%d bytes from %s: %s\n", packet.n_bytes, inet_ntoa(packet.addr.sin_addr), get_error_message(packet.icmp_hdr->type, packet.icmp_hdr->code)); +} \ No newline at end of file