From: Tobias Brunner Date: Tue, 2 May 2023 15:54:07 +0000 (+0200) Subject: ike: Use a struct to store retransmission settings X-Git-Tag: 5.9.11rc1~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6ceb39b1da6d0d34becf65ed29250df3d9e384d4;p=thirdparty%2Fstrongswan.git ike: Use a struct to store retransmission settings The calculation of the timeout is also shared now and the total timeout in seconds is corrected in case retransmit_base is <= 1. This could make it easier in the future to apply different retransmission settings to messages/exchanges. --- diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c index a128269a11..de36b77141 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c @@ -56,15 +56,17 @@ METHOD(plugin_t, get_features, int, 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); } diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 9344b99e4f..13e9081447 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -196,37 +196,9 @@ struct private_task_manager_t { 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 @@ -364,30 +336,16 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr, 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", @@ -1893,10 +1851,9 @@ METHOD(task_manager_t, queue_dpd, void, 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 */ @@ -2132,16 +2089,6 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .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) @@ -2159,11 +2106,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) } 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; } diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index d1c698e9ef..978ea41206 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -165,37 +165,9 @@ struct private_task_manager_t { 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? @@ -358,7 +330,7 @@ METHOD(task_manager_t, retransmit, status_t, 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; @@ -384,7 +356,7 @@ METHOD(task_manager_t, retransmit, status_t, 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); @@ -392,21 +364,8 @@ METHOD(task_manager_t, retransmit, status_t, 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", @@ -2625,25 +2584,11 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .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; } diff --git a/src/libcharon/sa/task_manager.c b/src/libcharon/sa/task_manager.c index 972487bd99..17b09154d9 100644 --- a/src/libcharon/sa/task_manager.c +++ b/src/libcharon/sa/task_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Tobias Brunner + * Copyright (C) 2011-2023 Tobias Brunner * * Copyright (C) secunet Security Networks AG * @@ -21,39 +21,64 @@ #include /* - * 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; } @@ -80,4 +105,3 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa) } return NULL; } - diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index ea87765e0c..365e6e6bd4 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2018 Tobias Brunner + * Copyright (C) 2013-2023 Tobias Brunner * Copyright (C) 2006 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -25,6 +25,7 @@ typedef struct task_manager_t task_manager_t; typedef enum task_queue_t task_queue_t; +typedef struct retransmission_t retransmission_t; #include @@ -63,6 +64,49 @@ typedef enum task_queue_t task_queue_t; */ #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 */ @@ -88,24 +132,8 @@ enum task_queue_t { * 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 { @@ -305,15 +333,55 @@ 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.