From: Thomas Egerer Date: Thu, 22 Mar 2018 17:17:48 +0000 (+0100) Subject: ike: For NATted connections use host from traffic selector X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9ba739cb1ef683f87dae05ded5178dfa84fbb3eb;p=thirdparty%2Fstrongswan.git ike: For NATted connections use host from traffic selector When resolving dynamic hosts with no pool configured, the code used to replace the remote IP with the peer's perceived address. This fails for road warriors behind NAT. Hence this patch uses the address from the remote traffic selector proposed by the peer if NAT was detected and the address(es) proposed are /32 or /128 subnets to the same host. Signed-off-by: Thomas Egerer --- diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 77592e59ae..1ec88befaa 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -244,14 +244,33 @@ static bool have_pool(ike_sa_t *ike_sa) return found; } +/** + * Get the host from the first (only) traffc selector if peer is behind a NAT + */ +static host_t *get_nat_host(linked_list_t *proposed) +{ + host_t *host = NULL; + traffic_selector_t *ts; + uint8_t mask; + + if ((proposed->get_first(proposed, (void**)&ts) == SUCCESS) && + ts->is_host(ts, NULL)) + { + ts->to_subnet(ts, &host, &mask); + } + return host; +} + /** * Get hosts to use for dynamic traffic selectors */ -static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) +static linked_list_t *get_dynamic_hosts(private_quick_mode_t *this, + bool local, linked_list_t *proposed) { + ike_sa_t *ike_sa = this->ike_sa; enumerator_t *enumerator; linked_list_t *list; - host_t *host; + host_t *host = NULL; list = linked_list_create(); enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); @@ -261,16 +280,26 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) } enumerator->destroy(enumerator); - if (list->get_count(list) == 0) + if (!list->get_count(list)) { /* no virtual IPs assigned */ if (local) { host = ike_sa->get_my_host(ike_sa); - list->insert_last(list, host); } else if (!have_pool(ike_sa)) { /* use host only if we don't have a pool configured */ - host = ike_sa->get_other_host(ike_sa); + if (proposed && this->mode == MODE_TUNNEL && + ike_sa->has_condition(ike_sa, COND_NAT_THERE)) + { + host = get_nat_host(proposed); + } + if (!host) + { + host = ike_sa->get_other_host(ike_sa); + } + } + if (host) + { list->insert_last(list, host); } } @@ -542,7 +571,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, traffic_selector_t *ts; linked_list_t *list, *hosts; - hosts = get_dynamic_hosts(this->ike_sa, local); + hosts = get_dynamic_hosts(this, local, supplied); list = this->config->get_traffic_selectors(this->config, local, supplied, hosts); hosts->destroy(hosts); @@ -1088,8 +1117,8 @@ METHOD(task_t, process_r, status_t, tsi = linked_list_create_with_items(this->tsi, NULL); tsr = linked_list_create_with_items(this->tsr, NULL); this->tsi = this->tsr = NULL; - hostsi = get_dynamic_hosts(this->ike_sa, FALSE); - hostsr = get_dynamic_hosts(this->ike_sa, TRUE); + hostsi = get_dynamic_hosts(this, FALSE, tsi); + hostsr = get_dynamic_hosts(this, TRUE, NULL); this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, hostsr, hostsi); hostsi->destroy(hostsi); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index c90af23b99..78603d42cb 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -388,14 +388,44 @@ static bool have_pool(ike_sa_t *ike_sa) return found; } +/** + * Return the single host proposed by the peer if it is behind a NAT + */ +static host_t *get_nat_host(linked_list_t *supplied) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + host_t *host = NULL; + uint8_t mask; + + enumerator = supplied->create_enumerator(supplied); + while (enumerator->enumerate(enumerator, &ts)) + { /* all supplied traffic selectors must be to the same host */ + if (!ts->is_host(ts, host)) + { + DESTROY_IF(host); + host = NULL; + break; + } + if (!host) + { + ts->to_subnet(ts, &host, &mask); + } + } + enumerator->destroy(enumerator); + return host; +} + /** * Get hosts to use for dynamic traffic selectors */ -static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) +static linked_list_t *get_dynamic_hosts(private_child_create_t *this, + bool local, linked_list_t *supplied) { + ike_sa_t *ike_sa = this->ike_sa; enumerator_t *enumerator; linked_list_t *list; - host_t *host; + host_t *host = NULL; list = linked_list_create(); enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); @@ -405,16 +435,26 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) } enumerator->destroy(enumerator); - if (list->get_count(list) == 0) + if (!list->get_count(list)) { /* no virtual IPs assigned */ if (local) { host = ike_sa->get_my_host(ike_sa); - list->insert_last(list, host); } else if (!have_pool(ike_sa)) { /* use host only if we don't have a pool configured */ - host = ike_sa->get_other_host(ike_sa); + if (supplied && this->mode == MODE_TUNNEL && + ike_sa->has_condition(ike_sa, COND_NAT_THERE)) + { + host = get_nat_host(supplied); + } + if (!host) + { + host = ike_sa->get_other_host(ike_sa); + } + } + if (host) + { list->insert_last(list, host); } } @@ -475,7 +515,7 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, ike_condition_t cond; cond = local ? COND_NAT_HERE : COND_NAT_THERE; - hosts = get_dynamic_hosts(this->ike_sa, local); + hosts = get_dynamic_hosts(this, local, in); if (this->mode == MODE_TRANSPORT && this->ike_sa->has_condition(this->ike_sa, cond)) @@ -1081,12 +1121,12 @@ METHOD(task_t, build_i, status_t, else { /* no virtual IPs configured */ list->destroy(list); - list = get_dynamic_hosts(this->ike_sa, TRUE); + list = get_dynamic_hosts(this, TRUE, NULL); this->tsi = this->config->get_traffic_selectors(this->config, TRUE, NULL, list); list->destroy(list); } - list = get_dynamic_hosts(this->ike_sa, FALSE); + list = get_dynamic_hosts(this, FALSE, NULL); this->tsr = this->config->get_traffic_selectors(this->config, FALSE, NULL, list); list->destroy(list); @@ -1269,8 +1309,8 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this) tsr = get_ts_if_nat_transport(this, TRUE, this->tsr); tsi = get_ts_if_nat_transport(this, FALSE, this->tsi); - listr = get_dynamic_hosts(this->ike_sa, TRUE); - listi = get_dynamic_hosts(this->ike_sa, FALSE); + listr = get_dynamic_hosts(this, TRUE, NULL); + listi = get_dynamic_hosts(this, FALSE, this->tsi); child_cfg = peer_cfg->select_child_cfg(peer_cfg, tsr ?: this->tsr, tsi ?: this->tsi, listr, listi);