]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
ike: Use a struct to store retransmission settings
authorTobias Brunner <tobias@strongswan.org>
Tue, 2 May 2023 15:54:07 +0000 (17:54 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 17 May 2023 13:58:28 +0000 (15:58 +0200)
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.

src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c
src/libcharon/sa/ikev1/task_manager_v1.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/task_manager.c
src/libcharon/sa/task_manager.h

index a128269a11645b5b5c8d7edefde54264e014cc7a..de36b77141cc254c7a5ea36a6cdb197855f55824 100644 (file)
@@ -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);
        }
index 9344b99e4f890a22d22e795faeb753d2d9b4d25e..13e9081447a95d9a405e2d951bea1226413709d8 100644 (file)
@@ -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;
 }
index d1c698e9eff33a5ff7eb338fe4dab4c23541af54..978ea41206b806ead4714805ccf7c54d03910d48 100644 (file)
@@ -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;
 }
index 972487bd9917c4e1fa6b077990a2b6713826ce77..17b09154d9ff849f8d50e5478477e20759b1a5b0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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;
 }
@@ -80,4 +105,3 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa)
        }
        return NULL;
 }
-
index ea87765e0ce7d58f738fdb6678da3fdd40d8f458..365e6e6bd4c59702b534c70259e6358004302127 100644 (file)
@@ -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 <limits.h>
 
@@ -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.