Merge branch 'mobike-fixes'
authorTobias Brunner <tobias@strongswan.org>
Fri, 12 Sep 2014 08:35:06 +0000 (10:35 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 12 Sep 2014 08:40:07 +0000 (10:40 +0200)
These changes improve the handling of MOBIKE tasks, for instance, when
retransmitting and no path is available.

Fixes #632.

src/libcharon/sa/ike_sa.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/ikev2/tasks/ike_mobike.c
src/libcharon/sa/ikev2/tasks/ike_mobike.h

index e63e0fa..516b243 100644 (file)
@@ -1911,11 +1911,29 @@ static bool is_any_path_valid(private_ike_sa_t *this)
        bool valid = FALSE;
        enumerator_t *enumerator;
        host_t *src = NULL, *addr;
+       int family = AF_UNSPEC;
+
+       switch (charon->socket->supported_families(charon->socket))
+       {
+               case SOCKET_FAMILY_IPV4:
+                       family = AF_INET;
+                       break;
+               case SOCKET_FAMILY_IPV6:
+                       family = AF_INET6;
+                       break;
+               case SOCKET_FAMILY_BOTH:
+               case SOCKET_FAMILY_NONE:
+                       break;
+       }
 
        DBG1(DBG_IKE, "old path is not available anymore, try to find another");
        enumerator = create_peer_address_enumerator(this);
        while (enumerator->enumerate(enumerator, &addr))
        {
+               if (family != AF_UNSPEC && addr->get_family(addr) != family)
+               {
+                       continue;
+               }
                DBG1(DBG_IKE, "looking for a route to %H ...", addr);
                src = hydra->kernel_interface->get_source_addr(
                                                                                hydra->kernel_interface, addr, NULL);
index ada798b..58f6bc6 100644 (file)
@@ -120,6 +120,11 @@ struct private_task_manager_t {
                 */
                exchange_type_t type;
 
+               /**
+                * TRUE if exchange was deferred because no path was available
+                */
+               bool deferred;
+
        } initiating;
 
        /**
@@ -236,16 +241,12 @@ METHOD(task_manager_t, retransmit, status_t,
                        if (task->get_type(task) == TASK_IKE_MOBIKE)
                        {
                                mobike = (ike_mobike_t*)task;
-                               if (!mobike->is_probing(mobike))
-                               {
-                                       mobike = NULL;
-                               }
                                break;
                        }
                }
                enumerator->destroy(enumerator);
 
-               if (mobike == NULL)
+               if (!mobike || !mobike->is_probing(mobike))
                {
                        if (this->initiating.retransmitted <= this->retransmit_tries)
                        {
@@ -268,8 +269,26 @@ METHOD(task_manager_t, retransmit, status_t,
                                charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND,
                                                                   this->initiating.packet);
                        }
-                       packet = this->initiating.packet->clone(this->initiating.packet);
-                       charon->sender->send(charon->sender, packet);
+                       if (!mobike)
+                       {
+                               packet = this->initiating.packet->clone(this->initiating.packet);
+                               charon->sender->send(charon->sender, packet);
+                       }
+                       else
+                       {
+                               if (!mobike->transmit(mobike, this->initiating.packet))
+                               {
+                                       DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
+                                                "deferred");
+                                       this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
+                                       this->initiating.deferred = TRUE;
+                                       return SUCCESS;
+                               }
+                               else if (mobike->is_probing(mobike))
+                               {
+                                       timeout = ROUTEABILITY_CHECK_INTERVAL;
+                               }
+                       }
                }
                else
                {       /* for routeability checks, we use a more aggressive behavior */
@@ -289,7 +308,14 @@ METHOD(task_manager_t, retransmit, status_t,
                                DBG1(DBG_IKE, "path probing attempt %d",
                                         this->initiating.retransmitted);
                        }
-                       mobike->transmit(mobike, this->initiating.packet);
+                       if (!mobike->transmit(mobike, this->initiating.packet))
+                       {
+                               DBG1(DBG_IKE, "no route found to reach peer, path probing "
+                                        "deferred");
+                               this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
+                               this->initiating.deferred = TRUE;
+                               return SUCCESS;
+                       }
                }
 
                this->initiating.retransmitted++;
@@ -315,6 +341,12 @@ METHOD(task_manager_t, initiate, status_t,
                DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
                                exchange_type_names, this->initiating.type);
                /* do not initiate if we already have a message in the air */
+               if (this->initiating.deferred)
+               {       /* re-initiate deferred exchange */
+                       this->initiating.deferred = FALSE;
+                       this->initiating.retransmitted = 0;
+                       return retransmit(this, this->initiating.mid);
+               }
                return SUCCESS;
        }
 
@@ -458,6 +490,7 @@ METHOD(task_manager_t, initiate, status_t,
        message->set_exchange_type(message, exchange);
        this->initiating.type = exchange;
        this->initiating.retransmitted = 0;
+       this->initiating.deferred = FALSE;
 
        enumerator = array_create_enumerator(this->active_tasks);
        while (enumerator->enumerate(enumerator, &task))
@@ -1246,6 +1279,8 @@ METHOD(task_manager_t, process_message, status_t,
 METHOD(task_manager_t, queue_task, void,
        private_task_manager_t *this, task_t *task)
 {
+       int pos = ARRAY_TAIL;
+
        if (task->get_type(task) == TASK_IKE_MOBIKE)
        {       /*  there is no need to queue more than one mobike task */
                enumerator_t *enumerator;
@@ -1262,9 +1297,12 @@ METHOD(task_manager_t, queue_task, void,
                        }
                }
                enumerator->destroy(enumerator);
+               /* insert MOBIKE tasks first as we currently might not have a usable
+                * path to initiate any other tasks */
+               pos = ARRAY_HEAD;
        }
        DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
-       array_insert(this->queued_tasks, ARRAY_TAIL, task);
+       array_insert(this->queued_tasks, pos, task);
 }
 
 /**
@@ -1368,7 +1406,25 @@ METHOD(task_manager_t, queue_mobike, void,
        mobike = ike_mobike_create(this->ike_sa, TRUE);
        if (roam)
        {
+               enumerator_t *enumerator;
+               task_t *current;
+
                mobike->roam(mobike, address);
+
+               /* enable path probing for a currently active MOBIKE task.  This might
+                * not be the case if an address appeared on a new interface while the
+                * current address is not working but has not yet disappeared. */
+               enumerator = array_create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, &current))
+               {
+                       if (current->get_type(current) == TASK_IKE_MOBIKE)
+                       {
+                               ike_mobike_t *active = (ike_mobike_t*)current;
+                               active->enable_probing(active);
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
        }
        else
        {
index 00ca615..d91fa58 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Tobias Brunner
+ * Copyright (C) 2010-2014 Tobias Brunner
  * Copyright (C) 2007 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -77,6 +77,11 @@ struct private_ike_mobike_t {
         * additional addresses got updated
         */
        bool addresses_updated;
+
+       /**
+        * whether the pending updates counter was increased
+        */
+       bool pending_update;
 };
 
 /**
@@ -301,35 +306,61 @@ static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local)
        host->set_port(host, port);
 }
 
-METHOD(ike_mobike_t, transmit, void,
+METHOD(ike_mobike_t, transmit, bool,
           private_ike_mobike_t *this, packet_t *packet)
 {
        host_t *me, *other, *me_old, *other_old;
        enumerator_t *enumerator;
        ike_cfg_t *ike_cfg;
        packet_t *copy;
+       int family = AF_UNSPEC;
+       bool found = FALSE;
+
+       me_old = this->ike_sa->get_my_host(this->ike_sa);
+       other_old = this->ike_sa->get_other_host(this->ike_sa);
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
 
        if (!this->check)
        {
-               return;
+               me = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
+                                                                                                         other_old, me_old);
+               if (me)
+               {
+                       if (me->ip_equals(me, me_old))
+                       {
+                               charon->sender->send(charon->sender, packet->clone(packet));
+                               me->destroy(me);
+                               return TRUE;
+                       }
+                       me->destroy(me);
+               }
+               this->check = TRUE;
        }
 
-       me_old = this->ike_sa->get_my_host(this->ike_sa);
-       other_old = this->ike_sa->get_other_host(this->ike_sa);
-       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+       switch (charon->socket->supported_families(charon->socket))
+       {
+               case SOCKET_FAMILY_IPV4:
+                       family = AF_INET;
+                       break;
+               case SOCKET_FAMILY_IPV6:
+                       family = AF_INET6;
+                       break;
+               case SOCKET_FAMILY_BOTH:
+               case SOCKET_FAMILY_NONE:
+                       break;
+       }
 
        enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa);
        while (enumerator->enumerate(enumerator, (void**)&other))
        {
+               if (family != AF_UNSPEC && other->get_family(other) != family)
+               {
+                       continue;
+               }
                me = hydra->kernel_interface->get_source_addr(
                                                                                hydra->kernel_interface, other, NULL);
                if (me)
                {
-                       if (me->get_family(me) != other->get_family(other))
-                       {
-                               me->destroy(me);
-                               continue;
-                       }
                        /* reuse port for an active address, 4500 otherwise */
                        apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg), TRUE);
                        other = other->clone(other);
@@ -339,9 +370,11 @@ METHOD(ike_mobike_t, transmit, void,
                        copy->set_source(copy, me);
                        copy->set_destination(copy, other);
                        charon->sender->send(charon->sender, copy);
+                       found = TRUE;
                }
        }
        enumerator->destroy(enumerator);
+       return found;
 }
 
 METHOD(task_t, build_i, status_t,
@@ -481,9 +514,7 @@ METHOD(task_t, process_i, status_t,
        }
        else if (message->get_exchange_type(message) == INFORMATIONAL)
        {
-               u_int32_t updates = this->ike_sa->get_pending_updates(this->ike_sa) - 1;
-               this->ike_sa->set_pending_updates(this->ike_sa, updates);
-               if (updates > 0)
+               if (this->ike_sa->get_pending_updates(this->ike_sa) > 1)
                {
                        /* newer update queued, ignore this one */
                        return SUCCESS;
@@ -560,7 +591,6 @@ METHOD(task_t, process_i, status_t,
                                        this->natd = ike_natd_create(this->ike_sa, this->initiator);
                                }
                                this->check = FALSE;
-                               this->ike_sa->set_pending_updates(this->ike_sa, 1);
                                return NEED_MORE;
                        }
                }
@@ -573,8 +603,12 @@ METHOD(ike_mobike_t, addresses, void,
           private_ike_mobike_t *this)
 {
        this->address = TRUE;
-       this->ike_sa->set_pending_updates(this->ike_sa,
+       if (!this->pending_update)
+       {
+               this->pending_update = TRUE;
+               this->ike_sa->set_pending_updates(this->ike_sa,
                                                this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+       }
 }
 
 METHOD(ike_mobike_t, roam, void,
@@ -582,8 +616,12 @@ METHOD(ike_mobike_t, roam, void,
 {
        this->check = TRUE;
        this->address = address;
-       this->ike_sa->set_pending_updates(this->ike_sa,
+       if (!this->pending_update)
+       {
+               this->pending_update = TRUE;
+               this->ike_sa->set_pending_updates(this->ike_sa,
                                                this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+       }
 }
 
 METHOD(ike_mobike_t, dpd, void,
@@ -593,8 +631,12 @@ METHOD(ike_mobike_t, dpd, void,
        {
                this->natd = ike_natd_create(this->ike_sa, this->initiator);
        }
-       this->ike_sa->set_pending_updates(this->ike_sa,
+       if (!this->pending_update)
+       {
+               this->pending_update = TRUE;
+               this->ike_sa->set_pending_updates(this->ike_sa,
                                                this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+       }
 }
 
 METHOD(ike_mobike_t, is_probing, bool,
@@ -603,6 +645,12 @@ METHOD(ike_mobike_t, is_probing, bool,
        return this->check;
 }
 
+METHOD(ike_mobike_t, enable_probing, void,
+       private_ike_mobike_t *this)
+{
+       this->check = TRUE;
+}
+
 METHOD(task_t, get_type, task_type_t,
           private_ike_mobike_t *this)
 {
@@ -618,11 +666,21 @@ METHOD(task_t, migrate, void,
        {
                this->natd->task.migrate(&this->natd->task, ike_sa);
        }
+       if (this->pending_update)
+       {
+               this->ike_sa->set_pending_updates(this->ike_sa,
+                                               this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+       }
 }
 
 METHOD(task_t, destroy, void,
           private_ike_mobike_t *this)
 {
+       if (this->pending_update)
+       {
+               this->ike_sa->set_pending_updates(this->ike_sa,
+                                               this->ike_sa->get_pending_updates(this->ike_sa) - 1);
+       }
        chunk_free(&this->cookie2);
        if (this->natd)
        {
@@ -650,6 +708,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
                        .dpd = _dpd,
                        .transmit = _transmit,
                        .is_probing = _is_probing,
+                       .enable_probing = _enable_probing,
                },
                .ike_sa = ike_sa,
                .initiator = initiator,
index b145a9a..bb2318c 100644 (file)
@@ -70,8 +70,9 @@ struct ike_mobike_t {
         * probing.
         *
         * @param packet                the packet to transmit
+        * @return                              TRUE if transmitted, FALSE if no path found
         */
-       void (*transmit)(ike_mobike_t *this, packet_t *packet);
+       bool (*transmit)(ike_mobike_t *this, packet_t *packet);
 
        /**
         * Check if this task is probing for routability.
@@ -79,6 +80,11 @@ struct ike_mobike_t {
         * @return                              TRUE if task is probing
         */
        bool (*is_probing)(ike_mobike_t *this);
+
+       /**
+        * Enable probing for routability.
+        */
+       void (*enable_probing)(ike_mobike_t *this);
 };
 
 /**