From b00bb5aaa3ab5cc99247faa580d9595ddb813bc1 Mon Sep 17 00:00:00 2001 From: Erik Auerswald Date: Mon, 21 Apr 2025 20:02:17 +0200 Subject: [PATCH] new option --seqmap-timeout=MSEC This allows to control the time after which sequence numbers can be reused for new probe packets. This is intended to help using count or loop modes with tens of thousands of target hosts. See GH issue #385 for more information. --- CHANGELOG.md | 3 +++ ci/test-03-forbidden.pl | 4 ++-- ci/test-09-option-r-t.pl | 18 +++++++++++++++++- ci/test-12-option-type.pl | 4 ++-- doc/fping.pod | 5 +++++ src/fping.c | 12 +++++++++++- src/options.h | 4 ++++ src/seqmap.c | 13 +++++++------ src/seqmap.h | 2 +- 9 files changed, 52 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca19552..ad2e9af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Next - The -g, --generate option now also supports IPv6 addresses (#376, thanks @auerswal) +- New option --seqmap-timeout to control the time after which sequence numbers + can be used again (#388, thanks @auerswal) + ## Bugfixes and other changes - Fix OpenBSD warning sprintf() is often misused, please use snprintf() (#394, thanks @gsnw-sebast) diff --git a/ci/test-03-forbidden.pl b/ci/test-03-forbidden.pl index d2db133..84a305f 100755 --- a/ci/test-03-forbidden.pl +++ b/ci/test-03-forbidden.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use Test::Command tests => 36; +use Test::Command tests => 39; # fping -i 0 my $cmd1 = Test::Command->new(cmd => "fping -i 0 -T10 -g 127.0.0.1/29"); @@ -57,7 +57,7 @@ $cmd10->stdout_is_eq(""); $cmd10->stderr_is_eq("fping: backoff factor 5.1 not valid, must be between 1.0 and 5.0\n"); # non-negative only -for my $arg (qw(i p Q t)) { +for my $arg (qw(i p Q t -seqmap-timeout)) { my $cmd = Test::Command->new(cmd => "fping -$arg -1"); $cmd->exit_is_num(1); $cmd->stdout_is_eq(""); diff --git a/ci/test-09-option-r-t.pl b/ci/test-09-option-r-t.pl index 2c26d24..aace972 100755 --- a/ci/test-09-option-r-t.pl +++ b/ci/test-09-option-r-t.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use Test::Command tests => 30; +use Test::Command tests => 36; use Test::More; # -R random bytes @@ -146,4 +146,20 @@ $cmd->stdout_is_eq(""); $cmd->stderr_is_eq("fping: can't parse source address: bla\n"); } +# fping --seqmap-timeout N +{ +my $cmd = Test::Command->new(cmd => "fping --seqmap-timeout 20000 127.0.0.1"); +$cmd->exit_is_num(0); +$cmd->stdout_is_eq("127.0.0.1 is alive\n"); +$cmd->stderr_is_eq(""); +} + +# fping --seqmap-timeout N +{ +my $cmd = Test::Command->new(cmd => "fping --seqmap-timeout 0 127.0.0.1"); +$cmd->exit_is_num(1); +$cmd->stdout_is_eq("127.0.0.1 is unreachable\n"); +$cmd->stderr_is_eq(""); +} + # (note: fping -t also tested in test-4-options-a-b.pl) diff --git a/ci/test-12-option-type.pl b/ci/test-12-option-type.pl index 6514425..5b6dac4 100755 --- a/ci/test-12-option-type.pl +++ b/ci/test-12-option-type.pl @@ -1,10 +1,10 @@ #!/usr/bin/perl -w -use Test::Command tests => 84; +use Test::Command tests => 90; use Test::More; # some options require a numeric argument -for my $arg (qw(b B c C H i O p Q r t x X)) { +for my $arg (qw(b B c C H i O p Q r t x X -seqmap-timeout)) { for my $test_input (qw(xxx '')) { my $cmd = Test::Command->new(cmd => "fping -$arg $test_input"); $cmd->exit_is_num(1); diff --git a/doc/fping.pod b/doc/fping.pod index d10014f..b466c30 100644 --- a/doc/fping.pod +++ b/doc/fping.pod @@ -241,6 +241,11 @@ Print cumulative statistics upon exit. Set source address. +=item B<--seqmap-timeout>=I + +Timeout for sequence number mappings in milliseconds. Sequence numbers can +be reused after the timeout. The default value is 10000ms. + =item B<-t>, B<--timeout>=I Initial target timeout in milliseconds. In the default, non-loop mode, the diff --git a/src/fping.c b/src/fping.c index 86da559..ba21e74 100644 --- a/src/fping.c +++ b/src/fping.c @@ -317,6 +317,7 @@ unsigned int debugging = 0; /* all time-related values are int64_t nanoseconds */ unsigned int retry = DEFAULT_RETRY; int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000; +int64_t seqmap_timeout = (int64_t)DEFAULT_SEQMAP_TIMEOUT * 1000000; int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000; int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000; float backoff = DEFAULT_BACKOFF_FACTOR; @@ -578,6 +579,7 @@ int main(int argc, char **argv) { "check-source", '0', OPTPARSE_NONE }, { "print-tos", '0', OPTPARSE_NONE }, { "print-ttl", '0', OPTPARSE_NONE }, + { "seqmap-timeout", '0', OPTPARSE_REQUIRED }, #if defined(DEBUG) || defined(_DEBUG) { NULL, 'z', OPTPARSE_REQUIRED }, #endif @@ -638,6 +640,12 @@ int main(int argc, char **argv) } } #endif + } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) { + if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1) + usage(1); + if (opt_value_float < 0) + usage(1); + seqmap_timeout = opt_value_float * 1000000; } else { usage(1); } @@ -1077,6 +1085,7 @@ int main(int argc, char **argv) prog, count, retry, interval / 1e6); fprintf(stderr, " perhost_interval: %.0f ms, timeout: %.0f\n", perhost_interval / 1e6, timeout / 1e6); + fprintf(stderr, " seqmap_timeout: %.0f\n", seqmap_timeout / 1e6); fprintf(stderr, " ping_data_size = %u, trials = %u\n", ping_data_size, trials); @@ -1355,7 +1364,7 @@ int main(int argc, char **argv) last_send_time = 0; - seqmap_init(); + seqmap_init(seqmap_timeout); /* main loop */ main_loop(); @@ -3446,6 +3455,7 @@ void usage(int is_error) fprintf(out, " -r, --retry=N number of retries (default: %d)\n", DEFAULT_RETRY); fprintf(out, " -R, --random random packet data (to foil link data compression)\n"); fprintf(out, " -S, --src=IP set source address\n"); + fprintf(out, " --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6); fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6); fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n"); fprintf(out, " --check-source discard replies not from target address\n"); diff --git a/src/options.h b/src/options.h index 229975c..29bbd53 100644 --- a/src/options.h +++ b/src/options.h @@ -32,6 +32,10 @@ #define AUTOTUNE_TIMEOUT_MAX 2000 #endif +/* default time in milliseconds before a sequence number can be used again */ +#ifndef DEFAULT_SEQMAP_TIMEOUT +#define DEFAULT_SEQMAP_TIMEOUT 10000 +#endif #ifndef DEFAULT_RETRY #define DEFAULT_RETRY 3 /* number of times to retry a host */ diff --git a/src/seqmap.c b/src/seqmap.c index 7675bc1..7d1d643 100644 --- a/src/seqmap.c +++ b/src/seqmap.c @@ -48,7 +48,7 @@ /* description of the data structure used: * * - we assume that no more than SEQMAP_MAXSEQ (65535) pings are sent in - * the timeout interval (SEQMAP_TIMEOUT_IN_NS) + * the timeout interval (seqmap_timeout_in_ns) * - we store the values in an array with SEQMAP_MAXSEQ elements * - current sequence number % SEQMAP_MAXSEQ gives the current index * - when entering a value, we check that the current entry is expired @@ -56,12 +56,13 @@ static SEQMAP_VALUE* seqmap_map = NULL; static unsigned int seqmap_next_id = 0; +static int64_t seqmap_timeout_in_ns; -#define SEQMAP_TIMEOUT_IN_NS INT64_C(10000000000) #define SEQMAP_UNASSIGNED_HOST_NR UINT_MAX -void seqmap_init() +void seqmap_init(int64_t timeout) { + seqmap_timeout_in_ns = timeout; seqmap_map = calloc(SEQMAP_MAXSEQ, sizeof(SEQMAP_VALUE)); if (seqmap_map == NULL) { perror("malloc error (can't allocate seqmap_map)"); @@ -81,9 +82,9 @@ unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t t /* check if expired (note that unused seqmap values will have fields set to * 0, so will be seen as expired */ next_value = &seqmap_map[seqmap_next_id]; - if (next_value->ping_ts != 0 && timestamp - next_value->ping_ts < SEQMAP_TIMEOUT_IN_NS) { + if (next_value->ping_ts != 0 && timestamp - next_value->ping_ts < seqmap_timeout_in_ns) { fprintf(stderr, "fping error: not enough sequence numbers available! (expire_timeout=%" PRId64 ", host_nr=%d, ping_count=%d, seqmap_next_id=%d)\n", - SEQMAP_TIMEOUT_IN_NS, host_nr, ping_count, seqmap_next_id); + seqmap_timeout_in_ns, host_nr, ping_count, seqmap_next_id); exit(4); } @@ -112,7 +113,7 @@ SEQMAP_VALUE* seqmap_fetch(unsigned int id, int64_t now) value = &seqmap_map[id]; /* verify that value is not expired */ - if (now - value->ping_ts >= SEQMAP_TIMEOUT_IN_NS) { + if (now - value->ping_ts >= seqmap_timeout_in_ns) { dbg_printf("seqmap_fetch(%d) -> host: %d, index: %d -> DISCARDED %ld\n", id, value->host_nr, value->ping_count, now - value->ping_ts); return NULL; diff --git a/src/seqmap.h b/src/seqmap.h index a1780b0..9dc14dc 100644 --- a/src/seqmap.h +++ b/src/seqmap.h @@ -14,7 +14,7 @@ typedef struct seqmap_value #define SEQMAP_MAXSEQ 65535 -void seqmap_init(); +void seqmap_init(int64_t timeout); unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, int64_t now); SEQMAP_VALUE *seqmap_fetch(unsigned int id, int64_t now); -- 2.43.0