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
This commit is contained in:
lohhiiccc 2026-03-13 03:22:14 +01:00
parent 5a8671bdde
commit 770265ee80
9 changed files with 107 additions and 17 deletions

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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))

View file

@ -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 \

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -1,4 +1,5 @@
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#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");
}