From 770265ee80b45ea669ab2c86017b43b32e9ec51d Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Fri, 13 Mar 2026 03:22:14 +0100 Subject: [PATCH] feat: add -M (dont-fragment) option - Add FLAG_DONT_FRAGMENT flag and cli_handle_dont_fragment handler - Register -M/--dont-fragment option in the X-macro option table - Apply DF bit via icmp_set_dont_fragment() before ping loop - Expand error output: table-driven ICMP error messages and dedicated frag-needed output with next-hop MTU - Add STATIC_ARRAY_FOREACH / COUNT_OF macros to compiler.h --- includes/compiler.h | 5 ++ includes/internal/cli/options.h | 8 +++ includes/internal/ping/cli_handlers.h | 1 + includes/ping/ft_ping_flags.h | 7 +- sources/ping.mk | 1 + src/cli/parse.c | 3 +- src/ping/cli/handlers/handle_dont_fragment.c | 11 +++ src/ping/core/ping.c | 14 ++-- src/ping/output/error.c | 74 +++++++++++++++++--- 9 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/ping/cli/handlers/handle_dont_fragment.c diff --git a/includes/compiler.h b/includes/compiler.h index de88d27..2ec5498 100644 --- a/includes/compiler.h +++ b/includes/compiler.h @@ -3,4 +3,9 @@ #define __unused __attribute__((unused)) +#define COUNT_OF(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define STATIC_ARRAY_FOREACH(arr, ptr) \ + for ((ptr) = (arr); (ptr) < (arr) + COUNT_OF(arr); (ptr)++) + #endif diff --git a/includes/internal/cli/options.h b/includes/internal/cli/options.h index 352f206..9720e4a 100644 --- a/includes/internal/cli/options.h +++ b/includes/internal/cli/options.h @@ -91,6 +91,14 @@ cli_handle_flood, \ OPT_ARG_NONE, \ "Flood mode" \ + ) \ + X( \ + 'M', \ + "dont-fragment", \ + no_argument, \ + cli_handle_dont_fragment, \ + OPT_ARG_NONE, \ + "Set the Don't Fragment bit" \ ) #undef X diff --git a/includes/internal/ping/cli_handlers.h b/includes/internal/ping/cli_handlers.h index 8e22136..c7a31b6 100644 --- a/includes/internal/ping/cli_handlers.h +++ b/includes/internal/ping/cli_handlers.h @@ -6,6 +6,7 @@ extern const struct option_descriptor g_options[]; int cli_handle_count(const char *arg, void *config); +int cli_handle_dont_fragment(const char *arg, void *config); int cli_handle_deadline(const char *arg, void *config); int cli_handle_flood(const char *arg, void *config); int cli_handle_help(const char *arg, void *config); diff --git a/includes/ping/ft_ping_flags.h b/includes/ping/ft_ping_flags.h index 493917e..aa55b0e 100644 --- a/includes/ping/ft_ping_flags.h +++ b/includes/ping/ft_ping_flags.h @@ -1,9 +1,10 @@ #ifndef FT_PING_FLAGS #define FT_PING_FLAGS -#define FLAG_VERBOSE (1 << 0) -#define FLAG_QUIET (1 << 1) -#define FLAG_FLOOD (1 << 2) +#define FLAG_VERBOSE (1 << 0) +#define FLAG_QUIET (1 << 1) +#define FLAG_FLOOD (1 << 2) +#define FLAG_DONT_FRAGMENT (1 << 3) #define HAS_FLAG(flags, flag) ((flags) & (flag)) #define SET_FLAG(flags, flag) ((flags) |= (flag)) diff --git a/sources/ping.mk b/sources/ping.mk index 4c9f7cc..9bfcec8 100644 --- a/sources/ping.mk +++ b/sources/ping.mk @@ -6,6 +6,7 @@ PING_SRCS = $(CLI_SRCS) \ $(PING_SRC_DIR)/main.c \ $(PING_SRC_DIR)/cli/parse.c \ $(PING_SRC_DIR)/cli/handlers/handle_count.c \ + $(PING_SRC_DIR)/cli/handlers/handle_dont_fragment.c \ $(PING_SRC_DIR)/cli/handlers/handle_deadline.c \ $(PING_SRC_DIR)/cli/handlers/handle_flood.c \ $(PING_SRC_DIR)/cli/handlers/handle_help.c \ diff --git a/src/cli/parse.c b/src/cli/parse.c index 49037df..a314aa6 100644 --- a/src/cli/parse.c +++ b/src/cli/parse.c @@ -96,7 +96,8 @@ static void build_long_options(struct option *long_opts, size_t nb_opts, const struct option_descriptor *src) { - for (size_t i = 0; i < nb_opts; ++i) { + for (size_t i = 0; i < nb_opts; ++i) + { long_opts[i].name = src[i].long_opt; long_opts[i].has_arg = src[i].has_arg; long_opts[i].flag = NULL; diff --git a/src/ping/cli/handlers/handle_dont_fragment.c b/src/ping/cli/handlers/handle_dont_fragment.c new file mode 100644 index 0000000..4f13ba9 --- /dev/null +++ b/src/ping/cli/handlers/handle_dont_fragment.c @@ -0,0 +1,11 @@ +#include "compiler.h" +#include "ping/cli.h" +#include "ping/ft_ping_flags.h" + +int +cli_handle_dont_fragment(__unused const char *arg, void *config_void) +{ + struct ping_config *config = (struct ping_config *)config_void; + SET_FLAG(config->flags, FLAG_DONT_FRAGMENT); + return CLI_SUCCESS; +} diff --git a/src/ping/core/ping.c b/src/ping/core/ping.c index b4ff2d2..8a307d5 100644 --- a/src/ping/core/ping.c +++ b/src/ping/core/ping.c @@ -25,22 +25,28 @@ int ping_run(const struct ping_config *config) { icmp_handle_t *handle; - int ret; - size_t i; + int ret = 0 ; + size_t i = 0 ; if (0 == config->nb_destinations) return 1; handle = icmp_create(); if (NULL == handle) return 1; - ret = 0; - i = 0; + if (HAS_FLAG(config->flags, FLAG_DONT_FRAGMENT) + && 0 != icmp_set_dont_fragment(handle)) + { + ret = 1; + goto cleanup; + } + while (i < config->nb_destinations) { if (0 != ping_one(config, config->destinations[i], handle)) ret = 1; i++; } +cleanup: icmp_destroy(handle); return ret; } diff --git a/src/ping/output/error.c b/src/ping/output/error.c index 72e5a1f..1a0b009 100644 --- a/src/ping/output/error.c +++ b/src/ping/output/error.c @@ -1,4 +1,5 @@ #include +#include #include #include "compiler.h" @@ -7,27 +8,82 @@ #include "icmp_types.h" #include "internal/ping/output.h" +// TODO: CLEAN THIS FILE ! /* Forward declarations */ -static const char *error_msg_for(const icmp_reply_t *reply); +static void output_frag_needed(const char *from, uint16_t seq, + uint16_t next_mtu); +static const char *error_msg_for(const icmp_reply_t *reply); /* -------------------- */ void ping_output_error(const icmp_reply_t *reply, - __unused const icmp_offending_packet_t *offending, + const icmp_offending_packet_t *offending, uint16_t seq, __unused const struct ping_config *config) { char from_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &reply->from, from_str, sizeof(from_str)); - fprintf(stderr, "From %s: icmp_seq=%u %s\n", - from_str, (unsigned int)seq, error_msg_for(reply)); + if (ICMP_TYPE_DEST_UNREACHABLE == reply->type + && ICMP_CODE_FRAG_NEEDED == reply->code) + output_frag_needed(from_str, seq, offending->next_mtu); + else + dprintf(STDERR_FILENO, "From %s: icmp_seq=%u %s\n", + from_str, (unsigned int)seq, error_msg_for(reply)); +} + +static void +output_frag_needed(const char *from, uint16_t seq, uint16_t next_mtu) +{ + if (next_mtu > 0) + dprintf(STDERR_FILENO, + "From %s: icmp_seq=%u Frag needed and DF set (mtu = %u)\n", + from, (unsigned int)seq, (unsigned int)next_mtu); + else + dprintf(STDERR_FILENO, + "From %s: icmp_seq=%u Frag needed and DF set (mtu unknown)\n", + from, (unsigned int)seq); } static const char * error_msg_for(const icmp_reply_t *reply) { - if (ICMP_TYPE_TIME_EXCEEDED == reply->type) - return ("Time to live exceeded"); - if (ICMP_CODE_HOST_UNREACHABLE == reply->code) - return ("Destination Host Unreachable"); - return ("Destination Net Unreachable"); + static const struct s_error_entry + { + uint8_t type; + uint8_t code; + const char *msg; + } error_table[] = { + { + ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_TTL_EXCEEDED, + "Time to live exceeded" + }, + { + ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_FRAG_REASM_EXCEEDED, + "Frag reassembly time exceeded" + }, + { + ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_NET_UNREACHABLE, + "Destination Net Unreachable" + }, + { + ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_HOST_UNREACHABLE, + "Destination Host Unreachable" + }, + { + ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PROTOCOL_UNREACHABLE, + "Destination Protocol Unreachable" + }, + { + ICMP_TYPE_DEST_UNREACHABLE, ICMP_CODE_PORT_UNREACHABLE, + "Destination Port Unreachable" + }, + }; + const struct s_error_entry *entry; + + STATIC_ARRAY_FOREACH(error_table, entry) + { + if (entry->type == reply->type && entry->code == reply->code) + return (entry->msg); + } + return ("Unknown ICMP error"); }