From: Tobias Brunner Date: Tue, 29 Jun 2021 14:11:51 +0000 (+0200) Subject: ike-natd: Fake NAT situation or disable NAT-D if source IP is undetermined X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aaa505dbf06f78d1b29fe179a8cf51bb12a625d1;p=people%2Fms%2Fstrongswan.git ike-natd: Fake NAT situation or disable NAT-D if source IP is undetermined This can happen if an IKE_SA is initiated to a static IP before DHCP is done. Instead of failing the initiation, we either fake a NAT situation (for IPv4) or disable NAT-D (for IPv6 where NATs and UDP-encap are not widely used or supported). This also removes the old fallbacks to determine the source address(es). A source address lookup is done in ike_sa_t::resolve_hosts() (wasn't the case initially) and enumerating local IPs (which was added even earlier) could still lead to issues if e.g. LAN addresses are available but the WAN address that's later used is not yet (in which case only the responder would detect a NAT and UDP-encap would be configured asymmetrically). To force UDP-encap locally in case there is no actual NAT, we store this as COND_NAT_HERE instead of COND_NAT_FAKE. This ensures DPDs will contain NAT-D notifies and we can later remove the state via MOBIKE. We trigger a MOBIKE update after such a DPD by registering a changed NAT mapping after checking for a disappearing local NAT, which is very unlikely to happen outside of a MOBIKE update (where that flag is not checked). --- diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index 008abbb1e..94f050e5f 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -52,12 +52,12 @@ struct private_ike_natd_t { hasher_t *hasher; /** - * Did we process any NAT detection notifys for a source address? + * Did we process any NAT detection notifies for a source address? */ bool src_seen; /** - * Did we process any NAT detection notifys for a destination address? + * Did we process any NAT detection notifies for a destination address? */ bool dst_seen; @@ -72,9 +72,14 @@ struct private_ike_natd_t { bool dst_matched; /** - * whether NAT mappings for our NATed address has changed + * Whether NAT mappings for our NATed address has changed */ bool mapping_changed; + + /** + * Whether we faked a NAT situation because we didn't know our own IP + */ + bool fake_no_source_ip; }; /** @@ -140,7 +145,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, ike_sa_id = this->ike_sa->get_id(this->ike_sa); config = this->ike_sa->get_ike_cfg(this->ike_sa); - if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP) + if (type == NAT_DETECTION_SOURCE_IP && (force_encap(config) || host->is_anyaddr(host))) { uint32_t addr; @@ -168,7 +173,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, } /** - * read notifys from message and evaluate them + * Process NAT detection notifies */ static void process_payloads(private_ike_natd_t *this, message_t *message) { @@ -250,6 +255,22 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) { this->ike_sa->enable_extension(this->ike_sa, EXT_NATT); + if (this->fake_no_source_ip && this->dst_matched) + { /* after we faked a NAT, we now see that we are not behind one. + * we still have to register this as NAT to enable UDP-encap but + * we do so as actual local NAT, so DPDs will contain NAT-D + * payloads and we can remove this condition later via MOBIKE */ + this->dst_matched = FALSE; + } + else if (this->initiator && this->dst_matched && + this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) + { /* we are not behind a NAT anymore, which might be because we + * didn't know our source IP initially. this flag triggers a MOBIKE + * update during a DPD, but it's not checked during MOBIKE updates + * when moving from behind a NAT to a public address */ + this->mapping_changed = TRUE; + } + this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, !this->dst_matched); this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, @@ -272,17 +293,15 @@ METHOD(task_t, process_i, status_t, { peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY) || - /* if peer supports NAT-T, we switch to port 4500 even - * if no NAT is detected. can't be done later (when we would know - * whether the peer supports MOBIKE) because there would be no - * exchange to actually do the switch (other than a forced DPD). */ (peer_cfg->use_mobike(peer_cfg) && this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))) - { + { /* if the peer supports NAT-T, we switch to port 4500 even if no + * NAT is detected. can't be done later (when we would know + * whether the peer supports MOBIKE) because there is no exchange + * to actually do the switch (other than a forced DPD) */ this->ike_sa->float_ports(this->ike_sa); } } - return SUCCESS; } @@ -290,7 +309,6 @@ METHOD(task_t, build_i, status_t, private_ike_natd_t *this, message_t *message) { notify_payload_t *notify; - enumerator_t *enumerator; ike_cfg_t *ike_cfg; host_t *host; @@ -302,22 +320,9 @@ METHOD(task_t, build_i, status_t, ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); - /* destination is always set */ - host = message->get_destination(message); - notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); - if (notify) - { - message->add_payload(message, (payload_t*)notify); - } - - /* source may be any, we have 3 possibilities to get our source address: - * 1. It is defined in the config => use the one of the IKE_SA - * 2. We do a routing lookup in the kernel interface - * 3. Include all possible addresses - */ host = message->get_source(message); if (!host->is_anyaddr(host) || force_encap(ike_cfg)) - { /* 1. or if we force UDP encap, as it doesn't matter if it's %any */ + { notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); if (notify) { @@ -325,36 +330,33 @@ METHOD(task_t, build_i, status_t, } } else - { - host = charon->kernel->get_source_addr(charon->kernel, - this->ike_sa->get_other_host(this->ike_sa), NULL); - if (host) - { /* 2. */ - host->set_port(host, ike_cfg->get_my_port(ike_cfg)); + { /* if we were not able to determine the source address (e.g. because + * DHCP is not done yet), we default to forcing encap for IPv4 and + * disabling NAT-D for IPv6 by not sending any notifies */ + if (host->get_family(host) == AF_INET) + { + DBG1(DBG_IKE, "unable to determine source address, faking NAT " + "situation"); + this->fake_no_source_ip = TRUE; notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); if (notify) { message->add_payload(message, (payload_t*)notify); } - host->destroy(host); } else - { /* 3. */ - enumerator = charon->kernel->create_address_enumerator( - charon->kernel, ADDR_TYPE_REGULAR); - while (enumerator->enumerate(enumerator, (void**)&host)) - { - /* apply port 500 to host, but work on a copy */ - host = host->clone(host); - host->set_port(host, ike_cfg->get_my_port(ike_cfg)); - notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); - host->destroy(host); - if (notify) - { - message->add_payload(message, (payload_t*)notify); - } - } - enumerator->destroy(enumerator); + { + DBG1(DBG_IKE, "unable to determine source address, disabling NAT-D"); + } + } + + if (message->get_notify(message, NAT_DETECTION_SOURCE_IP)) + { + host = message->get_destination(message); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); + if (notify) + { + message->add_payload(message, (payload_t*)notify); } } return NEED_MORE; @@ -421,6 +423,7 @@ METHOD(task_t, migrate, void, this->src_matched = FALSE; this->dst_matched = FALSE; this->mapping_changed = FALSE; + this->fake_no_source_ip = FALSE; } METHOD(task_t, destroy, void,