ikev2: Add possibility to delay initiation of a queued task
authorTobias Brunner <tobias@strongswan.org>
Thu, 2 Jun 2016 13:50:11 +0000 (15:50 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 17 Jun 2016 16:48:07 +0000 (18:48 +0200)
Such a task is not initiated unless a certain time has passed.  This
allows delaying certain tasks but avoids problems if we'd do this
via a scheduled job (e.g. if the IKE_SA is rekeyed in the meantime).

If the IKE_SA is rekeyed the delay of such tasks is reset when the
tasks are adopted i.e. they get executed immediately on the new IKE_SA.

This hasn't been implemented for IKEv1 yet.

src/libcharon/sa/ike_sa.c
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev1/task_manager_v1.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/task_manager.h

index e81f6ce..009277d 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * Copyright (C) 2006-2015 Tobias Brunner
+ * Copyright (C) 2006-2016 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -2620,6 +2620,12 @@ METHOD(ike_sa_t, queue_task, void,
        this->task_manager->queue_task(this->task_manager, task);
 }
 
+METHOD(ike_sa_t, queue_task_delayed, void,
+       private_ike_sa_t *this, task_t *task, uint32_t delay)
+{
+       this->task_manager->queue_task_delayed(this->task_manager, task, delay);
+}
+
 METHOD(ike_sa_t, inherit_pre, void,
        private_ike_sa_t *this, ike_sa_t *other_public)
 {
@@ -2938,6 +2944,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                        .create_task_enumerator = _create_task_enumerator,
                        .flush_queue = _flush_queue,
                        .queue_task = _queue_task,
+                       .queue_task_delayed = _queue_task_delayed,
 #ifdef ME
                        .act_as_mediation_server = _act_as_mediation_server,
                        .get_server_reflexive_host = _get_server_reflexive_host,
index 596399d..6f5040d 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * Copyright (C) 2006-2015 Tobias Brunner
+ * Copyright (C) 2006-2016 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -1124,6 +1124,15 @@ struct ike_sa_t {
        void (*queue_task)(ike_sa_t *this, task_t *task);
 
        /**
+        * Queue a task in the manager, but delay its initiation for at least the
+        * given number of seconds.
+        *
+        * @param task                  task to queue
+        * @param delay                 minimum delay in s before initiating the task
+        */
+       void (*queue_task_delayed)(ike_sa_t *this, task_t *task, uint32_t delay);
+
+       /**
         * Inherit required attributes to new SA before rekeying.
         *
         * Some properties of the SA must be applied before starting IKE_SA
index 126863a..b0c4f5f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2007-2016 Tobias Brunner
  * Copyright (C) 2007-2011 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -1513,8 +1513,8 @@ static bool has_queued(private_task_manager_t *this, task_type_t type)
        return found;
 }
 
-METHOD(task_manager_t, queue_task, void,
-       private_task_manager_t *this, task_t *task)
+METHOD(task_manager_t, queue_task_delayed, void,
+       private_task_manager_t *this, task_t *task, uint32_t delay)
 {
        task_type_t type = task->get_type(task);
 
@@ -1535,6 +1535,12 @@ METHOD(task_manager_t, queue_task, void,
        this->queued_tasks->insert_last(this->queued_tasks, task);
 }
 
+METHOD(task_manager_t, queue_task, void,
+       private_task_manager_t *this, task_t *task)
+{
+       queue_task_delayed(this, task, 0);
+}
+
 METHOD(task_manager_t, queue_ike, void,
        private_task_manager_t *this)
 {
@@ -1975,6 +1981,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
                        .task_manager = {
                                .process_message = _process_message,
                                .queue_task = _queue_task,
+                               .queue_task_delayed = _queue_task_delayed,
                                .queue_ike = _queue_ike,
                                .queue_ike_rekey = _queue_ike_rekey,
                                .queue_ike_reauth = _queue_ike_reauth,
index d00896d..41a4e1b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2007-2016 Tobias Brunner
  * Copyright (C) 2007-2010 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
 #include <encoding/payloads/unknown_payload.h>
 #include <processing/jobs/retransmit_job.h>
 #include <processing/jobs/delete_ike_sa_job.h>
+#include <processing/jobs/initiate_tasks_job.h>
 
 #ifdef ME
 #include <sa/ikev2/tasks/ike_me.h>
 #endif
 
-typedef struct exchange_t exchange_t;
-
-/**
- * An exchange in the air, used do detect and handle retransmission
- */
-struct exchange_t {
-
-       /**
-        * Message ID used for this transaction
-        */
-       uint32_t mid;
-
-       /**
-        * generated packet for retransmission
-        */
-       packet_t *packet;
-};
-
 typedef struct private_task_manager_t private_task_manager_t;
+typedef struct queued_task_t queued_task_t;
 
 /**
  * private data of the task manager
@@ -182,6 +166,22 @@ struct private_task_manager_t {
 };
 
 /**
+ * Queued tasks
+ */
+struct queued_task_t {
+
+       /**
+        * Queued task
+        */
+       task_t *task;
+
+       /**
+        * Time before which the task is not to be initiated
+        */
+       timeval_t time;
+};
+
+/**
  * Reset retransmission packet list
  */
 static void clear_packets(array_t *array)
@@ -216,6 +216,12 @@ METHOD(task_manager_t, flush_queue, void,
        }
        while (array_remove(array, ARRAY_TAIL, &task))
        {
+               if (queue == TASK_QUEUE_QUEUED)
+               {
+                       queued_task_t *queued = (queued_task_t*)task;
+                       task = queued->task;
+                       free(queued);
+               }
                task->destroy(task);
        }
 }
@@ -229,22 +235,28 @@ METHOD(task_manager_t, flush, void,
 }
 
 /**
- * move a task of a specific type from the queue to the active list
+ * Move a task of a specific type from the queue to the active list, if it is
+ * not delayed.
  */
 static bool activate_task(private_task_manager_t *this, task_type_t type)
 {
        enumerator_t *enumerator;
-       task_t *task;
+       queued_task_t *queued;
+       timeval_t now;
        bool found = FALSE;
 
+       time_monotonic(&now);
+
        enumerator = array_create_enumerator(this->queued_tasks);
-       while (enumerator->enumerate(enumerator, (void**)&task))
+       while (enumerator->enumerate(enumerator, (void**)&queued))
        {
-               if (task->get_type(task) == type)
+               if (queued->task->get_type(queued->task) == type &&
+                       !timercmp(&now, &queued->time, <))
                {
                        DBG2(DBG_IKE, "  activating %N task", task_type_names, type);
                        array_remove_at(this->queued_tasks, enumerator);
-                       array_insert(this->active_tasks, ARRAY_TAIL, task);
+                       array_insert(this->active_tasks, ARRAY_TAIL, queued->task);
+                       free(queued);
                        found = TRUE;
                        break;
                }
@@ -1524,18 +1536,19 @@ METHOD(task_manager_t, process_message, status_t,
        return SUCCESS;
 }
 
-METHOD(task_manager_t, queue_task, void,
-       private_task_manager_t *this, task_t *task)
+METHOD(task_manager_t, queue_task_delayed, void,
+       private_task_manager_t *this, task_t *task, uint32_t delay)
 {
+       enumerator_t *enumerator;
+       queued_task_t *queued;
+       timeval_t time;
+
        if (task->get_type(task) == TASK_IKE_MOBIKE)
        {       /*  there is no need to queue more than one mobike task */
-               enumerator_t *enumerator;
-               task_t *current;
-
                enumerator = array_create_enumerator(this->queued_tasks);
-               while (enumerator->enumerate(enumerator, &current))
+               while (enumerator->enumerate(enumerator, &queued))
                {
-                       if (current->get_type(current) == TASK_IKE_MOBIKE)
+                       if (queued->task->get_type(queued->task) == TASK_IKE_MOBIKE)
                        {
                                enumerator->destroy(enumerator);
                                task->destroy(task);
@@ -1544,8 +1557,35 @@ METHOD(task_manager_t, queue_task, void,
                }
                enumerator->destroy(enumerator);
        }
-       DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
-       array_insert(this->queued_tasks, ARRAY_TAIL, task);
+       time_monotonic(&time);
+       if (delay)
+       {
+               job_t *job;
+
+               DBG2(DBG_IKE, "queueing %N task (delayed by %us)", task_type_names,
+                        task->get_type(task), delay);
+               time.tv_sec += delay;
+
+               job = (job_t*)initiate_tasks_job_create(
+                                                                                       this->ike_sa->get_id(this->ike_sa));
+               lib->scheduler->schedule_job_tv(lib->scheduler, job, time);
+       }
+       else
+       {
+               DBG2(DBG_IKE, "queueing %N task", task_type_names,
+                        task->get_type(task));
+       }
+       INIT(queued,
+               .task = task,
+               .time = time,
+       );
+       array_insert(this->queued_tasks, ARRAY_TAIL, queued);
+}
+
+METHOD(task_manager_t, queue_task, void,
+       private_task_manager_t *this, task_t *task)
+{
+       queue_task_delayed(this, task, 0);
 }
 
 /**
@@ -1555,12 +1595,12 @@ static bool has_queued(private_task_manager_t *this, task_type_t type)
 {
        enumerator_t *enumerator;
        bool found = FALSE;
-       task_t *task;
+       queued_task_t *queued;
 
        enumerator = array_create_enumerator(this->queued_tasks);
-       while (enumerator->enumerate(enumerator, &task))
+       while (enumerator->enumerate(enumerator, &queued))
        {
-               if (task->get_type(task) == type)
+               if (queued->task->get_type(queued->task) == type)
                {
                        found = TRUE;
                        break;
@@ -1639,7 +1679,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
        child_cfg_t *cfg;
        ike_sa_t *new;
        host_t *host;
-       task_t *task;
+       queued_task_t *queued;
 
        new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
                                                                this->ike_sa->get_version(this->ike_sa), TRUE);
@@ -1670,13 +1710,14 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
        enumerator->destroy(enumerator);
 
        enumerator = array_create_enumerator(this->queued_tasks);
-       while (enumerator->enumerate(enumerator, &task))
+       while (enumerator->enumerate(enumerator, &queued))
        {
-               if (task->get_type(task) == TASK_CHILD_CREATE)
+               if (queued->task->get_type(queued->task) == TASK_CHILD_CREATE)
                {
-                       task->migrate(task, new);
-                       new->queue_task(new, task);
+                       queued->task->migrate(queued->task, new);
+                       new->queue_task(new, queued->task);
                        array_remove_at(this->queued_tasks, enumerator);
+                       free(queued);
                }
        }
        enumerator->destroy(enumerator);
@@ -1801,34 +1842,62 @@ METHOD(task_manager_t, adopt_tasks, void,
        private_task_manager_t *this, task_manager_t *other_public)
 {
        private_task_manager_t *other = (private_task_manager_t*)other_public;
-       task_t *task;
+       queued_task_t *queued;
+       timeval_t now;
+
+       time_monotonic(&now);
 
        /* move queued tasks from other to this */
-       while (array_remove(other->queued_tasks, ARRAY_TAIL, &task))
+       while (array_remove(other->queued_tasks, ARRAY_TAIL, &queued))
        {
-               DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
-               task->migrate(task, this->ike_sa);
-               array_insert(this->queued_tasks, ARRAY_HEAD, task);
+               DBG2(DBG_IKE, "migrating %N task", task_type_names,
+                        queued->task->get_type(queued->task));
+               queued->task->migrate(queued->task, this->ike_sa);
+               /* don't delay tasks on the new IKE_SA */
+               queued->time = now;
+               array_insert(this->queued_tasks, ARRAY_HEAD, queued);
        }
 }
 
 /**
- * Migrates child-creating tasks from src to dst
+ * Migrates child-creating tasks from other to this
  */
 static void migrate_child_tasks(private_task_manager_t *this,
-                                                               array_t *src, array_t *dst)
+                                                               private_task_manager_t *other,
+                                                               task_queue_t queue)
 {
        enumerator_t *enumerator;
+       array_t *array;
        task_t *task;
 
-       enumerator = array_create_enumerator(src);
+       switch (queue)
+       {
+               case TASK_QUEUE_ACTIVE:
+                       array = other->active_tasks;
+                       break;
+               case TASK_QUEUE_QUEUED:
+                       array = other->queued_tasks;
+                       break;
+               default:
+                       return;
+       }
+
+       enumerator = array_create_enumerator(array);
        while (enumerator->enumerate(enumerator, &task))
        {
+               queued_task_t *queued = NULL;
+
+               if (queue == TASK_QUEUE_QUEUED)
+               {
+                       queued = (queued_task_t*)task;
+                       task = queued->task;
+               }
                if (task->get_type(task) == TASK_CHILD_CREATE)
                {
-                       array_remove_at(src, enumerator);
+                       array_remove_at(array, enumerator);
                        task->migrate(task, this->ike_sa);
-                       array_insert(dst, ARRAY_TAIL, task);
+                       queue_task(this, task);
+                       free(queued);
                }
        }
        enumerator->destroy(enumerator);
@@ -1840,9 +1909,9 @@ METHOD(task_manager_t, adopt_child_tasks, void,
        private_task_manager_t *other = (private_task_manager_t*)other_public;
 
        /* move active child tasks from other to this */
-       migrate_child_tasks(this, other->active_tasks, this->queued_tasks);
+       migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE);
        /* do the same for queued tasks */
-       migrate_child_tasks(this, other->queued_tasks, this->queued_tasks);
+       migrate_child_tasks(this, other, TASK_QUEUE_QUEUED);
 }
 
 METHOD(task_manager_t, busy, bool,
@@ -1855,7 +1924,9 @@ METHOD(task_manager_t, reset, void,
        private_task_manager_t *this, uint32_t initiate, uint32_t respond)
 {
        enumerator_t *enumerator;
+       queued_task_t *queued;
        task_t *task;
+       timeval_t now;
 
        /* reset message counters and retransmit packets */
        clear_packets(this->responding.packets);
@@ -1874,11 +1945,13 @@ METHOD(task_manager_t, reset, void,
        }
        this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
 
+       time_monotonic(&now);
        /* reset queued tasks */
        enumerator = array_create_enumerator(this->queued_tasks);
-       while (enumerator->enumerate(enumerator, &task))
+       while (enumerator->enumerate(enumerator, &queued))
        {
-               task->migrate(task, this->ike_sa);
+               queued->time = now;
+               queued->task->migrate(queued->task, this->ike_sa);
        }
        enumerator->destroy(enumerator);
 
@@ -1886,12 +1959,25 @@ METHOD(task_manager_t, reset, void,
        while (array_remove(this->active_tasks, ARRAY_TAIL, &task))
        {
                task->migrate(task, this->ike_sa);
-               array_insert(this->queued_tasks, ARRAY_HEAD, task);
+               INIT(queued,
+                       .task = task,
+                       .time = now,
+               );
+               array_insert(this->queued_tasks, ARRAY_HEAD, queued);
        }
 
        this->reset = TRUE;
 }
 
+/**
+ * Filter queued tasks
+ */
+static bool filter_queued(void *unused, queued_task_t **queued, task_t **task)
+{
+       *task = (*queued)->task;
+       return TRUE;
+}
+
 METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
        private_task_manager_t *this, task_queue_t queue)
 {
@@ -1902,7 +1988,9 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
                case TASK_QUEUE_PASSIVE:
                        return array_create_enumerator(this->passive_tasks);
                case TASK_QUEUE_QUEUED:
-                       return array_create_enumerator(this->queued_tasks);
+                       return enumerator_create_filter(
+                                                                       array_create_enumerator(this->queued_tasks),
+                                                                       (void*)filter_queued, NULL, NULL);
                default:
                        return enumerator_create_empty();
        }
@@ -1938,6 +2026,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
                        .task_manager = {
                                .process_message = _process_message,
                                .queue_task = _queue_task,
+                               .queue_task_delayed = _queue_task_delayed,
                                .queue_ike = _queue_ike,
                                .queue_ike_rekey = _queue_ike_rekey,
                                .queue_ike_reauth = _queue_ike_reauth,
index b109c82..86077d3 100644 (file)
@@ -1,6 +1,7 @@
 /*
+ * Copyright (C) 2013-2016 Tobias Brunner
  * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -122,7 +123,17 @@ struct task_manager_t {
         *
         * @param task                  task to queue
         */
-       void (*queue_task) (task_manager_t *this, task_t *task);
+       void (*queue_task)(task_manager_t *this, task_t *task);
+
+       /**
+        * Queue a task in the manager, but delay its initiation for at least the
+        * given number of seconds.
+        *
+        * @param task                  task to queue
+        * @param delay                 minimum delay in s before initiating the task
+        */
+       void (*queue_task_delayed)(task_manager_t *this, task_t *task,
+                                                          uint32_t delay);
 
        /**
         * Queue IKE_SA establishing tasks.