ike: Migrate queued CHILD_SA-creating tasks when reestablishing an IKE_SA
authorTobias Brunner <tobias@strongswan.org>
Wed, 3 Jul 2013 16:28:37 +0000 (18:28 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 17 Jul 2013 16:16:58 +0000 (18:16 +0200)
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ikev1/task_manager_v1.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/task_manager.h

index 50d9694..aa04d15 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2006-2013 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -1535,6 +1535,30 @@ METHOD(ike_sa_t, reauth, status_t,
        return this->task_manager->initiate(this->task_manager);
 }
 
        return this->task_manager->initiate(this->task_manager);
 }
 
+/**
+ * Check if tasks to create CHILD_SAs are queued in the given queue
+ */
+static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue)
+{
+       enumerator_t *enumerator;
+       task_t *task;
+       bool found = FALSE;
+
+       enumerator = this->task_manager->create_task_enumerator(this->task_manager,
+                                                                                                                       queue);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == TASK_CHILD_CREATE ||
+                       task->get_type(task) == TASK_QUICK_MODE)
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+}
+
 METHOD(ike_sa_t, reestablish, status_t,
        private_ike_sa_t *this)
 {
 METHOD(ike_sa_t, reestablish, status_t,
        private_ike_sa_t *this)
 {
@@ -1592,6 +1616,9 @@ METHOD(ike_sa_t, reestablish, status_t,
                        }
                }
                enumerator->destroy(enumerator);
                        }
                }
                enumerator->destroy(enumerator);
+               /* check if we have tasks that recreate children */
+               restart = is_child_queued(this, TASK_QUEUE_ACTIVE) ||
+                                 is_child_queued(this, TASK_QUEUE_QUEUED);
 #ifdef ME
                /* mediation connections have no children, keep them up anyway */
                if (this->peer_cfg->is_mediation(this->peer_cfg))
 #ifdef ME
                /* mediation connections have no children, keep them up anyway */
                if (this->peer_cfg->is_mediation(this->peer_cfg))
@@ -1645,6 +1672,7 @@ METHOD(ike_sa_t, reestablish, status_t,
        else
 #endif /* ME */
        {
        else
 #endif /* ME */
        {
+               /* handle existing CHILD_SAs */
                enumerator = array_create_enumerator(this->child_sas);
                while (enumerator->enumerate(enumerator, (void**)&child_sa))
                {
                enumerator = array_create_enumerator(this->child_sas);
                while (enumerator->enumerate(enumerator, (void**)&child_sa))
                {
@@ -1696,6 +1724,16 @@ METHOD(ike_sa_t, reestablish, status_t,
                        }
                }
                enumerator->destroy(enumerator);
                        }
                }
                enumerator->destroy(enumerator);
+               /* adopt any active or queued CHILD-creating tasks */
+               if (status != DESTROY_ME)
+               {
+                       task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
+                       other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
+                       if (new->get_state(new) == IKE_CREATED)
+                       {
+                               status = new->initiate(new, NULL, 0, NULL, NULL);
+                       }
+               }
        }
 
        if (status == DESTROY_ME)
        }
 
        if (status == DESTROY_ME)
index bfa6fc8..857cb02 100644 (file)
@@ -1900,6 +1900,39 @@ METHOD(task_manager_t, adopt_tasks, void,
        }
 }
 
        }
 }
 
+/**
+ * Migrates child-creating tasks from src to dst
+ */
+static void migrate_child_tasks(private_task_manager_t *this,
+                                                               linked_list_t *src, linked_list_t *dst)
+{
+       enumerator_t *enumerator;
+       task_t *task;
+
+       enumerator = src->create_enumerator(src);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == TASK_QUICK_MODE)
+               {
+                       src->remove_at(src, enumerator);
+                       task->migrate(task, this->ike_sa);
+                       dst->insert_last(dst, task);
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(task_manager_t, adopt_child_tasks, void,
+       private_task_manager_t *this, task_manager_t *other_public)
+{
+       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);
+       /* do the same for queued tasks */
+       migrate_child_tasks(this, other->queued_tasks, this->queued_tasks);
+}
+
 METHOD(task_manager_t, busy, bool,
        private_task_manager_t *this)
 {
 METHOD(task_manager_t, busy, bool,
        private_task_manager_t *this)
 {
@@ -2014,6 +2047,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
                                .incr_mid = _incr_mid,
                                .reset = _reset,
                                .adopt_tasks = _adopt_tasks,
                                .incr_mid = _incr_mid,
                                .reset = _reset,
                                .adopt_tasks = _adopt_tasks,
+                               .adopt_child_tasks = _adopt_child_tasks,
                                .busy = _busy,
                                .create_task_enumerator = _create_task_enumerator,
                                .flush_queue = _flush_queue,
                                .busy = _busy,
                                .create_task_enumerator = _create_task_enumerator,
                                .flush_queue = _flush_queue,
index 79a9aa5..a2a968b 100644 (file)
@@ -1426,6 +1426,39 @@ METHOD(task_manager_t, adopt_tasks, void,
        }
 }
 
        }
 }
 
+/**
+ * Migrates child-creating tasks from src to dst
+ */
+static void migrate_child_tasks(private_task_manager_t *this,
+                                                               array_t *src, array_t *dst)
+{
+       enumerator_t *enumerator;
+       task_t *task;
+
+       enumerator = array_create_enumerator(src);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == TASK_CHILD_CREATE)
+               {
+                       array_remove_at(src, enumerator);
+                       task->migrate(task, this->ike_sa);
+                       array_insert(dst, ARRAY_TAIL, task);
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(task_manager_t, adopt_child_tasks, void,
+       private_task_manager_t *this, task_manager_t *other_public)
+{
+       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);
+       /* do the same for queued tasks */
+       migrate_child_tasks(this, other->queued_tasks, this->queued_tasks);
+}
+
 METHOD(task_manager_t, busy, bool,
        private_task_manager_t *this)
 {
 METHOD(task_manager_t, busy, bool,
        private_task_manager_t *this)
 {
@@ -1527,6 +1560,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
                                .incr_mid = _incr_mid,
                                .reset = _reset,
                                .adopt_tasks = _adopt_tasks,
                                .incr_mid = _incr_mid,
                                .reset = _reset,
                                .adopt_tasks = _adopt_tasks,
+                               .adopt_child_tasks = _adopt_child_tasks,
                                .busy = _busy,
                                .create_task_enumerator = _create_task_enumerator,
                                .flush_queue = _flush_queue,
                                .busy = _busy,
                                .create_task_enumerator = _create_task_enumerator,
                                .flush_queue = _flush_queue,
index c649cf7..a1ebb41 100644 (file)
@@ -202,7 +202,7 @@ struct task_manager_t {
        status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
 
        /**
        status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
 
        /**
-        * Migrate all tasks from other to this.
+        * Migrate all queued tasks from other to this.
         *
         * To rekey or reestablish an IKE_SA completely, all queued or active
         * tasks should get migrated to the new IKE_SA.
         *
         * To rekey or reestablish an IKE_SA completely, all queued or active
         * tasks should get migrated to the new IKE_SA.
@@ -212,6 +212,13 @@ struct task_manager_t {
        void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
 
        /**
        void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
 
        /**
+        * Migrate all active or queued CHILD_SA-creating tasks from other to this.
+        *
+        * @param other                 manager which gives away its tasks
+        */
+       void (*adopt_child_tasks) (task_manager_t *this, task_manager_t *other);
+
+       /**
         * Increment a message ID counter, in- or outbound.
         *
         * If a message is processed outside of the manager, this call increments
         * Increment a message ID counter, in- or outbound.
         *
         * If a message is processed outside of the manager, this call increments