diff options
Diffstat (limited to 'src/libcharon/sa/trap_manager.c')
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 178 |
1 files changed, 97 insertions, 81 deletions
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index f91eff077..86d9f4c22 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -74,8 +75,10 @@ typedef struct { peer_cfg_t *peer_cfg; /** ref to instanciated CHILD_SA */ child_sa_t *child_sa; + /** TRUE if an acquire is pending */ + bool pending; /** pending IKE_SA connecting upon acquire */ - ike_sa_t *pending; + ike_sa_t *ike_sa; } entry_t; /** @@ -88,11 +91,8 @@ static void destroy_entry(entry_t *entry) free(entry); } -/** - * Implementation of trap_manager_t.install - */ -static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, - child_cfg_t *child) +METHOD(trap_manager_t, install, u_int32_t, + private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) { entry_t *entry; ike_cfg_t *ike_cfg; @@ -158,8 +158,8 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, other->destroy(other); /* while we don't know the finally negotiated protocol (ESP|AH), we - * could iterate all proposals for a best guest (TODO). But as we - * support ESP only for now, we set here. */ + * could iterate all proposals for a best guess (TODO). But as we + * support ESP only for now, we set it here. */ child_sa->set_protocol(child_sa, PROTO_ESP); child_sa->set_mode(child_sa, child->get_mode(child)); status = child_sa->add_policies(child_sa, my_ts, other_ts); @@ -173,10 +173,10 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, } reqid = child_sa->get_reqid(child_sa); - entry = malloc_thing(entry_t); - entry->child_sa = child_sa; - entry->peer_cfg = peer->get_ref(peer); - entry->pending = NULL; + INIT(entry, + .child_sa = child_sa, + .peer_cfg = peer->get_ref(peer), + ); this->lock->write_lock(this->lock); this->traps->insert_last(this->traps, entry); @@ -185,10 +185,8 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, return reqid; } -/** - * Implementation of trap_manager_t.uninstall - */ -static bool uninstall(private_trap_manager_t *this, u_int32_t reqid) +METHOD(trap_manager_t, uninstall, bool, + private_trap_manager_t *this, u_int32_t reqid) { enumerator_t *enumerator; entry_t *entry, *found = NULL; @@ -234,10 +232,8 @@ static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg, return TRUE; } -/** - * Implementation of trap_manager_t.create_enumerator - */ -static enumerator_t* create_enumerator(private_trap_manager_t *this) +METHOD(trap_manager_t, create_enumerator, enumerator_t*, + private_trap_manager_t *this) { this->lock->read_lock(this->lock); return enumerator_create_filter(this->traps->create_enumerator(this->traps), @@ -245,11 +241,9 @@ static enumerator_t* create_enumerator(private_trap_manager_t *this) (void*)this->lock->unlock); } -/** - * Implementation of trap_manager_t.acquire - */ -static void acquire(private_trap_manager_t *this, u_int32_t reqid, - traffic_selector_t *src, traffic_selector_t *dst) +METHOD(trap_manager_t, acquire, void, + private_trap_manager_t *this, u_int32_t reqid, + traffic_selector_t *src, traffic_selector_t *dst) { enumerator_t *enumerator; entry_t *entry, *found = NULL; @@ -272,35 +266,46 @@ static void acquire(private_trap_manager_t *this, u_int32_t reqid, if (!found) { DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); + this->lock->unlock(this->lock); + return; } - else if (found->pending) + if (!cas_bool(&found->pending, FALSE, TRUE)) { DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); + this->lock->unlock(this->lock); + return; } - else + peer = found->peer_cfg->get_ref(found->peer_cfg); + child = found->child_sa->get_config(found->child_sa); + child = child->get_ref(child); + reqid = found->child_sa->get_reqid(found->child_sa); + /* don't hold the lock while checking out the IKE_SA */ + this->lock->unlock(this->lock); + + ike_sa = charon->ike_sa_manager->checkout_by_config( + charon->ike_sa_manager, peer); + if (ike_sa->get_peer_cfg(ike_sa) == NULL) { - child = found->child_sa->get_config(found->child_sa); - peer = found->peer_cfg; - ike_sa = charon->ike_sa_manager->checkout_by_config( - charon->ike_sa_manager, peer); - if (ike_sa->get_peer_cfg(ike_sa) == NULL) - { - ike_sa->set_peer_cfg(ike_sa, peer); - } - child->get_ref(child); - reqid = found->child_sa->get_reqid(found->child_sa); - if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) - { - found->pending = ike_sa; - charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - } - else + ike_sa->set_peer_cfg(ike_sa, peer); + } + if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) + { + /* make sure the entry is still there */ + this->lock->read_lock(this->lock); + if (this->traps->find_first(this->traps, NULL, + (void**)&found) == SUCCESS) { - charon->ike_sa_manager->checkin_and_destroy( - charon->ike_sa_manager, ike_sa); + found->ike_sa = ike_sa; } + this->lock->unlock(this->lock); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } - this->lock->unlock(this->lock); + else + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + peer->destroy(peer); } /** @@ -316,7 +321,7 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->pending != ike_sa) + if (entry->ike_sa != ike_sa) { continue; } @@ -325,17 +330,15 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, { continue; } - entry->pending = NULL; + entry->ike_sa = NULL; + entry->pending = FALSE; } enumerator->destroy(enumerator); this->lock->unlock(this->lock); } -/** - * Implementation of listener_t.ike_state_change - */ -static bool ike_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, - ike_sa_state_t state) +METHOD(listener_t, ike_state_change, bool, + trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state) { switch (state) { @@ -347,11 +350,9 @@ static bool ike_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, } } -/** - * Implementation of listener_t.child_state_change - */ -static bool child_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, - child_sa_t *child_sa, child_sa_state_t state) +METHOD(listener_t, child_state_change, bool, + trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa, + child_sa_state_t state) { switch (state) { @@ -364,14 +365,24 @@ static bool child_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, } } -/** - * Implementation of trap_manager_t.destroy. - */ -static void destroy(private_trap_manager_t *this) +METHOD(trap_manager_t, flush, void, + private_trap_manager_t *this) +{ + linked_list_t *traps; + /* since destroying the CHILD_SA results in events which require a read + * lock we cannot destroy the list while holding the write lock */ + this->lock->write_lock(this->lock); + traps = this->traps; + this->traps = linked_list_create(); + this->lock->unlock(this->lock); + traps->destroy_function(traps, (void*)destroy_entry); +} + +METHOD(trap_manager_t, destroy, void, + private_trap_manager_t *this) { charon->bus->remove_listener(charon->bus, &this->listener.listener); - this->traps->invoke_function(this->traps, (void*)destroy_entry); - this->traps->destroy(this->traps); + this->traps->destroy_function(this->traps, (void*)destroy_entry); this->lock->destroy(this->lock); free(this); } @@ -379,24 +390,29 @@ static void destroy(private_trap_manager_t *this) /** * See header */ -trap_manager_t *trap_manager_create() +trap_manager_t *trap_manager_create(void) { - private_trap_manager_t *this = malloc_thing(private_trap_manager_t); - - this->public.install = (u_int(*)(trap_manager_t*, peer_cfg_t *peer, child_cfg_t *child))install; - this->public.uninstall = (bool(*)(trap_manager_t*, u_int32_t id))uninstall; - this->public.create_enumerator = (enumerator_t*(*)(trap_manager_t*))create_enumerator; - this->public.acquire = (void(*)(trap_manager_t*, u_int32_t reqid, traffic_selector_t *src, traffic_selector_t *dst))acquire; - this->public.destroy = (void(*)(trap_manager_t*))destroy; - - this->traps = linked_list_create(); - this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - - /* register listener for IKE state changes */ - this->listener.traps = this; - memset(&this->listener.listener, 0, sizeof(listener_t)); - this->listener.listener.ike_state_change = (void*)ike_state_change; - this->listener.listener.child_state_change = (void*)child_state_change; + private_trap_manager_t *this; + + INIT(this, + .public = { + .install = _install, + .uninstall = _uninstall, + .create_enumerator = _create_enumerator, + .acquire = _acquire, + .flush = _flush, + .destroy = _destroy, + }, + .listener = { + .traps = this, + .listener = { + .ike_state_change = _ike_state_change, + .child_state_change = _child_state_change, + }, + }, + .traps = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); charon->bus->add_listener(charon->bus, &this->listener.listener); return &this->public; |