]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/libcharon/sa/trap_manager.c
child-sa: Pass default interface ID inherited from IKE_SA
[thirdparty/strongswan.git] / src / libcharon / sa / trap_manager.c
index ab638ff3e0e9d0db9f14d1f5d4396339f65315da..2bc531b387dd2a0d4a6d91dd5422f33627ee27a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2011-2012 Tobias Brunner
+ * Copyright (C) 2011-2017 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * HSR 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
 
 #include "trap_manager.h"
 
-#include <hydra.h>
 #include <daemon.h>
+#include <threading/mutex.h>
 #include <threading/rwlock.h>
+#include <threading/rwlock_condvar.h>
 #include <collections/linked_list.h>
 
+#define INSTALL_DISABLED ((u_int)~0)
 
 typedef struct private_trap_manager_t private_trap_manager_t;
 typedef struct trap_listener_t trap_listener_t;
@@ -65,33 +67,123 @@ struct private_trap_manager_t {
         * listener to track acquiring IKE_SAs
         */
        trap_listener_t listener;
+
+       /**
+        * list of acquires we currently handle
+        */
+       linked_list_t *acquires;
+
+       /**
+        * mutex for list of acquires
+        */
+       mutex_t *mutex;
+
+       /**
+        * number of threads currently installing trap policies, or INSTALL_DISABLED
+        */
+       u_int installing;
+
+       /**
+        * condvar to signal trap policy installation
+        */
+       rwlock_condvar_t *condvar;
+
+       /**
+        * Whether to ignore traffic selectors from acquires
+        */
+       bool ignore_acquire_ts;
 };
 
 /**
  * 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 */
+       /** ref to instantiated CHILD_SA (i.e the trap policy) */
        child_sa_t *child_sa;
-       /** TRUE if an acquire is pending */
-       bool pending;
+       /** TRUE in case of wildcard Transport Mode SA */
+       bool wildcard;
+} entry_t;
+
+/**
+ * A handled acquire
+ */
+typedef struct {
        /** pending IKE_SA connecting upon acquire */
        ike_sa_t *ike_sa;
-} entry_t;
+       /** reqid of pending trap policy */
+       uint32_t reqid;
+       /** destination address (wildcard case) */
+       host_t *dst;
+} acquire_t;
 
 /**
  * actually uninstall and destroy an installed entry
  */
-static void destroy_entry(entry_t *entry)
+static void destroy_entry(entry_t *this)
 {
-       entry->child_sa->destroy(entry->child_sa);
-       entry->peer_cfg->destroy(entry->peer_cfg);
-       free(entry);
+       this->child_sa->destroy(this->child_sa);
+       this->peer_cfg->destroy(this->peer_cfg);
+       free(this->name);
+       free(this);
 }
 
-METHOD(trap_manager_t, install, u_int32_t,
+/**
+ * destroy a cached acquire entry
+ */
+static void destroy_acquire(acquire_t *this)
+{
+       DESTROY_IF(this->dst);
+       free(this);
+}
+
+CALLBACK(acquire_by_reqid, bool,
+       acquire_t *this, va_list args)
+{
+       uint32_t reqid;
+
+       VA_ARGS_VGET(args, reqid);
+       return this->reqid == reqid;
+}
+
+CALLBACK(acquire_by_dst, bool,
+       acquire_t *this, va_list args)
+{
+       host_t *dst;
+
+       VA_ARGS_VGET(args, dst);
+       return this->dst && this->dst->ip_equals(this->dst, dst);
+}
+
+/**
+ * Check if any remote TS are dynamic
+ */
+static bool dynamic_remote_ts(child_cfg_t *child)
+{
+       enumerator_t *enumerator;
+       linked_list_t *other_ts;
+       traffic_selector_t *ts;
+       bool found = FALSE;
+
+       other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE);
+       enumerator = other_ts->create_enumerator(other_ts);
+       while (enumerator->enumerate(enumerator, &ts))
+       {
+               if (ts->is_dynamic(ts))
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+       return found;
+}
+
+METHOD(trap_manager_t, install, bool,
        private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
 {
        entry_t *entry, *found = NULL;
@@ -101,115 +193,181 @@ METHOD(trap_manager_t, install, u_int32_t,
        linked_list_t *my_ts, *other_ts, *list;
        enumerator_t *enumerator;
        status_t status;
-       u_int32_t reqid = 0;
+       linked_list_t *proposals;
+       proposal_t *proposal;
+       protocol_id_t proto = PROTO_ESP;
+       bool result = FALSE, wildcard = FALSE;
 
        /* try to resolve addresses */
        ike_cfg = peer->get_ike_cfg(peer);
-       other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg, NULL),
-                                                                0, ike_cfg->get_other_port(ike_cfg));
-       if (!other || other->is_anyaddr(other))
+       other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
+       if (other && other->is_anyaddr(other) &&
+               child->get_mode(child) == MODE_TRANSPORT)
        {
-               DESTROY_IF(other);
-               DBG1(DBG_CFG, "installing trap failed, remote address unknown");
-               return 0;
+               /* allow wildcard for Transport Mode SAs */
+               me = host_create_any(other->get_family(other));
+               wildcard = TRUE;
        }
-       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))
+       else if (other && other->is_anyaddr(other))
        {
-               DESTROY_IF(me);
-               me = hydra->kernel_interface->get_source_addr(
-                                                                       hydra->kernel_interface, other, NULL);
+               other->destroy(other);
+               DBG1(DBG_CFG, "installing trap failed, remote address unknown");
+               return FALSE;
+       }
+       else
+       {       /* depending on the traffic selectors we don't really need a remote
+                * host yet, but we might fail later if no IP can be resolved */
+               if (!other && dynamic_remote_ts(child))
+               {       /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used,
+                        * which is probably not what users expect*/
+                       DBG1(DBG_CFG, "installing trap failed, remote address unknown with "
+                                "dynamic traffic selector");
+                       return FALSE;
+               }
+               me = ike_cfg->resolve_me(ike_cfg, other ? other->get_family(other)
+                                                                                               : AF_UNSPEC);
+               if (!other)
+               {
+                       other = host_create_any(me ? me->get_family(me) : AF_INET);
+               }
+               other->set_port(other, ike_cfg->get_other_port(ike_cfg));
+               if ((!me || me->is_anyaddr(me)) && !other->is_anyaddr(other))
+               {
+                       DESTROY_IF(me);
+                       me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
+               }
                if (!me)
                {
-                       DBG1(DBG_CFG, "installing trap failed, local address unknown");
-                       other->destroy(other);
-                       return 0;
+                       me = host_create_any(other->get_family(other));
                }
                me->set_port(me, ike_cfg->get_my_port(ike_cfg));
        }
 
        this->lock->write_lock(this->lock);
+       if (this->installing == INSTALL_DISABLED)
+       {       /* flush() has been called */
+               this->lock->unlock(this->lock);
+               other->destroy(other);
+               me->destroy(me);
+               return FALSE;
+       }
        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)) &&
+                       streq(entry->peer_cfg->get_name(entry->peer_cfg),
+                                 peer->get_name(peer)))
                {
-                       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);
-       this->lock->unlock(this->lock);
 
        if (found)
-       {       /* config might have changed so update everything */
-               DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'",
-                        child->get_name(child));
-               reqid = found->child_sa->get_reqid(found->child_sa);
+       {
+               if (!found->child_sa)
+               {
+                       DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name);
+                       this->lock->unlock(this->lock);
+                       other->destroy(other);
+                       me->destroy(me);
+                       return FALSE;
+               }
+               /* config might have changed so update everything */
+               DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name);
        }
 
+       INIT(entry,
+               .name = strdup(child->get_name(child)),
+               .peer_cfg = peer->get_ref(peer),
+               .wildcard = wildcard,
+       );
+       this->traps->insert_first(this->traps, entry);
+       this->installing++;
+       /* 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);
+       child_sa_create_t child_data = {
+               /* TODO: no reason to allocate unique interface IDs, there is currently
+                * no event to use them upon trap installation and we'd also have to
+                * pass them in a later initiate() call */
+               .if_id_in_def = peer->get_if_id(peer, TRUE),
+               .if_id_out_def = peer->get_if_id(peer, FALSE),
+       };
+       child_sa = child_sa_create(me, other, child, &child_data);
 
        list = linked_list_create_with_items(me, NULL);
-       my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
+       my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE);
        list->destroy_offset(list, offsetof(host_t, destroy));
 
        list = linked_list_create_with_items(other, NULL);
-       other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
+       other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE);
        list->destroy_offset(list, offsetof(host_t, destroy));
 
-       /* while we don't know the finally negotiated protocol (ESP|AH), we
-        * 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);
+       /* We don't know the finally negotiated protocol (ESP|AH), we install
+        * the SA with the protocol of the first proposal */
+       proposals = child->get_proposals(child, TRUE);
+       if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
+       {
+               proto = proposal->get_protocol(proposal);
+       }
+       proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+       child_sa->set_protocol(child_sa, proto);
        child_sa->set_mode(child_sa, child->get_mode(child));
-       status = child_sa->add_policies(child_sa, my_ts, other_ts);
+       child_sa->set_policies(child_sa, my_ts, other_ts);
+       status = child_sa->install_policies(child_sa);
        my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
        other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
        if (status != SUCCESS)
        {
                DBG1(DBG_CFG, "installing trap failed");
-               reqid = 0;
-               /* hold off destroying the CHILD_SA until we released the lock */
+               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);
        }
        else
        {
-               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);
+               entry->child_sa = child_sa;
                this->lock->unlock(this->lock);
-               reqid = child_sa->get_reqid(child_sa);
-       }
-
-       if (status != SUCCESS)
-       {
-               child_sa->destroy(child_sa);
+               result = TRUE;
        }
        if (found)
        {
                destroy_entry(found);
        }
-       return reqid;
+       this->lock->write_lock(this->lock);
+       /* do this at the end, so entries created temporarily are also destroyed */
+       this->installing--;
+       this->condvar->signal(this->condvar);
+       this->lock->unlock(this->lock);
+       return result;
 }
 
 METHOD(trap_manager_t, uninstall, bool,
-       private_trap_manager_t *this, u_int32_t reqid)
+       private_trap_manager_t *this, char *peer, char *child)
 {
        enumerator_t *enumerator;
        entry_t *entry, *found = NULL;
 
        this->lock->write_lock(this->lock);
+       while (this->installing)
+       {
+               this->condvar->wait(this->condvar, this->lock);
+       }
        enumerator = this->traps->create_enumerator(this->traps);
        while (enumerator->enumerate(enumerator, &entry))
        {
-               if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
+               if (streq(entry->name, child) &&
+                  (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg))))
                {
                        this->traps->remove_at(this->traps, enumerator);
                        found = entry;
@@ -221,29 +379,38 @@ METHOD(trap_manager_t, uninstall, bool,
 
        if (!found)
        {
-               DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
                return FALSE;
        }
-
        destroy_entry(found);
        return TRUE;
 }
 
-/**
- * convert enumerated entries to peer_cfg, child_sa
- */
-static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg,
-                                               void *none, child_sa_t **child_sa)
+CALLBACK(trap_filter, bool,
+       rwlock_t *lock, enumerator_t *orig, va_list args)
 {
-       if (peer_cfg)
-       {
-               *peer_cfg = (*entry)->peer_cfg;
-       }
-       if (child_sa)
+       entry_t *entry;
+       peer_cfg_t **peer_cfg;
+       child_sa_t **child_sa;
+
+       VA_ARGS_VGET(args, peer_cfg, child_sa);
+
+       while (orig->enumerate(orig, &entry))
        {
-               *child_sa = (*entry)->child_sa;
+               if (!entry->child_sa)
+               {       /* skip entries that are currently being installed */
+                       continue;
+               }
+               if (peer_cfg)
+               {
+                       *peer_cfg = entry->peer_cfg;
+               }
+               if (child_sa)
+               {
+                       *child_sa = entry->child_sa;
+               }
+               return TRUE;
        }
-       return TRUE;
+       return FALSE;
 }
 
 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
@@ -251,50 +418,29 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*,
 {
        this->lock->read_lock(this->lock);
        return enumerator_create_filter(this->traps->create_enumerator(this->traps),
-                                                                       (void*)trap_filter, this->lock,
+                                                                       trap_filter, this->lock,
                                                                        (void*)this->lock->unlock);
 }
 
-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;
-
-       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)))
-               {
-                       reqid = entry->child_sa->get_reqid(entry->child_sa);
-                       break;
-               }
-       }
-       enumerator->destroy(enumerator);
-       this->lock->unlock(this->lock);
-
-       return reqid;
-}
-
 METHOD(trap_manager_t, acquire, void,
-       private_trap_manager_t *this, u_int32_t reqid,
+       private_trap_manager_t *this, uint32_t reqid,
        traffic_selector_t *src, traffic_selector_t *dst)
 {
        enumerator_t *enumerator;
        entry_t *entry, *found = NULL;
+       acquire_t *acquire;
        peer_cfg_t *peer;
        child_cfg_t *child;
        ike_sa_t *ike_sa;
+       host_t *host;
+       bool wildcard, ignore = FALSE;
 
        this->lock->read_lock(this->lock);
        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;
@@ -304,11 +450,52 @@ METHOD(trap_manager_t, acquire, void,
 
        if (!found)
        {
-               DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
+               DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
                this->lock->unlock(this->lock);
                return;
        }
-       if (!cas_bool(&found->pending, FALSE, TRUE))
+       reqid = found->child_sa->get_reqid(found->child_sa);
+       wildcard = found->wildcard;
+
+       this->mutex->lock(this->mutex);
+       if (wildcard)
+       {       /* for wildcard acquires we check that we don't have a pending acquire
+                * with the same peer */
+               uint8_t mask;
+
+               dst->to_subnet(dst, &host, &mask);
+               if (this->acquires->find_first(this->acquires, acquire_by_dst,
+                                                                         (void**)&acquire, host))
+               {
+                       host->destroy(host);
+                       ignore = TRUE;
+               }
+               else
+               {
+                       INIT(acquire,
+                               .dst = host,
+                               .reqid = reqid,
+                       );
+                       this->acquires->insert_last(this->acquires, acquire);
+               }
+       }
+       else
+       {
+               if (this->acquires->find_first(this->acquires, acquire_by_reqid,
+                                                                         (void**)&acquire, reqid))
+               {
+                       ignore = TRUE;
+               }
+               else
+               {
+                       INIT(acquire,
+                               .reqid = reqid,
+                       );
+                       this->acquires->insert_last(this->acquires, acquire);
+               }
+       }
+       this->mutex->unlock(this->mutex);
+       if (ignore)
        {
                DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
                this->lock->unlock(this->lock);
@@ -317,40 +504,74 @@ METHOD(trap_manager_t, acquire, void,
        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(
+       if (wildcard)
+       {       /* the peer config would match IKE_SAs with other peers */
+               ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                                                       peer->get_ike_version(peer), TRUE);
+               if (ike_sa)
+               {
+                       ike_cfg_t *ike_cfg;
+                       uint16_t port;
+                       uint8_t mask;
+
+                       ike_sa->set_peer_cfg(ike_sa, peer);
+                       ike_cfg = ike_sa->get_ike_cfg(ike_sa);
+
+                       port = ike_cfg->get_other_port(ike_cfg);
+                       dst->to_subnet(dst, &host, &mask);
+                       host->set_port(host, port);
+                       ike_sa->set_other_host(ike_sa, host);
+
+                       port = ike_cfg->get_my_port(ike_cfg);
+                       src->to_subnet(src, &host, &mask);
+                       host->set_port(host, port);
+                       ike_sa->set_my_host(ike_sa, host);
+
+                       charon->bus->set_sa(charon->bus, ike_sa);
+               }
+       }
+       else
+       {
+               ike_sa = charon->ike_sa_manager->checkout_by_config(
                                                                                        charon->ike_sa_manager, peer);
+       }
        if (ike_sa)
        {
                if (ike_sa->get_peer_cfg(ike_sa) == NULL)
                {
                        ike_sa->set_peer_cfg(ike_sa, peer);
                }
-               if (ike_sa->get_version(ike_sa) == IKEV1)
+               if (this->ignore_acquire_ts || 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;
                }
+
+               this->mutex->lock(this->mutex);
+               acquire->ike_sa = ike_sa;
+               this->mutex->unlock(this->mutex);
+
                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)
-                       {
-                               found->ike_sa = ike_sa;
-                       }
-                       this->lock->unlock(this->lock);
                        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                }
                else
                {
-                       ike_sa->destroy(ike_sa);
+                       charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+                                                                                                               ike_sa);
                }
        }
+       else
+       {
+               this->mutex->lock(this->mutex);
+               this->acquires->remove(this->acquires, acquire, NULL);
+               this->mutex->unlock(this->mutex);
+               destroy_acquire(acquire);
+               child->destroy(child);
+       }
        peer->destroy(peer);
 }
 
@@ -361,26 +582,33 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
                                         child_sa_t *child_sa)
 {
        enumerator_t *enumerator;
-       entry_t *entry;
+       acquire_t *acquire;
 
-       this->lock->read_lock(this->lock);
-       enumerator = this->traps->create_enumerator(this->traps);
-       while (enumerator->enumerate(enumerator, &entry))
+       this->mutex->lock(this->mutex);
+       enumerator = this->acquires->create_enumerator(this->acquires);
+       while (enumerator->enumerate(enumerator, &acquire))
        {
-               if (entry->ike_sa != ike_sa)
+               if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
                {
                        continue;
                }
-               if (child_sa && child_sa->get_reqid(child_sa) !=
-                                                                       entry->child_sa->get_reqid(entry->child_sa))
+               if (child_sa)
                {
-                       continue;
+                       if (acquire->dst)
+                       {
+                               /* since every wildcard acquire results in a separate IKE_SA
+                                * there is no need to compare the destination address */
+                       }
+                       else if (child_sa->get_reqid(child_sa) != acquire->reqid)
+                       {
+                               continue;
+                       }
                }
-               entry->ike_sa = NULL;
-               entry->pending = FALSE;
+               this->acquires->remove_at(this->acquires, enumerator);
+               destroy_acquire(acquire);
        }
        enumerator->destroy(enumerator);
-       this->lock->unlock(this->lock);
+       this->mutex->unlock(this->mutex);
 }
 
 METHOD(listener_t, ike_state_change, bool,
@@ -414,14 +642,15 @@ METHOD(listener_t, child_state_change, bool,
 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;
+       while (this->installing)
+       {
+               this->condvar->wait(this->condvar, this->lock);
+       }
+       this->traps->destroy_function(this->traps, (void*)destroy_entry);
        this->traps = linked_list_create();
+       this->installing = INSTALL_DISABLED;
        this->lock->unlock(this->lock);
-       traps->destroy_function(traps, (void*)destroy_entry);
 }
 
 METHOD(trap_manager_t, destroy, void,
@@ -429,6 +658,9 @@ 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->acquires->destroy_function(this->acquires, (void*)destroy_acquire);
+       this->condvar->destroy(this->condvar);
+       this->mutex->destroy(this->mutex);
        this->lock->destroy(this->lock);
        free(this);
 }
@@ -445,7 +677,6 @@ trap_manager_t *trap_manager_create(void)
                        .install = _install,
                        .uninstall = _uninstall,
                        .create_enumerator = _create_enumerator,
-                       .find_reqid = _find_reqid,
                        .acquire = _acquire,
                        .flush = _flush,
                        .destroy = _destroy,
@@ -458,7 +689,12 @@ trap_manager_t *trap_manager_create(void)
                        },
                },
                .traps = linked_list_create(),
+               .acquires = linked_list_create(),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+               .condvar = rwlock_condvar_create(),
+               .ignore_acquire_ts = lib->settings->get_bool(lib->settings,
+                                                                               "%s.ignore_acquire_ts", FALSE, lib->ns),
        );
        charon->bus->add_listener(charon->bus, &this->listener.listener);