Don't unset IKE_SA on bus before we released virtual IPs and attributes
[strongswan.git] / src / libcharon / sa / ike_sa.c
index 635528c..d7a9076 100644 (file)
@@ -26,7 +26,7 @@
 #include <library.h>
 #include <hydra.h>
 #include <daemon.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 #include <utils/lexparser.h>
 #include <processing/jobs/retransmit_job.h>
 #include <processing/jobs/delete_ike_sa_job.h>
@@ -285,7 +285,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound)
        enumerator = this->child_sas->create_enumerator(this->child_sas);
        while (enumerator->enumerate(enumerator, &child_sa))
        {
-               child_sa->get_usestats(child_sa, inbound, &current, NULL);
+               child_sa->get_usestats(child_sa, inbound, &current, NULL, NULL);
                use_time = max(use_time, current);
        }
        enumerator->destroy(enumerator);
@@ -575,6 +575,7 @@ METHOD(ike_sa_t, send_dpd, status_t,
 {
        job_t *job;
        time_t diff, delay;
+       bool task_queued = FALSE;
 
        if (this->state == IKE_PASSIVE)
        {
@@ -595,9 +596,10 @@ METHOD(ike_sa_t, send_dpd, status_t,
                diff = now - last_in;
                if (!delay || diff >= delay)
                {
-                       /* to long ago, initiate dead peer detection */
+                       /* too long ago, initiate dead peer detection */
                        DBG1(DBG_IKE, "sending DPD request");
                        this->task_manager->queue_dpd(this->task_manager);
+                       task_queued = TRUE;
                        diff = 0;
                }
        }
@@ -607,7 +609,11 @@ METHOD(ike_sa_t, send_dpd, status_t,
                job = (job_t*)send_dpd_job_create(this->ike_sa_id);
                lib->scheduler->schedule_job(lib->scheduler, job, delay - diff);
        }
-       return this->task_manager->initiate(this->task_manager);
+       if (task_queued)
+       {
+               return this->task_manager->initiate(this->task_manager);
+       }
+       return SUCCESS;
 }
 
 METHOD(ike_sa_t, get_state, ike_sa_state_t,
@@ -735,15 +741,26 @@ METHOD(ike_sa_t, add_virtual_ip, void,
 {
        if (local)
        {
-               DBG1(DBG_IKE, "installing new virtual IP %H", ip);
-               if (hydra->kernel_interface->add_ip(hydra->kernel_interface, ip,
-                                                                                       this->my_host) == SUCCESS)
+               char *iface;
+
+               if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
+                                                                                                  this->my_host, &iface))
                {
-                       this->my_vips->insert_last(this->my_vips, ip->clone(ip));
+                       DBG1(DBG_IKE, "installing new virtual IP %H", ip);
+                       if (hydra->kernel_interface->add_ip(hydra->kernel_interface,
+                                                                                               ip, -1, iface) == SUCCESS)
+                       {
+                               this->my_vips->insert_last(this->my_vips, ip->clone(ip));
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
+                       }
+                       free(iface);
                }
                else
                {
-                       DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
+                       DBG1(DBG_IKE, "looking up interface for virtual IP %H failed", ip);
                }
        }
        else
@@ -759,11 +776,16 @@ METHOD(ike_sa_t, clear_virtual_ips, void,
        linked_list_t *vips = local ? this->my_vips : this->other_vips;
        host_t *vip;
 
+       if (!local && vips->get_count(vips))
+       {
+               charon->bus->assign_vips(charon->bus, &this->public, FALSE);
+       }
        while (vips->remove_first(vips, (void**)&vip) == SUCCESS)
        {
                if (local)
                {
-                       hydra->kernel_interface->del_ip(hydra->kernel_interface, vip);
+                       hydra->kernel_interface->del_ip(hydra->kernel_interface,
+                                                                                       vip, -1, TRUE);
                }
                vip->destroy(vip);
        }
@@ -882,7 +904,7 @@ METHOD(ike_sa_t, update_hosts, void,
        else
        {
                /* update our address in any case */
-               if (!me->equals(me, this->my_host))
+               if (force && !me->equals(me, this->my_host))
                {
                        set_my_host(this, me->clone(me));
                        update = TRUE;
@@ -891,7 +913,8 @@ METHOD(ike_sa_t, update_hosts, void,
                if (!other->equals(other, this->other_host))
                {
                        /* update others address if we are NOT NATed */
-                       if (force || !has_condition(this, COND_NAT_HERE))
+                       if ((has_condition(this, COND_NAT_THERE) &&
+                                !has_condition(this, COND_NAT_HERE)) || force )
                        {
                                set_other_host(this, other->clone(other));
                                update = TRUE;
@@ -921,14 +944,38 @@ METHOD(ike_sa_t, update_hosts, void,
        }
 }
 
+/**
+ * Set configured DSCP value on packet
+ */
+static void set_dscp(private_ike_sa_t *this, packet_t *packet)
+{
+       ike_cfg_t *ike_cfg;
+
+       /* prefer IKE config on peer_cfg, as its selection is more accurate
+        * then the initial IKE config */
+       if (this->peer_cfg)
+       {
+               ike_cfg = this->peer_cfg->get_ike_cfg(this->peer_cfg);
+       }
+       else
+       {
+               ike_cfg = this->ike_cfg;
+       }
+       if (ike_cfg)
+       {
+               packet->set_dscp(packet, ike_cfg->get_dscp(ike_cfg));
+       }
+}
+
 METHOD(ike_sa_t, generate_message, status_t,
        private_ike_sa_t *this, message_t *message, packet_t **packet)
 {
        status_t status;
 
        if (message->is_encoded(message))
-       {       /* already done */
+       {       /* already encoded in task, but set DSCP value */
                *packet = message->get_packet(message);
+               set_dscp(this, *packet);
                return SUCCESS;
        }
        this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
@@ -937,6 +984,7 @@ METHOD(ike_sa_t, generate_message, status_t,
        status = message->generate(message, this->keymat, packet);
        if (status == SUCCESS)
        {
+               set_dscp(this, *packet);
                charon->bus->message(charon->bus, message, FALSE, FALSE);
        }
        return status;
@@ -1207,23 +1255,6 @@ METHOD(ike_sa_t, process_message, status_t,
        {       /* do not handle messages in passive state */
                return FAILED;
        }
-       switch (message->get_exchange_type(message))
-       {
-               case ID_PROT:
-               case AGGRESSIVE:
-               case IKE_SA_INIT:
-               case IKE_AUTH:
-                       if (this->state != IKE_CREATED &&
-                               this->state != IKE_CONNECTING)
-                       {
-                               DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
-                                        exchange_type_names, message->get_exchange_type(message));
-                               return FAILED;
-                       }
-                       break;
-               default:
-                       break;
-       }
        if (message->get_major_version(message) != this->version)
        {
                DBG1(DBG_IKE, "ignoring %N IKEv%u exchange on %N SA",
@@ -1418,6 +1449,10 @@ METHOD(ike_sa_t, delete_, status_t,
                        }
                        /* FALL */
                case IKE_ESTABLISHED:
+                       if (time_monotonic(NULL) >= this->stats[STAT_DELETE])
+                       {       /* IKE_SA hard lifetime hit */
+                               charon->bus->alert(charon->bus, ALERT_IKE_SA_EXPIRED);
+                       }
                        this->task_manager->queue_ike_delete(this->task_manager);
                        return this->task_manager->initiate(this->task_manager);
                case IKE_CREATED:
@@ -1684,6 +1719,8 @@ METHOD(ike_sa_t, retransmit, status_t,
                        {
                                /* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
                                u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
+                               charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
+                                                                  this->keyingtry);
                                this->keyingtry++;
                                if (tries == 0 || tries > this->keyingtry)
                                {
@@ -1726,7 +1763,6 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
        private_ike_sa_t *this, u_int32_t lifetime)
 {
        u_int32_t diff, hard, soft, now;
-       ike_auth_lifetime_t *task;
        bool send_update;
 
        diff = this->peer_cfg->get_over_time(this->peer_cfg);
@@ -1776,12 +1812,16 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
        /* give at least some seconds to reauthenticate */
        this->stats[STAT_DELETE] = max(hard, now + 10);
 
+#ifdef USE_IKEV2
        if (send_update)
        {
+               ike_auth_lifetime_t *task;
+
                task = ike_auth_lifetime_create(&this->public, TRUE);
                this->task_manager->queue_task(this->task_manager, &task->task);
                return this->task_manager->initiate(this->task_manager);
        }
+#endif
        return SUCCESS;
 }
 
@@ -1956,14 +1996,14 @@ METHOD(ike_sa_t, inherit, void,
        this->other_id = other->other_id->clone(other->other_id);
 
        /* apply assigned virtual IPs... */
-       while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS)
+       while (other->my_vips->remove_last(other->my_vips, (void**)&vip) == SUCCESS)
        {
-               other->my_vips->insert_first(other->my_vips, vip);
+               this->my_vips->insert_first(this->my_vips, vip);
        }
-       while (this->other_vips->remove_last(this->other_vips,
-                                                                                (void**)&vip) == SUCCESS)
+       while (other->other_vips->remove_last(other->other_vips,
+                                                                                 (void**)&vip) == SUCCESS)
        {
-               other->other_vips->insert_first(other->other_vips, vip);
+               this->other_vips->insert_first(this->other_vips, vip);
        }
 
        /* authentication information */
@@ -2054,42 +2094,39 @@ METHOD(ike_sa_t, destroy, void,
                free(entry->data.ptr);
                free(entry);
        }
-       this->attributes->destroy(this->attributes);
-
-       this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy));
-
-       /* unset SA after here to avoid usage by the listeners */
-       charon->bus->set_sa(charon->bus, NULL);
-
-       DESTROY_IF(this->keymat);
-
        while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS)
        {
-               hydra->kernel_interface->del_ip(hydra->kernel_interface, vip);
+               hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
                vip->destroy(vip);
        }
-       this->my_vips->destroy(this->my_vips);
+       if (this->other_vips->get_count(this->other_vips))
+       {
+               charon->bus->assign_vips(charon->bus, &this->public, FALSE);
+       }
        while (this->other_vips->remove_last(this->other_vips,
                                                                                 (void**)&vip) == SUCCESS)
        {
                if (this->peer_cfg)
                {
-                       enumerator_t *enumerator;
-                       char *pool;
-
-                       enumerator = this->peer_cfg->create_pool_enumerator(this->peer_cfg);
-                       while (enumerator->enumerate(enumerator, &pool))
-                       {
-                               if (hydra->attributes->release_address(hydra->attributes, pool,
-                                                                                               vip, get_other_eap_id(this)))
-                               {
-                                       break;
-                               }
-                       }
-                       enumerator->destroy(enumerator);
+                       linked_list_t *pools;
+                       identification_t *id;
+
+                       id = get_other_eap_id(this);
+                       pools = linked_list_create_from_enumerator(
+                                               this->peer_cfg->create_pool_enumerator(this->peer_cfg));
+                       hydra->attributes->release_address(hydra->attributes, pools, vip, id);
+                       pools->destroy(pools);
                }
                vip->destroy(vip);
        }
+
+       /* unset SA after here to avoid usage by the listeners */
+       charon->bus->set_sa(charon->bus, NULL);
+
+       this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy));
+       DESTROY_IF(this->keymat);
+       this->attributes->destroy(this->attributes);
+       this->my_vips->destroy(this->my_vips);
        this->other_vips->destroy(this->other_vips);
        this->peer_addresses->destroy_offset(this->peer_addresses,
                                                                                 offsetof(host_t, destroy));