ike: Use optional jitter to calculate retransmission timeouts
authorTobias Brunner <tobias@strongswan.org>
Fri, 19 May 2017 14:14:40 +0000 (16:14 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 23 May 2017 16:02:15 +0000 (18:02 +0200)
Also adds an optional limit to avoid very high retransmission timeouts
with high numbers of retries.

conf/options/charon.opt
conf/strongswan.conf.5.tail.in
src/libcharon/sa/ikev1/task_manager_v1.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/task_manager.h

index 4c4311e..a5f03f2 100644 (file)
@@ -311,6 +311,13 @@ charon.retransmit_timeout = 4.0
 charon.retransmit_tries = 5
        Number of times to retransmit a packet before giving up.
 
+charon.retransmit_jitter = 0
+       Maximum jitter in percent to apply randomly to calculated retransmission
+       timeout (0 to disable).
+
+charon.retransmit_limit = 0
+       Upper limit in seconds for calculated retransmission timeout (0 to disable).
+
 charon.retry_initiate_interval = 0
        Interval in seconds to use when retrying to initiate an IKE_SA (e.g. if DNS
        resolution failed), 0 to disable retries.
index 72aa7f8..f428fc3 100644 (file)
@@ -408,6 +408,8 @@ using the three keys listed below:
 .BR charon.retransmit_base " [1.8]"
 .BR charon.retransmit_timeout " [4.0]"
 .BR charon.retransmit_tries " [5]"
+.BR charon.retransmit_jitter " [0]"
+.BR charon.retransmit_limit " [0]"
 .fi
 .RE
 .PP
@@ -419,7 +421,15 @@ The following algorithm is used to calculate the timeout:
 .PP
 Where
 .I n
-is the current retransmission count.
+is the current retransmission count. The calculated timeout can't exceed the
+configured retransmit_limit (if any), which is useful if the number of retries
+is high.
+.PP
+If a jitter in percent is configured, the timeout is modified as follows:
+.PP
+.EX
+       relative timeout -= random(0, retransmit_jitter * relative timeout)
+.EE
 .PP
 Using the default values, packets are retransmitted in:
 
index 1da17ee..89077b0 100644 (file)
@@ -210,6 +210,16 @@ struct private_task_manager_t {
        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;
+
+       /**
         * Sequence number for sending DPD requests
         */
        uint32_t dpd_send;
@@ -345,7 +355,7 @@ 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_t t, max_jitter;
 
        array_get(packets, 0, &packet);
        if (retransmitted > this->retransmit_tries)
@@ -356,6 +366,15 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr,
        }
        t = (uint32_t)(this->retransmit_timeout * 1000.0 *
                                        pow(this->retransmit_base, retransmitted));
+       if (this->retransmit_jitter)
+       {
+               max_jitter = (t / 100.0) * this->retransmit_jitter;
+               t -= max_jitter * (random() / (RAND_MAX + 1.0));
+       }
+       if (this->retransmit_limit)
+       {
+               t = min(t, this->retransmit_limit);
+       }
        if (retransmitted)
        {
                DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u",
@@ -2034,11 +2053,15 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
                .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),
+                                       "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns),
                .retransmit_timeout = lib->settings->get_double(lib->settings,
-                                               "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
+                                       "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
                .retransmit_base = lib->settings->get_double(lib->settings,
-                                               "%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
+                                       "%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)
index e4a16fa..5bd308f 100644 (file)
@@ -161,6 +161,16 @@ struct private_task_manager_t {
        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;
+
+       /**
         * Use make-before-break instead of break-before-make reauth?
         */
        bool make_before_break;
@@ -321,7 +331,7 @@ METHOD(task_manager_t, retransmit, status_t,
        if (message_id == this->initiating.mid &&
                array_count(this->initiating.packets))
        {
-               uint32_t timeout;
+               uint32_t timeout, max_jitter;
                job_t *job;
                enumerator_t *enumerator;
                packet_t *packet;
@@ -351,6 +361,16 @@ METHOD(task_manager_t, retransmit, status_t,
                        {
                                timeout = (uint32_t)(this->retransmit_timeout * 1000.0 *
                                        pow(this->retransmit_base, this->initiating.retransmitted));
+
+                               if (this->retransmit_jitter)
+                               {
+                                       max_jitter = (timeout / 100.0) * this->retransmit_jitter;
+                                       timeout -= max_jitter * (random() / (RAND_MAX + 1.0));
+                               }
+                               if (this->retransmit_limit)
+                               {
+                                       timeout = min(timeout, this->retransmit_limit);
+                               }
                        }
                        else
                        {
@@ -2151,6 +2171,10 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
                                        "%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),
        );
index 7e92622..4d3f9be 100644 (file)
@@ -48,6 +48,11 @@ typedef enum task_queue_t task_queue_t;
 #define RETRANSMIT_TRIES 5
 
 /**
+ * Maximum jitter in percent.
+ */
+#define RETRANSMIT_JITTER_MAX 20
+
+/**
  * Interval for mobike routability checks in ms.
  */
 #define ROUTEABILITY_CHECK_INTERVAL 2500