diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 3020967fd6..aa0070a01e 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -117,6 +117,16 @@ struct private_child_create_t { */ traffic_selector_t *packet_tsr; + /** + * Local traffic selectors as configured or previously negotiated + */ + traffic_selector_list_t *my_ts; + + /** + * Remote traffic selectors as configured or previously negotiated + */ + traffic_selector_list_t *other_ts; + /** * Key exchanges to perform */ @@ -256,6 +266,15 @@ static void schedule_delayed_retry(private_child_create_t *this) task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out); task->use_label(task, this->child.label); + /* clone these directly as we don't have a public method */ + if (this->my_ts && this->other_ts) + { + private_child_create_t *priv = (private_child_create_t*)task; + + priv->my_ts = this->my_ts->clone(this->my_ts); + priv->other_ts = this->other_ts->clone(this->other_ts); + } + DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds", retry); this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry); @@ -452,15 +471,37 @@ static linked_list_t* get_transport_nat_ts(private_child_create_t *this, return out; } +/** + * Ensure we have traffic selector lists when not recreating an SA. + */ +static void ensure_ts_lists(private_child_create_t *this) +{ + linked_list_t *ts; + + if (!this->my_ts) + { + ts = this->config->get_traffic_selectors(this->config, TRUE, NULL); + this->my_ts = traffic_selector_list_create_from_list(ts); + } + if (!this->other_ts) + { + ts = this->config->get_traffic_selectors(this->config, FALSE, NULL); + this->other_ts = traffic_selector_list_create_from_list(ts); + } +} + /** * Narrow received traffic selectors with configuration */ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, linked_list_t *in) { - linked_list_t *hosts, *nat, *ts; + traffic_selector_list_t *ts; + linked_list_t *hosts, *nat, *result; ike_condition_t cond; + ensure_ts_lists(this); + ts = local ? this->my_ts : this->other_ts; cond = local ? COND_NAT_HERE : COND_NAT_THERE; hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local); @@ -468,19 +509,16 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, this->ike_sa->has_condition(this->ike_sa, cond)) { nat = get_transport_nat_ts(this, local, in); - ts = this->config->select_traffic_selectors(this->config, local, nat, - hosts); + result = child_cfg_select_ts(this->config, local, ts, nat, hosts); nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy)); } else { - ts = this->config->select_traffic_selectors(this->config, local, in, - hosts); + result = child_cfg_select_ts(this->config, local, ts, in, hosts); } hosts->destroy(hosts); - - return ts; + return result; } /** @@ -1435,13 +1473,66 @@ METHOD(task_t, build_i_multi_ke, status_t, return NEED_MORE; } +/** + * Prepare proposed traffic selectors as initiator. + */ +static void prepare_proposed_ts(private_child_create_t *this) +{ + enumerator_t *enumerator; + peer_cfg_t *peer_cfg; + linked_list_t *list; + host_t *vip; + + ensure_ts_lists(this); + + list = linked_list_create(); + if (!this->rekey) + { + /* check if we want a virtual IP */ + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, &vip)) + { + /* propose a 0.0.0.0/0 or ::/0 subnet when we use a virtual IP */ + vip = host_create_any(vip->get_family(vip)); + list->insert_last(list, vip); + } + enumerator->destroy(enumerator); + } + if (list->get_count(list)) + { + this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL, + list); + list->destroy_offset(list, offsetof(host_t, destroy)); + } + else + { + list->destroy(list); + list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE); + this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL, + list); + list->destroy(list); + } + list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE); + this->tsr = child_cfg_select_ts(this->config, FALSE, this->other_ts, NULL, + list); + list->destroy(list); + + if (this->packet_tsi) + { + this->tsi->insert_first(this->tsi, + this->packet_tsi->clone(this->packet_tsi)); + } + if (this->packet_tsr) + { + this->tsr->insert_first(this->tsr, + this->packet_tsr->clone(this->packet_tsr)); + } +} + METHOD(task_t, build_i, status_t, private_child_create_t *this, message_t *message) { - enumerator_t *enumerator; - host_t *vip; - peer_cfg_t *peer_cfg; - linked_list_t *list; bool no_ke = TRUE; switch (message->get_exchange_type(message)) @@ -1477,49 +1568,7 @@ METHOD(task_t, build_i, status_t, return NEED_MORE; } - /* check if we want a virtual IP, but don't have one */ - list = linked_list_create(); - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (!this->rekey) - { - enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); - while (enumerator->enumerate(enumerator, &vip)) - { - /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */ - vip = host_create_any(vip->get_family(vip)); - list->insert_last(list, vip); - } - enumerator->destroy(enumerator); - } - if (list->get_count(list)) - { - this->tsi = this->config->select_traffic_selectors(this->config, TRUE, - NULL, list); - list->destroy_offset(list, offsetof(host_t, destroy)); - } - else - { /* no virtual IPs configured */ - list->destroy(list); - list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE); - this->tsi = this->config->select_traffic_selectors(this->config, TRUE, - NULL, list); - list->destroy(list); - } - list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE); - this->tsr = this->config->select_traffic_selectors(this->config, FALSE, - NULL, list); - list->destroy(list); - - if (this->packet_tsi) - { - this->tsi->insert_first(this->tsi, - this->packet_tsi->clone(this->packet_tsi)); - } - if (this->packet_tsr) - { - this->tsr->insert_first(this->tsr, - this->packet_tsr->clone(this->packet_tsr)); - } + prepare_proposed_ts(this); if (!generic_label_only(this) && !this->child.label) { /* in the simple label mode we propose the configured label as we @@ -2544,6 +2593,61 @@ METHOD(child_create_t, use_label, void, this->child.label = label ? label->clone(label) : NULL; } +/** + * Prepare traffic selectors for reuse when recreating a CHILD_SA. + */ +static void reuse_ts(private_child_create_t *this, bool local, child_sa_t *old, + traffic_selector_list_t **target) +{ + enumerator_t *old_ts, *hosts_enum; + linked_list_t *hosts, *list; + traffic_selector_t *ts, *new_ts; + host_t *host; + + old_ts = old->create_ts_enumerator(old, local); + if (this->rekey) + { + /* when rekeying, we just reuse the previous TS. this is also the only + * way a responder reuses TS */ + *target = traffic_selector_list_create_from_enumerator(old_ts); + return; + } + + /* when recreating/reauthenticating, we check whether the dynamic IPs of + * the IKE_SA (as copied from the old SA) match the TS and replace + * them with dynamic TS (reusing protocol/ports in case of narrowing) so + * they get updated to possibly new IPs when the TS are prepared later */ + list = linked_list_create(); + hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local); + hosts_enum = hosts->create_enumerator(hosts); + while (old_ts->enumerate(old_ts, &ts)) + { + new_ts = NULL; + while (hosts_enum->enumerate(hosts_enum, &host)) + { + if (ts->is_host(ts, host)) + { + new_ts = traffic_selector_create_dynamic(ts->get_protocol(ts), + ts->get_from_port(ts), + ts->get_to_port(ts)); + break; + } + } + hosts->reset_enumerator(hosts, hosts_enum); + + if (!new_ts) + { + new_ts = ts->clone(ts); + } + list->insert_last(list, new_ts); + } + hosts_enum->destroy(hosts_enum); + hosts->destroy(hosts); + old_ts->destroy(old_ts); + + *target = traffic_selector_list_create_from_list(list); +} + METHOD(child_create_t, recreate_sa, void, private_child_create_t *this, child_sa_t *old) { @@ -2560,6 +2664,10 @@ METHOD(child_create_t, recreate_sa, void, this->ke_method = ke_method; } } + + /* use previously negotiated traffic selectors */ + reuse_ts(this, TRUE, old, &this->my_ts); + reuse_ts(this, FALSE, old, &this->other_ts); } METHOD(child_create_t, get_child, child_sa_t*, @@ -2619,32 +2727,17 @@ METHOD(task_t, migrate, void, chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); chunk_free(&this->link); - if (this->tsr) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - if (this->tsi) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - if (this->labels_i) - { - this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy)); - } - if (this->labels_r) - { - this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy)); - } + DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy)); + DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy)); + DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy)); + DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy)); DESTROY_IF(this->child_sa); DESTROY_IF(this->proposal); DESTROY_IF(this->nonceg); DESTROY_IF(this->ke); this->ke_failed = FALSE; clear_key_exchanges(this); - if (this->proposals) - { - this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); - } + DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy)); if (!this->rekey && !this->retry) { this->ke_method = KE_NONE; @@ -2675,22 +2768,10 @@ METHOD(task_t, destroy, void, chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); chunk_free(&this->link); - if (this->tsr) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - if (this->tsi) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - if (this->labels_i) - { - this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy)); - } - if (this->labels_r) - { - this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy)); - } + DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy)); + DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy)); + DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy)); + DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy)); if (!this->established) { DESTROY_IF(this->child_sa); @@ -2701,13 +2782,12 @@ METHOD(task_t, destroy, void, } DESTROY_IF(this->packet_tsi); DESTROY_IF(this->packet_tsr); + DESTROY_IF(this->my_ts); + DESTROY_IF(this->other_ts); DESTROY_IF(this->proposal); DESTROY_IF(this->ke); clear_key_exchanges(this); - if (this->proposals) - { - this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); - } + DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy)); DESTROY_IF(this->config); DESTROY_IF(this->nonceg); DESTROY_IF(this->child.label);