From 389e4b8e673fe679c3074f5563c3581b3271ef2a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 19 May 2017 16:14:40 +0200 Subject: [PATCH] ike: Use optional jitter to calculate retransmission timeouts Also adds an optional limit to avoid very high retransmission timeouts with high numbers of retries. --- conf/options/charon.opt | 7 ++++++ conf/strongswan.conf.5.tail.in | 12 ++++++++- src/libcharon/sa/ikev1/task_manager_v1.c | 31 +++++++++++++++++++++--- src/libcharon/sa/ikev2/task_manager_v2.c | 26 +++++++++++++++++++- src/libcharon/sa/task_manager.h | 5 ++++ 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index 4c4311e81b..a5f03f2722 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -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. diff --git a/conf/strongswan.conf.5.tail.in b/conf/strongswan.conf.5.tail.in index 72aa7f8569..f428fc323a 100644 --- a/conf/strongswan.conf.5.tail.in +++ b/conf/strongswan.conf.5.tail.in @@ -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: diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 1da17ee50c..89077b013d 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -209,6 +209,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 */ @@ -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) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index e4a16faf0b..5bd308f7a2 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -160,6 +160,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? */ @@ -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), ); diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 7e92622913..4d3f9bef55 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -47,6 +47,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. */