Use a sync message cache to resynchronize IKE_SAs without rekeying
authorMartin Willi <martin@revosec.ch>
Thu, 22 Jul 2010 16:54:35 +0000 (18:54 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 26 Jul 2010 11:53:49 +0000 (13:53 +0200)
16 files changed:
src/libcharon/plugins/ha/Makefile.am
src/libcharon/plugins/ha/ha_cache.c [new file with mode: 0644]
src/libcharon/plugins/ha/ha_cache.h [new file with mode: 0644]
src/libcharon/plugins/ha/ha_child.c
src/libcharon/plugins/ha/ha_child.h
src/libcharon/plugins/ha/ha_ctl.c
src/libcharon/plugins/ha/ha_ctl.h
src/libcharon/plugins/ha/ha_dispatcher.c
src/libcharon/plugins/ha/ha_dispatcher.h
src/libcharon/plugins/ha/ha_ike.c
src/libcharon/plugins/ha/ha_ike.h
src/libcharon/plugins/ha/ha_plugin.c
src/libcharon/plugins/ha/ha_segments.c
src/libcharon/plugins/ha/ha_segments.h
src/libcharon/plugins/ha/ha_socket.c
src/libcharon/plugins/ha/ha_socket.h

index 74fe1f4..165f8c9 100644 (file)
@@ -17,6 +17,7 @@ libstrongswan_ha_la_SOURCES = \
   ha_tunnel.h ha_tunnel.c \
   ha_dispatcher.h ha_dispatcher.c \
   ha_segments.h ha_segments.c \
+  ha_cache.h ha_cache.c \
   ha_kernel.h ha_kernel.c \
   ha_ctl.h ha_ctl.c \
   ha_ike.h ha_ike.c \
diff --git a/src/libcharon/plugins/ha/ha_cache.c b/src/libcharon/plugins/ha/ha_cache.c
new file mode 100644 (file)
index 0000000..1ebc33c
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "ha_cache.h"
+
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+#include <threading/mutex.h>
+#include <processing/jobs/callback_job.h>
+
+typedef struct private_ha_cache_t private_ha_cache_t;
+
+/**
+ * Private data of an ha_cache_t object.
+ */
+struct private_ha_cache_t {
+
+       /**
+        * Public ha_cache_t interface.
+        */
+       ha_cache_t public;
+
+       /**
+        * Kernel helper functions
+        */
+       ha_kernel_t *kernel;
+
+       /**
+        * Socket to send sync messages over
+        */
+       ha_socket_t *socket;
+
+       /**
+        * Total number of segments
+        */
+       u_int count;
+
+       /**
+        * cached entries (ike_sa_t, entry_t)
+        */
+       hashtable_t *cache;
+
+       /**
+        * Mutex to lock cache
+        */
+       mutex_t *mutex;
+};
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(void *key)
+{
+       return (uintptr_t)key;
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(void *a, void *b)
+{
+       return a == b;
+}
+
+/**
+ * Cache entry for an IKE_SA
+ */
+typedef struct {
+       /* segment this entry is associate to */
+       u_int segment;
+       /* ADD message */
+       ha_message_t *add;
+       /* list of updates UPDATE message */
+       linked_list_t *updates;
+       /* last initiator mid */
+       ha_message_t *midi;
+       /* last responder mid */
+       ha_message_t *midr;
+} entry_t;
+
+/**
+ * Create a entry with an add message
+ */
+static entry_t *entry_create(ha_message_t *add)
+{
+       entry_t *entry;
+
+       INIT(entry,
+               .add = add,
+               .updates = linked_list_create(),
+       );
+       return entry;
+}
+
+/**
+ * clean up a entry
+ */
+static void entry_destroy(entry_t *entry)
+{
+       entry->updates->destroy_offset(entry->updates,
+                                                                       offsetof(ha_message_t, destroy));
+       entry->add->destroy(entry->add);
+       DESTROY_IF(entry->midi);
+       DESTROY_IF(entry->midr);
+       free(entry);
+}
+
+METHOD(ha_cache_t, cache, void,
+       private_ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message)
+{
+       entry_t *entry;
+
+       this->mutex->lock(this->mutex);
+       switch (message->get_type(message))
+       {
+               case HA_IKE_ADD:
+                       entry = entry_create(message);
+                       entry = this->cache->put(this->cache, ike_sa, entry);
+                       if (entry)
+                       {
+                               entry_destroy(entry);
+                       }
+                       break;
+               case HA_IKE_UPDATE:
+                       entry = this->cache->get(this->cache, ike_sa);
+                       if (entry)
+                       {
+                               entry->segment = this->kernel->get_segment(this->kernel,
+                                                                                       ike_sa->get_other_host(ike_sa));
+                               entry->updates->insert_last(entry->updates, message);
+                               break;
+                       }
+                       message->destroy(message);
+                       break;
+               case HA_IKE_MID_INITIATOR:
+                       entry = this->cache->get(this->cache, ike_sa);
+                       if (entry)
+                       {
+                               DESTROY_IF(entry->midi);
+                               entry->midi = message;
+                               break;
+                       }
+                       message->destroy(message);
+                       break;
+               case HA_IKE_MID_RESPONDER:
+                       entry = this->cache->get(this->cache, ike_sa);
+                       if (entry)
+                       {
+                               DESTROY_IF(entry->midr);
+                               entry->midr = message;
+                               break;
+                       }
+                       message->destroy(message);
+                       break;
+               case HA_IKE_DELETE:
+                       entry = this->cache->remove(this->cache, ike_sa);
+                       if (entry)
+                       {
+                               entry_destroy(entry);
+                       }
+                       message->destroy(message);
+                       break;
+               default:
+                       message->destroy(message);
+                       break;
+       }
+       this->mutex->unlock(this->mutex);
+}
+
+METHOD(ha_cache_t, delete_, void,
+       private_ha_cache_t *this, ike_sa_t *ike_sa)
+{
+       entry_t *entry;
+
+       entry = this->cache->remove(this->cache, ike_sa);
+       if (entry)
+       {
+               entry_destroy(entry);
+       }
+}
+
+/**
+ * Rekey all children of an IKE_SA
+ */
+static status_t rekey_children(ike_sa_t *ike_sa)
+{
+       iterator_t *iterator;
+       child_sa_t *child_sa;
+       status_t status = SUCCESS;
+
+       iterator = ike_sa->create_child_sa_iterator(ike_sa);
+       while (iterator->iterate(iterator, (void**)&child_sa))
+       {
+               DBG1(DBG_CFG, "resyncing CHILD_SA");
+               status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa),
+                                                                               child_sa->get_spi(child_sa, TRUE));
+               if (status == DESTROY_ME)
+               {
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       return status;
+}
+
+/**
+ * Trigger rekeying of CHILD_SA in segment
+ */
+static void rekey_segment(private_ha_cache_t *this, u_int segment)
+{
+       ike_sa_t *ike_sa;
+       enumerator_t *enumerator;
+       linked_list_t *list;
+       ike_sa_id_t *id;
+
+       list = linked_list_create();
+
+       enumerator = charon->ike_sa_manager->create_enumerator(
+                                                                                               charon->ike_sa_manager);
+       while (enumerator->enumerate(enumerator, &ike_sa))
+       {
+               if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+                       this->kernel->get_segment(this->kernel,
+                                               ike_sa->get_other_host(ike_sa)) == segment)
+               {
+                       id = ike_sa->get_id(ike_sa);
+                       list->insert_last(list, id->clone(id));
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       while (list->remove_last(list, (void**)&id) == SUCCESS)
+       {
+               ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+               if (ike_sa)
+               {
+                       if (rekey_children(ike_sa) != DESTROY_ME)
+                       {
+                               charon->ike_sa_manager->checkin(
+                                                                               charon->ike_sa_manager, ike_sa);
+                       }
+                       else
+                       {
+                               charon->ike_sa_manager->checkin_and_destroy(
+                                                                               charon->ike_sa_manager, ike_sa);
+                       }
+               }
+               id->destroy(id);
+       }
+       list->destroy(list);
+}
+
+METHOD(ha_cache_t, resync, void,
+       private_ha_cache_t *this, u_int segment)
+{
+       enumerator_t *enumerator, *updates;
+       ike_sa_t *ike_sa;
+       entry_t *entry;
+       ha_message_t *message;
+
+       DBG1(DBG_CFG, "resyncing HA segment %d", segment);
+
+       this->mutex->lock(this->mutex);
+       enumerator = this->cache->create_enumerator(this->cache);
+       while (enumerator->enumerate(enumerator, &ike_sa, &entry))
+       {
+               if (entry->segment == segment)
+               {
+                       this->socket->push(this->socket, entry->add);
+                       updates = entry->updates->create_enumerator(entry->updates);
+                       while (updates->enumerate(updates, &message))
+                       {
+                               this->socket->push(this->socket, message);
+                       }
+                       updates->destroy(updates);
+                       if (entry->midi)
+                       {
+                               this->socket->push(this->socket, entry->midi);
+                       }
+                       if (entry->midr)
+                       {
+                               this->socket->push(this->socket, entry->midr);
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->mutex->unlock(this->mutex);
+
+       rekey_segment(this, segment);
+}
+
+/**
+ * Request a resync of all segments
+ */
+static job_requeue_t request_resync(private_ha_cache_t *this)
+{
+       ha_message_t *message;
+       int i;
+
+       DBG1(DBG_CFG, "requesting HA resynchronization");
+
+       message = ha_message_create(HA_RESYNC);
+       for (i = 1; i <= this->count; i++)
+       {
+               message->add_attribute(message, HA_SEGMENT, i);
+       }
+       this->socket->push(this->socket, message);
+       message->destroy(message);
+       return JOB_REQUEUE_NONE;
+}
+
+METHOD(ha_cache_t, destroy, void,
+       private_ha_cache_t *this)
+{
+       this->cache->destroy(this->cache);
+       this->mutex->destroy(this->mutex);
+       free(this);
+}
+
+/**
+ * See header
+ */
+ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket,
+                                                       bool sync, u_int count)
+{
+       private_ha_cache_t *this;
+
+       INIT(this,
+               .public = {
+                       .cache = _cache,
+                       .delete = _delete_,
+                       .resync = _resync,
+                       .destroy = _destroy,
+               },
+               .count = count,
+               .kernel = kernel,
+               .socket = socket,
+               .cache = hashtable_create(hash, equals, 8),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+       );
+
+       if (sync)
+       {
+               /* request a resync as soon as we are up */
+               charon->scheduler->schedule_job(charon->scheduler, (job_t*)
+                                               callback_job_create((callback_job_cb_t)request_resync,
+                                                                                       this, NULL, NULL), 1);
+       }
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/ha/ha_cache.h b/src/libcharon/plugins/ha/ha_cache.h
new file mode 100644 (file)
index 0000000..39f1947
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup ha_cache ha_cache
+ * @{ @ingroup ha
+ */
+
+#ifndef HA_CACHE_H_
+#define HA_CACHE_H_
+
+typedef struct ha_cache_t ha_cache_t;
+
+#include "ha_message.h"
+#include "ha_kernel.h"
+#include "ha_socket.h"
+
+#include <utils/enumerator.h>
+
+#include <sa/ike_sa.h>
+
+/**
+ * HA message caching facility, allows reintegration of new nodes.
+ */
+struct ha_cache_t {
+
+       /**
+        * Cache an IKE specific message.
+        *
+        * @param ike_sa                associated IKE_SA
+        * @param message               message to cache
+        */
+       void (*cache)(ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message);
+
+       /**
+        * Delete a cache entry for an IKE_SA.
+        *
+        * @param ike_sa                cache entry to delete
+        */
+       void (*delete)(ha_cache_t *this, ike_sa_t *ike_sa);
+
+       /**
+        * Resync a segment to the node using the cached messages.
+        *
+        * @param segment               segment to resync
+        */
+       void (*resync)(ha_cache_t *this, u_int segment);
+
+       /**
+        * Destroy a ha_cache_t.
+        */
+       void (*destroy)(ha_cache_t *this);
+};
+
+/**
+ * Create a ha_cache instance.
+ *
+ * @param kernel               kernel helper
+ * @param socket               socket to send resync messages
+ * @param resync               request a resync during startup?
+ * @param count                        total number of segments
+ */
+ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket,
+                                                       bool resync, u_int count);
+
+#endif /** HA_CACHE_H_ @}*/
index 808a420..5a437c5 100644 (file)
@@ -36,6 +36,11 @@ struct private_ha_child_t {
         * tunnel securing sync messages
         */
        ha_tunnel_t *tunnel;
+
+       /**
+        * message cache
+        */
+       ha_cache_t *cache;
 };
 
 METHOD(listener_t, child_keys, bool,
@@ -103,6 +108,7 @@ METHOD(listener_t, child_keys, bool,
        enumerator->destroy(enumerator);
 
        this->socket->push(this->socket, m);
+       m->destroy(m);
 
        return TRUE;
 }
@@ -133,6 +139,7 @@ METHOD(listener_t, child_state_change, bool,
                m->add_attribute(m, HA_INBOUND_SPI,
                                                 child_sa->get_spi(child_sa, TRUE));
                this->socket->push(this->socket, m);
+               m->destroy(m);
        }
        return TRUE;
 }
@@ -146,7 +153,8 @@ METHOD(ha_child_t, destroy, void,
 /**
  * See header
  */
-ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel)
+ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
+                                                       ha_cache_t *cache)
 {
        private_ha_child_t *this;
 
@@ -160,6 +168,7 @@ ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel)
                },
                .socket = socket,
                .tunnel = tunnel,
+               .cache = cache,
        );
 
        return &this->public;
index 9b4a575..9a28e51 100644 (file)
 #ifndef HA_CHILD_H_
 #define HA_CHILD_H_
 
+typedef struct ha_child_t ha_child_t;
+
 #include "ha_socket.h"
 #include "ha_tunnel.h"
 #include "ha_segments.h"
+#include "ha_cache.h"
 
 #include <daemon.h>
 
-typedef struct ha_child_t ha_child_t;
-
 /**
  * Listener to synchronize CHILD_SAs.
  */
@@ -50,8 +51,10 @@ struct ha_child_t {
  *
  * @param socket               socket to use for sending synchronization messages
  * @param tunnel               tunnel securing sync messages, if any
+ * @param cache                        message resync cache
  * @return                             CHILD listener
  */
-ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel);
+ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
+                                                       ha_cache_t *cache);
 
 #endif /** HA_CHILD_ @}*/
index 011b350..e188a84 100644 (file)
@@ -45,6 +45,11 @@ struct private_ha_ctl_t {
        ha_segments_t *segments;
 
        /**
+        * Resynchronization message cache
+        */
+       ha_cache_t *cache;
+
+       /**
         * FIFO reader thread
         */
        callback_job_t *job;
@@ -84,7 +89,7 @@ static job_requeue_t dispatch_fifo(private_ha_ctl_t *this)
                                        this->segments->deactivate(this->segments, segment, TRUE);
                                        break;
                                case '*':
-                                       this->segments->resync(this->segments, segment);
+                                       this->cache->resync(this->cache, segment);
                                        break;
                                default:
                                        break;
@@ -106,7 +111,7 @@ METHOD(ha_ctl_t, destroy, void,
 /**
  * See header
  */
-ha_ctl_t *ha_ctl_create(ha_segments_t *segments)
+ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache)
 {
        private_ha_ctl_t *this;
 
@@ -115,6 +120,7 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments)
                        .destroy = _destroy,
                },
                .segments = segments,
+               .cache = cache,
        );
 
        if (access(HA_FIFO, R_OK|W_OK) != 0)
index 3aae132..1e71783 100644 (file)
@@ -22,6 +22,7 @@
 #define HA_CTL_H_
 
 #include "ha_segments.h"
+#include "ha_cache.h"
 
 typedef struct ha_ctl_t ha_ctl_t;
 
@@ -40,8 +41,9 @@ struct ha_ctl_t {
  * Create a ha_ctl instance.
  *
  * @param segments     segments to control
+ * @param cache                message cache for resynchronization
  * @return                     HA control interface
  */
-ha_ctl_t *ha_ctl_create(ha_segments_t *segments);
+ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache);
 
 #endif /** HA_CTL_ @}*/
index 575d8ee..d956304 100644 (file)
@@ -41,6 +41,11 @@ struct private_ha_dispatcher_t {
        ha_segments_t *segments;
 
        /**
+        * Cache for resync
+        */
+       ha_cache_t *cache;
+
+       /**
         * Dispatcher job
         */
        callback_job_t *job;
@@ -153,6 +158,8 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message
                                old_sa = NULL;
                        }
                        ike_sa->set_state(ike_sa, IKE_CONNECTING);
+                       this->cache->cache(this->cache, ike_sa, message);
+                       message = NULL;
                        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                }
                else
@@ -167,6 +174,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message
        {
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa);
        }
+       DESTROY_IF(message);
 }
 
 /**
@@ -276,10 +284,20 @@ static void process_ike_update(private_ha_dispatcher_t *this,
                if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
                        ike_sa->get_peer_cfg(ike_sa))
                {
+                       DBG1(DBG_CFG, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]",
+                                ike_sa->get_name(ike_sa),
+                                ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa),
+                                ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa));
                        ike_sa->set_state(ike_sa, IKE_PASSIVE);
                }
+               this->cache->cache(this->cache, ike_sa, message);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
+       else
+       {
+               DBG1(DBG_CFG, "passive HA IKE_SA to update not found");
+               message->destroy(message);
+       }
 }
 
 /**
@@ -318,8 +336,13 @@ static void process_ike_mid(private_ha_dispatcher_t *this,
                {
                        ike_sa->set_message_id(ike_sa, initiator, mid);
                }
+               this->cache->cache(this->cache, ike_sa, message);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
+       else
+       {
+               message->destroy(message);
+       }
 }
 
 /**
@@ -331,7 +354,7 @@ static void process_ike_delete(private_ha_dispatcher_t *this,
        ha_message_attribute_t attribute;
        ha_message_value_t value;
        enumerator_t *enumerator;
-       ike_sa_t *ike_sa;
+       ike_sa_t *ike_sa = NULL;
 
        enumerator = message->create_attribute_enumerator(message);
        while (enumerator->enumerate(enumerator, &attribute, &value))
@@ -341,17 +364,22 @@ static void process_ike_delete(private_ha_dispatcher_t *this,
                        case HA_IKE_ID:
                                ike_sa = charon->ike_sa_manager->checkout(
                                                                        charon->ike_sa_manager, value.ike_sa_id);
-                               if (ike_sa)
-                               {
-                                       charon->ike_sa_manager->checkin_and_destroy(
-                                                                       charon->ike_sa_manager, ike_sa);
-                               }
                                break;
                        default:
                                break;
                }
        }
        enumerator->destroy(enumerator);
+       if (ike_sa)
+       {
+               this->cache->cache(this->cache, ike_sa, message);
+               charon->ike_sa_manager->checkin_and_destroy(
+                                               charon->ike_sa_manager, ike_sa);
+       }
+       else
+       {
+               message->destroy(message);
+       }
 }
 
 /**
@@ -465,6 +493,7 @@ static void process_child_add(private_ha_dispatcher_t *this,
        if (!ike_sa)
        {
                DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found");
+               message->destroy(message);
                return;
        }
        config = find_child_cfg(ike_sa, config_name);
@@ -472,6 +501,7 @@ static void process_child_add(private_ha_dispatcher_t *this,
        {
                DBG1(DBG_CHD, "HA is missing nodes child configuration");
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+               message->destroy(message);
                return;
        }
 
@@ -558,15 +588,19 @@ static void process_child_add(private_ha_dispatcher_t *this,
                local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
                remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+               message->destroy(message);
                return;
        }
 
+       DBG1(DBG_CFG, "installed HA CHILD_SA '%s' %#R=== %#R",
+                child_sa->get_name(child_sa), local_ts, remote_ts);
        child_sa->add_policies(child_sa, local_ts, remote_ts);
        local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
        remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
 
        child_sa->set_state(child_sa, CHILD_INSTALLED);
        ike_sa->add_child_sa(ike_sa, child_sa);
+       message->destroy(message);
        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
 }
 
@@ -580,6 +614,8 @@ static void process_child_delete(private_ha_dispatcher_t *this,
        ha_message_value_t value;
        enumerator_t *enumerator;
        ike_sa_t *ike_sa = NULL;
+       child_sa_t *child_sa;
+       u_int32_t spi = 0;
 
        enumerator = message->create_attribute_enumerator(message);
        while (enumerator->enumerate(enumerator, &attribute, &value))
@@ -591,20 +627,24 @@ static void process_child_delete(private_ha_dispatcher_t *this,
                                                                                                                  value.ike_sa_id);
                                break;
                        case HA_INBOUND_SPI:
-                               if (ike_sa)
-                               {
-                                       ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, value.u32);
-                               }
+                               spi = value.u32;
                                break;
                        default:
                                break;
                }
        }
+       enumerator->destroy(enumerator);
+
        if (ike_sa)
        {
+               child_sa = ike_sa->get_child_sa(ike_sa, PROTO_ESP, spi, TRUE);
+               if (child_sa)
+               {
+                       ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, spi);
+               }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
-       enumerator->destroy(enumerator);
+       message->destroy(message);
 }
 
 /**
@@ -639,6 +679,7 @@ static void process_segment(private_ha_dispatcher_t *this,
                }
        }
        enumerator->destroy(enumerator);
+       message->destroy(message);
 }
 
 /**
@@ -667,6 +708,7 @@ static void process_status(private_ha_dispatcher_t *this,
        enumerator->destroy(enumerator);
 
        this->segments->handle_status(this->segments, mask);
+       message->destroy(message);
 }
 
 /**
@@ -685,13 +727,14 @@ static void process_resync(private_ha_dispatcher_t *this,
                switch (attribute)
                {
                        case HA_SEGMENT:
-                               this->segments->resync(this->segments, value.u16);
+                               this->cache->resync(this->cache, value.u16);
                                break;
                        default:
                                break;
                }
        }
        enumerator->destroy(enumerator);
+       message->destroy(message);
 }
 
 /**
@@ -746,10 +789,9 @@ static job_requeue_t dispatch(private_ha_dispatcher_t *this)
                        break;
                default:
                        DBG1(DBG_CFG, "received unknown HA message type %d", type);
+                       message->destroy(message);
                        break;
        }
-       message->destroy(message);
-
        return JOB_REQUEUE_DIRECT;
 }
 
@@ -764,7 +806,7 @@ METHOD(ha_dispatcher_t, destroy, void,
  * See header
  */
 ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
-                                                                         ha_segments_t *segments)
+                                                                       ha_segments_t *segments, ha_cache_t *cache)
 {
        private_ha_dispatcher_t *this;
 
@@ -775,6 +817,7 @@ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
                },
                .socket = socket,
                .segments = segments,
+               .cache = cache,
        );
        this->job = callback_job_create((callback_job_cb_t)dispatch,
                                                                        this, NULL, NULL);
index 55c0858..3190458 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "ha_socket.h"
 #include "ha_segments.h"
+#include "ha_cache.h"
 
 typedef struct ha_dispatcher_t ha_dispatcher_t;
 
@@ -42,9 +43,10 @@ struct ha_dispatcher_t {
  *
  * @param socket               socket to pull messages from
  * @param segments             segments to control based on received messages
+ * @param cache                        message cache to use for resynchronization
  * @return                             dispatcher object
  */
 ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
-                                                                         ha_segments_t *segments);
+                                                                       ha_segments_t *segments, ha_cache_t *cache);
 
 #endif /** HA_DISPATCHER_ @}*/
index 9c0919a..c7a7c58 100644 (file)
@@ -36,6 +36,11 @@ struct private_ha_ike_t {
         * tunnel securing sync messages
         */
        ha_tunnel_t *tunnel;
+
+       /**
+        * message cache
+        */
+       ha_cache_t *cache;
 };
 
 /**
@@ -117,6 +122,7 @@ METHOD(listener_t, ike_keys, bool,
        chunk_clear(&secret);
 
        this->socket->push(this->socket, m);
+       this->cache->cache(this->cache, ike_sa, m);
 
        return TRUE;
 }
@@ -181,6 +187,7 @@ METHOD(listener_t, ike_updown, bool,
                m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
        }
        this->socket->push(this->socket, m);
+       this->cache->cache(this->cache, ike_sa, m);
        return TRUE;
 }
 
@@ -192,6 +199,17 @@ METHOD(listener_t, ike_rekey, bool,
        return TRUE;
 }
 
+METHOD(listener_t, ike_state_change, bool,
+       private_ha_ike_t *this, ike_sa_t *ike_sa, ike_sa_state_t new)
+{
+       /* clean up cache if a passive IKE_SA goes away */
+       if (ike_sa->get_state(ike_sa) == IKE_PASSIVE && new == IKE_DESTROYING)
+       {
+               this->cache->delete(this->cache, ike_sa);
+       }
+       return TRUE;
+}
+
 METHOD(listener_t, message_hook, bool,
        private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming)
 {
@@ -216,6 +234,7 @@ METHOD(listener_t, message_hook, bool,
                m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
                m->add_attribute(m, HA_MID, message->get_message_id(message) + 1);
                this->socket->push(this->socket, m);
+               this->cache->cache(this->cache, ike_sa, m);
        }
        if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
                message->get_exchange_type(message) == IKE_AUTH &&
@@ -233,6 +252,7 @@ METHOD(listener_t, message_hook, bool,
                        m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
                        m->add_attribute(m, HA_REMOTE_VIP, vip);
                        this->socket->push(this->socket, m);
+                       this->cache->cache(this->cache, ike_sa, m);
                }
        }
        return TRUE;
@@ -247,7 +267,8 @@ METHOD(ha_ike_t, destroy, void,
 /**
  * See header
  */
-ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel)
+ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
+                                               ha_cache_t *cache)
 {
        private_ha_ike_t *this;
 
@@ -257,12 +278,14 @@ ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel)
                                .ike_keys = _ike_keys,
                                .ike_updown = _ike_updown,
                                .ike_rekey = _ike_rekey,
+                               .ike_state_change = _ike_state_change,
                                .message = _message_hook,
                        },
                        .destroy = _destroy,
                },
                .socket = socket,
                .tunnel = tunnel,
+               .cache = cache,
        );
 
        return &this->public;
index 9ba8f55..b22cd62 100644 (file)
 #ifndef HA_IKE_H_
 #define HA_IKE_H_
 
+typedef struct ha_ike_t ha_ike_t;
+
 #include "ha_socket.h"
 #include "ha_tunnel.h"
 #include "ha_segments.h"
+#include "ha_cache.h"
 
 #include <daemon.h>
 
-typedef struct ha_ike_t ha_ike_t;
-
 /**
  * Listener to synchronize IKE_SAs.
  */
@@ -50,8 +51,10 @@ struct ha_ike_t {
  *
  * @param socket               socket to use for sending synchronization messages
  * @param tunnel               tunnel securing sync messages, if any
+ * @param cache                        message cache
  * @return                             IKE listener
  */
-ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel);
+ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
+                                               ha_cache_t *cache);
 
 #endif /** HA_IKE_ @}*/
index 70daca0..cfce45e 100644 (file)
@@ -21,6 +21,7 @@
 #include "ha_dispatcher.h"
 #include "ha_segments.h"
 #include "ha_ctl.h"
+#include "ha_cache.h"
 
 #include <daemon.h>
 #include <config/child_cfg.h>
@@ -76,6 +77,11 @@ struct private_ha_plugin_t {
         * Segment control interface via FIFO
         */
        ha_ctl_t *ctl;
+
+       /**
+        * Message cache for resynchronization
+        */
+       ha_cache_t *cache;
 };
 
 METHOD(plugin_t, destroy, void,
@@ -88,6 +94,7 @@ METHOD(plugin_t, destroy, void,
        this->ike->destroy(this->ike);
        this->child->destroy(this->child);
        this->dispatcher->destroy(this->dispatcher);
+       this->cache->destroy(this->cache);
        this->segments->destroy(this->segments);
        this->kernel->destroy(this->kernel);
        this->socket->destroy(this->socket);
@@ -142,14 +149,16 @@ plugin_t *ha_plugin_create()
        }
        this->kernel = ha_kernel_create(count);
        this->segments = ha_segments_create(this->socket, this->kernel, this->tunnel,
-                                                       count, strcmp(local, remote) > 0, monitor, resync);
+                                                       count, strcmp(local, remote) > 0, monitor);
+       this->cache = ha_cache_create(this->kernel, this->socket, resync, count);
        if (fifo)
        {
-               this->ctl = ha_ctl_create(this->segments);
+               this->ctl = ha_ctl_create(this->segments, this->cache);
        }
-       this->dispatcher = ha_dispatcher_create(this->socket, this->segments);
-       this->ike = ha_ike_create(this->socket, this->tunnel);
-       this->child = ha_child_create(this->socket, this->tunnel);
+       this->dispatcher = ha_dispatcher_create(this->socket, this->segments,
+                                                                                       this->cache);
+       this->ike = ha_ike_create(this->socket, this->tunnel, this->cache);
+       this->child = ha_child_create(this->socket, this->tunnel, this->cache);
        charon->bus->add_listener(charon->bus, &this->segments->listener);
        charon->bus->add_listener(charon->bus, &this->ike->listener);
        charon->bus->add_listener(charon->bus, &this->child->listener);
index 5cec3c5..bdd850f 100644 (file)
@@ -183,6 +183,7 @@ static void enable_disable(private_ha_segments_t *this, u_int segment,
                message = ha_message_create(type);
                message->add_attribute(message, HA_SEGMENT, segment);
                this->socket->push(this->socket, message);
+               message->destroy(message);
        }
 }
 
@@ -221,116 +222,25 @@ METHOD(ha_segments_t, deactivate, void,
        enable_disable_all(this, segment, FALSE, notify);
 }
 
-/**
- * Rekey all children of an IKE_SA
- */
-static status_t rekey_children(ike_sa_t *ike_sa)
+METHOD(listener_t, alert_hook, bool,
+       private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
 {
-       iterator_t *iterator;
-       child_sa_t *child_sa;
-       status_t status = SUCCESS;
-
-       iterator = ike_sa->create_child_sa_iterator(ike_sa);
-       while (iterator->iterate(iterator, (void**)&child_sa))
+       if (alert == ALERT_SHUTDOWN_SIGNAL)
        {
-               DBG1(DBG_CFG, "resyncing CHILD_SA");
-               status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa),
-                                                                               child_sa->get_spi(child_sa, TRUE));
-               if (status == DESTROY_ME)
+               if (this->job)
                {
-                       break;
+                       DBG1(DBG_CFG, "HA heartbeat active, dropping all segments");
+                       deactivate(this, 0, TRUE);
                }
-       }
-       iterator->destroy(iterator);
-       return status;
-}
-
-METHOD(ha_segments_t, resync, void,
-       private_ha_segments_t *this, u_int segment)
-{
-       ike_sa_t *ike_sa;
-       enumerator_t *enumerator;
-       linked_list_t *list;
-       ike_sa_id_t *id;
-
-       list = linked_list_create();
-       this->mutex->lock(this->mutex);
-
-       if (segment > 0 && segment <= this->count)
-       {
-               DBG1(DBG_CFG, "resyncing HA segment %d", segment);
-
-               /* we do the actual rekeying in a seperate loop to avoid rekeying
-                * an SA twice. */
-               enumerator = charon->ike_sa_manager->create_enumerator(
-                                                                                                       charon->ike_sa_manager);
-               while (enumerator->enumerate(enumerator, &ike_sa))
+               else
                {
-                       if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
-                               this->kernel->get_segment(this->kernel,
-                                                                       ike_sa->get_other_host(ike_sa)) == segment)
-                       {
-                               id = ike_sa->get_id(ike_sa);
-                               list->insert_last(list, id->clone(id));
-                       }
+                       DBG1(DBG_CFG, "no HA heartbeat active, closing IKE_SAs");
                }
-               enumerator->destroy(enumerator);
-       }
-       this->mutex->unlock(this->mutex);
-
-       while (list->remove_last(list, (void**)&id) == SUCCESS)
-       {
-               ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
-               id->destroy(id);
-               if (ike_sa)
-               {
-                       DBG1(DBG_CFG, "resyncing IKE_SA");
-                       if (ike_sa->rekey(ike_sa) != DESTROY_ME)
-                       {
-                               if (rekey_children(ike_sa) != DESTROY_ME)
-                               {
-                                       charon->ike_sa_manager->checkin(
-                                                                               charon->ike_sa_manager, ike_sa);
-                                       continue;
-                               }
-                       }
-                       charon->ike_sa_manager->checkin_and_destroy(
-                                                                               charon->ike_sa_manager, ike_sa);
-               }
-       }
-       list->destroy(list);
-}
-
-METHOD(listener_t, alert_hook, bool,
-       private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
-{
-       if (alert == ALERT_SHUTDOWN_SIGNAL)
-       {
-               deactivate(this, 0, TRUE);
        }
        return TRUE;
 }
 
 /**
- * Request a resync of all segments
- */
-static job_requeue_t request_resync(private_ha_segments_t *this)
-{
-       ha_message_t *message;
-       int i;
-
-       DBG1(DBG_CFG, "requesting HA resynchronization");
-
-       message = ha_message_create(HA_RESYNC);
-       for (i = 1; i <= this->count; i++)
-       {
-               message->add_attribute(message, HA_SEGMENT, i);
-       }
-       this->socket->push(this->socket, message);
-       return JOB_REQUEUE_NONE;
-}
-
-/**
  * Monitor heartbeat activity of remote node
  */
 static job_requeue_t watchdog(private_ha_segments_t *this)
@@ -422,6 +332,7 @@ static job_requeue_t send_status(private_ha_segments_t *this)
        }
 
        this->socket->push(this->socket, message);
+       message->destroy(message);
 
        /* schedule next invocation */
        charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*)
@@ -449,7 +360,7 @@ METHOD(ha_segments_t, destroy, void,
  */
 ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
                                                                  ha_tunnel_t *tunnel, u_int count, u_int node,
-                                                                 bool monitor, bool sync)
+                                                                 bool monitor)
 {
        private_ha_segments_t *this;
 
@@ -458,7 +369,6 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
                        .listener.alert = _alert_hook,
                        .activate = _activate,
                        .deactivate = _deactivate,
-                       .resync = _resync,
                        .handle_status = _handle_status,
                        .destroy = _destroy,
                },
@@ -477,14 +387,6 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
                start_watchdog(this);
        }
 
-       if (sync)
-       {
-               /* request a resync as soon as we are up */
-               charon->scheduler->schedule_job(charon->scheduler, (job_t*)
-                                               callback_job_create((callback_job_cb_t)request_resync,
-                                                                                       this, NULL, NULL), 2);
-       }
-
        return &this->public;
 }
 
index f6ce738..1699f7b 100644 (file)
@@ -68,18 +68,6 @@ struct ha_segments_t {
        void (*deactivate)(ha_segments_t *this, u_int segment, bool notify);
 
        /**
-        * Resync an active segment.
-        *
-        * To reintegrade a node into the cluster, resynchronization is reqired.
-        * IKE_SAs and CHILD_SAs are synced automatically during rekeying. A call
-        * to this method enforces a rekeying immediately sync all state of a
-        * segment.
-        *
-        * @param segment       segment to resync
-        */
-       void (*resync)(ha_segments_t *this, u_int segment);
-
-       /**
         * Handle a status message from the remote node.
         *
         * @param mask          segments the remote node is serving actively
@@ -101,11 +89,10 @@ struct ha_segments_t {
  * @param count                        number of segments the cluster uses
  * @param node                 node, currently 1 or 0
  * @param monitor              should we use monitoring functionality
- * @param resync               request a complete resync on startup
  * @return                             segment object
  */
 ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
                                                                  ha_tunnel_t *tunnel, u_int count, u_int node,
-                                                                 bool monitor, bool resync);
+                                                                 bool monitor);
 
 #endif /** HA_SEGMENTS_ @}*/
index b30f3a3..21e6eb6 100644 (file)
@@ -58,8 +58,8 @@ struct private_ha_socket_t {
  * Data to pass to the send_message() callback job
  */
 typedef struct {
-       ha_message_t *message;
-       private_ha_socket_t *this;
+       chunk_t chunk;
+       int fd;
 } job_data_t;
 
 /**
@@ -67,7 +67,7 @@ typedef struct {
  */
 static void job_data_destroy(job_data_t *this)
 {
-       this->message->destroy(this->message);
+       free(this->chunk.ptr);
        free(this);
 }
 
@@ -76,12 +76,7 @@ static void job_data_destroy(job_data_t *this)
  */
 static job_requeue_t send_message(job_data_t *data)
 {
-       private_ha_socket_t *this;
-       chunk_t chunk;
-
-       this = data->this;
-       chunk = data->message->get_encoding(data->message);
-       if (send(this->fd, chunk.ptr, chunk.len, 0) < chunk.len)
+       if (send(data->fd, data->chunk.ptr, data->chunk.len, 0) < data->chunk.len)
        {
                DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno));
        }
@@ -105,9 +100,10 @@ METHOD(ha_socket_t, push, void,
                        /* Fallback to asynchronous transmission. This is required, as sendto()
                         * is a blocking call if it acquires a policy. We could end up in a
                         * deadlock, as we own an IKE_SA. */
-                       data = malloc_thing(job_data_t);
-                       data->message = message;
-                       data->this = this;
+                       INIT(data,
+                               .chunk = chunk_clone(chunk),
+                               .fd = this->fd,
+                       );
 
                        job = callback_job_create((callback_job_cb_t)send_message,
                                                                          data, (void*)job_data_destroy, NULL);
@@ -116,7 +112,6 @@ METHOD(ha_socket_t, push, void,
                }
                DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno));
        }
-       message->destroy(message);
 }
 
 METHOD(ha_socket_t, pull, ha_message_t*,
index 4155e26..a4789a5 100644 (file)
@@ -35,7 +35,7 @@ struct ha_socket_t {
        /**
         * Push synchronization information to the responsible node.
         *
-        * @param message       message to send, gets destroyed by push()
+        * @param message       message to send
         */
        void (*push)(ha_socket_t *this, ha_message_t *message);