implemented routeability checks for mobike (experimental)
authorMartin Willi <martin@strongswan.org>
Mon, 3 Sep 2007 12:37:25 +0000 (12:37 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 3 Sep 2007 12:37:25 +0000 (12:37 -0000)
src/charon/kernel/kernel_interface.c
src/charon/sa/ike_sa.c
src/charon/sa/task_manager.c
src/charon/sa/tasks/ike_mobike.c
src/charon/sa/tasks/ike_mobike.h

index 64448e6..83f542e 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/xfrm.h>
@@ -67,6 +68,9 @@
 #define PRIO_LOW 3000
 #define PRIO_HIGH 2000
 
+/** delay before firing roam jobs (ms) */
+#define ROAM_DELAY 100
+
 #define BUFFER_SIZE 1024
 
 /**
@@ -344,6 +348,11 @@ struct private_kernel_interface_t {
         * Netlink rt socket to receive address change events
         */
        int socket_rt_events;
+       
+       /**
+        * time of the last roam_job
+        */
+       struct timeval last_roam;
 };
 
 /**
@@ -528,6 +537,31 @@ static void process_expire(private_kernel_interface_t *this, struct nlmsghdr *hd
 }
 
 /**
+ * start a roaming job. We delay it for a second and fire only one job
+ * for multiple events. Otherwise we would create two many jobs.
+ */
+static void fire_roam_job(private_kernel_interface_t *this, bool address)
+{
+       struct timeval now;
+               
+       if (gettimeofday(&now, NULL) == 0)
+       {
+               if (timercmp(&now, &this->last_roam, >))
+               {
+                       now.tv_usec += ROAM_DELAY * 1000;
+                       while (now.tv_usec > 1000000)
+                       {
+                               now.tv_sec++;
+                               now.tv_usec -= 1000000;
+                       }
+                       this->last_roam = now;
+                       charon->scheduler->schedule_job(charon->scheduler,
+                                       (job_t*)roam_job_create(address), ROAM_DELAY);
+               }
+       }
+}
+
+/**
  * process RTM_NEWLINK/RTM_DELLINK from kernel
  */
 static void process_link(private_kernel_interface_t *this,
@@ -623,8 +657,7 @@ static void process_link(private_kernel_interface_t *this,
        /* send an update to all IKE_SAs */
        if (update && event)
        {
-               charon->processor->queue_job(charon->processor,
-                                                                        (job_t*)roam_job_create(TRUE));
+               fire_roam_job(this, TRUE);
        }
 }
 
@@ -731,8 +764,7 @@ static void process_addr(private_kernel_interface_t *this,
        /* send an update to all IKE_SAs */
        if (update && event && changed)
        {
-               charon->processor->queue_job(charon->processor,
-                                                                        (job_t*)roam_job_create(TRUE));
+               fire_roam_job(this, TRUE);
        }
 }
 
@@ -828,8 +860,7 @@ static job_requeue_t receive_events(private_kernel_interface_t *this)
                                        break;
                                case RTM_NEWROUTE:
                                case RTM_DELROUTE:
-                                       charon->processor->queue_job(charon->processor,
-                                                                                                (job_t*)roam_job_create(FALSE));
+                                       fire_roam_job(this, FALSE);
                                        break;
                                default:
                                        break;
@@ -2517,6 +2548,7 @@ kernel_interface_t *kernel_interface_create()
        this->hiter = NULL;
        this->seq = 200;
        pthread_mutex_init(&this->mutex,NULL);
+       timerclear(&this->last_roam);
        
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
index 4822c84..d23f08e 100644 (file)
@@ -812,8 +812,6 @@ static status_t generate_message(private_ike_sa_t *this, message_t *message,
 {
        this->time.outbound = time(NULL);
        message->set_ike_sa_id(message, this->ike_sa_id);
-       message->set_destination(message, this->other_host->clone(this->other_host));
-       message->set_source(message, this->my_host->clone(this->my_host));
        return message->generate(message, this->crypter_out, this->signer_out, packet);
 }
 
@@ -859,7 +857,6 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg)
        
        if (this->state == IKE_CREATED)
        {
-               
                if (this->other_host->is_anyaddr(this->other_host))
                {
                        child_cfg->destroy(child_cfg);
@@ -1802,30 +1799,19 @@ static status_t roam(private_ike_sa_t *this, bool address)
        other = this->other_host;
        me = charon->kernel_interface->get_source_addr(charon->kernel_interface,
                                                                                                   other);
-                                                                                                  
-       /* TODO: find a better path using additional addresses of peer */
-       
-       if (!me)
-       {
-               /* no route found to host, set to stale, wait for a new route */
-               set_condition(this, COND_STALE, TRUE);
-               return FAILED;
-       }
        
        set_condition(this, COND_STALE, FALSE);
-       if (me->ip_equals(me, this->my_host) &&
-               other->ip_equals(other, this->other_host))
+       if (me)
        {
-               DBG2(DBG_IKE, "%H still reached through %H, no update needed",
-                        this->other_host, me);
+               if (me->ip_equals(me, this->my_host) &&
+                       other->ip_equals(other, this->other_host))
+               {
+                       DBG2(DBG_IKE, "keeping connection path %H - %H", this->other_host, me);
+                       me->destroy(me);
+                       return SUCCESS;
+               }
                me->destroy(me);
-               return SUCCESS;
        }
-       me->set_port(me, this->my_host->get_port(this->my_host));
-       other = other->clone(other);
-       other->set_port(other, this->other_host->get_port(this->other_host));
-       set_my_host(this, me);
-       set_other_host(this, other);
        
        /* update addresses with mobike, if supported ... */
        if (supports_extension(this, EXT_MOBIKE))
index 55592f4..a44f88b 100644 (file)
@@ -217,6 +217,9 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id)
        {
                u_int32_t timeout;
                job_t *job;
+               iterator_t *iterator;
+               packet_t *packet;
+               task_t *task;
 
                if (this->initiating.retransmitted <= RETRANSMIT_TRIES)
                {
@@ -237,8 +240,22 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id)
                }
                this->initiating.retransmitted++;
                
-               charon->sender->send(charon->sender,
-                                       this->initiating.packet->clone(this->initiating.packet));
+               packet = this->initiating.packet->clone(this->initiating.packet);
+               
+               /* mobike needs to now when we retransmit, so we call it here */
+               iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+               while (iterator->iterate(iterator, (void*)&task))
+               {
+                       if (task->get_type(task) == IKE_MOBIKE)
+                       {
+                               ike_mobike_t *mobike = (ike_mobike_t*)task;
+                               mobike->transmit(mobike, packet);
+                               break;
+                       }
+               }
+               iterator->destroy(iterator);
+               
+               charon->sender->send(charon->sender, packet);
                job = (job_t*)retransmit_job_create(this->initiating.mid,
                                                                                        this->ike_sa->get_id(this->ike_sa));
                charon->scheduler->schedule_job(charon->scheduler, job, timeout);
@@ -255,6 +272,7 @@ static status_t build_request(private_task_manager_t *this)
        iterator_t *iterator;
        task_t *task;
        message_t *message;
+       host_t *me, *other;
        status_t status;
        exchange_type_t exchange = 0;
        
@@ -372,8 +390,13 @@ static status_t build_request(private_task_manager_t *this)
                return SUCCESS;
        }
        
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       
        message = message_create();
        message->set_message_id(message, this->initiating.mid);
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
        message->set_exchange_type(message, exchange);
        this->initiating.type = exchange;
        this->initiating.retransmitted = 0;
@@ -412,7 +435,7 @@ static status_t build_request(private_task_manager_t *this)
                 * close the SA */
                flush(this);
            return DESTROY_ME;
-       }                                               
+       }
        
        return retransmit(this, this->initiating.mid);
 }
@@ -523,17 +546,23 @@ static void handle_collisions(private_task_manager_t *this, task_t *task)
 /**
  * build a response depending on the "passive" task list
  */
-static status_t build_response(private_task_manager_t *this,
-                                                          exchange_type_t exchange)
+static status_t build_response(private_task_manager_t *this, message_t *request)
 {
        iterator_t *iterator;
        task_t *task;
        message_t *message;
+       host_t *me, *other;
        bool delete = FALSE;
        status_t status;
 
+       me = request->get_destination(request);
+       other = request->get_source(request);
+
        message = message_create();
-       message->set_exchange_type(message, exchange);
+       message->set_exchange_type(message, request->get_exchange_type(request));
+       /* send response along the path the request came in */
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
        message->set_message_id(message, this->responding.mid);
        message->set_request(message, FALSE);
 
@@ -563,7 +592,7 @@ static status_t build_response(private_task_manager_t *this,
        iterator->destroy(iterator);
        
        /* remove resonder SPI if IKE_SA_INIT failed */
-       if (delete && exchange == IKE_SA_INIT)
+       if (delete && request->get_exchange_type(request) == IKE_SA_INIT)
        {
                ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
                id->set_responder_spi(id, 0);
@@ -596,15 +625,12 @@ static status_t process_request(private_task_manager_t *this,
 {
        iterator_t *iterator;
        task_t *task = NULL;
-       exchange_type_t exchange;
        payload_t *payload;
        notify_payload_t *notify;
        delete_payload_t *delete;
 
-       exchange = message->get_exchange_type(message);
-
        /* create tasks depending on request type */
-       switch (exchange)
+       switch (message->get_exchange_type(message))
        {
                case IKE_SA_INIT:
                {
@@ -760,7 +786,7 @@ static status_t process_request(private_task_manager_t *this,
        }
        iterator->destroy(iterator);
 
-       return build_response(this, exchange);
+       return build_response(this, message);
 }
 
 /**
index 7d98172..8d35d96 100644 (file)
@@ -64,7 +64,12 @@ struct private_ike_mobike_t {
        /**
         * use task to update addresses
         */
-       bool roam;
+       bool update;
+       
+       /**
+        * do routability check
+        */
+       bool check;
        
        /**
         * include address list update
@@ -140,7 +145,7 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message)
                        }
                        case UPDATE_SA_ADDRESSES:
                        {
-                               this->roam = TRUE;
+                               this->update = TRUE;
                                break;
                        }
                        case NO_ADDITIONAL_ADDRESSES:
@@ -225,6 +230,54 @@ static void update_children(private_ike_mobike_t *this)
 }
 
 /**
+ * Implementation of ike_mobike_t.transmit
+ */
+static void transmit(private_ike_mobike_t *this, packet_t *packet)
+{
+       host_t *me, *other, *me_old, *other_old;
+       iterator_t *iterator;
+       packet_t *copy;
+       
+       if (!this->check)
+       {
+               return;
+       }
+
+       me_old = this->ike_sa->get_my_host(this->ike_sa);
+       other_old = this->ike_sa->get_other_host(this->ike_sa);
+       
+       me = charon->kernel_interface->get_source_addr(
+                                                                               charon->kernel_interface, other_old);
+       if (me)
+       {
+               me->set_port(me, me->ip_equals(me, me_old) ?
+                                        me_old->get_port(me_old) : IKEV2_NATT_PORT);
+               packet->set_source(packet, me);
+       }
+       
+       iterator = this->ike_sa->create_additional_address_iterator(this->ike_sa);
+       while (iterator->iterate(iterator, (void**)&other))
+       {
+               me = charon->kernel_interface->get_source_addr(
+                                                                                       charon->kernel_interface, other);
+               if (me)
+               {
+                       /* reuse port for an active address, 4500 otherwise */
+                       me->set_port(me, me->ip_equals(me, me_old) ?
+                                                me_old->get_port(me_old) : IKEV2_NATT_PORT);
+                       other = other->clone(other);
+                       other->set_port(other, other->ip_equals(other, other_old) ?
+                                                       other_old->get_port(other_old) : IKEV2_NATT_PORT);
+                       copy = packet->clone(packet);
+                       copy->set_source(copy, me);
+                       copy->set_destination(copy, other);
+                       charon->sender->send(charon->sender, copy);
+               }
+       }
+       iterator->destroy(iterator);
+}
+
+/**
  * Implementation of task_t.process for initiator
  */
 static status_t build_i(private_ike_mobike_t *this, message_t *message)
@@ -237,20 +290,20 @@ static status_t build_i(private_ike_mobike_t *this, message_t *message)
        }
        else if (message->get_exchange_type(message) == INFORMATIONAL)
        {
-               if (this->roam)
+               if (this->update)
                {
                        message->add_notify(message, FALSE, UPDATE_SA_ADDRESSES, chunk_empty);
+                       update_children(this);
                }
                if (this->address)
                {
                        build_address_list(this, message);
                }
-               
-               this->natd = ike_natd_create(this->ike_sa, this->initiator);
-               this->natd->task.build(&this->natd->task, message);
-               update_children(this);
+               if (this->natd)
+               {
+                       this->natd->task.build(&this->natd->task, message);
+               }
        }
-       
        return NEED_MORE;
 }
 
@@ -267,7 +320,7 @@ static status_t process_r(private_ike_mobike_t *this, message_t *message)
        else if (message->get_exchange_type(message) == INFORMATIONAL)
        {
                process_payloads(this, message);
-               if (this->roam)
+               if (this->update)
                {
                        host_t *me, *other;
                        
@@ -306,7 +359,7 @@ static status_t build_r(private_ike_mobike_t *this, message_t *message)
                {
                        this->natd->task.build(&this->natd->task, message);
                }
-               if (this->roam)
+               if (this->update)
                {
                        update_children(this);
                }
@@ -324,7 +377,6 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message)
                message->get_payload(message, SECURITY_ASSOCIATION))
        {
                process_payloads(this, message);
-
                return SUCCESS;
        }
        else if (message->get_exchange_type(message) == INFORMATIONAL)
@@ -341,11 +393,39 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message)
                {
                        this->natd->task.process(&this->natd->task, message);
                }
-               if (this->roam)
+               if (this->update)
                {
                        /* update again, as NAT state may have changed */
                        update_children(this);
                }
+               if (this->check)
+               {
+                       host_t *me_new, *me_old, *other_new, *other_old;
+                       
+                       me_new = message->get_destination(message);
+                       other_new = message->get_source(message);
+                       me_old = this->ike_sa->get_my_host(this->ike_sa);
+                       other_old = this->ike_sa->get_other_host(this->ike_sa);
+                       
+                       if (!me_new->equals(me_new, me_old))
+                       {
+                               this->update = TRUE;
+                               this->ike_sa->set_my_host(this->ike_sa, me_new->clone(me_new));
+                       }                       
+                       if (!other_new->equals(other_new, other_old))
+                       {
+                               this->update = TRUE;
+                               this->ike_sa->set_other_host(this->ike_sa, other_new->clone(other_new));
+                       }
+                       if (this->update)
+                       {
+                               /* start the update with the same task */
+                               this->check = FALSE;
+                               this->address = FALSE;
+                               this->ike_sa->set_pending_updates(this->ike_sa, 1);
+                               return NEED_MORE;
+                       }
+               }
                return SUCCESS;
        }
        return NEED_MORE;
@@ -356,8 +436,9 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message)
  */
 static void roam(private_ike_mobike_t *this, bool address)
 {
-       this->roam = TRUE;
+       this->check = TRUE;
        this->address = address;
+       this->natd = ike_natd_create(this->ike_sa, this->initiator);
        this->ike_sa->set_pending_updates(this->ike_sa, 
                                                        this->ike_sa->get_pending_updates(this->ike_sa) + 1);
 }
@@ -404,6 +485,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
        private_ike_mobike_t *this = malloc_thing(private_ike_mobike_t);
 
        this->public.roam = (void(*)(ike_mobike_t*,bool))roam;
+       this->public.transmit = (void(*)(ike_mobike_t*,packet_t*))transmit;
        this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
        this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
        this->public.task.destroy = (void(*)(task_t*))destroy;
@@ -421,7 +503,8 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
        
        this->ike_sa = ike_sa;
        this->initiator = initiator;
-       this->roam = FALSE;
+       this->update = FALSE;
+       this->check = FALSE;
        this->address = TRUE;
        this->cookie2 = chunk_empty;
        this->natd = NULL;
index db493c4..cee9da0 100644 (file)
@@ -28,6 +28,7 @@ typedef struct ike_mobike_t ike_mobike_t;
 #include <library.h>
 #include <sa/ike_sa.h>
 #include <sa/tasks/task.h>
+#include <network/packet.h>
 
 /**
  * @brief Task of type ike_mobike, detects and handles MOBIKE extension.
@@ -58,6 +59,18 @@ struct ike_mobike_t {
         * @param address               TRUE to include address list update
         */
        void (*roam)(ike_mobike_t *this, bool address);
+       
+       /**
+        * @brief Transmision hook, called by task manager.
+        *
+        * The task manager calls this hook whenever it transmits a packet. It 
+        * allows the mobike task to send the packet on multiple paths to do path
+        * probing.
+        *
+        * @param this                  calling object
+        * @param packet                the packet to transmit
+        */
+       void (*transmit)(ike_mobike_t *this, packet_t *packet);
 };
 
 /**