From 914479370ed23aa420a15ef3f19c2c39dce3b133 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 13:21:45 +0200 Subject: Added IPsec SA manager --- src/libipsec/ipsec_sa_mgr.c | 314 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 src/libipsec/ipsec_sa_mgr.c (limited to 'src/libipsec/ipsec_sa_mgr.c') diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c new file mode 100644 index 000000000..74fb2ac7e --- /dev/null +++ b/src/libipsec/ipsec_sa_mgr.c @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ipsec_sa_mgr.h" + +#include +#include +#include +#include +#include + +typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t; + +/** + * Private additions to ipsec_sa_mgr_t. + */ +struct private_ipsec_sa_mgr_t { + + /** + * Public members of ipsec_sa_mgr_t. + */ + ipsec_sa_mgr_t public; + + /** + * Installed SAs + */ + linked_list_t *sas; + + /** + * SPIs allocated using get_spi() + */ + hashtable_t *allocated_spis; + + /** + * Mutex used to synchronize access to the SA manager + */ + mutex_t *mutex; + + /** + * RNG used to generate SPIs + */ + rng_t *rng; +}; + +/* + * Used for the hash table of allocated SPIs + */ +static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi) +{ + return *spi == *other_spi; +} + +static u_int spi_hash(u_int32_t *spi) +{ + return chunk_hash(chunk_from_thing(*spi)); +} + +/** + * Flushes all entries + * Must be called with this->mutex held. + */ +static void flush_entries(private_ipsec_sa_mgr_t *this) +{ + enumerator_t *enumerator; + ipsec_sa_t *current; + + DBG2(DBG_ESP, "flushing SAD"); + + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + this->sas->remove_at(this->sas, enumerator); + current->destroy(current); + } + enumerator->destroy(enumerator); +} + +/* + * Different match functions to find SAs in the linked list + */ +static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi, + bool inbound) +{ + return sa->get_spi(sa) == spi && sa->is_inbound(sa) == inbound; +} + +static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi, + host_t *src, host_t *dst) +{ + return sa->match_by_spi_src_dst(sa, spi, src, dst); +} + +/** + * Remove all allocated SPIs + */ +static void flush_allocated_spis(private_ipsec_sa_mgr_t *this) +{ + enumerator_t *enumerator; + u_int32_t *current; + + DBG2(DBG_ESP, "flushing allocated SPIs"); + enumerator = this->allocated_spis->create_enumerator(this->allocated_spis); + while (enumerator->enumerate(enumerator, NULL, (void**)¤t)) + { + this->allocated_spis->remove_at(this->allocated_spis, enumerator); + DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current)); + free(current); + } + enumerator->destroy(enumerator); +} + +/** + * Pre-allocate an SPI for an inbound SA + */ +static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi) +{ + u_int32_t *spi_alloc; + + if (this->allocated_spis->get(this->allocated_spis, &spi) || + this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound, + NULL, spi, TRUE) == SUCCESS) + { + return FALSE; + } + spi_alloc = malloc_thing(u_int32_t); + *spi_alloc = spi; + this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc); + return TRUE; +} + +METHOD(ipsec_sa_mgr_t, get_spi, status_t, + private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol, + u_int32_t reqid, u_int32_t *spi) +{ + u_int32_t spi_new; + + DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid); + + this->mutex->lock(this->mutex); + if (!this->rng) + { + this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!this->rng) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to create RNG for SPI generation"); + return FAILED; + } + } + + do + { + if (!this->rng->get_bytes(this->rng, sizeof(spi_new), + (u_int8_t*)&spi_new)) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid); + return FAILED; + } + /* make sure the SPI is valid (not in range 0-255) */ + spi_new |= 0x00000100; + spi_new = htonl(spi_new); + } + while (!allocate_spi(this, spi_new)); + this->mutex->unlock(this->mutex); + + *spi = spi_new; + + DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, add_sa, status_t, + private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi, + u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, + lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, + u_int16_t cpi, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) +{ + ipsec_sa_t *sa_new; + + DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}", + ntohl(spi), reqid); + DBG2(DBG_ESP, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg, enc_key.len * 8); + DBG2(DBG_ESP, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg, int_key.len * 8); + + sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc, + lifetime, enc_alg, enc_key, int_alg, int_key, mode, + ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts); + if (!sa_new) + { + DBG1(DBG_ESP, "failed to create SAD entry"); + return FAILED; + } + + this->mutex->lock(this->mutex); + + if (inbound) + { /* remove any pre-allocated SPIs */ + u_int32_t *spi_alloc; + + spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi); + free(spi_alloc); + } + + if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst, + NULL, spi, src, dst) == SUCCESS) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to install SAD entry: already installed"); + sa_new->destroy(sa_new); + return FAILED; + } + this->sas->insert_last(this->sas, sa_new); + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, del_sa, status_t, + private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi, + u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + ipsec_sa_t *current, *found = NULL; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (match_entry_by_spi_src_dst(current, spi, src, dst)) + { + this->sas->remove_at(this->sas, enumerator); + found = current; + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + if (found) + { + DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x", + found->is_inbound(found) ? "in" : "out", ntohl(spi)); + found->destroy(found); + return SUCCESS; + } + return FAILED; +} + +METHOD(ipsec_sa_mgr_t, flush_sas, status_t, + private_ipsec_sa_mgr_t *this) +{ + this->mutex->lock(this->mutex); + flush_entries(this); + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, destroy, void, + private_ipsec_sa_mgr_t *this) +{ + this->mutex->lock(this->mutex); + flush_entries(this); + flush_allocated_spis(this); + this->mutex->unlock(this->mutex); + + this->allocated_spis->destroy(this->allocated_spis); + this->sas->destroy(this->sas); + + this->mutex->destroy(this->mutex); + DESTROY_IF(this->rng); + free(this); +} + +/** + * Described in header. + */ +ipsec_sa_mgr_t *ipsec_sa_mgr_create() +{ + private_ipsec_sa_mgr_t *this; + + INIT(this, + .public = { + .get_spi = _get_spi, + .add_sa = _add_sa, + .del_sa = _del_sa, + .flush_sas = _flush_sas, + .destroy = _destroy, + }, + .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), + ); + + return &this->public; +} -- cgit v1.2.3 From e6cfd527df482f7bfd881201b75178f0980723b6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 13:54:29 +0200 Subject: Schedule and relay expiration events for created IPsec SAs --- src/libipsec/ipsec_sa_mgr.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'src/libipsec/ipsec_sa_mgr.c') diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c index 74fb2ac7e..3a851ba5c 100644 --- a/src/libipsec/ipsec_sa_mgr.c +++ b/src/libipsec/ipsec_sa_mgr.c @@ -15,10 +15,12 @@ * for more details. */ +#include "ipsec.h" #include "ipsec_sa_mgr.h" #include #include +#include #include #include #include @@ -56,6 +58,28 @@ struct private_ipsec_sa_mgr_t { rng_t *rng; }; +/** + * Helper struct for expiration events + */ +typedef struct { + + /** + * IPsec SA manager + */ + private_ipsec_sa_mgr_t *manager; + + /** + * SA that expired + */ + ipsec_sa_t *sa; + + /** + * 0 if this is a hard expire, otherwise the offset in s (soft->hard) + */ + u_int32_t hard_offset; + +} ipsec_sa_expired_t; + /* * Used for the hash table of allocated SPIs */ @@ -92,6 +116,11 @@ static void flush_entries(private_ipsec_sa_mgr_t *this) /* * Different match functions to find SAs in the linked list */ +static bool match_entry_by_ptr(ipsec_sa_t *sa, ipsec_sa_t *other) +{ + return sa == other; +} + static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi, bool inbound) { @@ -104,6 +133,69 @@ static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi, return sa->match_by_spi_src_dst(sa, spi, src, dst); } +/** + * Callback for expiration events + */ +static job_requeue_t sa_expired(ipsec_sa_expired_t *expired) +{ + private_ipsec_sa_mgr_t *this = expired->manager; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr, + NULL, expired->sa) == SUCCESS) + { + u_int32_t hard_offset = expired->hard_offset; + ipsec_sa_t *sa = expired->sa; + + ipsec->events->expire(ipsec->events, sa->get_reqid(sa), + sa->get_protocol(sa), sa->get_spi(sa), + hard_offset == 0); + if (hard_offset) + { /* soft limit reached, schedule hard expire */ + expired->hard_offset = 0; + this->mutex->unlock(this->mutex); + return JOB_RESCHEDULE(hard_offset); + } + /* hard limit reached */ + this->sas->remove(this->sas, sa, NULL); + sa->destroy(sa); + } + this->mutex->unlock(this->mutex); + return JOB_REQUEUE_NONE; +} + +/** + * Schedule a job to handle IPsec SA expiration + */ +static void schedule_expiration(private_ipsec_sa_mgr_t *this, + ipsec_sa_t *sa) +{ + lifetime_cfg_t *lifetime = sa->get_lifetime(sa); + ipsec_sa_expired_t *expired; + callback_job_t *job; + u_int32_t timeout; + + INIT(expired, + .manager = this, + .sa = sa, + ); + + /* schedule a rekey first, a hard timeout will be scheduled then, if any */ + expired->hard_offset = lifetime->time.life - lifetime->time.rekey; + timeout = lifetime->time.rekey; + + if (lifetime->time.life <= lifetime->time.rekey || + lifetime->time.rekey == 0) + { /* no rekey, schedule hard timeout */ + expired->hard_offset = 0; + timeout = lifetime->time.life; + } + + job = callback_job_create((callback_job_cb_t)sa_expired, expired, + (callback_job_cleanup_t)free, NULL); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout); +} + /** * Remove all allocated SPIs */ @@ -228,7 +320,10 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t, sa_new->destroy(sa_new); return FAILED; } + + schedule_expiration(this, sa_new); this->sas->insert_last(this->sas, sa_new); + this->mutex->unlock(this->mutex); return SUCCESS; } -- cgit v1.2.3 From f1b423831104ccfc38487e2095ddc0c39555dded Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 14:32:03 +0200 Subject: Implemented a checkout/checkin mechanism for IPsec SAs SAs can only be checked out by a single thread and all other threads block until the SA is checked in again. --- src/libipsec/ipsec_sa_mgr.c | 267 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 242 insertions(+), 25 deletions(-) (limited to 'src/libipsec/ipsec_sa_mgr.c') diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c index 3a851ba5c..e42c77aa5 100644 --- a/src/libipsec/ipsec_sa_mgr.c +++ b/src/libipsec/ipsec_sa_mgr.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,38 @@ struct private_ipsec_sa_mgr_t { rng_t *rng; }; +/** + * Struct to keep track of locked IPsec SAs + */ +typedef struct { + + /** + * IPsec SA + */ + ipsec_sa_t *sa; + + /** + * Set if this SA is currently in use by a thread + */ + bool locked; + + /** + * Condvar used by threads to wait for this entry + */ + condvar_t *condvar; + + /** + * Number of threads waiting for this entry + */ + u_int waiting_threads; + + /** + * Set if this entry is awaiting deletion + */ + bool awaits_deletion; + +} ipsec_sa_entry_t; + /** * Helper struct for expiration events */ @@ -69,9 +102,9 @@ typedef struct { private_ipsec_sa_mgr_t *manager; /** - * SA that expired + * Entry that expired */ - ipsec_sa_t *sa; + ipsec_sa_entry_t *entry; /** * 0 if this is a hard expire, otherwise the offset in s (soft->hard) @@ -93,22 +126,100 @@ static u_int spi_hash(u_int32_t *spi) return chunk_hash(chunk_from_thing(*spi)); } +/** + * Create an SA entry + */ +static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa) +{ + ipsec_sa_entry_t *this; + + INIT(this, + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .sa = sa, + ); + return this; +} + +/** + * Destroy an SA entry + */ +static void destroy_entry(ipsec_sa_entry_t *entry) +{ + entry->condvar->destroy(entry->condvar); + entry->sa->destroy(entry->sa); + free(entry); +} + +/** + * Makes sure an entry is safe to remove + * Must be called with this->mutex held. + * + * @return TRUE if entry can be removed, FALSE if entry is already +* being removed by another thread + */ +static bool wait_remove_entry(private_ipsec_sa_mgr_t *this, + ipsec_sa_entry_t *entry) +{ + if (entry->awaits_deletion) + { + /* this will be deleted by another thread already */ + return FALSE; + } + entry->awaits_deletion = TRUE; + while (entry->locked) + { + entry->condvar->wait(entry->condvar, this->mutex); + } + while (entry->waiting_threads > 0) + { + entry->condvar->broadcast(entry->condvar); + entry->condvar->wait(entry->condvar, this->mutex); + } + return TRUE; +} + +/** + * Waits until an is available and then locks it. + * Must only be called with this->mutex held + */ +static bool wait_for_entry(private_ipsec_sa_mgr_t *this, + ipsec_sa_entry_t *entry) +{ + while (entry->locked && !entry->awaits_deletion) + { + entry->waiting_threads++; + entry->condvar->wait(entry->condvar, this->mutex); + entry->waiting_threads--; + } + if (entry->awaits_deletion) + { + /* others may still be waiting, */ + entry->condvar->signal(entry->condvar); + return FALSE; + } + entry->locked = TRUE; + return TRUE; +} + /** * Flushes all entries * Must be called with this->mutex held. */ static void flush_entries(private_ipsec_sa_mgr_t *this) { + ipsec_sa_entry_t *current; enumerator_t *enumerator; - ipsec_sa_t *current; DBG2(DBG_ESP, "flushing SAD"); enumerator = this->sas->create_enumerator(this->sas); while (enumerator->enumerate(enumerator, (void**)¤t)) { - this->sas->remove_at(this->sas, enumerator); - current->destroy(current); + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + destroy_entry(current); + } } enumerator->destroy(enumerator); } @@ -116,21 +227,65 @@ static void flush_entries(private_ipsec_sa_mgr_t *this) /* * Different match functions to find SAs in the linked list */ -static bool match_entry_by_ptr(ipsec_sa_t *sa, ipsec_sa_t *other) +static bool match_entry_by_ptr(ipsec_sa_entry_t *item, ipsec_sa_entry_t *entry) +{ + return item == entry; +} + +static bool match_entry_by_sa_ptr(ipsec_sa_entry_t *item, ipsec_sa_t *sa) { - return sa == other; + return item->sa == sa; } -static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi, +static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t spi, bool inbound) { - return sa->get_spi(sa) == spi && sa->is_inbound(sa) == inbound; + return item->sa->get_spi(item->sa) == spi && + item->sa->is_inbound(item->sa) == inbound; } -static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi, +static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t spi, host_t *src, host_t *dst) { - return sa->match_by_spi_src_dst(sa, spi, src, dst); + return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst); +} + +static bool match_entry_by_reqid_inbound(ipsec_sa_entry_t *item, + u_int32_t reqid, bool inbound) +{ + return item->sa->match_by_reqid(item->sa, reqid, inbound); +} + +static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t spi, + host_t *dst) +{ + return item->sa->match_by_spi_dst(item->sa, spi, dst); +} + +/** + * Remove an entry + */ +static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry) +{ + ipsec_sa_entry_t *current; + enumerator_t *enumerator; + bool removed = FALSE; + + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current == entry) + { + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + removed = TRUE; + } + break; + } + } + enumerator->destroy(enumerator); + return removed; } /** @@ -142,10 +297,10 @@ static job_requeue_t sa_expired(ipsec_sa_expired_t *expired) this->mutex->lock(this->mutex); if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr, - NULL, expired->sa) == SUCCESS) + NULL, expired->entry) == SUCCESS) { u_int32_t hard_offset = expired->hard_offset; - ipsec_sa_t *sa = expired->sa; + ipsec_sa_t *sa = expired->entry->sa; ipsec->events->expire(ipsec->events, sa->get_reqid(sa), sa->get_protocol(sa), sa->get_spi(sa), @@ -157,8 +312,10 @@ static job_requeue_t sa_expired(ipsec_sa_expired_t *expired) return JOB_RESCHEDULE(hard_offset); } /* hard limit reached */ - this->sas->remove(this->sas, sa, NULL); - sa->destroy(sa); + if (remove_entry(this, expired->entry)) + { + destroy_entry(expired->entry); + } } this->mutex->unlock(this->mutex); return JOB_REQUEUE_NONE; @@ -168,16 +325,16 @@ static job_requeue_t sa_expired(ipsec_sa_expired_t *expired) * Schedule a job to handle IPsec SA expiration */ static void schedule_expiration(private_ipsec_sa_mgr_t *this, - ipsec_sa_t *sa) + ipsec_sa_entry_t *entry) { - lifetime_cfg_t *lifetime = sa->get_lifetime(sa); + lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa); ipsec_sa_expired_t *expired; callback_job_t *job; u_int32_t timeout; INIT(expired, .manager = this, - .sa = sa, + .entry = entry, ); /* schedule a rekey first, a hard timeout will be scheduled then, if any */ @@ -284,6 +441,7 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t, u_int16_t cpi, bool encap, bool esn, bool inbound, traffic_selector_t *src_ts, traffic_selector_t *dst_ts) { + ipsec_sa_entry_t *entry; ipsec_sa_t *sa_new; DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}", @@ -321,8 +479,9 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t, return FAILED; } - schedule_expiration(this, sa_new); - this->sas->insert_last(this->sas, sa_new); + entry = create_entry(sa_new); + schedule_expiration(this, entry); + this->sas->insert_last(this->sas, entry); this->mutex->unlock(this->mutex); return SUCCESS; @@ -332,7 +491,7 @@ METHOD(ipsec_sa_mgr_t, del_sa, status_t, private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark) { - ipsec_sa_t *current, *found = NULL; + ipsec_sa_entry_t *current, *found = NULL; enumerator_t *enumerator; this->mutex->lock(this->mutex); @@ -341,8 +500,11 @@ METHOD(ipsec_sa_mgr_t, del_sa, status_t, { if (match_entry_by_spi_src_dst(current, spi, src, dst)) { - this->sas->remove_at(this->sas, enumerator); - found = current; + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + found = current; + } break; } } @@ -352,13 +514,65 @@ METHOD(ipsec_sa_mgr_t, del_sa, status_t, if (found) { DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x", - found->is_inbound(found) ? "in" : "out", ntohl(spi)); - found->destroy(found); + found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi)); + destroy_entry(found); return SUCCESS; } return FAILED; } +METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*, + private_ipsec_sa_mgr_t *this, u_int32_t reqid, bool inbound) +{ + ipsec_sa_entry_t *entry; + ipsec_sa_t *sa = NULL; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_reqid_inbound, + (void**)&entry, reqid, inbound) == SUCCESS && + wait_for_entry(this, entry)) + { + sa = entry->sa; + } + this->mutex->unlock(this->mutex); + return sa; +} + +METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*, + private_ipsec_sa_mgr_t *this, u_int32_t spi, host_t *dst) +{ + ipsec_sa_entry_t *entry; + ipsec_sa_t *sa = NULL; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_dst, + (void**)&entry, spi, dst) == SUCCESS && + wait_for_entry(this, entry)) + { + sa = entry->sa; + } + this->mutex->unlock(this->mutex); + return sa; +} + +METHOD(ipsec_sa_mgr_t, checkin, void, + private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa) +{ + ipsec_sa_entry_t *entry; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_sa_ptr, + (void**)&entry, sa) == SUCCESS) + { + if (entry->locked) + { + entry->locked = FALSE; + entry->condvar->signal(entry->condvar); + } + } + this->mutex->unlock(this->mutex); +} + METHOD(ipsec_sa_mgr_t, flush_sas, status_t, private_ipsec_sa_mgr_t *this) { @@ -396,6 +610,9 @@ ipsec_sa_mgr_t *ipsec_sa_mgr_create() .get_spi = _get_spi, .add_sa = _add_sa, .del_sa = _del_sa, + .checkout_by_spi = _checkout_by_spi, + .checkout_by_reqid = _checkout_by_reqid, + .checkin = _checkin, .flush_sas = _flush_sas, .destroy = _destroy, }, -- cgit v1.2.3