Use the vararg list constructor in quick mode task
[strongswan.git] / src / libcharon / sa / trap_manager.c
index 878170c..b3d9e15 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2011 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -15,6 +16,7 @@
 
 #include "trap_manager.h"
 
+#include <hydra.h>
 #include <daemon.h>
 #include <threading/rwlock.h>
 #include <utils/linked_list.h>
@@ -73,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;
 
 /**
@@ -87,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;
@@ -126,20 +127,20 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer,
 
        /* try to resolve addresses */
        ike_cfg = peer->get_ike_cfg(peer);
-       other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg),
+       other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg, NULL),
                                                                 0, ike_cfg->get_other_port(ike_cfg));
-       if (!other)
+       if (!other || other->is_anyaddr(other))
        {
                DBG1(DBG_CFG, "installing trap failed, remote address unknown");
                return 0;
        }
-       me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg),
+       me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg, NULL),
                                        other->get_family(other), ike_cfg->get_my_port(ike_cfg));
        if (!me || me->is_anyaddr(me))
        {
                DESTROY_IF(me);
-               me = charon->kernel_interface->get_source_addr(
-                                                                       charon->kernel_interface, other, NULL);
+               me = hydra->kernel_interface->get_source_addr(
+                                                                       hydra->kernel_interface, other, NULL);
                if (!me)
                {
                        DBG1(DBG_CFG, "installing trap failed, local address unknown");
@@ -157,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);
@@ -172,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);
@@ -184,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;
@@ -233,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),
@@ -244,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;
@@ -271,26 +266,45 @@ 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)
        {
-               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->get_version(ike_sa) == IKEV1)
+               {       /* in IKEv1, don't prepend the acquiring packet TS, as we only
+                        * have a single TS that we can establish in a Quick Mode. */
+                       src = dst = NULL;
+               }
                if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
                {
-                       found->pending = ike_sa;
+                       /* make sure the entry is still there */
+                       this->lock->read_lock(this->lock);
+                       if (this->traps->find_first(this->traps, NULL,
+                                                                               (void**)&found) == SUCCESS)
+                       {
+                               found->ike_sa = ike_sa;
+                       }
+                       this->lock->unlock(this->lock);
                        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                }
                else
@@ -299,7 +313,7 @@ static void acquire(private_trap_manager_t *this, u_int32_t reqid,
                                                                                                charon->ike_sa_manager, ike_sa);
                }
        }
-       this->lock->unlock(this->lock);
+       peer->destroy(peer);
 }
 
 /**
@@ -315,7 +329,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;
                }
@@ -324,17 +338,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)
        {
@@ -346,11 +358,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)
        {
@@ -363,14 +373,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);
 }
@@ -378,24 +398,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;