*/
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
*/
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);
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);
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;
}
/**
return NEED_MORE;
}
-METHOD(task_t, build_i, status_t,
- private_child_create_t *this, message_t *message)
+/**
+ * Prepare proposed traffic selectors as initiator.
+ */
+static void prepare_proposed_ts(private_child_create_t *this)
{
enumerator_t *enumerator;
- host_t *vip;
peer_cfg_t *peer_cfg;
linked_list_t *list;
- bool no_ke = TRUE;
+ host_t *vip;
- switch (message->get_exchange_type(message))
- {
- case IKE_SA_INIT:
- return get_nonce(message, &this->my_nonce);
- case CREATE_CHILD_SA:
- if (!generate_nonce(this))
- {
- message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
- return SUCCESS;
- }
- no_ke = FALSE;
- break;
- case IKE_AUTH:
- switch (defer_child_sa(this))
- {
- case DESTROY_ME:
- /* config mismatch */
- return DESTROY_ME;
- case NEED_MORE:
- /* defer until after IKE_SA has been established */
- chunk_free(&this->my_nonce);
- return NEED_MORE;
- default:
- /* just continue to establish the CHILD_SA */
- break;
- }
- /* send only in the first request, not in subsequent rounds */
- this->public.task.build = (void*)return_need_more;
- break;
- default:
- return NEED_MORE;
- }
+ ensure_ts_lists(this);
- /* 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)
{
+ /* 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 virtual ip */
+ /* 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);
}
}
if (list->get_count(list))
{
- this->tsi = this->config->select_traffic_selectors(this->config, TRUE,
- NULL, list);
+ this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, 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);
+ 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 = this->config->select_traffic_selectors(this->config, FALSE,
- NULL, list);
+ this->tsr = child_cfg_select_ts(this->config, FALSE, this->other_ts, NULL,
+ list);
list->destroy(list);
if (this->packet_tsi)
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)
+{
+ bool no_ke = TRUE;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (!generate_nonce(this))
+ {
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ return SUCCESS;
+ }
+ no_ke = FALSE;
+ break;
+ case IKE_AUTH:
+ switch (defer_child_sa(this))
+ {
+ case DESTROY_ME:
+ /* config mismatch */
+ return DESTROY_ME;
+ case NEED_MORE:
+ /* defer until after IKE_SA has been established */
+ chunk_free(&this->my_nonce);
+ return NEED_MORE;
+ default:
+ /* just continue to establish the CHILD_SA */
+ break;
+ }
+ /* send only in the first request, not in subsequent rounds */
+ this->public.task.build = (void*)return_need_more;
+ break;
+ default:
+ return NEED_MORE;
+ }
+
+ prepare_proposed_ts(this);
if (!generic_label_only(this) && !this->child.label)
{ /* in the simple label mode we propose the configured label as we
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)
{
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*,
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;
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);
}
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);