From: Tobias Brunner Date: Thu, 4 May 2023 12:20:09 +0000 (+0200) Subject: ipsec-sa-mgr: Optionally keep track of acquires for outbound SAs X-Git-Tag: 5.9.11rc1~9^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e9acf98d0e2e256e8994b94361d653c1c019f31;p=thirdparty%2Fstrongswan.git ipsec-sa-mgr: Optionally keep track of acquires for outbound SAs 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. --- diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c index bf13fb885b..80b25e01a6 100644 --- a/src/libipsec/ipsec_processor.c +++ b/src/libipsec/ipsec_processor.c @@ -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}, " diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c index ab6a2f0794..12f5fc141d 100644 --- a/src/libipsec/ipsec_sa_mgr.c +++ b/src/libipsec/ipsec_sa_mgr.c @@ -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 * @@ -27,6 +27,12 @@ #include #include +/** + * 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; diff --git a/src/libipsec/ipsec_sa_mgr.h b/src/libipsec/ipsec_sa_mgr.h index 549b9f22bc..957de5f468 100644 --- a/src/libipsec/ipsec_sa_mgr.h +++ b/src/libipsec/ipsec_sa_mgr.h @@ -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.