/*
- * 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;
/**
*/
hashtable_t *allocated_spis;
+ /**
+ * Pending acquires (uint32_t => acquire_entry_t)
+ */
+ hashtable_t *acquires;
+
/**
* Mutex used to synchronize access to the SA manager
*/
*/
bool awaits_deletion;
-} ipsec_sa_entry_t;
+} ipsec_sa_entry_t;
/**
* Helper struct for expiration events
} 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));
}
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))
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;
{
sa = entry->sa;
}
+ if (!sa && acquire)
+ {
+ *acquire = !inbound && check_acquire(this, reqid);
+ }
this->mutex->unlock(this->mutex);
return sa;
}
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);
},
.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;
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2023 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
*
* 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.