libhydra: Move kernel interface to libcharon
[strongswan.git] / src / libcharon / sa / child_sa.c
index bd41cba..56b7cb5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2009 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005 Jan Hutter
@@ -24,6 +24,7 @@
 #include <time.h>
 
 #include <daemon.h>
+#include <collections/array.h>
 
 ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
        "CREATED",
@@ -32,6 +33,8 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
        "INSTALLED",
        "UPDATING",
        "REKEYING",
+       "REKEYED",
+       "RETRYING",
        "DELETING",
        "DESTROYING",
 );
@@ -78,14 +81,14 @@ struct private_child_sa_t {
        u_int16_t other_cpi;
 
        /**
-        * List for local traffic selectors
+        * Array for local traffic selectors
         */
-       linked_list_t *my_ts;
+       array_t *my_ts;
 
        /**
-        * List for remote traffic selectors
+        * Array for remote traffic selectors
         */
-       linked_list_t *other_ts;
+       array_t *other_ts;
 
        /**
         * Protocol used to protect this SA, ESP|AH
@@ -98,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;
@@ -118,11 +136,21 @@ 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;
 
        /**
+        * TRUE if this CHILD_SA is used to install trap policies
+        */
+       bool trap;
+
+       /**
         * Specifies if UDP encapsulation is enabled (NAT traversal)
         */
        bool encap;
@@ -160,12 +188,12 @@ struct private_child_sa_t {
        /**
         * time of last use in seconds (inbound)
         */
-       u_int32_t my_usetime;
+       time_t my_usetime;
 
        /**
         * time of last use in seconds (outbound)
         */
-       u_int32_t other_usetime;
+       time_t other_usetime;
 
        /**
         * last number of inbound bytes
@@ -176,175 +204,169 @@ struct private_child_sa_t {
         * last number of outbound bytes
         */
        u_int64_t other_usebytes;
+
+       /**
+        * last number of inbound packets
+        */
+       u_int64_t my_usepackets;
+
+       /**
+        * last number of outbound bytes
+        */
+       u_int64_t other_usepackets;
 };
 
 /**
- * Implementation of child_sa_t.get_name
+ * convert an IKEv2 specific protocol identifier to the IP protocol identifier.
  */
-static char *get_name(private_child_sa_t *this)
+static inline u_int8_t proto_ike2ip(protocol_id_t protocol)
+{
+       switch (protocol)
+       {
+               case PROTO_ESP:
+                       return IPPROTO_ESP;
+               case PROTO_AH:
+                       return IPPROTO_AH;
+               default:
+                       return protocol;
+       }
+}
+
+METHOD(child_sa_t, get_name, char*,
+          private_child_sa_t *this)
 {
        return this->config->get_name(this->config);
 }
 
-/**
- * Implements child_sa_t.get_reqid
- */
-static u_int32_t get_reqid(private_child_sa_t *this)
+METHOD(child_sa_t, get_reqid, u_int32_t,
+          private_child_sa_t *this)
 {
        return this->reqid;
 }
 
-/**
- * Implements child_sa_t.get_config
- */
-static child_cfg_t* get_config(private_child_sa_t *this)
+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)
 {
        return this->config;
 }
 
-/**
- * Implements child_sa_t.set_state
- */
-static void set_state(private_child_sa_t *this, child_sa_state_t state)
+METHOD(child_sa_t, set_state, void,
+          private_child_sa_t *this, child_sa_state_t state)
 {
        charon->bus->child_state_change(charon->bus, &this->public, state);
        this->state = state;
 }
 
-/**
- * Implements child_sa_t.get_state
- */
-static child_sa_state_t get_state(private_child_sa_t *this)
+METHOD(child_sa_t, get_state, child_sa_state_t,
+          private_child_sa_t *this)
 {
        return this->state;
 }
 
-/**
- * Implements child_sa_t.get_spi
- */
-u_int32_t get_spi(private_child_sa_t *this, bool inbound)
+METHOD(child_sa_t, get_spi, u_int32_t,
+          private_child_sa_t *this, bool inbound)
 {
        return inbound ? this->my_spi : this->other_spi;
 }
 
-/**
- * Implements child_sa_t.get_cpi
- */
-u_int16_t get_cpi(private_child_sa_t *this, bool inbound)
+METHOD(child_sa_t, get_cpi, u_int16_t,
+          private_child_sa_t *this, bool inbound)
 {
        return inbound ? this->my_cpi : this->other_cpi;
 }
 
-/**
- * Implements child_sa_t.get_protocol
- */
-protocol_id_t get_protocol(private_child_sa_t *this)
+METHOD(child_sa_t, get_protocol, protocol_id_t,
+          private_child_sa_t *this)
 {
        return this->protocol;
 }
 
-/**
- * Implementation of child_sa_t.set_protocol
- */
-static void set_protocol(private_child_sa_t *this, protocol_id_t protocol)
+METHOD(child_sa_t, set_protocol, void,
+          private_child_sa_t *this, protocol_id_t protocol)
 {
        this->protocol = protocol;
 }
 
-/**
- * Implementation of child_sa_t.get_mode
- */
-static ipsec_mode_t get_mode(private_child_sa_t *this)
+METHOD(child_sa_t, get_mode, ipsec_mode_t,
+          private_child_sa_t *this)
 {
        return this->mode;
 }
 
-/**
- * Implementation of child_sa_t.set_mode
- */
-static void set_mode(private_child_sa_t *this, ipsec_mode_t mode)
+METHOD(child_sa_t, set_mode, void,
+          private_child_sa_t *this, ipsec_mode_t mode)
 {
        this->mode = mode;
 }
 
-/**
- * Implementation of child_sa_t.has_encap
- */
-static bool has_encap(private_child_sa_t *this)
+METHOD(child_sa_t, has_encap, bool,
+          private_child_sa_t *this)
 {
        return this->encap;
 }
 
-/**
- * Implementation of child_sa_t.get_ipcomp
- */
-static ipcomp_transform_t get_ipcomp(private_child_sa_t *this)
+METHOD(child_sa_t, get_ipcomp, ipcomp_transform_t,
+          private_child_sa_t *this)
 {
        return this->ipcomp;
 }
 
-/**
- * Implementation of child_sa_t.set_ipcomp.
- */
-static void set_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp)
+METHOD(child_sa_t, set_ipcomp, void,
+          private_child_sa_t *this, ipcomp_transform_t ipcomp)
 {
        this->ipcomp = ipcomp;
 }
 
-/**
- * Implementation of child_sa_t.set_close_action.
- */
-static void set_close_action(private_child_sa_t *this, action_t action)
+METHOD(child_sa_t, set_close_action, void,
+          private_child_sa_t *this, action_t action)
 {
        this->close_action = action;
 }
 
-/**
- * Implementation of child_sa_t.get_close_action.
- */
-static action_t get_close_action(private_child_sa_t *this)
+METHOD(child_sa_t, get_close_action, action_t,
+          private_child_sa_t *this)
 {
        return this->close_action;
 }
 
-/**
- * Implementation of child_sa_t.set_dpd_action.
- */
-static void set_dpd_action(private_child_sa_t *this, action_t action)
+METHOD(child_sa_t, set_dpd_action, void,
+          private_child_sa_t *this, action_t action)
 {
        this->dpd_action = action;
 }
 
-/**
- * Implementation of child_sa_t.get_dpd_action.
- */
-static action_t get_dpd_action(private_child_sa_t *this)
+METHOD(child_sa_t, get_dpd_action, action_t,
+          private_child_sa_t *this)
 {
        return this->dpd_action;
 }
 
-/**
- * Implementation of child_sa_t.get_proposal
- */
-static proposal_t* get_proposal(private_child_sa_t *this)
+METHOD(child_sa_t, get_proposal, proposal_t*,
+          private_child_sa_t *this)
 {
        return this->proposal;
 }
 
-/**
- * Implementation of child_sa_t.set_proposal
- */
-static void set_proposal(private_child_sa_t *this, proposal_t *proposal)
+METHOD(child_sa_t, set_proposal, void,
+          private_child_sa_t *this, proposal_t *proposal)
 {
        this->proposal = proposal->clone(proposal);
 }
 
-/**
- * Implementation of child_sa_t.get_traffic_selectors.
- */
-static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local)
+METHOD(child_sa_t, create_ts_enumerator, enumerator_t*,
+       private_child_sa_t *this, bool local)
 {
-       return local ? this->my_ts : this->other_ts;
+       if (local)
+       {
+               return array_create_enumerator(this->my_ts);
+       }
+       return array_create_enumerator(this->other_ts);
 }
 
 typedef struct policy_enumerator_t policy_enumerator_t;
@@ -359,17 +381,15 @@ struct policy_enumerator_t {
        enumerator_t *mine;
        /** enumerator over others TS */
        enumerator_t *other;
-       /** list of others TS, to recreate enumerator */
-       linked_list_t *list;
+       /** array of others TS, to recreate enumerator */
+       array_t *array;
        /** currently enumerating TS for "me" side */
        traffic_selector_t *ts;
 };
 
-/**
- * enumerator function of create_policy_enumerator()
- */
-static bool policy_enumerate(policy_enumerator_t *this,
-                                traffic_selector_t **my_out, traffic_selector_t **other_out)
+METHOD(enumerator_t, policy_enumerate, bool,
+          policy_enumerator_t *this, traffic_selector_t **my_out,
+          traffic_selector_t **other_out)
 {
        traffic_selector_t *other_ts;
 
@@ -378,7 +398,7 @@ static bool policy_enumerate(policy_enumerator_t *this,
                if (!this->other->enumerate(this->other, &other_ts))
                {       /* end of others list, restart with new of mine */
                        this->other->destroy(this->other);
-                       this->other = this->list->create_enumerator(this->list);
+                       this->other = array_create_enumerator(this->array);
                        this->ts = NULL;
                        continue;
                }
@@ -392,36 +412,42 @@ static bool policy_enumerate(policy_enumerator_t *this,
                {       /* 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;
 }
 
-/**
- * destroy function of create_policy_enumerator()
- */
-static void policy_destroy(policy_enumerator_t *this)
+METHOD(enumerator_t, policy_destroy, void,
+          policy_enumerator_t *this)
 {
        this->mine->destroy(this->mine);
        this->other->destroy(this->other);
        free(this);
 }
 
-/**
- * Implementation of child_sa_t.create_policy_enumerator
- */
-static enumerator_t* create_policy_enumerator(private_child_sa_t *this)
+METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
+          private_child_sa_t *this)
 {
-       policy_enumerator_t *e = malloc_thing(policy_enumerator_t);
-
-       e->public.enumerate = (void*)policy_enumerate;
-       e->public.destroy = (void*)policy_destroy;
-       e->mine = this->my_ts->create_enumerator(this->my_ts);
-       e->other = this->other_ts->create_enumerator(this->other_ts);
-       e->list = this->other_ts;
-       e->ts = NULL;
+       policy_enumerator_t *e;
+
+       INIT(e,
+               .public = {
+                       .enumerate = (void*)_policy_enumerate,
+                       .destroy = _policy_destroy,
+               },
+               .mine = array_create_enumerator(this->my_ts),
+               .other = array_create_enumerator(this->other_ts),
+               .array = this->other_ts,
+               .ts = NULL,
+       );
 
        return &e->public;
 }
@@ -435,21 +461,27 @@ static enumerator_t* create_policy_enumerator(private_child_sa_t *this)
 static status_t update_usebytes(private_child_sa_t *this, bool inbound)
 {
        status_t status = FAILED;
-       u_int64_t bytes;
+       u_int64_t bytes, packets;
+       time_t time;
 
        if (inbound)
        {
                if (this->my_spi)
                {
-                       status = charon->kernel_interface->query_sa(charon->kernel_interface,
-                                                                       this->other_addr, this->my_addr,
-                                                                       this->my_spi, this->protocol,
-                                                                       this->mark_in, &bytes);
+                       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)
                                {
                                        this->my_usebytes = bytes;
+                                       this->my_usepackets = packets;
+                                       if (time)
+                                       {
+                                               this->my_usetime = time;
+                                       }
                                        return SUCCESS;
                                }
                                return FAILED;
@@ -460,15 +492,20 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
        {
                if (this->other_spi)
                {
-                       status = charon->kernel_interface->query_sa(charon->kernel_interface,
-                                                                       this->my_addr, this->other_addr,
-                                                                       this->other_spi, this->protocol,
-                                                                       this->mark_out, &bytes);
+                       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)
                                {
                                        this->other_usebytes = bytes;
+                                       this->other_usepackets = packets;
+                                       if (time)
+                                       {
+                                               this->other_usetime = time;
+                                       }
                                        return SUCCESS;
                                }
                                return FAILED;
@@ -481,28 +518,28 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
 /**
  * updates the cached usetime
  */
-static void update_usetime(private_child_sa_t *this, bool inbound)
+static bool update_usetime(private_child_sa_t *this, bool inbound)
 {
        enumerator_t *enumerator;
        traffic_selector_t *my_ts, *other_ts;
-       u_int32_t last_use = 0;
+       time_t last_use = 0;
 
        enumerator = create_policy_enumerator(this);
        while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
        {
-               u_int32_t in, out, fwd;
+               time_t in, out, fwd;
 
                if (inbound)
                {
-                       if (charon->kernel_interface->query_policy(charon->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 (charon->kernel_interface->query_policy(charon->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);
                                }
@@ -510,8 +547,8 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
                }
                else
                {
-                       if (charon->kernel_interface->query_policy(charon->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);
                        }
@@ -521,7 +558,7 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
 
        if (last_use == 0)
        {
-               return;
+               return FALSE;
        }
        if (inbound)
        {
@@ -531,20 +568,26 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
        {
                this->other_usetime = last_use;
        }
+       return TRUE;
 }
 
-/**
- * Implementation of child_sa_t.get_usestats
- */
-static void get_usestats(private_child_sa_t *this, bool inbound,
-                                                time_t *time, u_int64_t *bytes)
+METHOD(child_sa_t, get_usestats, void,
+       private_child_sa_t *this, bool inbound,
+       time_t *time, u_int64_t *bytes, u_int64_t *packets)
 {
-       if (update_usebytes(this, inbound) != FAILED)
+       if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED)
        {
                /* there was traffic since last update or the kernel interface
                 * does not support querying the number of usebytes.
                 */
-               update_usetime(this, inbound);
+               if (time)
+               {
+                       if (!update_usetime(this, inbound) && !bytes && !packets)
+                       {
+                               /* if policy query did not yield a usetime, query SAs instead */
+                               update_usebytes(this, inbound);
+                       }
+               }
        }
        if (time)
        {
@@ -554,55 +597,70 @@ static void get_usestats(private_child_sa_t *this, bool inbound,
        {
                *bytes = inbound ? this->my_usebytes : this->other_usebytes;
        }
+       if (packets)
+       {
+               *packets = inbound ? this->my_usepackets : this->other_usepackets;
+       }
 }
 
-/**
- * Implementation of child_sa_t.get_lifetime
- */
-static time_t get_lifetime(private_child_sa_t *this, bool hard)
+METHOD(child_sa_t, get_mark, mark_t,
+       private_child_sa_t *this, bool inbound)
+{
+       if (inbound)
+       {
+               return this->mark_in;
+       }
+       return this->mark_out;
+}
+
+METHOD(child_sa_t, get_lifetime, time_t,
+          private_child_sa_t *this, bool hard)
 {
        return hard ? this->expire_time : this->rekey_time;
 }
 
-/**
- * Implementation of child_sa_t.alloc_spi
- */
-static u_int32_t alloc_spi(private_child_sa_t *this, protocol_id_t protocol)
+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 (charon->kernel_interface->get_spi(charon->kernel_interface,
-                                                       this->other_addr, this->my_addr, 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 */
+               this->protocol = protocol;
                return this->my_spi;
        }
        return 0;
 }
 
-/**
- * Implementation of child_sa_t.alloc_cpi
- */
-static u_int16_t alloc_cpi(private_child_sa_t *this)
+METHOD(child_sa_t, alloc_cpi, u_int16_t,
+          private_child_sa_t *this)
 {
-       if (charon->kernel_interface->get_cpi(charon->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;
        }
        return 0;
 }
 
-/**
- * Implementation of child_sa_t.install
- */
-static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
-                                               u_int32_t spi, u_int16_t cpi, bool inbound,
-                                               linked_list_t *my_ts, linked_list_t *other_ts)
+METHOD(child_sa_t, install, status_t,
+       private_child_sa_t *this, chunk_t encr, chunk_t integ, u_int32_t spi,
+       u_int16_t cpi, bool initiator, bool inbound, bool tfcv3,
+       linked_list_t *my_ts, linked_list_t *other_ts)
 {
        u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
-       traffic_selector_t *src_ts = NULL, *dst_ts = NULL;
+       u_int16_t esn = NO_EXT_SEQ_NUMBERS;
+       linked_list_t *src_ts = NULL, *dst_ts = NULL;
        time_t now;
        lifetime_cfg_t *lifetime;
+       u_int32_t tfc = 0;
        host_t *src, *dst;
        status_t status;
        bool update = FALSE;
@@ -627,6 +685,11 @@ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
                dst = this->other_addr;
                this->other_spi = spi;
                this->other_cpi = cpi;
+
+               if (tfcv3)
+               {
+                       tfc = this->config->get_tfc(this->config);
+               }
        }
 
        DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
@@ -639,13 +702,33 @@ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
                                                                  &enc_alg, &size);
        this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM,
                                                                  &int_alg, &size);
+       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);
        if (lifetime->time.rekey)
        {
-               this->rekey_time = now + lifetime->time.rekey;
+               if (this->rekey_time)
+               {
+                       this->rekey_time = min(this->rekey_time, now + lifetime->time.rekey);
+               }
+               else
+               {
+                       this->rekey_time = now + lifetime->time.rekey;
+               }
        }
        if (lifetime->time.life)
        {
@@ -657,28 +740,24 @@ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
                lifetime->time.rekey = 0;
        }
 
-       if (this->mode == MODE_BEET)
+       /* BEET requires the bound address from the traffic selectors */
+       if (inbound)
        {
-               /* 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 */
-               if (inbound)
-               {
-                       my_ts->get_first(my_ts, (void**)&dst_ts);
-                       other_ts->get_first(other_ts, (void**)&src_ts);
-               }
-               else
-               {
-                       my_ts->get_first(my_ts, (void**)&src_ts);
-                       other_ts->get_first(other_ts, (void**)&dst_ts);
-               }
+               dst_ts = my_ts;
+               src_ts = other_ts;
+       }
+       else
+       {
+               src_ts = my_ts;
+               dst_ts = other_ts;
        }
 
-       status = charon->kernel_interface->add_sa(charon->kernel_interface,
-                               src, dst, spi, this->protocol, this->reqid,
-                               inbound ? this->mark_in : this->mark_out,
+       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, this->encap, 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);
 
@@ -686,54 +765,181 @@ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
 }
 
 /**
- * Implementation of child_sa_t.add_policies
+ * Check kernel interface if policy updates are required
  */
-static status_t add_policies(private_child_sa_t *this,
-                                       linked_list_t *my_ts_list, linked_list_t *other_ts_list)
+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,
+       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)
+{
+       status_t status = SUCCESS;
+       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 |= 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 |= charon->kernel->add_policy(charon->kernel,
+                                                       other_addr, my_addr, other_ts, my_ts,
+                                                       POLICY_FWD, type, my_sa,
+                                                       this->mark_in, priority);
+       }
+       return status;
+}
+
+/**
+ * Delete 3 policies: out, in and forward
+ */
+static void del_policies_internal(private_child_sa_t *this,
+       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)
+{
+
+       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)
+       {
+               charon->kernel->del_policy(charon->kernel,
+                                               other_addr, my_addr, other_ts, my_ts, POLICY_FWD,
+                                               type, my_sa, this->mark_in, priority);
+       }
+}
+
+METHOD(child_sa_t, add_policies, status_t,
+          private_child_sa_t *this, linked_list_t *my_ts_list,
+          linked_list_t *other_ts_list)
 {
        enumerator_t *enumerator;
        traffic_selector_t *my_ts, *other_ts;
        status_t status = SUCCESS;
-       bool routed = (this->state == CHILD_CREATED);
+
+       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))
        {
-               this->my_ts->insert_last(this->my_ts, my_ts->clone(my_ts));
+               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))
        {
-               this->other_ts->insert_last(this->other_ts, other_ts->clone(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, 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 */
+               this->trap = this->state == CHILD_CREATED;
+               priority = this->trap ? POLICY_PRIORITY_ROUTED
+                                                         : POLICY_PRIORITY_DEFAULT;
+
                /* enumerate pairs of traffic selectors */
                enumerator = create_policy_enumerator(this);
                while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
                {
-                       /* install 3 policies: out, in and forward */
-                       status |= charon->kernel_interface->add_policy(charon->kernel_interface,
-                                       this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT,
-                                       this->other_spi, this->protocol, this->reqid, this->mark_out,
-                                       this->mode, this->ipcomp, this->other_cpi, routed);
-
-                       status |= charon->kernel_interface->add_policy(charon->kernel_interface,
-                                       this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN,
-                                       this->my_spi, this->protocol, this->reqid, this->mark_in,
-                                       this->mode,     this->ipcomp, this->my_cpi, routed);
-                       if (this->mode != MODE_TRANSPORT)
+                       /* install outbound drop policy to avoid packets leaving unencrypted
+                        * when updating policies */
+                       if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
                        {
-                               status |= charon->kernel_interface->add_policy(charon->kernel_interface,
-                                       this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD,
-                                       this->my_spi, this->protocol, this->reqid, this->mark_in,
-                                       this->mode,     this->ipcomp, this->my_cpi, routed);
+                               status |= install_policies_internal(this, this->my_addr,
+                                                                       this->other_addr, my_ts, other_ts,
+                                                                       &my_sa, &other_sa, POLICY_DROP,
+                                                                       POLICY_PRIORITY_FALLBACK);
                        }
 
+                       /* install policies */
+                       status |= install_policies_internal(this, this->my_addr,
+                                                                       this->other_addr, my_ts, other_ts,
+                                                                       &my_sa, &other_sa, POLICY_IPSEC, priority);
+
                        if (status != SUCCESS)
                        {
                                break;
@@ -742,18 +948,31 @@ static status_t add_policies(private_child_sa_t *this,
                enumerator->destroy(enumerator);
        }
 
-       if (status == SUCCESS && this->state == CHILD_CREATED)
-       {       /* switch to routed state if no SAD entry set up */
+       if (status == SUCCESS && this->trap)
+       {
                set_state(this, CHILD_ROUTED);
        }
        return status;
 }
 
 /**
- * Implementation of child_sa_t.update.
+ * Callback to reinstall a virtual IP
  */
-static status_t update(private_child_sa_t *this,  host_t *me, host_t *other,
-                                          host_t *vip, bool encap)
+static void reinstall_vip(host_t *vip, host_t *me)
+{
+       char *iface;
+
+       if (charon->kernel->get_interface(charon->kernel, me, &iface))
+       {
+               charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
+               charon->kernel->add_ip(charon->kernel, vip, -1, iface);
+               free(iface);
+       }
+}
+
+METHOD(child_sa_t, update, status_t,
+       private_child_sa_t *this,  host_t *me, host_t *other, linked_list_t *vips,
+       bool encap)
 {
        child_sa_state_t old;
        bool transport_proxy_mode;
@@ -772,15 +991,16 @@ static status_t update(private_child_sa_t *this,  host_t *me, host_t *other,
 
        if (!transport_proxy_mode)
        {
-               /* update our (initator) SA */
+               /* update our (initiator) SA */
                if (this->my_spi)
                {
-                       if (charon->kernel_interface->update_sa(charon->kernel_interface,
-                                                       this->my_spi, this->protocol,
+                       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;
                        }
                }
@@ -788,77 +1008,73 @@ static status_t update(private_child_sa_t *this,  host_t *me, host_t *other,
                /* update his (responder) SA */
                if (this->other_spi)
                {
-                       if (charon->kernel_interface->update_sa(charon->kernel_interface,
-                                                       this->other_spi, this->protocol,
+                       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())
        {
-               /* 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 */
-                               charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       my_ts, other_ts, POLICY_OUT, this->mark_out, FALSE);
-                               charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       other_ts, my_ts,  POLICY_IN, this->mark_in, FALSE);
-                               if (this->mode != MODE_TRANSPORT)
-                               {
-                                       charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       other_ts, my_ts, POLICY_FWD, this->mark_in, FALSE);
-                               }
+                               del_policies_internal(this, this->my_addr, this->other_addr,
+                                                                         my_ts, other_ts, &my_sa, &other_sa,
+                                                                         POLICY_IPSEC, POLICY_PRIORITY_DEFAULT);
 
-                               /* check whether we have to update a "dynamic" traffic selector */
+                               /* check if we have to update a "dynamic" traffic selector */
                                if (!me->ip_equals(me, this->my_addr) &&
                                        my_ts->is_host(my_ts, this->my_addr))
                                {
+                                       old_my_ts = my_ts->clone(my_ts);
                                        my_ts->set_address(my_ts, me);
                                }
                                if (!other->ip_equals(other, this->other_addr) &&
                                        other_ts->is_host(other_ts, this->other_addr))
                                {
+                                       old_other_ts = other_ts->clone(other_ts);
                                        other_ts->set_address(other_ts, other);
                                }
 
                                /* we reinstall the virtual IP to handle interface roaming
                                 * correctly */
-                               if (vip)
-                               {
-                                       charon->kernel_interface->del_ip(charon->kernel_interface, vip);
-                                       charon->kernel_interface->add_ip(charon->kernel_interface, vip, me);
-                               }
+                               vips->invoke_function(vips, (void*)reinstall_vip, me);
 
                                /* reinstall updated policies */
-                               charon->kernel_interface->add_policy(charon->kernel_interface,
-                                               me, other, my_ts, other_ts, POLICY_OUT, this->other_spi,
-                                               this->protocol, this->reqid, this->mark_out, this->mode,
-                                               this->ipcomp, this->other_cpi, FALSE);
-                               charon->kernel_interface->add_policy(charon->kernel_interface,
-                                               other, me, other_ts, my_ts, POLICY_IN, this->my_spi,
-                                               this->protocol, this->reqid, this->mark_in, this->mode,
-                                               this->ipcomp, this->my_cpi, FALSE);
-                               if (this->mode != MODE_TRANSPORT)
-                               {
-                                       charon->kernel_interface->add_policy(charon->kernel_interface,
-                                               other, me, other_ts, my_ts, POLICY_FWD, this->my_spi,
-                                               this->protocol, this->reqid, this->mark_in, this->mode,
-                                               this->ipcomp, this->my_cpi, FALSE);
-                               }
+                               install_policies_internal(this, me, other, my_ts, other_ts,
+                                                                                 &my_sa, &other_sa, POLICY_IPSEC,
+                                                                                 POLICY_PRIORITY_DEFAULT);
+
+                               /* update fallback policies after the new policy is in place */
+                               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);
+                               DESTROY_IF(old_my_ts);
+                               DESTROY_IF(old_other_ts);
                        }
                        enumerator->destroy(enumerator);
                }
@@ -885,58 +1101,66 @@ static status_t update(private_child_sa_t *this,  host_t *me, host_t *other,
        return SUCCESS;
 }
 
-/**
- * Implementation of child_sa_t.destroy.
- */
-static void destroy(private_child_sa_t *this)
+METHOD(child_sa_t, destroy, void,
+          private_child_sa_t *this)
 {
        enumerator_t *enumerator;
        traffic_selector_t *my_ts, *other_ts;
-       bool unrouted = (this->state == CHILD_ROUTED);
+       policy_priority_t priority;
+
+       priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT;
 
        set_state(this, CHILD_DESTROYING);
 
-       /* delete SAs in the kernel, if they are set up */
-       if (this->my_spi)
+       if (this->config->install_policy(this->config))
        {
-               /* if CHILD was not established, use PROTO_ESP used during alloc_spi().
-                * TODO: For AH support, we have to store protocol specific SPI.s */
-               if (this->protocol == PROTO_NONE)
+               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))
                {
-                       this->protocol = PROTO_ESP;
+                       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);
+                       }
                }
-               charon->kernel_interface->del_sa(charon->kernel_interface,
+               enumerator->destroy(enumerator);
+       }
+
+       /* delete SAs in the kernel, if they are set up */
+       if (this->my_spi)
+       {
+               charon->kernel->del_sa(charon->kernel,
                                        this->other_addr, this->my_addr, this->my_spi,
-                                       this->protocol, this->my_cpi, this->mark_in);
+                                       proto_ike2ip(this->protocol), this->my_cpi,
+                                       this->mark_in);
        }
        if (this->other_spi)
        {
-               charon->kernel_interface->del_sa(charon->kernel_interface,
+               charon->kernel->del_sa(charon->kernel,
                                        this->my_addr, this->other_addr, this->other_spi,
-                                       this->protocol, this->other_cpi, this->mark_out);
+                                       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)
                {
-                       charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       my_ts, other_ts, POLICY_OUT, this->mark_out, unrouted);
-                       charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       other_ts, my_ts, POLICY_IN, this->mark_in, unrouted);
-                       if (this->mode != MODE_TRANSPORT)
-                       {
-                               charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                       other_ts, my_ts, POLICY_FWD, this->mark_in, unrouted);
-                       }
+                       DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
                }
-               enumerator->destroy(enumerator);
        }
 
-       this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
-       this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
+       array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
+       array_destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
        this->my_addr->destroy(this->my_addr);
        this->other_addr->destroy(this->other_addr);
        DESTROY_IF(this->proposal);
@@ -944,138 +1168,164 @@ static void destroy(private_child_sa_t *this)
        free(this);
 }
 
-/*
+/**
+ * Get proxy address for one side, if any
+ */
+static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local)
+{
+       host_t *host = NULL;
+       u_int8_t mask;
+       enumerator_t *enumerator;
+       linked_list_t *ts_list, *list;
+       traffic_selector_t *ts;
+
+       list = linked_list_create_with_items(ike, NULL);
+       ts_list = config->get_traffic_selectors(config, local, NULL, list);
+       list->destroy(list);
+
+       enumerator = ts_list->create_enumerator(ts_list);
+       while (enumerator->enumerate(enumerator, &ts))
+       {
+               if (ts->is_host(ts, NULL) && ts->to_subnet(ts, &host, &mask))
+               {
+                       DBG1(DBG_CHD, "%s address: %H is a transport mode proxy for %H",
+                                local ? "my" : "other", ike, host);
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
+
+       if (!host)
+       {
+               host = ike->clone(ike);
+       }
+       return host;
+}
+
+/**
  * 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 u_int32_t reqid = 0;
-       private_child_sa_t *this = malloc_thing(private_child_sa_t);
-
-       /* public functions */
-       this->public.get_name = (char*(*)(child_sa_t*))get_name;
-       this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
-       this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config;
-       this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
-       this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
-       this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
-       this->public.get_cpi = (u_int16_t(*)(child_sa_t*, bool))get_cpi;
-       this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
-       this->public.set_protocol = (void(*)(child_sa_t*, protocol_id_t protocol))set_protocol;
-       this->public.get_mode = (ipsec_mode_t(*)(child_sa_t*))get_mode;
-       this->public.set_mode = (void(*)(child_sa_t*, ipsec_mode_t mode))set_mode;
-       this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal;
-       this->public.set_proposal = (void(*)(child_sa_t*, proposal_t *proposal))set_proposal;
-       this->public.get_lifetime = (time_t(*)(child_sa_t*, bool))get_lifetime;
-       this->public.get_usestats = (void(*)(child_sa_t*,bool,time_t*,u_int64_t*))get_usestats;
-       this->public.has_encap = (bool(*)(child_sa_t*))has_encap;
-       this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp;
-       this->public.set_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t))set_ipcomp;
-       this->public.get_close_action = (action_t(*)(child_sa_t*))get_close_action;
-       this->public.set_close_action = (void(*)(child_sa_t*,action_t))set_close_action;
-       this->public.get_dpd_action = (action_t(*)(child_sa_t*))get_dpd_action;
-       this->public.set_dpd_action = (void(*)(child_sa_t*,action_t))set_dpd_action;
-       this->public.alloc_spi = (u_int32_t(*)(child_sa_t*, protocol_id_t protocol))alloc_spi;
-       this->public.alloc_cpi = (u_int16_t(*)(child_sa_t*))alloc_cpi;
-       this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound, linked_list_t *my_ts_list, linked_list_t *other_ts_list))install;
-       this->public.update = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update;
-       this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
-       this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors;
-       this->public.create_policy_enumerator = (enumerator_t*(*)(child_sa_t*))create_policy_enumerator;
-       this->public.destroy = (void(*)(child_sa_t*))destroy;
-
-       /* private data */
-       this->my_addr = me->clone(me);
-       this->other_addr = other->clone(other);
-       this->my_spi = 0;
-       this->other_spi = 0;
-       this->my_cpi = 0;
-       this->other_cpi = 0;
-       this->encap = encap;
-       this->ipcomp = IPCOMP_NONE;
-       this->state = CHILD_CREATED;
-       this->my_usetime = 0;
-       this->other_usetime = 0;
-       this->my_usebytes = 0;
-       this->other_usebytes = 0;
-       this->my_ts = linked_list_create();
-       this->other_ts = linked_list_create();
-       this->protocol = PROTO_NONE;
-       this->mode = MODE_TUNNEL;
-       this->close_action = config->get_close_action(config);
-       this->dpd_action = config->get_dpd_action(config);
-       this->proposal = NULL;
-       this->rekey_time = 0;
-       this->expire_time = 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,
+                       .get_spi = _get_spi,
+                       .get_cpi = _get_cpi,
+                       .get_protocol = _get_protocol,
+                       .set_protocol = _set_protocol,
+                       .get_mode = _get_mode,
+                       .set_mode = _set_mode,
+                       .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,
+                       .get_ipcomp = _get_ipcomp,
+                       .set_ipcomp = _set_ipcomp,
+                       .get_close_action = _get_close_action,
+                       .set_close_action = _set_close_action,
+                       .get_dpd_action = _get_dpd_action,
+                       .set_dpd_action = _set_dpd_action,
+                       .alloc_spi = _alloc_spi,
+                       .alloc_cpi = _alloc_cpi,
+                       .install = _install,
+                       .update = _update,
+                       .add_policies = _add_policies,
+                       .create_ts_enumerator = _create_ts_enumerator,
+                       .create_policy_enumerator = _create_policy_enumerator,
+                       .destroy = _destroy,
+               },
+               .encap = encap,
+               .ipcomp = IPCOMP_NONE,
+               .state = CHILD_CREATED,
+               .my_ts = array_create(0, 0),
+               .other_ts = array_create(0, 0),
+               .protocol = PROTO_NONE,
+               .mode = MODE_TUNNEL,
+               .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);
-       this->reqid = config->get_reqid(config);
-       this->mark_in = config->get_mark(config, TRUE);
-       this->mark_out = config->get_mark(config, FALSE);
+
+       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 */
-               this->reqid = rekey ? rekey : ++reqid;
+               /* 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;
+               }
+               else
+               {
+                       this->reqid = charon->traps->find_reqid(charon->traps, config);
+               }
+       }
+       else
+       {
+               this->static_reqid = TRUE;
        }
 
        /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
        if (config->get_mode(config) == MODE_TRANSPORT &&
                config->use_proxy_mode(config))
        {
-               ts_type_t type;
-               int family;
-               chunk_t addr;
-               host_t *host;
-               enumerator_t *enumerator;
-               linked_list_t *my_ts_list, *other_ts_list;
-               traffic_selector_t *my_ts, *other_ts;
-
                this->mode = MODE_TRANSPORT;
 
-               my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me);
-               enumerator = my_ts_list->create_enumerator(my_ts_list);
-               if (enumerator->enumerate(enumerator, &my_ts))
-               {
-                       if (my_ts->is_host(my_ts, NULL) &&
-                          !my_ts->is_host(my_ts, this->my_addr))
-                       {
-                               type = my_ts->get_type(my_ts);
-                               family = (type == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6;
-                               addr = my_ts->get_from_address(my_ts);
-                               host = host_create_from_chunk(family, addr, 0);
-                               free(addr.ptr);
-                               DBG1(DBG_CHD, "my address: %H is a transport mode proxy for %H",
-                                                          this->my_addr, host);
-                               this->my_addr->destroy(this->my_addr);
-                               this->my_addr = host;
-                       }
-               }
-               enumerator->destroy(enumerator);
-               my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy));
-
-               other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other);
-               enumerator = other_ts_list->create_enumerator(other_ts_list);
-               if (enumerator->enumerate(enumerator, &other_ts))
-               {
-                       if (other_ts->is_host(other_ts, NULL) &&
-                          !other_ts->is_host(other_ts, this->other_addr))
-                       {
-                               type = other_ts->get_type(other_ts);
-                               family = (type == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6;
-                               addr = other_ts->get_from_address(other_ts);
-                               host = host_create_from_chunk(family, addr, 0);
-                               free(addr.ptr);
-                               DBG1(DBG_CHD, "other address: %H is a transport mode proxy for %H",
-                                                          this->other_addr, host);
-                               this->other_addr->destroy(this->other_addr);
-                               this->other_addr = host;
-                       }
-               }
-               enumerator->destroy(enumerator);
-               other_ts_list->destroy_offset(other_ts_list, offsetof(traffic_selector_t, destroy));
+               this->my_addr = get_proxy_addr(config, me, TRUE);
+               this->other_addr = get_proxy_addr(config, other, FALSE);
+       }
+       else
+       {
+               this->my_addr = me->clone(me);
+               this->other_addr = other->clone(other);
        }
-
        return &this->public;
 }