]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/libcharon/sa/ikev1/tasks/quick_mode.c
ike: Optionally allow private algorithms for IKE/CHILD_SAs
[thirdparty/strongswan.git] / src / libcharon / sa / ikev1 / tasks / quick_mode.c
index 6e7da9852504502dd3ebce3ddce6205a676fea2f..f494e48c83ddb175b4e65141d4868abef1286285 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
- * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2012-2019 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
  *
  * Copyright (C) 2011 Martin Willi
  * Copyright (C) 2011 revosec AG
@@ -98,22 +98,22 @@ struct private_quick_mode_t {
        /**
         * Initiators ESP SPI
         */
-       u_int32_t spi_i;
+       uint32_t spi_i;
 
        /**
         * Responder ESP SPI
         */
-       u_int32_t spi_r;
+       uint32_t spi_r;
 
        /**
         * Initiators IPComp CPI
         */
-       u_int16_t cpi_i;
+       uint16_t cpi_i;
 
        /**
         * Responders IPComp CPI
         */
-       u_int16_t cpi_r;
+       uint16_t cpi_r;
 
        /**
         * selected CHILD_SA proposal
@@ -143,22 +143,27 @@ struct private_quick_mode_t {
        /**
         * Negotiated lifetime of new SA
         */
-       u_int32_t lifetime;
+       uint32_t lifetime;
 
        /**
-        * Negotaited lifebytes of new SA
+        * Negotiated lifebytes of new SA
         */
-       u_int64_t lifebytes;
+       uint64_t lifebytes;
 
        /**
-        * Reqid to use, 0 for auto-allocate
+        * Data collected to create the CHILD_SA
         */
-       u_int32_t reqid;
+       child_sa_create_t child;
 
        /**
         * SPI of SA we rekey
         */
-       u_int32_t rekey;
+       uint32_t rekey;
+
+       /**
+        * Delete old child after successful rekey
+        */
+       bool delete;
 
        /**
         * Negotiated mode, tunnel or transport
@@ -171,9 +176,9 @@ struct private_quick_mode_t {
        protocol_id_t proto;
 
        /**
-        * Use UDP encapsulation
+        * Message ID of handled quick mode exchange
         */
-       bool udp;
+       uint32_t mid;
 
        /** states of quick mode */
        enum {
@@ -187,7 +192,7 @@ struct private_quick_mode_t {
  */
 static void schedule_inactivity_timeout(private_quick_mode_t *this)
 {
-       u_int32_t timeout;
+       uint32_t timeout;
        bool close_ike;
 
        timeout = this->config->get_inactivity(this->config);
@@ -196,8 +201,8 @@ static void schedule_inactivity_timeout(private_quick_mode_t *this)
                close_ike = lib->settings->get_bool(lib->settings,
                                                                        "%s.inactivity_close_ike", FALSE, lib->ns);
                lib->scheduler->schedule_job(lib->scheduler, (job_t*)
-                               inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
-                                                                         timeout, close_ike), timeout);
+                       inactivity_job_create(this->child_sa->get_unique_id(this->child_sa),
+                                                                 timeout, close_ike), timeout);
        }
 }
 
@@ -305,6 +310,17 @@ static bool install(private_quick_mode_t *this)
                return FALSE;
        }
 
+       if (this->initiator)
+       {
+               this->child_sa->set_policies(this->child_sa, tsi, tsr);
+       }
+       else
+       {
+               this->child_sa->set_policies(this->child_sa, tsr, tsi);
+       }
+       tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+       tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+
        if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
                                                this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
                                                &encr_i, &integ_i, &encr_r, &integ_r))
@@ -313,25 +329,21 @@ static bool install(private_quick_mode_t *this)
                {
                        status_i = this->child_sa->install(this->child_sa,
                                                                        encr_r, integ_r, this->spi_i, this->cpi_i,
-                                                                       this->initiator, TRUE, FALSE, tsi, tsr);
+                                                                       this->initiator, TRUE, FALSE);
                        status_o = this->child_sa->install(this->child_sa,
                                                                        encr_i, integ_i, this->spi_r, this->cpi_r,
-                                                                       this->initiator, FALSE, FALSE, tsi, tsr);
+                                                                       this->initiator, FALSE, FALSE);
                }
                else
                {
                        status_i = this->child_sa->install(this->child_sa,
                                                                        encr_i, integ_i, this->spi_r, this->cpi_r,
-                                                                       this->initiator, TRUE, FALSE, tsr, tsi);
+                                                                       this->initiator, TRUE, FALSE);
                        status_o = this->child_sa->install(this->child_sa,
                                                                        encr_r, integ_r, this->spi_i, this->cpi_i,
-                                                                       this->initiator, FALSE, FALSE, tsr, tsi);
+                                                                       this->initiator, FALSE, FALSE);
                }
        }
-       chunk_clear(&integ_i);
-       chunk_clear(&integ_r);
-       chunk_clear(&encr_i);
-       chunk_clear(&encr_r);
 
        if (status_i != SUCCESS || status_o != SUCCESS)
        {
@@ -339,49 +351,54 @@ static bool install(private_quick_mode_t *this)
                        (status_i != SUCCESS) ? "inbound " : "",
                        (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
                        (status_o != SUCCESS) ? "outbound " : "");
-               tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
-               tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
-               return FALSE;
-       }
-
-       if (this->initiator)
-       {
-               status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
+               status = FAILED;
        }
        else
        {
-               status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
+               status = this->child_sa->install_policies(this->child_sa);
+
+               if (status != SUCCESS)
+               {
+                       DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
+               }
+               else
+               {
+                       charon->bus->child_derived_keys(charon->bus, this->child_sa,
+                                                                                       this->initiator, encr_i, encr_r,
+                                                                                       integ_i, integ_r);
+               }
        }
-       tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
-       tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+       chunk_clear(&integ_i);
+       chunk_clear(&integ_r);
+       chunk_clear(&encr_i);
+       chunk_clear(&encr_r);
+
        if (status != SUCCESS)
        {
-               DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
                return FALSE;
        }
 
        charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
                                                        this->dh, this->nonce_i, this->nonce_r);
 
-       /* add to IKE_SA, and remove from task */
-       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
-       this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
-
        my_ts = linked_list_create_from_enumerator(
                                this->child_sa->create_ts_enumerator(this->child_sa, TRUE));
        other_ts = linked_list_create_from_enumerator(
                                this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
 
        DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
-                "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+                "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
                 this->child_sa->get_name(this->child_sa),
-                this->child_sa->get_reqid(this->child_sa),
+                this->child_sa->get_unique_id(this->child_sa),
                 ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
                 ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts);
 
        my_ts->destroy(my_ts);
        other_ts->destroy(other_ts);
 
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+       this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+
        if (this->rekey)
        {
                old = this->ike_sa->get_child_sa(this->ike_sa,
@@ -391,15 +408,23 @@ static bool install(private_quick_mode_t *this)
        if (old)
        {
                charon->bus->child_rekey(charon->bus, old, this->child_sa);
+               /* rekeyed CHILD_SAs stay installed until they expire or are deleted
+                * by the other peer */
+               old->set_state(old, CHILD_REKEYED);
+               /* as initiator we delete the CHILD_SA if configured to do so */
+               if (this->initiator && this->delete)
+               {
+                       this->ike_sa->queue_task(this->ike_sa,
+                               (task_t*)quick_delete_create(this->ike_sa,
+                                                               this->proposal->get_protocol(this->proposal),
+                                                               this->rekey, TRUE, FALSE));
+               }
        }
        else
        {
                charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
        }
-       if (!this->rekey)
-       {
-               schedule_inactivity_timeout(this);
-       }
+       schedule_inactivity_timeout(this);
        this->child_sa = NULL;
        return TRUE;
 }
@@ -427,7 +452,7 @@ static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
        }
        nonceg->destroy(nonceg);
 
-       nonce_payload = nonce_payload_create(NONCE_V1);
+       nonce_payload = nonce_payload_create(PLV1_NONCE);
        nonce_payload->set_nonce(nonce_payload, *nonce);
        message->add_payload(message, &nonce_payload->payload_interface);
 
@@ -442,7 +467,7 @@ static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
 {
        nonce_payload_t *nonce_payload;
 
-       nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
+       nonce_payload = (nonce_payload_t*)message->get_payload(message, PLV1_NONCE);
        if (!nonce_payload)
        {
                DBG1(DBG_IKE, "NONCE payload missing in message");
@@ -456,12 +481,19 @@ static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
 /**
  * Add KE payload to message
  */
-static void add_ke(private_quick_mode_t *this, message_t *message)
+static bool add_ke(private_quick_mode_t *this, message_t *message)
 {
        ke_payload_t *ke_payload;
 
-       ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, this->dh);
+       ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE,
+                                                                                                          this->dh);
+       if (!ke_payload)
+       {
+               DBG1(DBG_IKE, "creating KE payload failed");
+               return FALSE;
+       }
        message->add_payload(message, &ke_payload->payload_interface);
+       return TRUE;
 }
 
 /**
@@ -471,14 +503,18 @@ static bool get_ke(private_quick_mode_t *this, message_t *message)
 {
        ke_payload_t *ke_payload;
 
-       ke_payload = (ke_payload_t*)message->get_payload(message, KEY_EXCHANGE_V1);
+       ke_payload = (ke_payload_t*)message->get_payload(message, PLV1_KEY_EXCHANGE);
        if (!ke_payload)
        {
                DBG1(DBG_IKE, "KE payload missing");
                return FALSE;
        }
-       this->dh->set_other_public_value(this->dh,
-                                                               ke_payload->get_key_exchange_data(ke_payload));
+       if (!this->dh->set_other_public_value(this->dh,
+                                                               ke_payload->get_key_exchange_data(ke_payload)))
+       {
+               DBG1(DBG_IKE, "unable to apply received KE value");
+               return FALSE;
+       }
        return TRUE;
 }
 
@@ -493,7 +529,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
 
        hosts = get_dynamic_hosts(this->ike_sa, local);
        list = this->config->get_traffic_selectors(this->config,
-                                                                                          local, supplied, hosts);
+                                                                                          local, supplied, hosts, TRUE);
        hosts->destroy(hosts);
        if (list->get_first(list, (void**)&ts) == SUCCESS)
        {
@@ -502,7 +538,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
        else
        {
                DBG1(DBG_IKE, "%s traffic selector missing in configuration",
-                        local ? "local" : "local");
+                        local ? "local" : "remote");
                ts = NULL;
        }
        list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
@@ -537,7 +573,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
        enumerator = message->create_payload_enumerator(message);
        while (enumerator->enumerate(enumerator, &payload))
        {
-               if (payload->get_type(payload) == ID_V1)
+               if (payload->get_type(payload) == PLV1_ID)
                {
                        id_payload = (id_payload_t*)payload;
 
@@ -576,7 +612,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
                tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
                                        hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535);
        }
-       if (this->mode == MODE_TRANSPORT && this->udp &&
+       if (this->mode == MODE_TRANSPORT && this->child.encap &&
           (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr)))
        {       /* change TS in case of a NAT in transport mode */
                DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT",
@@ -640,9 +676,9 @@ static payload_type_t get_nat_oa_payload_type(ike_sa_t *ike_sa)
 {
        if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03))
        {
-               return NAT_OA_DRAFT_00_03_V1;
+               return PLV1_NAT_OA_DRAFT_00_03;
        }
-       return NAT_OA_V1;
+       return PLV1_NAT_OA;
 }
 
 /**
@@ -652,25 +688,30 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
 {
        identification_t *id;
        id_payload_t *nat_oa;
-       host_t *src, *dst;
+       host_t *init, *resp;
        payload_type_t nat_oa_payload_type;
 
-       src = message->get_source(message);
-       dst = message->get_destination(message);
-
-       src = this->initiator ? src : dst;
-       dst = this->initiator ? dst : src;
+       if (this->initiator)
+       {
+               init = message->get_source(message);
+               resp = message->get_destination(message);
+       }
+       else
+       {
+               init = message->get_destination(message);
+               resp = message->get_source(message);
+       }
 
        nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa);
 
        /* first NAT-OA is the initiator's address */
-       id = identification_create_from_sockaddr(src->get_sockaddr(src));
+       id = identification_create_from_sockaddr(init->get_sockaddr(init));
        nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id);
        message->add_payload(message, (payload_t*)nat_oa);
        id->destroy(id);
 
        /* second NAT-OA is that of the responder */
-       id = identification_create_from_sockaddr(dst->get_sockaddr(dst));
+       id = identification_create_from_sockaddr(resp->get_sockaddr(resp));
        nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id);
        message->add_payload(message, (payload_t*)nat_oa);
        id->destroy(id);
@@ -683,12 +724,12 @@ static void get_lifetimes(private_quick_mode_t *this)
 {
        lifetime_cfg_t *lft;
 
-       lft = this->config->get_lifetime(this->config);
+       lft = this->config->get_lifetime(this->config, TRUE);
        if (lft->time.life)
        {
                this->lifetime = lft->time.life;
        }
-       else if (lft->bytes.life)
+       if (lft->bytes.life)
        {
                this->lifebytes = lft->bytes.life;
        }
@@ -700,11 +741,11 @@ static void get_lifetimes(private_quick_mode_t *this)
  */
 static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload)
 {
-       u_int32_t lifetime;
-       u_int64_t lifebytes;
+       uint32_t lifetime;
+       uint64_t lifebytes;
 
-       lifetime = sa_payload->get_lifetime(sa_payload);
-       lifebytes = sa_payload->get_lifebytes(sa_payload);
+       lifetime = sa_payload->get_lifetime(sa_payload, this->proposal);
+       lifebytes = sa_payload->get_lifebytes(sa_payload, this->proposal);
        if (this->lifetime != lifetime)
        {
                DBG1(DBG_IKE, "received %us lifetime, configured %us",
@@ -726,7 +767,7 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
 {
        notify_payload_t *notify;
 
-       notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+       notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY,
                                                                                                                  this->proto, type);
        notify->set_spi(notify, this->spi_i);
 
@@ -761,7 +802,7 @@ static linked_list_t *get_proposals(private_quick_mode_t *this,
                                proposal->destroy(proposal);
                                continue;
                        }
-                       proposal->strip_dh(proposal, group);
+                       proposal->promote_dh_group(proposal, group);
                }
                proposal->set_spi(proposal, this->spi_i);
        }
@@ -783,20 +824,25 @@ METHOD(task_t, build_i, status_t,
                        diffie_hellman_group_t group;
                        encap_t encap;
 
-                       this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
                        this->mode = this->config->get_mode(this->config);
+                       this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa,
+                                                                                                                          TRUE);
+                       this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa,
+                                                                                                                               FALSE);
+                       this->child.encap = this->ike_sa->has_condition(this->ike_sa,
+                                                                                                                       COND_NAT_ANY);
                        this->child_sa = child_sa_create(
                                                                        this->ike_sa->get_my_host(this->ike_sa),
                                                                        this->ike_sa->get_other_host(this->ike_sa),
-                                                                       this->config, this->reqid, this->udp);
+                                                                       this->config, &this->child);
 
-                       if (this->udp && this->mode == MODE_TRANSPORT)
+                       if (this->child.encap && this->mode == MODE_TRANSPORT)
                        {
                                /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
                                add_nat_oa_payloads(this, message);
                        }
 
-                       if (this->config->use_ipcomp(this->config))
+                       if (this->config->has_option(this->config, OPT_IPCOMP))
                        {
                                this->cpi_i = this->child_sa->alloc_cpi(this->child_sa);
                                if (!this->cpi_i)
@@ -806,7 +852,7 @@ METHOD(task_t, build_i, status_t,
                                }
                        }
 
-                       list = this->config->get_proposals(this->config, MODP_NONE);
+                       list = this->config->get_proposals(this->config, FALSE);
                        if (list->get_first(list, (void**)&proposal) == SUCCESS)
                        {
                                this->proto = proposal->get_protocol(proposal);
@@ -823,7 +869,7 @@ METHOD(task_t, build_i, status_t,
                        if (group != MODP_NONE)
                        {
                                proposal_t *proposal;
-                               u_int16_t preferred_group;
+                               uint16_t preferred_group;
 
                                proposal = this->ike_sa->get_proposal(this->ike_sa);
                                proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
@@ -857,7 +903,7 @@ METHOD(task_t, build_i, status_t,
                        }
 
                        get_lifetimes(this);
-                       encap = get_encap(this->ike_sa, this->udp);
+                       encap = get_encap(this->ike_sa, this->child.encap);
                        sa_payload = sa_payload_create_from_proposals_v1(list,
                                                                this->lifetime, this->lifebytes, AUTH_NONE,
                                                                this->mode, encap, this->cpi_i);
@@ -870,7 +916,10 @@ METHOD(task_t, build_i, status_t,
                        }
                        if (group != MODP_NONE)
                        {
-                               add_ke(this, message);
+                               if (!add_ke(this, message))
+                               {
+                                       return FAILED;
+                               }
                        }
                        if (!this->tsi)
                        {
@@ -917,7 +966,7 @@ static bool has_notify_errors(private_quick_mode_t *this, message_t *message)
        enumerator = message->create_payload_enumerator(message);
        while (enumerator->enumerate(enumerator, &payload))
        {
-               if (payload->get_type(payload) == NOTIFY_V1)
+               if (payload->get_type(payload) == PLV1_NOTIFY)
                {
                        notify_payload_t *notify;
                        notify_type_t type;
@@ -945,17 +994,28 @@ static bool has_notify_errors(private_quick_mode_t *this, message_t *message)
 /**
  * Check if this is a rekey for an existing CHILD_SA, reuse reqid if so
  */
-static void check_for_rekeyed_child(private_quick_mode_t *this)
+static void check_for_rekeyed_child(private_quick_mode_t *this, bool responder)
 {
        enumerator_t *enumerator, *policies;
-       traffic_selector_t *local, *remote;
+       traffic_selector_t *local, *remote, *my_ts, *other_ts;
        child_sa_t *child_sa;
        proposal_t *proposal;
        char *name;
 
+       if (responder)
+       {
+               my_ts = this->tsr;
+               other_ts = this->tsi;
+       }
+       else
+       {
+               my_ts = this->tsi;
+               other_ts = this->tsr;
+       }
+
        name = this->config->get_name(this->config);
        enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
-       while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa))
+       while (!this->child.reqid && enumerator->enumerate(enumerator, &child_sa))
        {
                if (streq(child_sa->get_name(child_sa), name))
                {
@@ -966,20 +1026,30 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
                                case CHILD_REKEYING:
                                        policies = child_sa->create_policy_enumerator(child_sa);
                                        if (policies->enumerate(policies, &local, &remote) &&
-                                               local->equals(local, this->tsr) &&
-                                               remote->equals(remote, this->tsi) &&
+                                               local->equals(local, my_ts) &&
+                                               remote->equals(remote, other_ts) &&
                                                this->proposal->equals(this->proposal, proposal))
                                        {
-                                               this->reqid = child_sa->get_reqid(child_sa);
                                                this->rekey = child_sa->get_spi(child_sa, TRUE);
+                                               this->child.reqid = child_sa->get_reqid(child_sa);
+                                               this->child.mark_in = child_sa->get_mark(child_sa,
+                                                                                                                                TRUE).value;
+                                               this->child.mark_out = child_sa->get_mark(child_sa,
+                                                                                                                                 FALSE).value;
+                                               this->child.if_id_in = child_sa->get_if_id(child_sa,
+                                                                                                                                  TRUE);
+                                               this->child.if_id_out = child_sa->get_if_id(child_sa,
+                                                                                                                                       FALSE);
                                                child_sa->set_state(child_sa, CHILD_REKEYING);
                                                DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
-                                                        child_sa->get_name(child_sa), this->reqid);
+                                                        child_sa->get_name(child_sa),
+                                                        child_sa->get_unique_id(child_sa));
                                        }
                                        policies->destroy(policies);
-                               break;
-                       default:
-                               break;
+                                       break;
+                               case CHILD_REKEYED:
+                               default:
+                                       break;
                        }
                }
        }
@@ -989,6 +1059,11 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
 METHOD(task_t, process_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
+       if (this->mid && this->mid != message->get_message_id(message))
+       {       /* not responsible for this quick mode exchange */
+               return INVALID_ARG;
+       }
+
        switch (this->state)
        {
                case QM_INIT:
@@ -996,18 +1071,19 @@ METHOD(task_t, process_r, status_t,
                        sa_payload_t *sa_payload;
                        linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL;
                        peer_cfg_t *peer_cfg;
-                       u_int16_t group;
-                       bool private;
+                       uint16_t group;
+                       proposal_selection_flag_t flags = 0;
 
                        sa_payload = (sa_payload_t*)message->get_payload(message,
-                                                                                                       SECURITY_ASSOCIATION_V1);
+                                                                                                       PLV1_SECURITY_ASSOCIATION);
                        if (!sa_payload)
                        {
                                DBG1(DBG_IKE, "sa payload missing");
                                return send_notify(this, INVALID_PAYLOAD_TYPE);
                        }
 
-                       this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp);
+                       this->mode = sa_payload->get_encap_mode(sa_payload,
+                                                                                                       &this->child.encap);
 
                        if (!get_ts(this, message))
                        {
@@ -1028,15 +1104,19 @@ METHOD(task_t, process_r, status_t,
                                this->tsi = select_ts(this, FALSE, tsi);
                                this->tsr = select_ts(this, TRUE, tsr);
                        }
-                       tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
-                       tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
-                       if (!this->config || !this->tsi || !this->tsr)
+                       if (!this->config || !this->tsi || !this->tsr ||
+                               this->mode != this->config->get_mode(this->config))
                        {
-                               DBG1(DBG_IKE, "no matching CHILD_SA config found");
+                               DBG1(DBG_IKE, "no matching CHILD_SA config found for "
+                                        "%#R === %#R", tsi, tsr);
+                               tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+                               tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
                                return send_notify(this, INVALID_ID_INFORMATION);
                        }
+                       tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+                       tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
 
-                       if (this->config->use_ipcomp(this->config))
+                       if (this->config->has_option(this->config, OPT_IPCOMP))
                        {
                                list = sa_payload->get_ipcomp_proposals(sa_payload,
                                                                                                                &this->cpi_i);
@@ -1052,10 +1132,19 @@ METHOD(task_t, process_r, status_t,
                                DESTROY_IF(list);
                                list = sa_payload->get_proposals(sa_payload);
                        }
-                       private = this->ike_sa->supports_extension(this->ike_sa,
-                                                                                                          EXT_STRONGSWAN);
-                       this->proposal = this->config->select_proposal(this->config,
-                                                                                                                  list, FALSE, private);
+                       if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)
+                               && !lib->settings->get_bool(lib->settings,
+                                                                       "%s.accept_private_algs", FALSE, lib->ns))
+                       {
+                               flags |= PROPOSAL_SKIP_PRIVATE;
+                       }
+                       if (!lib->settings->get_bool(lib->settings,
+                                                       "%s.prefer_configured_proposals", TRUE, lib->ns))
+                       {
+                               flags |= PROPOSAL_PREFER_SUPPLIED;
+                       }
+                       this->proposal = this->config->select_proposal(this->config, list,
+                                                                                                                  flags);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
 
                        get_lifetimes(this);
@@ -1091,12 +1180,15 @@ METHOD(task_t, process_r, status_t,
                                }
                        }
 
-                       check_for_rekeyed_child(this);
-
+                       check_for_rekeyed_child(this, TRUE);
+                       this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa,
+                                                                                                                          TRUE);
+                       this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa,
+                                                                                                                               FALSE);
                        this->child_sa = child_sa_create(
                                                                        this->ike_sa->get_my_host(this->ike_sa),
                                                                        this->ike_sa->get_other_host(this->ike_sa),
-                                                                       this->config, this->reqid, this->udp);
+                                                                       this->config, &this->child);
 
                        tsi = linked_list_create_with_items(this->tsi, NULL);
                        tsr = linked_list_create_with_items(this->tsr, NULL);
@@ -1117,11 +1209,37 @@ METHOD(task_t, process_r, status_t,
                }
                case QM_NEGOTIATED:
                {
-                       if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
-                               has_notify_errors(this, message))
+                       if (has_notify_errors(this, message))
                        {
                                return SUCCESS;
                        }
+                       if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+                       {
+                               if (message->get_payload(message, PLV1_DELETE))
+                               {
+                                       /* If the DELETE for a Quick Mode follows immediately
+                                        * after rekeying, we might receive it before the
+                                        * third completing Quick Mode message. Ignore it, as
+                                        * it gets handled by a separately queued delete task. */
+                                       return NEED_MORE;
+                               }
+                               return SUCCESS;
+                       }
+                       if (!this->rekey)
+                       {
+                               /* do another check in case SAs were created since we handled
+                                * the QM request, this is consistent with the rekey check
+                                * before installation on the initiator */
+                               check_for_rekeyed_child(this, TRUE);
+                               if (this->rekey)
+                               {
+                                       this->child_sa->destroy(this->child_sa);
+                                       this->child_sa = child_sa_create(
+                                                                       this->ike_sa->get_my_host(this->ike_sa),
+                                                                       this->ike_sa->get_other_host(this->ike_sa),
+                                                                       this->config, &this->child);
+                               }
+                       }
                        if (!install(this))
                        {
                                ike_sa_t *ike_sa = this->ike_sa;
@@ -1145,6 +1263,11 @@ METHOD(task_t, process_r, status_t,
 METHOD(task_t, build_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
+       if (this->mid && this->mid != message->get_message_id(message))
+       {       /* not responsible for this quick mode exchange */
+               return INVALID_ARG;
+       }
+
        switch (this->state)
        {
                case QM_INIT:
@@ -1172,13 +1295,13 @@ METHOD(task_t, build_r, status_t,
                                }
                        }
 
-                       if (this->udp && this->mode == MODE_TRANSPORT)
+                       if (this->child.encap && this->mode == MODE_TRANSPORT)
                        {
                                /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
                                add_nat_oa_payloads(this, message);
                        }
 
-                       encap = get_encap(this->ike_sa, this->udp);
+                       encap = get_encap(this->ike_sa, this->child.encap);
                        sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
                                                                this->lifetime, this->lifebytes, AUTH_NONE,
                                                                this->mode, encap, this->cpi_r);
@@ -1190,14 +1313,26 @@ METHOD(task_t, build_r, status_t,
                        }
                        if (this->dh)
                        {
-                               add_ke(this, message);
+                               if (!add_ke(this, message))
+                               {
+                                       return FAILED;
+                               }
                        }
 
                        add_ts(this, message);
 
                        this->state = QM_NEGOTIATED;
+                       this->mid = message->get_message_id(message);
                        return NEED_MORE;
                }
+               case QM_NEGOTIATED:
+                       if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+                       {
+                               /* skip INFORMATIONAL response if we received a INFORMATIONAL
+                                * delete, see process_r() */
+                               return ALREADY_DONE;
+                       }
+                       /* fall */
                default:
                        return FAILED;
        }
@@ -1212,10 +1347,10 @@ METHOD(task_t, process_i, status_t,
                {
                        sa_payload_t *sa_payload;
                        linked_list_t *list = NULL;
-                       bool private;
+                       proposal_selection_flag_t flags = 0;
 
                        sa_payload = (sa_payload_t*)message->get_payload(message,
-                                                                                                       SECURITY_ASSOCIATION_V1);
+                                                                                                       PLV1_SECURITY_ASSOCIATION);
                        if (!sa_payload)
                        {
                                DBG1(DBG_IKE, "sa payload missing");
@@ -1227,7 +1362,7 @@ METHOD(task_t, process_i, status_t,
                                                                                                                &this->cpi_r);
                                if (!list->get_count(list))
                                {
-                                       DBG1(DBG_IKE, "peer did not acccept our IPComp proposal, "
+                                       DBG1(DBG_IKE, "peer did not accept our IPComp proposal, "
                                                 "IPComp disabled");
                                        this->cpi_i = 0;
                                }
@@ -1237,10 +1372,14 @@ METHOD(task_t, process_i, status_t,
                                DESTROY_IF(list);
                                list = sa_payload->get_proposals(sa_payload);
                        }
-                       private = this->ike_sa->supports_extension(this->ike_sa,
-                                                                                                          EXT_STRONGSWAN);
-                       this->proposal = this->config->select_proposal(this->config,
-                                                                                                                  list, FALSE, private);
+                       if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)
+                               && !lib->settings->get_bool(lib->settings,
+                                                                       "%s.accept_private_algs", FALSE, lib->ns))
+                       {
+                               flags |= PROPOSAL_SKIP_PRIVATE;
+                       }
+                       this->proposal = this->config->select_proposal(this->config, list,
+                                                                                                                  flags);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
                        if (!this->proposal)
                        {
@@ -1263,6 +1402,7 @@ METHOD(task_t, process_i, status_t,
                        {
                                return send_notify(this, INVALID_PAYLOAD_TYPE);
                        }
+                       check_for_rekeyed_child(this, FALSE);
                        if (!install(this))
                        {
                                return send_notify(this, NO_PROPOSAL_CHOSEN);
@@ -1281,14 +1421,34 @@ METHOD(task_t, get_type, task_type_t,
        return TASK_QUICK_MODE;
 }
 
+METHOD(quick_mode_t, get_mid, uint32_t,
+       private_quick_mode_t *this)
+{
+       return this->mid;
+}
+
 METHOD(quick_mode_t, use_reqid, void,
-       private_quick_mode_t *this, u_int32_t reqid)
+       private_quick_mode_t *this, uint32_t reqid)
+{
+       this->child.reqid = reqid;
+}
+
+METHOD(quick_mode_t, use_marks, void,
+       private_quick_mode_t *this, uint32_t in, uint32_t out)
+{
+       this->child.mark_in = in;
+       this->child.mark_out = out;
+}
+
+METHOD(quick_mode_t, use_if_ids, void,
+       private_quick_mode_t *this, uint32_t in, uint32_t out)
 {
-       this->reqid = reqid;
+       this->child.if_id_in = in;
+       this->child.if_id_out = out;
 }
 
 METHOD(quick_mode_t, rekey, void,
-       private_quick_mode_t *this, u_int32_t spi)
+       private_quick_mode_t *this, uint32_t spi)
 {
        this->rekey = spi;
 }
@@ -1307,6 +1467,7 @@ METHOD(task_t, migrate, void,
        this->ike_sa = ike_sa;
        this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
        this->state = QM_INIT;
+       this->mid = 0;
        this->tsi = NULL;
        this->tsr = NULL;
        this->proposal = NULL;
@@ -1314,6 +1475,7 @@ METHOD(task_t, migrate, void,
        this->dh = NULL;
        this->spi_i = 0;
        this->spi_r = 0;
+       this->child = (child_sa_create_t){};
 
        if (!this->initiator)
        {
@@ -1351,7 +1513,10 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                                .migrate = _migrate,
                                .destroy = _destroy,
                        },
+                       .get_mid = _get_mid,
                        .use_reqid = _use_reqid,
+                       .use_marks = _use_marks,
+                       .use_if_ids = _use_if_ids,
                        .rekey = _rekey,
                },
                .ike_sa = ike_sa,
@@ -1362,6 +1527,8 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                .tsi = tsi ? tsi->clone(tsi) : NULL,
                .tsr = tsr ? tsr->clone(tsr) : NULL,
                .proto = PROTO_ESP,
+               .delete = lib->settings->get_bool(lib->settings,
+                                                                                 "%s.delete_rekeyed", FALSE, lib->ns),
        );
 
        if (config)