]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
ike-sa-manager: Add method to change the initiator SPI of an IKE_SA
authorTobias Brunner <tobias@strongswan.org>
Fri, 25 Aug 2017 14:45:03 +0000 (16:45 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 4 Sep 2017 09:16:00 +0000 (11:16 +0200)
src/libcharon/sa/ike_sa_manager.c
src/libcharon/sa/ike_sa_manager.h

index c0bfebb8380d28ff74b4bd12bb3e52c866a2bad7..101d986786aab2865e2c187ae8ad48f300426a9a 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * Copyright (C) 2005-2011 Martin Willi
  * Copyright (C) 2011 revosec AG
- * Copyright (C) 2008-2016 Tobias Brunner
+ *
+ * Copyright (C) 2008-2017 Tobias Brunner
  * 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
@@ -1572,6 +1573,88 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
        return ike_sa;
 }
 
+METHOD(ike_sa_manager_t, new_initiator_spi, bool,
+       private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+{
+       ike_sa_state_t state;
+       ike_sa_id_t *ike_sa_id;
+       entry_t *entry;
+       u_int segment;
+       uint64_t new_spi, spi;
+
+       state = ike_sa->get_state(ike_sa);
+       if (state != IKE_CONNECTING)
+       {
+               DBG1(DBG_MGR, "unable to change initiator SPI for IKE_SA in state "
+                        "%N", ike_sa_state_names, state);
+               return FALSE;
+       }
+
+       ike_sa_id = ike_sa->get_id(ike_sa);
+       if (!ike_sa_id->is_initiator(ike_sa_id))
+       {
+               DBG1(DBG_MGR, "unable to change initiator SPI of IKE_SA as responder");
+               return FALSE;
+       }
+
+       if (ike_sa != charon->bus->get_sa(charon->bus))
+       {
+               DBG1(DBG_MGR, "unable to change initiator SPI of IKE_SA not checked "
+                        "out by current thread");
+               return FALSE;
+       }
+
+       new_spi = get_spi(this);
+       if (!new_spi)
+       {
+               DBG1(DBG_MGR, "unable to allocate new initiator SPI for IKE_SA");
+               return FALSE;
+       }
+
+       if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS)
+       {
+               if (entry->driveout_waiting_threads && entry->driveout_new_threads)
+               {       /* it looks like flush() has been called and the SA is being deleted
+                        * anyway, no need for a new SPI */
+                       DBG2(DBG_MGR, "ignored change of initiator SPI during shutdown");
+                       unlock_single_segment(this, segment);
+                       return FALSE;
+               }
+               /* threads waiting for this entry do so using the (soon) wrong IKE_SA
+                * ID and, therefore, likely on the wrong segment, so drive them out */
+               entry->driveout_waiting_threads = TRUE;
+               entry->driveout_new_threads = TRUE;
+               while (entry->waiting_threads)
+               {
+                       entry->condvar->broadcast(entry->condvar);
+                       entry->condvar->wait(entry->condvar, this->segments[segment].mutex);
+               }
+               remove_entry(this, entry);
+               unlock_single_segment(this, segment);
+       }
+       else
+       {
+               DBG1(DBG_MGR, "unable to change initiator SPI of IKE_SA, not found");
+               return FALSE;
+       }
+
+       spi = ike_sa_id->get_initiator_spi(ike_sa_id);
+
+       DBG2(DBG_MGR, "change initiator SPI of IKE_SA %s[%u] from %.16"PRIx64" to "
+                "%.16"PRIx64, ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa),
+                be64toh(spi), be64toh(new_spi));
+
+       ike_sa_id->set_initiator_spi(ike_sa_id, new_spi);
+       entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa_id);
+
+       entry->driveout_waiting_threads = FALSE;
+       entry->driveout_new_threads = FALSE;
+
+       segment = put_entry(this, entry);
+       unlock_single_segment(this, segment);
+       return TRUE;
+}
+
 CALLBACK(enumerator_filter_wait, bool,
        private_ike_sa_manager_t *this, enumerator_t *orig, va_list args)
 {
@@ -2277,6 +2360,7 @@ ike_sa_manager_t *ike_sa_manager_create()
                        .checkout_by_config = _checkout_by_config,
                        .checkout_by_id = _checkout_by_id,
                        .checkout_by_name = _checkout_by_name,
+                       .new_initiator_spi = _new_initiator_spi,
                        .check_uniqueness = _check_uniqueness,
                        .has_contact = _has_contact,
                        .create_enumerator = _create_enumerator,
index 4298c54e22f35580326992830b2e4dcb44b21c90..efad2e4d61f0a7cd0163f380c9928abe7662c9c1 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (C) 2008-2015 Tobias Brunner
+ * Copyright (C) 2008-2017 Tobias Brunner
  * Copyright (C) 2005-2008 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
@@ -108,6 +108,17 @@ struct ike_sa_manager_t {
        ike_sa_t* (*checkout_by_config) (ike_sa_manager_t* this,
                                                                         peer_cfg_t *peer_cfg);
 
+       /**
+        * Reset initiator SPI.
+        *
+        * Allocate a new initiator SPI for the given IKE_SA in state IKE_CONNECTING
+        * and update internal data.
+        *
+        * @param ike_sa                        IKE_SA to update
+        * @return                                      TRUE if SPI successfully changed
+        */
+       bool (*new_initiator_spi)(ike_sa_manager_t* this, ike_sa_t *ike_sa);
+
        /**
         * Check for duplicates of the given IKE_SA.
         *