IKE_SA activation/deactivation magic using a fifo socket
authorMartin Willi <martin@strongswan.org>
Thu, 27 Nov 2008 09:57:31 +0000 (09:57 +0000)
committerMartin Willi <martin@revosec.ch>
Wed, 7 Apr 2010 11:55:12 +0000 (13:55 +0200)
src/charon/plugins/ha_sync/Makefile.am
src/charon/plugins/ha_sync/ha_sync_cache.c
src/charon/plugins/ha_sync/ha_sync_cache.h
src/charon/plugins/ha_sync/ha_sync_ctl.c [new file with mode: 0644]
src/charon/plugins/ha_sync/ha_sync_ctl.h [new file with mode: 0644]
src/charon/plugins/ha_sync/ha_sync_plugin.c

index b48a6cf..e30376d 100644 (file)
@@ -1,7 +1,7 @@
 
-INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+INCLUDES = -I${linuxdir} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
 
-AM_CFLAGS = -rdynamic
+AM_CFLAGS = -rdynamic -DIPSEC_PIDDIR=\"${piddir}\"
 
 plugin_LTLIBRARIES = libstrongswan-ha-sync.la
 libstrongswan_ha_sync_la_SOURCES = \
@@ -9,7 +9,8 @@ libstrongswan_ha_sync_la_SOURCES = \
   ha_sync_message.h ha_sync_message.c \
   ha_sync_socket.h ha_sync_socket.c \
   ha_sync_dispatcher.h ha_sync_dispatcher.c \
-  ha_sync_dispatcher.h ha_sync_cache.c \
+  ha_sync_cache.h ha_sync_cache.c \
+  ha_sync_ctl.h ha_sync_ctl.c \
   ha_sync_ike.h ha_sync_ike.c \
   ha_sync_child.h ha_sync_child.c
 libstrongswan_ha_sync_la_LDFLAGS = -module
index 2626cef..8ada9fe 100644 (file)
 
 #include <utils/linked_list.h>
 
+typedef u_int32_t u32;
+typedef u_int8_t u8;
+
+#include <linux/jhash.h>
+
+#define MAX_SEGMENTS 16
+
 typedef struct private_ha_sync_cache_t private_ha_sync_cache_t;
 
 /**
@@ -35,6 +42,21 @@ struct private_ha_sync_cache_t {
         * Linked list of IKE_SAs, ike_sa_t
         */
        linked_list_t *list;
+
+       /**
+        * Init value for jhash
+        */
+       u_int initval;
+
+       /**
+        * Total number of ClusterIP segments
+        */
+       u_int segment_count;
+
+       /**
+        * mask of active segments
+        */
+       u_int16_t active;
 };
 
 /**
@@ -108,18 +130,98 @@ static void delete_ike_sa(private_ha_sync_cache_t *this, ike_sa_id_t *id)
 }
 
 /**
- * Implementation of ha_sync_cache_t.activate_segment
+ * Check if a host address is in the CLUSTERIP segment
+ */
+static bool in_segment(private_ha_sync_cache_t *this,
+                                          host_t *host, u_int segment)
+{
+       if (host->get_family(host) == AF_INET)
+       {
+               unsigned long hash;
+               u_int32_t addr;
+
+               addr = *(u_int32_t*)host->get_address(host).ptr;
+               hash = jhash_1word(ntohl(addr), this->initval);
+
+               if ((((u_int64_t)hash * this->segment_count) >> 32) + 1 == segment)
+               {
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+/**
+ * Log currently active segments
+ */
+static void log_segments(private_ha_sync_cache_t *this, bool activated,
+                                                u_int segment)
+{
+       char buf[64], *pos = buf;
+       int i;
+       bool first = TRUE;
+
+       for (i = 0; i < this->segment_count; i++)
+       {
+               if (this->active & 0x01 << i)
+               {
+                       if (first)
+                       {
+                               first = FALSE;
+                       }
+                       else
+                       {
+                               pos += snprintf(pos, buf + sizeof(buf) - pos, ",");
+                       }
+                       pos += snprintf(pos, buf + sizeof(buf) - pos, "%d", i+1);
+               }
+       }
+       DBG1(DBG_CFG, "HA sync segments %d %sactivated, now active: %s",
+                segment, activated ? "" : "de", buf);
+}
+
+/**
+ * Implementation of ha_sync_cache_t.activate
  */
-static void activate_segment(private_ha_sync_cache_t *this, u_int segment)
+static void activate(private_ha_sync_cache_t *this, u_int segment)
 {
        ike_sa_t *ike_sa;
+       enumerator_t *enumerator;
+       u_int16_t mask = 0x01 << (segment - 1);
+
+       DBG1(DBG_CFG, "activating segment %d", segment);
+
+       if (segment > 0 && segment <= this->segment_count && !(this->active & mask))
+       {
+               this->active |= mask;
+
+               enumerator = this->list->create_enumerator(this->list);
+               while (enumerator->enumerate(enumerator, &ike_sa))
+               {
+                       if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
+                               in_segment(this, ike_sa->get_other_host(ike_sa), segment))
+                       {
+                               this->list->remove_at(this->list, enumerator);
+                               ike_sa->set_state(ike_Sa, IKE_ESTABLISHED);
+                               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+                       }
+               }
+               enumerator->destroy(enumerator);
+               log_segments(this, TRUE, segment);
+       }
+}
+
+/**
+ * Implementation of ha_sync_cache_t.deactivate
+ */
+static void deactivate(private_ha_sync_cache_t *this, u_int segment)
+{
+       u_int16_t mask = 0x01 << (segment - 1);
 
-       /* TODO: activate only segment, not all */
-       while (this->list->remove_last(this->list, (void**)&ike_sa) == SUCCESS)
+       if (segment > 0 && segment <= this->segment_count && (this->active & mask))
        {
-               /* TODO: fix checkin of inexisting IKE_SA in manager */
-               /* TODO: do not activate SAs not in state CONNECTING */
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+               this->active &= ~mask;
+               log_segments(this, FALSE, segment);
        }
 }
 
@@ -138,14 +240,35 @@ static void destroy(private_ha_sync_cache_t *this)
 ha_sync_cache_t *ha_sync_cache_create()
 {
        private_ha_sync_cache_t *this = malloc_thing(private_ha_sync_cache_t);
+       enumerator_t *enumerator;
+       u_int segment;
+       char *str;
 
        this->public.get_ike_sa = (ike_sa_t*(*)(ha_sync_cache_t*, ike_sa_id_t *id))get_ike_sa;
        this->public.has_ike_sa = (bool(*)(ha_sync_cache_t*, ike_sa_id_t *id))has_ike_sa;
        this->public.delete_ike_sa = (void(*)(ha_sync_cache_t*, ike_sa_id_t *id))delete_ike_sa;
-       this->public.activate_segment = (void(*)(ha_sync_cache_t*, u_int segment))activate_segment;
+       this->public.activate = (void(*)(ha_sync_cache_t*, u_int segment))activate;
+       this->public.deactivate = (void(*)(ha_sync_cache_t*, u_int segment))deactivate;
        this->public.destroy = (void(*)(ha_sync_cache_t*))destroy;
 
        this->list = linked_list_create();
+       this->initval = 0;
+       this->active = 0;
+       this->segment_count = lib->settings->get_int(lib->settings,
+                                                               "charon.plugins.ha_sync.segment_count", 1);
+       this->segment_count = min(this->segment_count, MAX_SEGMENTS);
+       str = lib->settings->get_str(lib->settings,
+                                                               "charon.plugins.ha_sync.active_segments", "1");
+       enumerator = enumerator_create_token(str, ",", " ");
+       while (enumerator->enumerate(enumerator, &str))
+       {
+               segment = atoi(str);
+               if (segment && segment < MAX_SEGMENTS)
+               {
+                       this->active |= 0x01 << (segment - 1);
+               }
+       }
+       enumerator->destroy(enumerator);
 
        return &this->public;
 }
index 8508c7f..cf9e0d7 100644 (file)
@@ -66,7 +66,14 @@ struct ha_sync_cache_t {
         *
         * @param segment       numerical segment to takeover
         */
-       void (*activate_segment)(ha_sync_cache_t *this, u_int segment);
+       void (*activate)(ha_sync_cache_t *this, u_int segment);
+
+       /**
+        * Deactivate a set of IKE_SAs identified by a segment.
+        *
+        * @param segment       numerical segment to takeover
+        */
+       void (*deactivate)(ha_sync_cache_t *this, u_int segment);
 
        /**
         * Destroy a ha_sync_cache_t.
diff --git a/src/charon/plugins/ha_sync/ha_sync_ctl.c b/src/charon/plugins/ha_sync/ha_sync_ctl.c
new file mode 100644 (file)
index 0000000..cf747f2
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "ha_sync_ctl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <processing/jobs/callback_job.h>
+
+#define HA_SYNC_FIFO IPSEC_PIDDIR "/charon.ha"
+
+typedef struct private_ha_sync_ctl_t private_ha_sync_ctl_t;
+
+/**
+ * Private data of an ha_sync_ctl_t object.
+ */
+struct private_ha_sync_ctl_t {
+
+       /**
+        * Public ha_sync_ctl_t interface.
+        */
+       ha_sync_ctl_t public;
+
+       /**
+        * Cache to control
+        */
+       ha_sync_cache_t *cache;
+
+       /**
+        * FIFO reader thread
+        */
+       callback_job_t *job;
+};
+
+/**
+ * FIFO dispatching function
+ */
+static job_requeue_t dispatch_fifo(private_ha_sync_ctl_t *this)
+{
+       int fifo, old;
+       char buf[8];
+       u_int segment;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
+       fifo = open(HA_SYNC_FIFO, O_RDONLY);
+       pthread_setcancelstate(old, NULL);
+       if (fifo == -1)
+       {
+               DBG1(DBG_CFG, "opening HA sync fifo failed: %s", strerror(errno));
+               sleep(1);
+               return JOB_REQUEUE_FAIR;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       if (read(fifo, buf, sizeof(buf)-1) > 1)
+       {
+               segment = atoi(&buf[1]);
+               if (segment)
+               {
+                       switch (buf[0])
+                       {
+                               case '+':
+                                       this->cache->activate(this->cache, segment);
+                                       break;
+                               case '-':
+                                       this->cache->deactivate(this->cache, segment);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+       close(fifo);
+
+       return JOB_REQUEUE_DIRECT;
+}
+
+/**
+ * Implementation of ha_sync_ctl_t.destroy.
+ */
+static void destroy(private_ha_sync_ctl_t *this)
+{
+       this->job->cancel(this->job);
+       free(this);
+}
+
+/**
+ * See header
+ */
+ha_sync_ctl_t *ha_sync_ctl_create(ha_sync_cache_t *cache)
+{
+       private_ha_sync_ctl_t *this = malloc_thing(private_ha_sync_ctl_t);
+
+       this->public.destroy = (void(*)(ha_sync_ctl_t*))destroy;
+
+       if (access(HA_SYNC_FIFO, R_OK|W_OK) != 0)
+       {
+               if (mkfifo(HA_SYNC_FIFO, 600) != 0)
+               {
+                       DBG1(DBG_CFG, "creating HA sync FIFO %s failed: %s",
+                                HA_SYNC_FIFO, strerror(errno));
+               }
+       }
+
+       this->cache = cache;
+       this->job = callback_job_create((callback_job_cb_t)dispatch_fifo,
+                                                                       this, NULL, NULL);
+       charon->processor->queue_job(charon->processor, (job_t*)this->job);
+       return &this->public;
+}
+
diff --git a/src/charon/plugins/ha_sync/ha_sync_ctl.h b/src/charon/plugins/ha_sync/ha_sync_ctl.h
new file mode 100644 (file)
index 0000000..cd2c7a2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup ha_sync_ctl ha_sync_ctl
+ * @{ @ingroup ha_sync
+ */
+
+#ifndef HA_SYNC_CTL_H_
+#define HA_SYNC_CTL_H_
+
+#include "ha_sync_cache.h"
+
+typedef struct ha_sync_ctl_t ha_sync_ctl_t;
+
+/**
+ * HA Sync control interface using a FIFO.
+ */
+struct ha_sync_ctl_t {
+
+       /**
+        * Destroy a ha_sync_ctl_t.
+        */
+       void (*destroy)(ha_sync_ctl_t *this);
+};
+
+/**
+ * Create a ha_sync_ctl instance.
+ *
+ * @param cache                cache to control in this socket
+ * @return                     HA sync control interface
+ */
+ha_sync_ctl_t *ha_sync_ctl_create(ha_sync_cache_t *cache);
+
+#endif /* HA_SYNC_CTL_ @}*/
index 9bbc7f8..2164a4b 100644 (file)
@@ -21,6 +21,7 @@
 #include "ha_sync_socket.h"
 #include "ha_sync_dispatcher.h"
 #include "ha_sync_cache.h"
+#include "ha_sync_ctl.h"
 
 #include <daemon.h>
 #include <config/child_cfg.h>
@@ -61,6 +62,11 @@ struct private_ha_sync_plugin_t {
         * Local cache of a nodes synced SAs
         */
        ha_sync_cache_t *cache;
+
+       /**
+        * Segment control interface via FIFO
+        */
+       ha_sync_ctl_t *ctl;
 };
 
 /**
@@ -73,6 +79,7 @@ static void destroy(private_ha_sync_plugin_t *this)
        this->ike->destroy(this->ike);
        this->child->destroy(this->child);
        this->dispatcher->destroy(this->dispatcher);
+       this->ctl->destroy(this->ctl);
        this->cache->destroy(this->cache);
        this->socket->destroy(this->socket);
        free(this);
@@ -94,6 +101,7 @@ plugin_t *plugin_create()
                return NULL;
        }
        this->cache = ha_sync_cache_create();
+       this->ctl = ha_sync_ctl_create(this->cache);
        this->dispatcher = ha_sync_dispatcher_create(this->socket, this->cache);
        this->ike = ha_sync_ike_create(this->socket, this->cache);
        this->child = ha_sync_child_create(this->socket, this->cache);