HA kernel interface can mangle netfilter rules, currently with iptables invocation
authorMartin Willi <martin@strongswan.org>
Tue, 22 Sep 2009 14:51:47 +0000 (16:51 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 7 Apr 2010 11:55:14 +0000 (13:55 +0200)
src/charon/plugins/ha_sync/ha_sync_kernel.c
src/charon/plugins/ha_sync/ha_sync_kernel.h
src/charon/plugins/ha_sync/ha_sync_plugin.c
src/charon/plugins/ha_sync/ha_sync_segments.c

index 49d09e9..caba2b0 100644 (file)
@@ -19,6 +19,12 @@ typedef u_int32_t u32;
 typedef u_int8_t u8;
 
 #include <linux/jhash.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 typedef struct private_ha_sync_kernel_t private_ha_sync_kernel_t;
 
@@ -43,15 +49,16 @@ struct private_ha_sync_kernel_t {
        u_int segment_count;
 
        /**
-        * mask of active segments
+        * List of virtual addresses, as host_t*
         */
-       segment_mask_t active;
+       linked_list_t *virtuals;
 };
 
 /**
  * Implementation of ha_sync_kernel_t.in_segment
  */
-static bool in_segment(private_ha_sync_kernel_t *this, host_t *host, u_int segment)
+static bool in_segment(private_ha_sync_kernel_t *this,
+                                          host_t *host, u_int segment)
 {
        if (host->get_family(host) == AF_INET)
        {
@@ -68,12 +75,140 @@ static bool in_segment(private_ha_sync_kernel_t *this, host_t *host, u_int segme
        }
        return FALSE;
 }
+/**
+ * Activate/Deactivate a segment
+ */
+static void activate_deactivate(private_ha_sync_kernel_t *this,
+                                                               u_int segment, char op)
+{
+       enumerator_t *enumerator;
+       host_t *host;
+       char cmd[8], file[256];
+       int fd;
+
+       enumerator = this->virtuals->create_enumerator(this->virtuals);
+       while (enumerator->enumerate(enumerator, &host))
+       {
+               snprintf(file, sizeof(file), "/proc/net/ipt_CLUSTERIP/%H", host);
+               snprintf(cmd, sizeof(cmd), "%c%d\n", op, segment);
+
+               fd = open(file, O_WRONLY);
+               if (fd == -1)
+               {
+                       DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s",
+                                file, strerror(errno));
+                       continue;
+               }
+               if (write(fd, cmd, strlen(cmd) == -1))
+               {
+                       DBG1(DBG_CFG, "writing to CLUSTERIP file '%s' failed: %s",
+                                file, strerror(errno));
+               }
+               close(fd);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * Implementation of ha_sync_kernel_t.activate
+ */
+static void activate(private_ha_sync_kernel_t *this, u_int segment)
+{
+       activate_deactivate(this, segment, '+');
+}
+
+/**
+ * Implementation of ha_sync_kernel_t.deactivate
+ */
+static void deactivate(private_ha_sync_kernel_t *this, u_int segment)
+{
+       activate_deactivate(this, segment, '-');
+}
+
+/**
+ * Mangle IPtable rules for virtual addresses
+ */
+static bool mangle_rules(private_ha_sync_kernel_t *this, bool add,
+                                                segment_mask_t active)
+{
+       enumerator_t *enumerator;
+       host_t *host;
+       u_char i, mac = 0x20;
+       char *iface, buf[256];
+
+       enumerator = this->virtuals->create_enumerator(this->virtuals);
+       while (enumerator->enumerate(enumerator, &host))
+       {
+               iface = charon->kernel_interface->get_interface(
+                                                                                       charon->kernel_interface, host);
+               if (!iface)
+               {
+                       DBG1(DBG_CFG, "cluster address %H not installed, ignored", host);
+                       this->virtuals->remove_at(this->virtuals, enumerator);
+                       host->destroy(host);
+                       continue;
+               }
+               /* iptables insists of a local node specification. We add '1' but drop
+                * it afterwards. */
+               snprintf(buf, sizeof(buf),
+                                "/sbin/iptables -%c INPUT -i %s -d %H -j CLUSTERIP --new "
+                                "--hashmode sourceip --clustermac 01:00:5e:00:00:%2x "
+                                "--total-nodes %d --local-node 1",
+                                add ? 'A' : 'D', iface, host, mac++, this->segment_count);
+               free(iface);
+               if (system(buf) != 0)
+               {
+                       DBG1(DBG_CFG, "installing CLUSTERIP rule '%s' failed", buf);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       if (add)
+       {
+               deactivate(this, 1);
+               for (i = 0; i < SEGMENTS_MAX; i++)
+               {
+                       if (active & SEGMENTS_BIT(i))
+                       {
+                               activate(this, i);
+                       }
+               }
+       }
+       return TRUE;
+}
+
+/**
+ * Parse the list of virtual cluster addresses
+ */
+static void parse_virtuals(private_ha_sync_kernel_t *this, char *virtual)
+{
+       enumerator_t *enumerator;
+       host_t *host;
+
+       enumerator = enumerator_create_token(virtual, ",", " ");
+       while (enumerator->enumerate(enumerator, &virtual))
+       {
+               host = host_create_from_string(virtual, 0);
+               if (host)
+               {
+                       this->virtuals->insert_last(this->virtuals, host);
+               }
+               else
+               {
+                       DBG1(DBG_CFG, "virtual cluster address '%s' invalid, ignored",
+                                virtual);
+               }
+       }
+       enumerator->destroy(enumerator);
+}
 
 /**
  * Implementation of ha_sync_kernel_t.destroy.
  */
 static void destroy(private_ha_sync_kernel_t *this)
 {
+       mangle_rules(this, FALSE, 0);
+       this->virtuals->destroy_offset(this->virtuals, offsetof(host_t, destroy));
        free(this);
 }
 
@@ -81,15 +216,26 @@ static void destroy(private_ha_sync_kernel_t *this)
  * See header
  */
 ha_sync_kernel_t *ha_sync_kernel_create(u_int count, segment_mask_t active,
-                                                                               char *external, char *internal)
+                                                                               char *virtuals)
 {
        private_ha_sync_kernel_t *this = malloc_thing(private_ha_sync_kernel_t);
 
        this->public.in_segment = (bool(*)(ha_sync_kernel_t*, host_t *host, u_int segment))in_segment;
+       this->public.activate = (void(*)(ha_sync_kernel_t*, u_int segment))activate;
+       this->public.deactivate = (void(*)(ha_sync_kernel_t*, u_int segment))deactivate;
        this->public.destroy = (void(*)(ha_sync_kernel_t*))destroy;
 
        this->initval = 0;
        this->segment_count = count;
+       this->virtuals = linked_list_create();
+
+       parse_virtuals(this, virtuals);
+
+       if (!mangle_rules(this, TRUE, active))
+       {
+               destroy(this);
+               return NULL;
+       }
 
        return &this->public;
 }
index 2ca8625..87ee02a 100644 (file)
@@ -40,6 +40,20 @@ struct ha_sync_kernel_t {
        bool (*in_segment)(ha_sync_kernel_t *this, host_t *host, u_int segment);
 
        /**
+        * Activate a segment at kernel level for all cluster addresses.
+        *
+        * @param segment       segment to activate
+        */
+       void (*activate)(ha_sync_kernel_t *this, u_int segment);
+
+       /**
+        * Deactivate a segment at kernel level for all cluster addresses.
+        *
+        * @param segment       segment to deactivate
+        */
+       void (*deactivate)(ha_sync_kernel_t *this, u_int segment);
+
+       /**
         * Destroy a ha_sync_kernel_t.
         */
        void (*destroy)(ha_sync_kernel_t *this);
@@ -50,10 +64,9 @@ struct ha_sync_kernel_t {
  *
  * @param count                        total number of segments to use
  * @param active               bitmask of initially active segments
- * @param external             external virtual IP the cluster acts as
- * @param internal             internal virtual IP the cluster uses
+ * @param virtuals             comma separated list of virtual cluster addresses
  */
 ha_sync_kernel_t *ha_sync_kernel_create(u_int count, segment_mask_t active,
-                                                                               char *external, char *internal);
+                                                                               char *virtuals);
 
 #endif /* HA_SYNC_KERNEL_ @}*/
index 78cced5..4fed2e3 100644 (file)
@@ -125,7 +125,7 @@ static segment_mask_t parse_active(char *active)
 plugin_t *plugin_create()
 {
        private_ha_sync_plugin_t *this;
-       char *local, *remote, *secret, *external, *internal;
+       char *local, *remote, *secret, *virtuals;
        segment_mask_t active;
        u_int count;
        bool fifo;
@@ -134,10 +134,8 @@ plugin_t *plugin_create()
                                                                "charon.plugins.ha_sync.local", NULL);
        remote = lib->settings->get_str(lib->settings,
                                                                "charon.plugins.ha_sync.remote", NULL);
-       external = lib->settings->get_str(lib->settings,
-                                                               "charon.plugins.ha_sync.external", NULL);
-       internal = lib->settings->get_str(lib->settings,
-                                                               "charon.plugins.ha_sync.internal", NULL);
+       virtuals = lib->settings->get_str(lib->settings,
+                                                               "charon.plugins.ha_sync.virtuals", "");
        secret = lib->settings->get_str(lib->settings,
                                                                "charon.plugins.ha_sync.secret", NULL);
        fifo = lib->settings->get_bool(lib->settings,
@@ -151,11 +149,6 @@ plugin_t *plugin_create()
                DBG1(DBG_CFG, "HA sync config misses local/remote address");
                return NULL;
        }
-       if (!external || !internal)
-       {
-               DBG1(DBG_CFG, "HA sync config misses external/internal virtual address");
-               return NULL;
-       }
 
        this = malloc_thing(private_ha_sync_plugin_t);
 
@@ -169,7 +162,7 @@ plugin_t *plugin_create()
                free(this);
                return NULL;
        }
-       this->kernel = ha_sync_kernel_create(count, active, external, internal);
+       this->kernel = ha_sync_kernel_create(count, active, virtuals);
        if (!this->kernel)
        {
                this->socket->destroy(this->socket);
index d3b2834..f264904 100644 (file)
@@ -107,17 +107,6 @@ static void enable_disable(private_ha_sync_segments_t *this, u_int segment,
                {       /* or segment_count times for all segments */
                        limit = this->segment_count;
                }
-               for (i = segment; i < limit; i++)
-               {
-                       if (enable)
-                       {
-                               this->active |= SEGMENTS_BIT(i);
-                       }
-                       else
-                       {
-                               this->active &= ~SEGMENTS_BIT(i);
-                       }
-               }
                enumerator = charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager);
                while (enumerator->enumerate(enumerator, &ike_sa))
                {
@@ -134,6 +123,19 @@ static void enable_disable(private_ha_sync_segments_t *this, u_int segment,
                        }
                }
                enumerator->destroy(enumerator);
+               for (i = segment; i < limit; i++)
+               {
+                       if (enable)
+                       {
+                               this->active |= SEGMENTS_BIT(i);
+                               this->kernel->activate(this->kernel, i);
+                       }
+                       else
+                       {
+                               this->active &= ~SEGMENTS_BIT(i);
+                               this->kernel->deactivate(this->kernel, i);
+                       }
+               }
 
                log_segments(this, enable, segment);
        }