From aaa505dbf06f78d1b29fe179a8cf51bb12a625d1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 29 Jun 2021 16:11:51 +0200 Subject: [PATCH 1/3] 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). --- src/libcharon/sa/ikev2/tasks/ike_natd.c | 101 ++++++++++++------------ 1 file changed, 52 insertions(+), 49 deletions(-) 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, From 90b1d09e42a93736dc1af2f708c9117dfac07593 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 31 Jan 2022 14:13:02 +0100 Subject: [PATCH 2/3] ike-mobike: Make task a no-op if MOBIKE is not supported This allows queuing such a task before IKE_AUTH has been processed. --- src/libcharon/sa/ikev2/tasks/ike_mobike.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index 283ffd082..092e685ac 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -375,6 +375,14 @@ METHOD(task_t, build_i, status_t, { host_t *old, *new; + /* this task might have been queued before we knew if MOBIKE will be + * supported */ + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) + { + message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED); + return SUCCESS; + } + /* we check if the existing address is still valid */ old = message->get_source(message); new = charon->kernel->get_source_addr(charon->kernel, From 011de0fe0dd20c51fe2bfa20a54f4fce8712a190 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 31 Jan 2022 14:17:40 +0100 Subject: [PATCH 3/3] ike-natd: Queue DPD after faking local NAT to check if UDP-encap can be disabled --- src/libcharon/sa/ikev2/tasks/ike_natd.c | 35 +++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index 94f050e5f..7cc964fc7 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -22,7 +22,7 @@ #include #include #include - +#include typedef struct private_ike_natd_t private_ike_natd_t; @@ -255,12 +255,19 @@ 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; + if (this->fake_no_source_ip) + { + if (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 + { /* we are behind a NAT anyway, so nothing special to be done */ + this->fake_no_source_ip = FALSE; + } } else if (this->initiator && this->dst_matched && this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) @@ -291,7 +298,10 @@ METHOD(task_t, process_i, status_t, if (message->get_exchange_type(message) == IKE_SA_INIT) { - peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + peer_cfg_t *peer_cfg; + ike_mobike_t *mobike; + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY) || (peer_cfg->use_mobike(peer_cfg) && this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))) @@ -301,6 +311,15 @@ METHOD(task_t, process_i, status_t, * to actually do the switch (other than a forced DPD) */ this->ike_sa->float_ports(this->ike_sa); } + + if (this->fake_no_source_ip) + { /* the NAT-D payloads revealed that we are not behind a NAT, so + * queue a MOBIKE-enabled DPD to check if UDP-encap can actually be + * disabled once we know if MOBIKE is supported */ + mobike = ike_mobike_create(this->ike_sa, TRUE); + mobike->dpd(mobike); + this->ike_sa->queue_task(this->ike_sa, &mobike->task); + } } return SUCCESS; }