diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c index bf13fb885..80b25e01a 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 ab6a2f079..12f5fc141 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 */ -static bool spi_equals(uint32_t *spi, uint32_t *other_spi) +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 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 549b9f22b..957de5f46 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.