METHOD(plugin_t, reload, bool,
private_kernel_netlink_plugin_t *this)
{
+ retransmission_t settings;
u_int timeout;
FILE *f;
f = fopen("/proc/sys/net/core/xfrm_acq_expires", "w");
if (f)
{
+ retransmission_parse_default(&settings);
timeout = lib->settings->get_int(lib->settings,
"%s.plugins.kernel-netlink.xfrm_acq_expires",
- task_manager_total_retransmit_timeout(), lib->ns);
+ retransmission_timeout_total(&settings), lib->ns);
fprintf(f, "%u", timeout);
fclose(f);
}
message_t *queued;
/**
- * Number of times we retransmit messages before giving up
+ * Retransmision settings.
*/
- u_int retransmit_tries;
-
- /**
- * Maximum number of tries possible with current retransmission settings
- * before overflowing the range of uint32_t, which we use for the timeout.
- * Note that UINT32_MAX milliseconds equal nearly 50 days, so that doesn't
- * make much sense without retransmit_limit anyway.
- */
- u_int retransmit_tries_max;
-
- /**
- * Retransmission timeout
- */
- double retransmit_timeout;
-
- /**
- * Base to calculate retransmission timeout
- */
- double retransmit_base;
-
- /**
- * Jitter to apply to calculated retransmit timeout (in percent)
- */
- u_int retransmit_jitter;
-
- /**
- * Limit retransmit timeout to this value
- */
- uint32_t retransmit_limit;
+ retransmission_t retransmit;
/**
* Sequence number for sending DPD requests
u_int mid, u_int retransmitted, array_t *packets)
{
packet_t *packet;
- uint32_t t = UINT32_MAX, max_jitter;
+ uint32_t t;
array_get(packets, 0, &packet);
- if (retransmitted > this->retransmit_tries)
+ if (retransmitted > this->retransmit.tries)
{
DBG1(DBG_IKE, "giving up after %u retransmits", retransmitted - 1);
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT, packet);
return DESTROY_ME;
}
- if (!this->retransmit_tries_max ||
- retransmitted <= this->retransmit_tries_max)
- {
- t = (uint32_t)(this->retransmit_timeout * 1000.0 *
- pow(this->retransmit_base, retransmitted));
- }
- if (this->retransmit_limit)
- {
- t = min(t, this->retransmit_limit);
- }
- if (this->retransmit_jitter)
- {
- max_jitter = (t / 100.0) * this->retransmit_jitter;
- t -= max_jitter * (random() / (RAND_MAX + 1.0));
- }
+ t = retransmission_timeout(&this->retransmit, retransmitted, TRUE);
if (retransmitted)
{
DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u",
if (t == 0)
{
/* use the same timeout as a retransmitting IKE message would have */
- for (retransmit = 0; retransmit <= this->retransmit_tries; retransmit++)
+ for (retransmit = 0; retransmit <= this->retransmit.tries; retransmit++)
{
- t += (uint32_t)(this->retransmit_timeout * 1000.0 *
- pow(this->retransmit_base, retransmit));
+ t += retransmission_timeout(&this->retransmit, retransmit, FALSE);
}
}
/* compensate for the already elapsed dpd delay */
.queued_tasks = linked_list_create(),
.active_tasks = linked_list_create(),
.passive_tasks = linked_list_create(),
- .retransmit_tries = lib->settings->get_int(lib->settings,
- "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns),
- .retransmit_timeout = lib->settings->get_double(lib->settings,
- "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
- .retransmit_base = lib->settings->get_double(lib->settings,
- "%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
- .retransmit_jitter = min(lib->settings->get_int(lib->settings,
- "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX),
- .retransmit_limit = lib->settings->get_int(lib->settings,
- "%s.retransmit_limit", 0, lib->ns) * 1000,
);
if (!this->rng)
}
this->dpd_send &= 0x7FFFFFFF;
- if (this->retransmit_base > 1)
- { /* based on 1000 * timeout * base^try */
- this->retransmit_tries_max = log(UINT32_MAX/
- (1000.0 * this->retransmit_timeout))/
- log(this->retransmit_base);
- }
+ retransmission_parse_default(&this->retransmit);
+
return &this->public;
}
bool reset;
/**
- * Number of times we retransmit messages before giving up
+ * Retransmission settings.
*/
- u_int retransmit_tries;
-
- /**
- * Maximum number of tries possible with current retransmission settings
- * before overflowing the range of uint32_t, which we use for the timeout.
- * Note that UINT32_MAX milliseconds equal nearly 50 days, so that doesn't
- * make much sense without retransmit_limit anyway.
- */
- u_int retransmit_tries_max;
-
- /**
- * Retransmission timeout
- */
- double retransmit_timeout;
-
- /**
- * Base to calculate retransmission timeout
- */
- double retransmit_base;
-
- /**
- * Jitter to apply to calculated retransmit timeout (in percent)
- */
- u_int retransmit_jitter;
-
- /**
- * Limit retransmit timeout to this value
- */
- uint32_t retransmit_limit;
+ retransmission_t retransmit;
/**
* Use make-before-break instead of break-before-make reauth?
if (message_id == this->initiating.mid &&
array_count(this->initiating.packets))
{
- uint32_t timeout = UINT32_MAX, max_jitter;
+ uint32_t timeout;
job_t *job;
enumerator_t *enumerator;
packet_t *packet;
if (!mobike || !mobike->is_probing(mobike))
{
- if (this->initiating.retransmitted > this->retransmit_tries)
+ if (this->initiating.retransmitted > this->retransmit.tries)
{
DBG1(DBG_IKE, "giving up after %d retransmits",
this->initiating.retransmitted - 1);
packet);
return DESTROY_ME;
}
- if (!this->retransmit_tries_max ||
- this->initiating.retransmitted <= this->retransmit_tries_max)
- {
- timeout = (uint32_t)(this->retransmit_timeout * 1000.0 *
- pow(this->retransmit_base, this->initiating.retransmitted));
- }
- if (this->retransmit_limit)
- {
- timeout = min(timeout, this->retransmit_limit);
- }
- if (this->retransmit_jitter)
- {
- max_jitter = (timeout / 100.0) * this->retransmit_jitter;
- timeout -= max_jitter * (random() / (RAND_MAX + 1.0));
- }
+ timeout = retransmission_timeout(&this->retransmit,
+ this->initiating.retransmitted, TRUE);
if (this->initiating.retransmitted)
{
DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
.queued_tasks = array_create(0, 0),
.active_tasks = array_create(0, 0),
.passive_tasks = array_create(0, 0),
- .retransmit_tries = lib->settings->get_int(lib->settings,
- "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns),
- .retransmit_timeout = lib->settings->get_double(lib->settings,
- "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
- .retransmit_base = lib->settings->get_double(lib->settings,
- "%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
- .retransmit_jitter = min(lib->settings->get_int(lib->settings,
- "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX),
- .retransmit_limit = lib->settings->get_int(lib->settings,
- "%s.retransmit_limit", 0, lib->ns) * 1000,
.make_before_break = lib->settings->get_bool(lib->settings,
"%s.make_before_break", FALSE, lib->ns),
);
- if (this->retransmit_base > 1)
- { /* based on 1000 * timeout * base^try */
- this->retransmit_tries_max = log(UINT32_MAX/
- (1000.0 * this->retransmit_timeout))/
- log(this->retransmit_base);
- }
+ retransmission_parse_default(&this->retransmit);
+
return &this->public;
}
/*
- * Copyright (C) 2011 Tobias Brunner
+ * Copyright (C) 2011-2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
#include <sa/ikev2/task_manager_v2.h>
/*
- * See header
+ * Described in header
*/
-u_int task_manager_total_retransmit_timeout()
+void retransmission_parse_default(retransmission_t *settings)
{
- double timeout, base, limit = 0, total = 0;
- int tries, max_tries = 0, i;
+ settings->timeout = lib->settings->get_double(lib->settings,
+ "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns);
+ settings->base = lib->settings->get_double(lib->settings,
+ "%s.retransmit_base", RETRANSMIT_BASE, lib->ns);
+ settings->jitter = min(lib->settings->get_int(lib->settings,
+ "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX);
+ settings->limit = lib->settings->get_int(lib->settings,
+ "%s.retransmit_limit", 0, lib->ns) * 1000;
+ settings->tries = lib->settings->get_int(lib->settings,
+ "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns);
- tries = lib->settings->get_int(lib->settings, "%s.retransmit_tries",
- RETRANSMIT_TRIES, lib->ns);
- base = lib->settings->get_double(lib->settings, "%s.retransmit_base",
- RETRANSMIT_BASE, lib->ns);
- timeout = lib->settings->get_double(lib->settings, "%s.retransmit_timeout",
- RETRANSMIT_TIMEOUT, lib->ns);
- limit = lib->settings->get_double(lib->settings, "%s.retransmit_limit",
- 0, lib->ns);
+ if (settings->base > 1)
+ { /* based on 1000 * timeout * base^try */
+ settings->max_tries = log(UINT32_MAX/
+ (1000.0 * settings->timeout))/
+ log(settings->base);
+ }
+}
- if (base > 1)
+/*
+ * Described in header
+ */
+uint32_t retransmission_timeout(retransmission_t *settings, u_int try,
+ bool randomize)
+{
+ double timeout = UINT32_MAX, max_jitter;
+
+ if (!settings->max_tries || try <= settings->max_tries)
+ {
+ timeout = settings->timeout * 1000.0 * pow(settings->base, try);
+ }
+ if (settings->limit)
{
- max_tries = log(UINT32_MAX/(1000.0 * timeout))/log(base);
+ timeout = min(timeout, settings->limit);
}
+ if (randomize && settings->jitter)
+ {
+ max_jitter = (timeout / 100.0) * settings->jitter;
+ timeout -= max_jitter * (random() / (RAND_MAX + 1.0));
+ }
+ return (uint32_t)timeout;
+}
+
+/*
+ * Described in header
+ */
+u_int retransmission_timeout_total(retransmission_t *settings)
+{
+ double total = 0;
+ int i;
- for (i = 0; i <= tries; i++)
+ for (i = 0; i <= settings->tries; i++)
{
- double interval = UINT32_MAX/1000.0;
- if (max_tries && i <= max_tries)
- {
- interval = timeout * pow(base, i);
- }
- if (limit)
- {
- interval = min(interval, limit);
- }
- total += interval;
+ total += retransmission_timeout(settings, i, FALSE) / 1000.0;
}
return (u_int)total;
}
}
return NULL;
}
-
/*
- * Copyright (C) 2013-2018 Tobias Brunner
+ * Copyright (C) 2013-2023 Tobias Brunner
* Copyright (C) 2006 Martin Willi
*
* Copyright (C) secunet Security Networks AG
typedef struct task_manager_t task_manager_t;
typedef enum task_queue_t task_queue_t;
+typedef struct retransmission_t retransmission_t;
#include <limits.h>
*/
#define ROUTABILITY_CHECK_TRIES 10
+/**
+ * Retransmission settings.
+ *
+ * See retransmission_timeout() for details on the calculation.
+ */
+struct retransmission_t {
+
+ /**
+ * Timeout in seconds.
+ */
+ double timeout;
+
+ /**
+ * Base that's raised to the power of the retransmission try to calculate
+ * the timeout.
+ */
+ double base;
+
+ /**
+ * Limit for the calculated timeout (in ms).
+ */
+ uint32_t limit;
+
+ /**
+ * Maximum jitter to apply to calculated timeout (in percent). A random
+ * amount of value this will be subtracted from the calculated timeout.
+ */
+ u_int jitter;
+
+ /**
+ * Number of tries.
+ */
+ u_int tries;
+
+ /**
+ * Maximum number of tries possible with current retransmission settings
+ * before overflowing the range of uint32_t, which we use for the timeout.
+ * Note that UINT32_MAX milliseconds equal nearly 50 days, so using a high
+ * number of retransmits doesn't make much sense without `limit` anyway.
+ */
+ u_int max_tries;
+};
+
/**
* Type of task queues the task manager uses to handle tasks
*/
* For the initial IKE_SA setup, several tasks are queued: One for the
* unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup
* and maybe one for virtual IP assignment.
- * The task manager is also responsible for retransmission. It uses a backoff
- * algorithm. The timeout is calculated using
- * RETRANSMIT_TIMEOUT * (RETRANSMIT_BASE ** try).
- * When try reaches RETRANSMIT_TRIES, retransmission is given up.
- *
- * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us:
- * @verbatim
- | relative | absolute
- ---------------------------------------------------------
- 4s * (1.8 ** 0) = 4s 4s
- 4s * (1.8 ** 1) = 7s 11s
- 4s * (1.8 ** 2) = 13s 24s
- 4s * (1.8 ** 3) = 23s 47s
- 4s * (1.8 ** 4) = 42s 89s
- 4s * (1.8 ** 5) = 76s 165s
-
- @endverbatim
- * The peer is considered dead after 2min 45s when no reply comes in.
+ * The task manager is also responsible for retransmission (see
+ * retransmission_timeout() for details).
*/
struct task_manager_t {
};
/**
- * Calculate total timeout of the retransmission mechanism.
+ * Parse the default retransmission settings.
+ *
+ * @param settings retransmission settings that are filled in
+ */
+void retransmission_parse_default(retransmission_t *settings);
+
+/**
+ * Calculate the retransmission timeout in ms based on the given settings and
+ * try.
+ *
+ * An exponential backoff algorithm is used. The timeout for each retransmit
+ * is calculated as follows: min(timeout * (base ** try), limit)
+ * From the result a random value (of at most jitter percent) is optionally
+ * subtracted.
+ *
+ * Using an initial timeout of 4s, a base of 1.8, and 5 tries gives us:
+ * @verbatim
+ | relative | absolute
+ ---------------------------------------------------------
+ 4s * (1.8 ** 0) = 4s 4s
+ 4s * (1.8 ** 1) = 7s 11s
+ 4s * (1.8 ** 2) = 13s 24s
+ 4s * (1.8 ** 3) = 23s 47s
+ 4s * (1.8 ** 4) = 42s 89s
+ 4s * (1.8 ** 5) = 76s 165s
+
+ @endverbatim
+ * So the peer is considered dead after 2min 45s when no reply is received.
+ *
+ * @param settings retransmission settings
+ * @param try zero-based try to send a packet
+ * @param randomize whether to apply jitter
+ * @return timeout for next retransmit in ms
+ */
+uint32_t retransmission_timeout(retransmission_t *settings, u_int try,
+ bool randomize);
+
+/**
+ * Calculate total timeout in s for the given retransmission settings (ignoring
+ * jitter).
*
- * This is affected by modifications of retransmit_base, retransmit_timeout,
- * retransmit_limit or retransmit_tries. The resulting value can then be used
- * e.g. in kernel plugins to set the system's acquire timeout properly.
+ * This is affected by modifications of base, timeout, limit and tries. The
+ * resulting value can then be used e.g. in kernel plugins to set the system's
+ * acquire timeout properly.
*
+ * @param settings retransmission settings
* @return calculated total retransmission timeout in seconds
*/
-u_int task_manager_total_retransmit_timeout();
+u_int retransmission_timeout_total(retransmission_t *settings);
/**
* Create a task manager instance for the correct IKE version.