summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/trap_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/trap_manager.c')
-rw-r--r--src/libcharon/sa/trap_manager.c178
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;