]> git.ipfire.org Git - people/ms/strongswan.git/commitdiff
ike-natd: Fake NAT situation or disable NAT-D if source IP is undetermined
authorTobias Brunner <tobias@strongswan.org>
Tue, 29 Jun 2021 14:11:51 +0000 (16:11 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 15 Feb 2022 15:09:26 +0000 (16:09 +0100)
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

index 008abbb1e54f2dcf9eba2ffc7fa84b58ec6f12ec..94f050e5feded39b6685ee068d212e14cc7bd0f8 100644 (file)
@@ -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,