ikev2: Trigger make-before-break reauthentication instead of reauth task
authorMartin Willi <martin@revosec.ch>
Mon, 3 Nov 2014 15:37:29 +0000 (16:37 +0100)
committerMartin Willi <martin@revosec.ch>
Fri, 20 Feb 2015 12:34:57 +0000 (13:34 +0100)
conf/options/charon.opt
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev2/task_manager_v2.c

index 02629a3..fc38a14 100644 (file)
@@ -196,6 +196,16 @@ charon.load_modular = no
 charon.max_packet = 10000
        Maximum packet size accepted by charon.
 
+charon.make_before_break = no
+       Initiate IKEv2 reauthentication with a make-before-break scheme.
+
+       Initiate IKEv2 reauthentication with a make-before-break instead of a
+       break-before-make scheme. Make-before-break uses overlapping IKE and
+       CHILD_SA during reauthentication by first recreating all new SAs before
+       deleting the old ones. This behavior can be beneficial to avoid connectivity
+       gaps during reauthentication, but requires support for overlapping SAs by
+       the peer. strongSwan can handle such overlapping SAs since version 5.3.0.
+
 charon.mem-pool.reassign_online = no
        Reassign an online IP address lease from an in-memory address pool if a
        client with the same identity requests it explicitly.
index c72d873..f65efd8 100644 (file)
@@ -936,8 +936,9 @@ struct ike_sa_t {
        /**
         * Reauthenticate the IKE_SA.
         *
-        * Create a completely new IKE_SA with authentication, recreates all children
-        * within the IKE_SA, closes this IKE_SA.
+        * Triggers a new IKE_SA that replaces this one. IKEv1 implicitly inherits
+        * all Quick Modes, while IKEv2 recreates all active and queued CHILD_SAs
+        * in the new IKE_SA.
         *
         * @return                              DESTROY_ME to destroy the IKE_SA
         */
index e9a677a..b558f42 100644 (file)
@@ -171,6 +171,11 @@ struct private_task_manager_t {
         * Base to calculate retransmission timeout
         */
        double retransmit_base;
+
+       /**
+        * Use make-before-break instead of break-before-make reauth?
+        */
+       bool make_before_break;
 };
 
 /**
@@ -1505,9 +1510,78 @@ METHOD(task_manager_t, queue_ike_rekey, void,
        queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
 }
 
+/**
+ * Start reauthentication using make-before-break
+ */
+static void trigger_mbb_reauth(private_task_manager_t *this)
+{
+       enumerator_t *enumerator;
+       child_sa_t *child_sa;
+       child_cfg_t *cfg;
+       ike_sa_t *new;
+       host_t *host;
+       task_t *task;
+
+       new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                               this->ike_sa->get_version(this->ike_sa), TRUE);
+       if (!new)
+       {       /* shouldn't happen */
+               return;
+       }
+
+       new->set_peer_cfg(new, this->ike_sa->get_peer_cfg(this->ike_sa));
+       host = this->ike_sa->get_other_host(this->ike_sa);
+       new->set_other_host(new, host->clone(host));
+       host = this->ike_sa->get_my_host(this->ike_sa);
+       new->set_my_host(new, host->clone(host));
+       enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
+       while (enumerator->enumerate(enumerator, &host))
+       {
+               new->add_virtual_ip(new, TRUE, host);
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+       while (enumerator->enumerate(enumerator, &child_sa))
+       {
+               cfg = child_sa->get_config(child_sa);
+               new->queue_task(new, &child_create_create(new, cfg->get_ref(cfg),
+                                                                                                 FALSE, NULL, NULL)->task);
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = array_create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == TASK_CHILD_CREATE)
+               {
+                       task->migrate(task, new);
+                       new->queue_task(new, task);
+                       array_remove_at(this->queued_tasks, enumerator);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME)
+       {
+               charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+               this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+       }
+       else
+       {
+               charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+               DBG1(DBG_IKE, "reauthenticating IKE_SA failed");
+       }
+       charon->bus->set_sa(charon->bus, this->ike_sa);
+}
+
 METHOD(task_manager_t, queue_ike_reauth, void,
        private_task_manager_t *this)
 {
+       if (this->make_before_break)
+       {
+               return trigger_mbb_reauth(this);
+       }
        queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
 }
 
@@ -1773,6 +1847,8 @@ 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),
+               .make_before_break = lib->settings->get_bool(lib->settings,
+                                       "%s.make_before_break", FALSE, lib->ns),
        );
 
        return &this->public;