diff options
Diffstat (limited to 'src/libcharon/sa/trap_manager.c')
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 87 |
1 files changed, 47 insertions, 40 deletions
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 1f66d6ceb..7e55d6b0f 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -19,7 +19,6 @@ #include <hydra.h> #include <daemon.h> #include <threading/rwlock.h> -#include <threading/thread_value.h> #include <collections/linked_list.h> @@ -63,11 +62,6 @@ struct private_trap_manager_t { rwlock_t *lock; /** - * track if the current thread is installing a trap policy - */ - thread_value_t *installing; - - /** * listener to track acquiring IKE_SAs */ trap_listener_t listener; @@ -77,6 +71,8 @@ struct private_trap_manager_t { * A installed trap entry */ typedef struct { + /** name of the trapped CHILD_SA */ + char *name; /** ref to peer_cfg to initiate */ peer_cfg_t *peer_cfg; /** ref to instanciated CHILD_SA */ @@ -94,6 +90,7 @@ static void destroy_entry(entry_t *entry) { entry->child_sa->destroy(entry->child_sa); entry->peer_cfg->destroy(entry->peer_cfg); + free(entry->name); free(entry); } @@ -137,27 +134,42 @@ METHOD(trap_manager_t, install, u_int32_t, } this->lock->write_lock(this->lock); - this->installing->set(this->installing, this); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (streq(entry->child_sa->get_name(entry->child_sa), - child->get_name(child))) + if (streq(entry->name, child->get_name(child))) { - this->traps->remove_at(this->traps, enumerator); found = entry; + if (entry->child_sa) + { /* replace it with an updated version, if already installed */ + this->traps->remove_at(this->traps, enumerator); + } break; } } enumerator->destroy(enumerator); if (found) - { /* config might have changed so update everything */ - DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", - child->get_name(child)); + { + if (!found->child_sa) + { + DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name); + this->lock->unlock(this->lock); + return 0; + } + /* config might have changed so update everything */ + DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name); reqid = found->child_sa->get_reqid(found->child_sa); } + INIT(entry, + .name = strdup(child->get_name(child)), + .peer_cfg = peer->get_ref(peer), + ); + this->traps->insert_first(this->traps, entry); + /* don't hold lock while creating CHILD_SA and installing policies */ + this->lock->unlock(this->lock); + /* create and route CHILD_SA */ child_sa = child_sa_create(me, other, child, reqid, FALSE); @@ -185,24 +197,19 @@ METHOD(trap_manager_t, install, u_int32_t, if (status != SUCCESS) { DBG1(DBG_CFG, "installing trap failed"); + this->lock->write_lock(this->lock); + this->traps->remove(this->traps, entry, NULL); + this->lock->unlock(this->lock); + entry->child_sa = child_sa; + destroy_entry(entry); reqid = 0; - /* hold off destroying the CHILD_SA until we released the lock */ } else { - INIT(entry, - .child_sa = child_sa, - .peer_cfg = peer->get_ref(peer), - ); - this->traps->insert_last(this->traps, entry); reqid = child_sa->get_reqid(child_sa); - } - this->installing->set(this->installing, NULL); - this->lock->unlock(this->lock); - - if (status != SUCCESS) - { - child_sa->destroy(child_sa); + this->lock->write_lock(this->lock); + entry->child_sa = child_sa; + this->lock->unlock(this->lock); } if (found) { @@ -221,7 +228,8 @@ METHOD(trap_manager_t, uninstall, bool, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->child_sa->get_reqid(entry->child_sa) == reqid) + if (entry->child_sa && + entry->child_sa->get_reqid(entry->child_sa) == reqid) { this->traps->remove_at(this->traps, enumerator); found = entry; @@ -236,7 +244,6 @@ METHOD(trap_manager_t, uninstall, bool, DBG1(DBG_CFG, "trap %d not found to uninstall", reqid); return FALSE; } - destroy_entry(found); return TRUE; } @@ -247,6 +254,10 @@ METHOD(trap_manager_t, uninstall, bool, static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg, void *none, child_sa_t **child_sa) { + if (!(*entry)->child_sa) + { /* skip entries that are currently being installed */ + return FALSE; + } if (peer_cfg) { *peer_cfg = (*entry)->peer_cfg; @@ -271,28 +282,24 @@ METHOD(trap_manager_t, find_reqid, u_int32_t, private_trap_manager_t *this, child_cfg_t *child) { enumerator_t *enumerator; - child_cfg_t *current; entry_t *entry; u_int32_t reqid = 0; - if (this->installing->get(this->installing)) - { /* current thread holds the lock */ - return reqid; - } this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - current = entry->child_sa->get_config(entry->child_sa); - if (streq(current->get_name(current), child->get_name(child))) + if (streq(entry->name, child->get_name(child))) { - reqid = entry->child_sa->get_reqid(entry->child_sa); + if (entry->child_sa) + { + reqid = entry->child_sa->get_reqid(entry->child_sa); + } break; } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); - return reqid; } @@ -310,7 +317,8 @@ METHOD(trap_manager_t, acquire, void, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->child_sa->get_reqid(entry->child_sa) == reqid) + if (entry->child_sa && + entry->child_sa->get_reqid(entry->child_sa) == reqid) { found = entry; break; @@ -365,6 +373,7 @@ METHOD(trap_manager_t, acquire, void, else { ike_sa->destroy(ike_sa); + charon->bus->set_sa(charon->bus, NULL); } } peer->destroy(peer); @@ -445,7 +454,6 @@ METHOD(trap_manager_t, destroy, void, { charon->bus->remove_listener(charon->bus, &this->listener.listener); this->traps->destroy_function(this->traps, (void*)destroy_entry); - this->installing->destroy(this->installing); this->lock->destroy(this->lock); free(this); } @@ -476,7 +484,6 @@ trap_manager_t *trap_manager_create(void) }, .traps = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), - .installing = thread_value_create(NULL), ); charon->bus->add_listener(charon->bus, &this->listener.listener); |