Don't unset IKE_SA on bus before we released virtual IPs and attributes
[strongswan.git] / src / libcharon / sa / ike_sa.c
index 926e2d1..d7a9076 100644 (file)
 #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>
 #include <processing/jobs/send_dpd_job.h>
 #include <processing/jobs/send_keepalive_job.h>
 #include <processing/jobs/rekey_ike_sa_job.h>
+#include <processing/jobs/retry_initiate_job.h>
 #include <sa/ikev2/tasks/ike_auth_lifetime.h>
 
 #ifdef ME
@@ -181,14 +182,14 @@ struct private_ike_sa_t {
        keymat_t *keymat;
 
        /**
-        * Virtual IP on local host, if any
+        * Virtual IPs on local host
         */
-       host_t *my_virtual_ip;
+       linked_list_t *my_vips;
 
        /**
-        * Virtual IP on remote host, if any
+        * Virtual IPs on remote host
         */
-       host_t *other_virtual_ip;
+       linked_list_t *other_vips;
 
        /**
         * List of configuration attributes (attribute_entry_t)
@@ -216,6 +217,17 @@ struct private_ike_sa_t {
        u_int32_t keepalive_interval;
 
        /**
+        * interval for retries during initiation (e.g. if DNS resolution failed),
+        * 0 to disable (default)
+        */
+       u_int32_t retry_initiate_interval;
+
+       /**
+        * TRUE if a retry_initiate_job has been queued
+        */
+       bool retry_initiate_queued;
+
+       /**
         * Timestamps for this IKE_SA
         */
        u_int32_t stats[STAT_MAX];
@@ -239,11 +251,6 @@ struct private_ike_sa_t {
         * Flush auth configs once established?
         */
        bool flush_auth_cfg;
-
-       /**
-        * TRUE if we are currently reauthenticating this IKE_SA
-        */
-       bool is_reauthenticating;
 };
 
 /**
@@ -278,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);
@@ -476,8 +483,8 @@ METHOD(ike_sa_t, send_keepalive, void,
                data.ptr[0] = 0xFF;
                data.len = 1;
                packet->set_data(packet, data);
-               DBG1(DBG_IKE, "sending keep alive");
-               charon->sender->send(charon->sender, packet);
+               DBG1(DBG_IKE, "sending keep alive to %#H", this->other_host);
+               charon->sender->send_no_marker(charon->sender, packet);
                diff = 0;
        }
        job = send_keepalive_job_create(this->ike_sa_id);
@@ -568,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)
        {
@@ -588,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;
                }
        }
@@ -600,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,
@@ -723,48 +736,69 @@ METHOD(ike_sa_t, get_keymat, keymat_t*,
        return this->keymat;
 }
 
-METHOD(ike_sa_t, set_virtual_ip, void,
+METHOD(ike_sa_t, add_virtual_ip, void,
        private_ike_sa_t *this, bool local, host_t *ip)
 {
        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))
                {
-                       if (this->my_virtual_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, "removing old virtual IP %H", this->my_virtual_ip);
-                               hydra->kernel_interface->del_ip(hydra->kernel_interface,
-                                                                                               this->my_virtual_ip);
+                               DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
                        }
-                       DESTROY_IF(this->my_virtual_ip);
-                       this->my_virtual_ip = ip->clone(ip);
+                       free(iface);
                }
                else
                {
-                       DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
-                       this->my_virtual_ip = NULL;
+                       DBG1(DBG_IKE, "looking up interface for virtual IP %H failed", ip);
                }
        }
        else
        {
-               DESTROY_IF(this->other_virtual_ip);
-               this->other_virtual_ip = ip->clone(ip);
+               this->other_vips->insert_last(this->other_vips, ip->clone(ip));
        }
 }
 
-METHOD(ike_sa_t, get_virtual_ip, host_t*,
+
+METHOD(ike_sa_t, clear_virtual_ips, void,
        private_ike_sa_t *this, bool local)
 {
-       if (local)
+       linked_list_t *vips = local ? this->my_vips : this->other_vips;
+       host_t *vip;
+
+       if (!local && vips->get_count(vips))
        {
-               return this->my_virtual_ip;
+               charon->bus->assign_vips(charon->bus, &this->public, FALSE);
        }
-       else
+       while (vips->remove_first(vips, (void**)&vip) == SUCCESS)
+       {
+               if (local)
+               {
+                       hydra->kernel_interface->del_ip(hydra->kernel_interface,
+                                                                                       vip, -1, TRUE);
+               }
+               vip->destroy(vip);
+       }
+}
+
+METHOD(ike_sa_t, create_virtual_ip_enumerator, enumerator_t*,
+       private_ike_sa_t *this, bool local)
+{
+       if (local)
        {
-               return this->other_virtual_ip;
+               return this->my_vips->create_enumerator(this->my_vips);
        }
+       return this->other_vips->create_enumerator(this->other_vips);
 }
 
 METHOD(ike_sa_t, add_peer_address, void,
@@ -833,9 +867,11 @@ METHOD(ike_sa_t, float_ports, void,
           private_ike_sa_t *this)
 {
        /* do not switch if we have a custom port from MOBIKE/NAT */
-       if (this->my_host->get_port(this->my_host) == IKEV2_UDP_PORT)
+       if (this->my_host->get_port(this->my_host) ==
+                       charon->socket->get_port(charon->socket, FALSE))
        {
-               this->my_host->set_port(this->my_host, IKEV2_NATT_PORT);
+               this->my_host->set_port(this->my_host,
+                                                               charon->socket->get_port(charon->socket, TRUE));
        }
        if (this->other_host->get_port(this->other_host) == IKEV2_UDP_PORT)
        {
@@ -868,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;
@@ -877,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;
@@ -895,7 +932,7 @@ METHOD(ike_sa_t, update_hosts, void,
                while (enumerator->enumerate(enumerator, (void**)&child_sa))
                {
                        if (child_sa->update(child_sa, this->my_host,
-                                               this->other_host, this->my_virtual_ip,
+                                               this->other_host, this->my_vips,
                                                has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
                        {
                                this->public.rekey_child_sa(&this->public,
@@ -907,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);
@@ -923,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;
@@ -1027,8 +1089,12 @@ static void resolve_hosts(private_ike_sa_t *this)
        }
        else
        {
-               host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg),
-                                                               0, this->ike_cfg->get_other_port(this->ike_cfg));
+               char *other_addr;
+               u_int16_t other_port;
+
+               other_addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
+               other_port = this->ike_cfg->get_other_port(this->ike_cfg);
+               host = host_create_from_dns(other_addr, 0, other_port);
        }
        if (host)
        {
@@ -1038,10 +1104,12 @@ static void resolve_hosts(private_ike_sa_t *this)
        if (this->local_host)
        {
                host = this->local_host->clone(this->local_host);
-               host->set_port(host, IKEV2_UDP_PORT);
+               host->set_port(host, charon->socket->get_port(charon->socket, FALSE));
        }
        else
        {
+               char *my_addr;
+               u_int16_t my_port;
                int family = 0;
 
                /* use same address family as for other */
@@ -1049,8 +1117,9 @@ static void resolve_hosts(private_ike_sa_t *this)
                {
                        family = this->other_host->get_family(this->other_host);
                }
-               host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg),
-                                                       family, this->ike_cfg->get_my_port(this->ike_cfg));
+               my_addr = this->ike_cfg->get_my_addr(this->ike_cfg, NULL);
+               my_port = this->ike_cfg->get_my_port(this->ike_cfg);
+               host = host_create_from_dns(my_addr, family, my_port);
 
                if (host && host->is_anyaddr(host) &&
                        !this->other_host->is_anyaddr(this->other_host))
@@ -1064,9 +1133,7 @@ static void resolve_hosts(private_ike_sa_t *this)
                        }
                        else
                        {       /* fallback to address family specific %any(6), if configured */
-                               host = host_create_from_dns(
-                                                               this->ike_cfg->get_my_addr(this->ike_cfg),
-                                                               0, this->ike_cfg->get_my_port(this->ike_cfg));
+                               host = host_create_from_dns(my_addr, 0, my_port);
                        }
                }
        }
@@ -1080,6 +1147,8 @@ METHOD(ike_sa_t, initiate, status_t,
        private_ike_sa_t *this, child_cfg_t *child_cfg, u_int32_t reqid,
        traffic_selector_t *tsi, traffic_selector_t *tsr)
 {
+       bool defer_initiate = FALSE;
+
        if (this->state == IKE_CREATED)
        {
                if (this->my_host->is_anyaddr(this->my_host) ||
@@ -1094,10 +1163,27 @@ METHOD(ike_sa_t, initiate, status_t,
 #endif /* ME */
                        )
                {
-                       DESTROY_IF(child_cfg);
-                       DBG1(DBG_IKE, "unable to initiate to %%any");
-                       charon->bus->alert(charon->bus, ALERT_PEER_ADDR_FAILED);
-                       return DESTROY_ME;
+                       char *addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
+                       bool is_anyaddr = streq(addr, "%any") || streq(addr, "%any6");
+
+                       if (is_anyaddr || !this->retry_initiate_interval)
+                       {
+                               if (is_anyaddr)
+                               {
+                                       DBG1(DBG_IKE, "unable to initiate to %s", addr);
+                               }
+                               else
+                               {
+                                       DBG1(DBG_IKE, "unable to resolve %s, initiate aborted",
+                                                addr);
+                               }
+                               DESTROY_IF(child_cfg);
+                               charon->bus->alert(charon->bus, ALERT_PEER_ADDR_FAILED);
+                               return DESTROY_ME;
+                       }
+                       DBG1(DBG_IKE, "unable to resolve %s, retrying in %ds",
+                                addr, this->retry_initiate_interval);
+                       defer_initiate = TRUE;
                }
 
                set_condition(this, COND_ORIGINAL_INITIATOR, TRUE);
@@ -1134,9 +1220,32 @@ METHOD(ike_sa_t, initiate, status_t,
 #endif /* ME */
        }
 
+       if (defer_initiate)
+       {
+               if (!this->retry_initiate_queued)
+               {
+                       job_t *job = (job_t*)retry_initiate_job_create(this->ike_sa_id);
+                       lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+                                                                                this->retry_initiate_interval);
+                       this->retry_initiate_queued = TRUE;
+               }
+               return SUCCESS;
+       }
+       this->retry_initiate_queued = FALSE;
        return this->task_manager->initiate(this->task_manager);
 }
 
+METHOD(ike_sa_t, retry_initiate, status_t,
+       private_ike_sa_t *this)
+{
+       if (this->retry_initiate_queued)
+       {
+               this->retry_initiate_queued = FALSE;
+               return initiate(this, NULL, 0, NULL, NULL);
+       }
+       return SUCCESS;
+}
+
 METHOD(ike_sa_t, process_message, status_t,
        private_ike_sa_t *this, message_t *message)
 {
@@ -1340,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:
@@ -1380,7 +1493,7 @@ METHOD(ike_sa_t, reauth, status_t,
        if (!has_condition(this, COND_ORIGINAL_INITIATOR))
        {
                DBG1(DBG_IKE, "initiator did not reauthenticate as requested");
-               if (this->other_virtual_ip != NULL ||
+               if (this->other_vips->get_count(this->other_vips) != 0 ||
                        has_condition(this, COND_XAUTH_AUTHENTICATED) ||
                        has_condition(this, COND_EAP_AUTHENTICATED)
 #ifdef ME
@@ -1408,7 +1521,7 @@ METHOD(ike_sa_t, reauth, status_t,
                DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d]",
                         get_name(this), this->unique_id);
        }
-       this->is_reauthenticating = TRUE;
+       set_condition(this, COND_REAUTHENTICATING, TRUE);
        this->task_manager->queue_ike_reauth(this->task_manager);
        return this->task_manager->initiate(this->task_manager);
 }
@@ -1425,7 +1538,7 @@ METHOD(ike_sa_t, reestablish, status_t,
        bool restart = FALSE;
        status_t status = FAILED;
 
-       if (this->is_reauthenticating)
+       if (has_condition(this, COND_REAUTHENTICATING))
        {       /* only reauthenticate if we have children */
                if (this->child_sas->get_count(this->child_sas) == 0
 #ifdef ME
@@ -1484,7 +1597,7 @@ METHOD(ike_sa_t, reestablish, status_t,
 
        /* check if we are able to reestablish this IKE_SA */
        if (!has_condition(this, COND_ORIGINAL_INITIATOR) &&
-               (this->other_virtual_ip != NULL ||
+               (this->other_vips->get_count(this->other_vips) != 0 ||
                 has_condition(this, COND_EAP_AUTHENTICATED)
 #ifdef ME
                 || this->is_mediation_server
@@ -1507,11 +1620,12 @@ METHOD(ike_sa_t, reestablish, status_t,
        host = this->my_host;
        new->set_my_host(new, host->clone(host));
        /* if we already have a virtual IP, we reuse it */
-       host = this->my_virtual_ip;
-       if (host)
+       enumerator = this->my_vips->create_enumerator(this->my_vips);
+       while (enumerator->enumerate(enumerator, &host))
        {
-               new->set_virtual_ip(new, TRUE, host);
+               new->add_virtual_ip(new, TRUE, host);
        }
+       enumerator->destroy(enumerator);
 
 #ifdef ME
        if (this->peer_cfg->is_mediation(this->peer_cfg))
@@ -1524,7 +1638,7 @@ METHOD(ike_sa_t, reestablish, status_t,
                enumerator = this->child_sas->create_enumerator(this->child_sas);
                while (enumerator->enumerate(enumerator, (void**)&child_sa))
                {
-                       if (this->is_reauthenticating)
+                       if (has_condition(this, COND_REAUTHENTICATING))
                        {
                                switch (child_sa->get_state(child_sa))
                                {
@@ -1580,6 +1694,7 @@ METHOD(ike_sa_t, reestablish, status_t,
        }
        else
        {
+               charon->bus->ike_reestablish(charon->bus, &this->public, new);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
                status = SUCCESS;
        }
@@ -1604,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)
                                {
@@ -1619,7 +1736,7 @@ METHOD(ike_sa_t, retransmit, status_t,
                        }
                        case IKE_DELETING:
                                DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
-                               if (this->is_reauthenticating)
+                               if (has_condition(this, COND_REAUTHENTICATING))
                                {
                                        DBG1(DBG_IKE, "delete during reauthentication failed, "
                                                 "trying to reestablish IKE_SA anyway");
@@ -1633,6 +1750,10 @@ METHOD(ike_sa_t, retransmit, status_t,
                                reestablish(this);
                                break;
                }
+               if (this->state != IKE_CONNECTING)
+               {
+                       charon->bus->ike_updown(charon->bus, &this->public, FALSE);
+               }
                return DESTROY_ME;
        }
        return SUCCESS;
@@ -1642,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);
@@ -1654,7 +1774,7 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
         * We send the notify in IKE_AUTH if not yet ESTABLISHED. */
        send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 &&
                                  !has_condition(this, COND_ORIGINAL_INITIATOR) &&
-                                 (this->other_virtual_ip != NULL ||
+                                 (this->other_vips->get_count(this->other_vips) != 0 ||
                                  has_condition(this, COND_EAP_AUTHENTICATED));
 
        if (lifetime < diff)
@@ -1692,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;
 }
 
@@ -1815,6 +1939,8 @@ METHOD(ike_sa_t, roam, status_t,
                return SUCCESS;
        }
        DBG1(DBG_IKE, "reauthenticating IKE_SA due to address change");
+       /* since our previous path is not valid anymore, try and find a new one */
+       resolve_hosts(this);
        return reauth(this);
 }
 
@@ -1857,6 +1983,7 @@ METHOD(ike_sa_t, inherit, void,
        attribute_entry_t *entry;
        enumerator_t *enumerator;
        auth_cfg_t *cfg;
+       host_t *vip;
 
        /* apply hosts and ids */
        this->my_host->destroy(this->my_host);
@@ -1868,16 +1995,15 @@ METHOD(ike_sa_t, inherit, void,
        this->my_id = other->my_id->clone(other->my_id);
        this->other_id = other->other_id->clone(other->other_id);
 
-       /* apply virtual assigned IPs... */
-       if (other->my_virtual_ip)
+       /* apply assigned virtual IPs... */
+       while (other->my_vips->remove_last(other->my_vips, (void**)&vip) == SUCCESS)
        {
-               this->my_virtual_ip = other->my_virtual_ip;
-               other->my_virtual_ip = NULL;
+               this->my_vips->insert_first(this->my_vips, vip);
        }
-       if (other->other_virtual_ip)
+       while (other->other_vips->remove_last(other->other_vips,
+                                                                                 (void**)&vip) == SUCCESS)
        {
-               this->other_virtual_ip = other->other_virtual_ip;
-               other->other_virtual_ip = NULL;
+               this->other_vips->insert_first(this->other_vips, vip);
        }
 
        /* authentication information */
@@ -1952,6 +2078,7 @@ METHOD(ike_sa_t, destroy, void,
        private_ike_sa_t *this)
 {
        attribute_entry_t *entry;
+       host_t *vip;
 
        charon->bus->set_sa(charon->bus, &this->public);
 
@@ -1967,31 +2094,40 @@ 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);
-
-       if (this->my_virtual_ip)
+       while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS)
        {
-               hydra->kernel_interface->del_ip(hydra->kernel_interface,
-                                                                               this->my_virtual_ip);
-               this->my_virtual_ip->destroy(this->my_virtual_ip);
+               hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
+               vip->destroy(vip);
        }
-       if (this->other_virtual_ip)
+       if (this->other_vips->get_count(this->other_vips))
        {
-               if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg))
+               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)
                {
-                       hydra->attributes->release_address(hydra->attributes,
-                                                       this->peer_cfg->get_pool(this->peer_cfg),
-                                                       this->other_virtual_ip, get_other_eap_id(this));
+                       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);
                }
-               this->other_virtual_ip->destroy(this->other_virtual_ip);
+               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));
 #ifdef ME
@@ -2054,6 +2190,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                        .set_statistic = _set_statistic,
                        .process_message = _process_message,
                        .initiate = _initiate,
+                       .retry_initiate = _retry_initiate,
                        .get_ike_cfg = _get_ike_cfg,
                        .set_ike_cfg = _set_ike_cfg,
                        .get_peer_cfg = _get_peer_cfg,
@@ -2109,8 +2246,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                        .generate_message = _generate_message,
                        .reset = _reset,
                        .get_unique_id = _get_unique_id,
-                       .set_virtual_ip = _set_virtual_ip,
-                       .get_virtual_ip = _get_virtual_ip,
+                       .add_virtual_ip = _add_virtual_ip,
+                       .clear_virtual_ips = _clear_virtual_ips,
+                       .create_virtual_ip_enumerator = _create_virtual_ip_enumerator,
                        .add_configuration_attribute = _add_configuration_attribute,
                        .set_kmaddress = _set_kmaddress,
                        .create_task_enumerator = _create_task_enumerator,
@@ -2145,9 +2283,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                .other_auths = linked_list_create(),
                .unique_id = ++unique_id,
                .peer_addresses = linked_list_create(),
+               .my_vips = linked_list_create(),
+               .other_vips = linked_list_create(),
                .attributes = linked_list_create(),
                .keepalive_interval = lib->settings->get_time(lib->settings,
                                                        "%s.keep_alive", KEEPALIVE_INTERVAL, charon->name),
+               .retry_initiate_interval = lib->settings->get_time(lib->settings,
+                                                       "%s.retry_initiate_interval", 0, charon->name),
                .flush_auth_cfg = lib->settings->get_bool(lib->settings,
                                                        "%s.flush_auth_cfg", FALSE, charon->name),
        );
@@ -2158,7 +2300,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
        }
 
        this->task_manager = task_manager_create(&this->public);
-       this->my_host->set_port(this->my_host, IKEV2_UDP_PORT);
+       this->my_host->set_port(this->my_host,
+                                                       charon->socket->get_port(charon->socket, FALSE));
 
        if (!this->task_manager || !this->keymat)
        {