libhydra: Move kernel interface to libcharon
[strongswan.git] / src / libcharon / sa / child_sa.c
index 9c3876a..56b7cb5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2011 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005 Jan Hutter
@@ -23,7 +23,6 @@
 #include <string.h>
 #include <time.h>
 
-#include <hydra.h>
 #include <daemon.h>
 #include <collections/array.h>
 
@@ -34,6 +33,8 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
        "INSTALLED",
        "UPDATING",
        "REKEYING",
+       "REKEYED",
+       "RETRYING",
        "DELETING",
        "DESTROYING",
 );
@@ -100,6 +101,21 @@ struct private_child_sa_t {
        u_int32_t reqid;
 
        /**
+        * Did we allocate/confirm and must release the reqid?
+        */
+       bool reqid_allocated;
+
+       /**
+        * Is the reqid statically configured
+        */
+       bool static_reqid;
+
+       /*
+        * Unique CHILD_SA identifier
+        */
+       u_int32_t unique_id;
+
+       /**
         * inbound mark used for this child_sa
         */
        mark_t mark_in;
@@ -120,6 +136,11 @@ struct private_child_sa_t {
        time_t expire_time;
 
        /**
+        * absolute time when SA has been installed
+        */
+       time_t install_time;
+
+       /**
         * state of the CHILD_SA
         */
        child_sa_state_t state;
@@ -223,6 +244,12 @@ METHOD(child_sa_t, get_reqid, u_int32_t,
        return this->reqid;
 }
 
+METHOD(child_sa_t, get_unique_id, u_int32_t,
+       private_child_sa_t *this)
+{
+       return this->unique_id;
+}
+
 METHOD(child_sa_t, get_config, child_cfg_t*,
           private_child_sa_t *this)
 {
@@ -385,8 +412,14 @@ METHOD(enumerator_t, policy_enumerate, bool,
                {       /* protocol mismatch */
                        continue;
                }
-               *my_out = this->ts;
-               *other_out = other_ts;
+               if (my_out)
+               {
+                       *my_out = this->ts;
+               }
+               if (other_out)
+               {
+                       *other_out = other_ts;
+               }
                return TRUE;
        }
        return FALSE;
@@ -435,10 +468,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
        {
                if (this->my_spi)
                {
-                       status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
-                                                       this->other_addr, this->my_addr, this->my_spi,
-                                                       proto_ike2ip(this->protocol), this->mark_in,
-                                                       &bytes, &packets, &time);
+                       status = charon->kernel->query_sa(charon->kernel, this->other_addr,
+                                                                       this->my_addr, this->my_spi,
+                                                                       proto_ike2ip(this->protocol), this->mark_in,
+                                                                       &bytes, &packets, &time);
                        if (status == SUCCESS)
                        {
                                if (bytes > this->my_usebytes)
@@ -459,10 +492,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
        {
                if (this->other_spi)
                {
-                       status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
-                                                       this->my_addr, this->other_addr, this->other_spi,
-                                                       proto_ike2ip(this->protocol), this->mark_out,
-                                                       &bytes, &packets, &time);
+                       status = charon->kernel->query_sa(charon->kernel, this->my_addr,
+                                                               this->other_addr, this->other_spi,
+                                                               proto_ike2ip(this->protocol), this->mark_out,
+                                                               &bytes, &packets, &time);
                        if (status == SUCCESS)
                        {
                                if (bytes > this->other_usebytes)
@@ -498,15 +531,15 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
 
                if (inbound)
                {
-                       if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
-                                               other_ts, my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS)
+                       if (charon->kernel->query_policy(charon->kernel, other_ts,
+                                                       my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS)
                        {
                                last_use = max(last_use, in);
                        }
                        if (this->mode != MODE_TRANSPORT)
                        {
-                               if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
-                                               other_ts, my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS)
+                               if (charon->kernel->query_policy(charon->kernel, other_ts,
+                                                       my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS)
                                {
                                        last_use = max(last_use, fwd);
                                }
@@ -514,8 +547,8 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
                }
                else
                {
-                       if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
-                                               my_ts, other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS)
+                       if (charon->kernel->query_policy(charon->kernel, my_ts,
+                                                       other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS)
                        {
                                last_use = max(last_use, out);
                        }
@@ -586,13 +619,17 @@ METHOD(child_sa_t, get_lifetime, time_t,
        return hard ? this->expire_time : this->rekey_time;
 }
 
+METHOD(child_sa_t, get_installtime, time_t,
+       private_child_sa_t *this)
+{
+       return this->install_time;
+}
+
 METHOD(child_sa_t, alloc_spi, u_int32_t,
           private_child_sa_t *this, protocol_id_t protocol)
 {
-       if (hydra->kernel_interface->get_spi(hydra->kernel_interface,
-                                                                                this->other_addr, this->my_addr,
-                                                                                proto_ike2ip(protocol), this->reqid,
-                                                                                &this->my_spi) == SUCCESS)
+       if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr,
+                                                       proto_ike2ip(protocol), &this->my_spi) == SUCCESS)
        {
                /* if we allocate a SPI, but then are unable to establish the SA, we
                 * need to know the protocol family to delete the partial SA */
@@ -605,9 +642,8 @@ METHOD(child_sa_t, alloc_spi, u_int32_t,
 METHOD(child_sa_t, alloc_cpi, u_int16_t,
           private_child_sa_t *this)
 {
-       if (hydra->kernel_interface->get_cpi(hydra->kernel_interface,
-                                                                                this->other_addr, this->my_addr,
-                                                                                this->reqid, &this->my_cpi) == SUCCESS)
+       if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr,
+                                                               &this->my_cpi) == SUCCESS)
        {
                return this->my_cpi;
        }
@@ -621,7 +657,7 @@ METHOD(child_sa_t, install, status_t,
 {
        u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
        u_int16_t esn = NO_EXT_SEQ_NUMBERS;
-       traffic_selector_t *src_ts = NULL, *dst_ts = NULL;
+       linked_list_t *src_ts = NULL, *dst_ts = NULL;
        time_t now;
        lifetime_cfg_t *lifetime;
        u_int32_t tfc = 0;
@@ -669,6 +705,17 @@ METHOD(child_sa_t, install, status_t,
        this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
                                                                  &esn, NULL);
 
+       if (!this->reqid_allocated && !this->static_reqid)
+       {
+               status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts,
+                                                               this->mark_in, this->mark_out, &this->reqid);
+               if (status != SUCCESS)
+               {
+                       return status;
+               }
+               this->reqid_allocated = TRUE;
+       }
+
        lifetime = this->config->get_lifetime(this->config);
 
        now = time_monotonic(NULL);
@@ -693,26 +740,24 @@ METHOD(child_sa_t, install, status_t,
                lifetime->time.rekey = 0;
        }
 
-       /* BEET requires the bound address from the traffic selectors.
-        * TODO: We add just the first traffic selector for now, as the
-        * kernel accepts a single TS per SA only */
+       /* BEET requires the bound address from the traffic selectors */
        if (inbound)
        {
-               my_ts->get_first(my_ts, (void**)&dst_ts);
-               other_ts->get_first(other_ts, (void**)&src_ts);
+               dst_ts = my_ts;
+               src_ts = other_ts;
        }
        else
        {
-               my_ts->get_first(my_ts, (void**)&src_ts);
-               other_ts->get_first(other_ts, (void**)&dst_ts);
+               src_ts = my_ts;
+               dst_ts = other_ts;
        }
 
-       status = hydra->kernel_interface->add_sa(hydra->kernel_interface,
+       status = charon->kernel->add_sa(charon->kernel,
                                src, dst, spi, proto_ike2ip(this->protocol), this->reqid,
                                inbound ? this->mark_in : this->mark_out, tfc,
                                lifetime, enc_alg, encr, int_alg, integ, this->mode,
-                               this->ipcomp, cpi, initiator, this->encap, esn, update,
-                               src_ts, dst_ts);
+                               this->ipcomp, cpi, this->config->get_replay_window(this->config),
+                               initiator, this->encap, esn, inbound, update, src_ts, dst_ts);
 
        free(lifetime);
 
@@ -720,6 +765,61 @@ METHOD(child_sa_t, install, status_t,
 }
 
 /**
+ * Check kernel interface if policy updates are required
+ */
+static bool require_policy_update()
+{
+       kernel_feature_t f;
+
+       f = charon->kernel->get_features(charon->kernel);
+       return !(f & KERNEL_NO_POLICY_UPDATES);
+}
+
+/**
+ * Prepare SA config to install/delete policies
+ */
+static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa,
+                                                  ipsec_sa_cfg_t *other_sa)
+{
+       enumerator_t *enumerator;
+
+       *my_sa = (ipsec_sa_cfg_t){
+               .mode = this->mode,
+               .reqid = this->reqid,
+               .ipcomp = {
+                       .transform = this->ipcomp,
+               },
+       };
+       *other_sa = *my_sa;
+
+       my_sa->ipcomp.cpi = this->my_cpi;
+       other_sa->ipcomp.cpi = this->other_cpi;
+
+       if (this->protocol == PROTO_ESP)
+       {
+               my_sa->esp.use = TRUE;
+               my_sa->esp.spi = this->my_spi;
+               other_sa->esp.use = TRUE;
+               other_sa->esp.spi = this->other_spi;
+       }
+       else
+       {
+               my_sa->ah.use = TRUE;
+               my_sa->ah.spi = this->my_spi;
+               other_sa->ah.use = TRUE;
+               other_sa->ah.spi = this->other_spi;
+       }
+
+       enumerator = create_policy_enumerator(this);
+       while (enumerator->enumerate(enumerator, NULL, NULL))
+       {
+               my_sa->policy_count++;
+               other_sa->policy_count++;
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
  * Install 3 policies: out, in and forward
  */
 static status_t install_policies_internal(private_child_sa_t *this,
@@ -728,18 +828,18 @@ static status_t install_policies_internal(private_child_sa_t *this,
        ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
 {
        status_t status = SUCCESS;
-       status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+       status |= charon->kernel->add_policy(charon->kernel,
                                                        my_addr, other_addr, my_ts, other_ts,
                                                        POLICY_OUT, type, other_sa,
                                                        this->mark_out, priority);
 
-       status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+       status |= charon->kernel->add_policy(charon->kernel,
                                                        other_addr, my_addr, other_ts, my_ts,
                                                        POLICY_IN, type, my_sa,
                                                        this->mark_in, priority);
        if (this->mode != MODE_TRANSPORT)
        {
-               status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+               status |= charon->kernel->add_policy(charon->kernel,
                                                        other_addr, my_addr, other_ts, my_ts,
                                                        POLICY_FWD, type, my_sa,
                                                        this->mark_in, priority);
@@ -751,20 +851,22 @@ static status_t install_policies_internal(private_child_sa_t *this,
  * Delete 3 policies: out, in and forward
  */
 static void del_policies_internal(private_child_sa_t *this,
-               traffic_selector_t *my_ts, traffic_selector_t *other_ts,
-               policy_priority_t priority)
+       host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts,
+       traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa,
+       ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
 {
-       hydra->kernel_interface->del_policy(hydra->kernel_interface,
-                                               my_ts, other_ts, POLICY_OUT, this->reqid,
-                                               this->mark_out, priority);
-       hydra->kernel_interface->del_policy(hydra->kernel_interface,
-                                               other_ts, my_ts,  POLICY_IN, this->reqid,
-                                               this->mark_in, priority);
+
+       charon->kernel->del_policy(charon->kernel,
+                                               my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type,
+                                               other_sa, this->mark_out, priority);
+       charon->kernel->del_policy(charon->kernel,
+                                               other_addr, my_addr, other_ts, my_ts, POLICY_IN,
+                                               type, my_sa, this->mark_in, priority);
        if (this->mode != MODE_TRANSPORT)
        {
-               hydra->kernel_interface->del_policy(hydra->kernel_interface,
-                                               other_ts, my_ts, POLICY_FWD, this->reqid,
-                                               this->mark_in, priority);
+               charon->kernel->del_policy(charon->kernel,
+                                               other_addr, my_addr, other_ts, my_ts, POLICY_FWD,
+                                               type, my_sa, this->mark_in, priority);
        }
 }
 
@@ -776,6 +878,19 @@ METHOD(child_sa_t, add_policies, status_t,
        traffic_selector_t *my_ts, *other_ts;
        status_t status = SUCCESS;
 
+       if (!this->reqid_allocated && !this->static_reqid)
+       {
+               /* trap policy, get or confirm reqid */
+               status = charon->kernel->alloc_reqid(
+                                                       charon->kernel, my_ts_list, other_ts_list,
+                                                       this->mark_in, this->mark_out, &this->reqid);
+               if (status != SUCCESS)
+               {
+                       return status;
+               }
+               this->reqid_allocated = TRUE;
+       }
+
        /* apply traffic selectors */
        enumerator = my_ts_list->create_enumerator(my_ts_list);
        while (enumerator->enumerate(enumerator, &my_ts))
@@ -783,41 +898,22 @@ METHOD(child_sa_t, add_policies, status_t,
                array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts));
        }
        enumerator->destroy(enumerator);
+       array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL);
+
        enumerator = other_ts_list->create_enumerator(other_ts_list);
        while (enumerator->enumerate(enumerator, &other_ts))
        {
                array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts));
        }
        enumerator->destroy(enumerator);
+       array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL);
 
        if (this->config->install_policy(this->config))
        {
                policy_priority_t priority;
-               ipsec_sa_cfg_t my_sa = {
-                       .mode = this->mode,
-                       .reqid = this->reqid,
-                       .ipcomp = {
-                               .transform = this->ipcomp,
-                       },
-               }, other_sa = my_sa;
-
-               my_sa.ipcomp.cpi = this->my_cpi;
-               other_sa.ipcomp.cpi = this->other_cpi;
-
-               if (this->protocol == PROTO_ESP)
-               {
-                       my_sa.esp.use = TRUE;
-                       my_sa.esp.spi = this->my_spi;
-                       other_sa.esp.use = TRUE;
-                       other_sa.esp.spi = this->other_spi;
-               }
-               else
-               {
-                       my_sa.ah.use = TRUE;
-                       my_sa.ah.spi = this->my_spi;
-                       other_sa.ah.use = TRUE;
-                       other_sa.ah.spi = this->other_spi;
-               }
+               ipsec_sa_cfg_t my_sa, other_sa;
+
+               prepare_sa_cfg(this, &my_sa, &other_sa);
 
                /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD
                 * entry) we install a trap policy */
@@ -831,7 +927,7 @@ METHOD(child_sa_t, add_policies, status_t,
                {
                        /* install outbound drop policy to avoid packets leaving unencrypted
                         * when updating policies */
-                       if (priority == POLICY_PRIORITY_DEFAULT)
+                       if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
                        {
                                status |= install_policies_internal(this, this->my_addr,
                                                                        this->other_addr, my_ts, other_ts,
@@ -866,11 +962,10 @@ static void reinstall_vip(host_t *vip, host_t *me)
 {
        char *iface;
 
-       if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
-                                                                                          me, &iface))
+       if (charon->kernel->get_interface(charon->kernel, me, &iface))
        {
-               hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
-               hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, -1, iface);
+               charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
+               charon->kernel->add_ip(charon->kernel, vip, -1, iface);
                free(iface);
        }
 }
@@ -899,12 +994,13 @@ METHOD(child_sa_t, update, status_t,
                /* update our (initiator) SA */
                if (this->my_spi)
                {
-                       if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
+                       if (charon->kernel->update_sa(charon->kernel,
                                                        this->my_spi, proto_ike2ip(this->protocol),
                                                        this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
                                                        this->other_addr, this->my_addr, other, me,
                                                        this->encap, encap, this->mark_in) == NOT_SUPPORTED)
                        {
+                               set_state(this, old);
                                return NOT_SUPPORTED;
                        }
                }
@@ -912,60 +1008,38 @@ METHOD(child_sa_t, update, status_t,
                /* update his (responder) SA */
                if (this->other_spi)
                {
-                       if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
+                       if (charon->kernel->update_sa(charon->kernel,
                                                        this->other_spi, proto_ike2ip(this->protocol),
                                                        this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
                                                        this->my_addr, this->other_addr, me, other,
                                                        this->encap, encap, this->mark_out) == NOT_SUPPORTED)
                        {
+                               set_state(this, old);
                                return NOT_SUPPORTED;
                        }
                }
        }
 
-       if (this->config->install_policy(this->config))
+       if (this->config->install_policy(this->config) && require_policy_update())
        {
-               ipsec_sa_cfg_t my_sa = {
-                       .mode = this->mode,
-                       .reqid = this->reqid,
-                       .ipcomp = {
-                               .transform = this->ipcomp,
-                       },
-               }, other_sa = my_sa;
-
-               my_sa.ipcomp.cpi = this->my_cpi;
-               other_sa.ipcomp.cpi = this->other_cpi;
-
-               if (this->protocol == PROTO_ESP)
-               {
-                       my_sa.esp.use = TRUE;
-                       my_sa.esp.spi = this->my_spi;
-                       other_sa.esp.use = TRUE;
-                       other_sa.esp.spi = this->other_spi;
-               }
-               else
-               {
-                       my_sa.ah.use = TRUE;
-                       my_sa.ah.spi = this->my_spi;
-                       other_sa.ah.use = TRUE;
-                       other_sa.ah.spi = this->other_spi;
-               }
-
-               /* update policies */
                if (!me->ip_equals(me, this->my_addr) ||
                        !other->ip_equals(other, this->other_addr))
                {
+                       ipsec_sa_cfg_t my_sa, other_sa;
                        enumerator_t *enumerator;
                        traffic_selector_t *my_ts, *other_ts;
 
+                       prepare_sa_cfg(this, &my_sa, &other_sa);
+
                        /* always use high priorities, as hosts getting updated are INSTALLED */
                        enumerator = create_policy_enumerator(this);
                        while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
                        {
                                traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
                                /* remove old policies first */
-                               del_policies_internal(this, my_ts, other_ts,
-                                                                         POLICY_PRIORITY_DEFAULT);
+                               del_policies_internal(this, this->my_addr, this->other_addr,
+                                                                         my_ts, other_ts, &my_sa, &other_sa,
+                                                                         POLICY_IPSEC, POLICY_PRIORITY_DEFAULT);
 
                                /* check if we have to update a "dynamic" traffic selector */
                                if (!me->ip_equals(me, this->my_addr) &&
@@ -987,21 +1061,20 @@ METHOD(child_sa_t, update, status_t,
 
                                /* reinstall updated policies */
                                install_policies_internal(this, me, other, my_ts, other_ts,
-                                                               &my_sa, &other_sa, POLICY_IPSEC,
-                                                               POLICY_PRIORITY_DEFAULT);
+                                                                                 &my_sa, &other_sa, POLICY_IPSEC,
+                                                                                 POLICY_PRIORITY_DEFAULT);
 
                                /* update fallback policies after the new policy is in place */
-                               if (old_my_ts || old_other_ts)
-                               {
-                                       del_policies_internal(this, old_my_ts ?: my_ts,
-                                                                                 old_other_ts ?: other_ts,
+                               del_policies_internal(this, this->my_addr, this->other_addr,
+                                                                         old_my_ts ?: my_ts,
+                                                                         old_other_ts ?: other_ts,
+                                                                         &my_sa, &other_sa, POLICY_DROP,
+                                                                         POLICY_PRIORITY_FALLBACK);
+                               install_policies_internal(this, me, other, my_ts, other_ts,
+                                                                                 &my_sa, &other_sa, POLICY_DROP,
                                                                                  POLICY_PRIORITY_FALLBACK);
-                                       install_policies_internal(this, me, other, my_ts, other_ts,
-                                                               &my_sa, &other_sa, POLICY_DROP,
-                                                               POLICY_PRIORITY_FALLBACK);
-                                       DESTROY_IF(old_my_ts);
-                                       DESTROY_IF(old_other_ts);
-                               }
+                               DESTROY_IF(old_my_ts);
+                               DESTROY_IF(old_other_ts);
                        }
                        enumerator->destroy(enumerator);
                }
@@ -1039,36 +1112,51 @@ METHOD(child_sa_t, destroy, void,
 
        set_state(this, CHILD_DESTROYING);
 
+       if (this->config->install_policy(this->config))
+       {
+               ipsec_sa_cfg_t my_sa, other_sa;
+
+               prepare_sa_cfg(this, &my_sa, &other_sa);
+
+               /* delete all policies in the kernel */
+               enumerator = create_policy_enumerator(this);
+               while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+               {
+                       del_policies_internal(this, this->my_addr, this->other_addr,
+                                       my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority);
+                       if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
+                       {
+                               del_policies_internal(this, this->my_addr, this->other_addr,
+                                                               my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP,
+                                                               POLICY_PRIORITY_FALLBACK);
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+
        /* delete SAs in the kernel, if they are set up */
        if (this->my_spi)
        {
-               hydra->kernel_interface->del_sa(hydra->kernel_interface,
+               charon->kernel->del_sa(charon->kernel,
                                        this->other_addr, this->my_addr, this->my_spi,
                                        proto_ike2ip(this->protocol), this->my_cpi,
                                        this->mark_in);
        }
        if (this->other_spi)
        {
-               hydra->kernel_interface->del_sa(hydra->kernel_interface,
+               charon->kernel->del_sa(charon->kernel,
                                        this->my_addr, this->other_addr, this->other_spi,
                                        proto_ike2ip(this->protocol), this->other_cpi,
                                        this->mark_out);
        }
 
-       if (this->config->install_policy(this->config))
+       if (this->reqid_allocated)
        {
-               /* delete all policies in the kernel */
-               enumerator = create_policy_enumerator(this);
-               while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+               if (charon->kernel->release_reqid(charon->kernel,
+                                               this->reqid, this->mark_in, this->mark_out) != SUCCESS)
                {
-                       del_policies_internal(this, my_ts, other_ts, priority);
-                       if (priority == POLICY_PRIORITY_DEFAULT)
-                       {
-                               del_policies_internal(this, my_ts, other_ts,
-                                                                         POLICY_PRIORITY_FALLBACK);
-                       }
+                       DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
                }
-               enumerator->destroy(enumerator);
        }
 
        array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
@@ -1119,15 +1207,17 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local)
  * Described in header.
  */
 child_sa_t * child_sa_create(host_t *me, host_t* other,
-                                                        child_cfg_t *config, u_int32_t rekey, bool encap)
+                                                        child_cfg_t *config, u_int32_t rekey, bool encap,
+                                                        u_int mark_in, u_int mark_out)
 {
-       static refcount_t reqid = 0;
        private_child_sa_t *this;
+       static refcount_t unique_id = 0, unique_mark = 0, mark;
 
        INIT(this,
                .public = {
                        .get_name = _get_name,
                        .get_reqid = _get_reqid,
+                       .get_unique_id = _get_unique_id,
                        .get_config = _get_config,
                        .get_state = _get_state,
                        .set_state = _set_state,
@@ -1140,6 +1230,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
                        .get_proposal = _get_proposal,
                        .set_proposal = _set_proposal,
                        .get_lifetime = _get_lifetime,
+                       .get_installtime = _get_installtime,
                        .get_usestats = _get_usestats,
                        .get_mark = _get_mark,
                        .has_encap = _has_encap,
@@ -1168,16 +1259,46 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
                .close_action = config->get_close_action(config),
                .dpd_action = config->get_dpd_action(config),
                .reqid = config->get_reqid(config),
+               .unique_id = ref_get(&unique_id),
                .mark_in = config->get_mark(config, TRUE),
                .mark_out = config->get_mark(config, FALSE),
+               .install_time = time_monotonic(NULL),
        );
 
        this->config = config;
        config->get_ref(config);
 
+       if (mark_in)
+       {
+               this->mark_in.value = mark_in;
+       }
+       if (mark_out)
+       {
+               this->mark_out.value = mark_out;
+       }
+       if (this->mark_in.value == MARK_UNIQUE ||
+               this->mark_out.value == MARK_UNIQUE)
+       {
+               mark = ref_get(&unique_mark);
+               if (this->mark_in.value == MARK_UNIQUE)
+               {
+                       this->mark_in.value = mark;
+               }
+               if (this->mark_out.value == MARK_UNIQUE)
+               {
+                       this->mark_out.value = mark;
+               }
+       }
+
        if (!this->reqid)
        {
-               /* reuse old reqid if we are rekeying an existing CHILD_SA */
+               /* reuse old reqid if we are rekeying an existing CHILD_SA. While the
+                * reqid cache would find the same reqid for our selectors, this does
+                * not work in a special case: If an SA is triggered by a trap policy,
+                * but the negotiated SA gets narrowed, we still must reuse the same
+                * reqid to successfully "trigger" the SA on the kernel level. Rekeying
+                * such an SA requires an explicit reqid, as the cache currently knows
+                * the original selectors only for that reqid. */
                if (rekey)
                {
                        this->reqid = rekey;
@@ -1185,20 +1306,11 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
                else
                {
                        this->reqid = charon->traps->find_reqid(charon->traps, config);
-                       if (!this->reqid)
-                       {
-                               this->reqid = ref_get(&reqid);
-                       }
                }
        }
-
-       if (this->mark_in.value == MARK_REQID)
-       {
-               this->mark_in.value = this->reqid;
-       }
-       if (this->mark_out.value == MARK_REQID)
+       else
        {
-               this->mark_out.value = this->reqid;
+               this->static_reqid = TRUE;
        }
 
        /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */