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,