]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
ipsec-sa-mgr: Optionally keep track of acquires for outbound SAs
authorTobias Brunner <tobias@strongswan.org>
Thu, 4 May 2023 12:20:09 +0000 (14:20 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 23 May 2023 09:53:51 +0000 (11:53 +0200)
Currently just based on the reqid.  An acquire for the same reqid is
triggered at most every 10 seconds (gets ignored in trap_manager_t if
the SA is still getting established).

Entries are only cleaned up if an SA is eventually installed (similar to
the allocated SPIs).  Should that ever be a problem, we could probably
schedule a job that regularly flushes old entries.

src/libipsec/ipsec_processor.c
src/libipsec/ipsec_sa_mgr.c
src/libipsec/ipsec_sa_mgr.h

index bf13fb885bff2dfab990b109064fe6c1754d8ef1..80b25e01a62edfccff06a60ba9f44a39a4fd73a8 100644 (file)
@@ -208,7 +208,7 @@ static job_requeue_t process_outbound(private_ipsec_processor_t *this)
        }
 
        sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
-                                                                          FALSE);
+                                                                          FALSE, NULL);
        if (!sa)
        {       /* TODO-IPSEC: send an acquire to upper layer */
                DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
index ab6a2f079421764a69d3b43489990fabdf9fa0e5..12f5fc141dc705e37ecb6ef52e6d395502275f50 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Tobias Brunner
+ * Copyright (C) 2012-2023 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  *
 #include <collections/hashtable.h>
 #include <collections/linked_list.h>
 
+/**
+ * Timeout in seconds for acquries for the same reqid (i.e. the interval used
+ * to trigger acquires while no SA is established).
+ */
+#define ACQUIRE_TIMEOUT 10
+
 typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
 
 /**
@@ -49,6 +55,11 @@ struct private_ipsec_sa_mgr_t {
         */
        hashtable_t *allocated_spis;
 
+       /**
+        * Pending acquires (uint32_t => acquire_entry_t)
+        */
+       hashtable_t *acquires;
+
        /**
         * Mutex used to synchronize access to the SA manager
         */
@@ -90,7 +101,7 @@ typedef struct {
         */
        bool awaits_deletion;
 
-}  ipsec_sa_entry_t;
+} ipsec_sa_entry_t;
 
 /**
  * Helper struct for expiration events
@@ -119,15 +130,32 @@ typedef struct {
 
 } ipsec_sa_expired_t;
 
-/*
- * Used for the hash table of allocated SPIs
+/**
+ * Struct to keep track of acquires
+ */
+typedef struct {
+
+       /**
+        * Reqid of this acquire
+        */
+       uint32_t reqid;
+
+       /**
+        * Time the entry was created or updated
+        */
+       time_t triggered;
+
+} acquire_entry_t;
+
+/**
+ * Used for the hash table of allocated SPIs and pending acquires
  */
-static bool spi_equals(uint32_t *spi, uint32_t *other_spi)
+static bool uint32_equals(const uint32_t *spi, const uint32_t *other_spi)
 {
        return *spi == *other_spi;
 }
 
-static u_int spi_hash(uint32_t *spi)
+static u_int uint32_hash(const uint32_t *spi)
 {
        return chunk_hash(chunk_from_thing(*spi));
 }
@@ -508,6 +536,10 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t,
                spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
                free(spi_alloc);
        }
+       if (!inbound)
+       {       /* remove any acquires for outbound SAs */
+               free(this->acquires->remove(this->acquires, &reqid));
+       }
 
        if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb, NULL,
                                                          spi, src, dst))
@@ -620,8 +652,53 @@ METHOD(ipsec_sa_mgr_t, del_sa, status_t,
        return FAILED;
 }
 
+/**
+ * Remove all acquires
+ */
+static void flush_acquires(private_ipsec_sa_mgr_t *this)
+{
+       enumerator_t *enumerator;
+       acquire_entry_t *entry;
+
+       DBG2(DBG_ESP, "flushing acquires");
+       enumerator = this->acquires->create_enumerator(this->acquires);
+       while (enumerator->enumerate(enumerator, NULL, (void**)&entry))
+       {
+               this->acquires->remove_at(this->acquires, enumerator);
+               DBG2(DBG_ESP, "  removed acquire for reqid {%u}", entry->reqid);
+               free(entry);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * Check whether an acquire should be sent for the given reqid.
+ */
+static bool check_acquire(private_ipsec_sa_mgr_t *this, uint32_t reqid)
+{
+       acquire_entry_t *entry;
+       time_t now;
+
+       now = time_monotonic(NULL);
+
+       entry = this->acquires->get(this->acquires, &reqid);
+       if (!entry)
+       {
+               INIT(entry,
+                       .reqid = reqid,
+               );
+               this->acquires->put(this->acquires, &entry->reqid, entry);
+       }
+       else if (now - entry->triggered <= ACQUIRE_TIMEOUT)
+       {
+               return FALSE;
+       }
+       entry->triggered = now;
+       return TRUE;
+}
+
 METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
-       private_ipsec_sa_mgr_t *this, uint32_t reqid, bool inbound)
+       private_ipsec_sa_mgr_t *this, uint32_t reqid, bool inbound, bool *acquire)
 {
        ipsec_sa_entry_t *entry;
        ipsec_sa_t *sa = NULL;
@@ -633,6 +710,10 @@ METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
        {
                sa = entry->sa;
        }
+       if (!sa && acquire)
+       {
+               *acquire = !inbound && check_acquire(this, reqid);
+       }
        this->mutex->unlock(this->mutex);
        return sa;
 }
@@ -687,9 +768,11 @@ METHOD(ipsec_sa_mgr_t, destroy, void,
        this->mutex->lock(this->mutex);
        flush_entries(this);
        flush_allocated_spis(this);
+       flush_acquires(this);
        this->mutex->unlock(this->mutex);
 
        this->allocated_spis->destroy(this->allocated_spis);
+       this->acquires->destroy(this->acquires);
        this->sas->destroy(this->sas);
 
        this->mutex->destroy(this->mutex);
@@ -719,8 +802,10 @@ ipsec_sa_mgr_t *ipsec_sa_mgr_create()
                },
                .sas = linked_list_create(),
                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
-               .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
-                                                                                  (hashtable_equals_t)spi_equals, 16),
+               .allocated_spis = hashtable_create((hashtable_hash_t)uint32_hash,
+                                                                                  (hashtable_equals_t)uint32_equals, 16),
+               .acquires = hashtable_create((hashtable_hash_t)uint32_hash,
+                                                                                  (hashtable_equals_t)uint32_equals, 16),
        );
 
        return &this->public;
index 549b9f22bcd878932bdd944ca3c4d2c6833a5820..957de5f4685c6908e00ca34a5394e233e9d5e522 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2023 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  *
@@ -174,12 +174,17 @@ struct ipsec_sa_mgr_t {
         * Since other threads may be waiting for a checked out SA, it should be
         * checked in as soon as possible after use.
         *
+        * If no matching outbound SA is found, acquire indicates if an acquire
+        * should be sent for the given reqid.
+        *
         * @param reqid                 reqid of the SA
         * @param inbound               TRUE for an inbound SA, FALSE for an outbound SA
+        * @param[out] acquire  TRUE if an acquire should be triggered, FALSE if one
+        *                                              is already pending or an SA was found
         * @return                              the matching IPsec SA, or NULL if none is found
         */
        ipsec_sa_t *(*checkout_by_reqid)(ipsec_sa_mgr_t *this, uint32_t reqid,
-                                                                        bool inbound);
+                                                                        bool inbound, bool *acquire);
 
        /**
         * Checkin an SA after use.