Implemented a HA enabled in-memory address pool
authorMartin Willi <martin@revosec.ch>
Wed, 28 Jul 2010 07:51:41 +0000 (09:51 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 28 Jul 2010 08:06:19 +0000 (10:06 +0200)
src/libcharon/plugins/ha/Makefile.am
src/libcharon/plugins/ha/ha_attribute.c [new file with mode: 0644]
src/libcharon/plugins/ha/ha_attribute.h [new file with mode: 0644]
src/libcharon/plugins/ha/ha_dispatcher.c
src/libcharon/plugins/ha/ha_dispatcher.h
src/libcharon/plugins/ha/ha_plugin.c

index 165f8c9..0df1b8d 100644 (file)
@@ -21,6 +21,7 @@ libstrongswan_ha_la_SOURCES = \
   ha_kernel.h ha_kernel.c \
   ha_ctl.h ha_ctl.c \
   ha_ike.h ha_ike.c \
-  ha_child.h ha_child.c
+  ha_child.h ha_child.c \
+  ha_attribute.h ha_attribute.c
 libstrongswan_ha_la_LDFLAGS = -module -avoid-version
 
diff --git a/src/libcharon/plugins/ha/ha_attribute.c b/src/libcharon/plugins/ha/ha_attribute.c
new file mode 100644 (file)
index 0000000..b08abe1
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * 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_attribute.h"
+
+#include <utils/linked_list.h>
+#include <threading/mutex.h>
+
+typedef struct private_ha_attribute_t private_ha_attribute_t;
+
+/**
+ * Private data of an ha_attribute_t object.
+ */
+struct private_ha_attribute_t {
+
+       /**
+        * Public ha_attribute_t interface.
+        */
+       ha_attribute_t public;
+
+       /**
+        * List of pools, pool_t
+        */
+       linked_list_t *pools;
+
+       /**
+        * Mutex to lock mask
+        */
+       mutex_t *mutex;
+
+       /**
+        * Kernel helper
+        */
+       ha_kernel_t *kernel;
+
+       /**
+        * Segment responsibility
+        */
+       ha_segments_t *segments;
+};
+
+/**
+ * In-memory pool.
+ */
+typedef struct {
+       /** name of the pool */
+       char *name;
+       /** base address of pool */
+       host_t *base;
+       /** total number of addresses in this pool */
+       int size;
+       /** bitmask for address usage */
+       u_char *mask;
+} pool_t;
+
+/**
+ * Clean up a pool entry
+ */
+static void pool_destroy(pool_t *pool)
+{
+       pool->base->destroy(pool->base);
+       free(pool->name);
+       free(pool->mask);
+       free(pool);
+}
+
+/**
+ * convert a pool offset to an address
+ */
+static host_t* offset2host(pool_t *pool, int offset)
+{
+       chunk_t addr;
+       host_t *host;
+       u_int32_t *pos;
+
+       if (offset > pool->size)
+       {
+               return NULL;
+       }
+
+       addr = chunk_clone(pool->base->get_address(pool->base));
+       if (pool->base->get_family(pool->base) == AF_INET6)
+       {
+               pos = (u_int32_t*)(addr.ptr + 12);
+       }
+       else
+       {
+               pos = (u_int32_t*)addr.ptr;
+       }
+       *pos = htonl(offset + ntohl(*pos));
+       host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0);
+       free(addr.ptr);
+       return host;
+}
+
+/**
+ * convert a host to a pool offset
+ */
+static int host2offset(pool_t *pool, host_t *addr)
+{
+       chunk_t host, base;
+       u_int32_t hosti, basei;
+
+       if (addr->get_family(addr) != pool->base->get_family(pool->base))
+       {
+               return -1;
+       }
+       host = addr->get_address(addr);
+       base = pool->base->get_address(pool->base);
+       if (addr->get_family(addr) == AF_INET6)
+       {
+               /* only look at last /32 block */
+               if (!memeq(host.ptr, base.ptr, 12))
+               {
+                       return -1;
+               }
+               host = chunk_skip(host, 12);
+               base = chunk_skip(base, 12);
+       }
+       hosti = ntohl(*(u_int32_t*)(host.ptr));
+       basei = ntohl(*(u_int32_t*)(base.ptr));
+       if (hosti > basei + pool->size)
+       {
+               return -1;
+       }
+       return hosti - basei;
+}
+
+/**
+ * Find a pool by its name
+ */
+static pool_t* get_pool(private_ha_attribute_t *this, char *name)
+{
+       enumerator_t *enumerator;
+       pool_t *pool, *found = NULL;
+
+       enumerator = this->pools->create_enumerator(this->pools);
+       while (enumerator->enumerate(enumerator, &pool))
+       {
+               if (streq(name, pool->name))
+               {
+                       found = pool;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+}
+
+/**
+ * Check if we are responsible for a bit in our bitmask
+ */
+static bool responsible_for(private_ha_attribute_t *this, int bit)
+{
+       u_int segment;
+
+       segment = this->kernel->get_segment_int(this->kernel, bit);
+       return this->segments->is_active(this->segments, segment);
+}
+
+METHOD(attribute_provider_t, acquire_address, host_t*,
+       private_ha_attribute_t *this, char *name, identification_t *id,
+       host_t *requested)
+{
+       pool_t *pool;
+       int offset = -1, byte, bit;
+       host_t *address;
+
+       this->mutex->lock(this->mutex);
+       pool = get_pool(this, name);
+       if (pool)
+       {
+               for (byte = 0; byte < pool->size / 8; byte++)
+               {
+                       if (pool->mask[byte] != 0xFF)
+                       {
+                               for (bit = 0; bit < 8; bit++)
+                               {
+                                       if (!(pool->mask[byte] & 1 << bit) &&
+                                               responsible_for(this, bit))
+                                       {
+                                               offset = byte * 8 + bit;
+                                               pool->mask[byte] |= 1 << bit;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (offset != -1)
+                       {
+                               break;
+                       }
+               }
+               if (offset == -1)
+               {
+                       DBG1(DBG_CFG, "no address left in HA pool '%s' belonging to"
+                                "a responsible segment", name);
+               }
+       }
+       this->mutex->unlock(this->mutex);
+       if (offset != -1)
+       {
+               address = offset2host(pool, offset);
+               DBG1(DBG_CFG, "acquired address %H from HA pool '%s'", address, name);
+               return address;
+       }
+       return NULL;
+}
+
+METHOD(attribute_provider_t, release_address, bool,
+       private_ha_attribute_t *this, char *name, host_t *address,
+       identification_t *id)
+{
+       pool_t *pool;
+       int offset;
+       bool found = FALSE;
+
+       this->mutex->lock(this->mutex);
+       pool = get_pool(this, name);
+       if (pool)
+       {
+               offset = host2offset(pool, address);
+               if (offset > 0 && offset < pool->size)
+               {
+                       pool->mask[offset / 8] &= ~(1 << (offset % 8));
+                       DBG1(DBG_CFG, "released address %H to HA pool '%s'", address, name);
+                       found = TRUE;
+               }
+       }
+       this->mutex->unlock(this->mutex);
+       return found;
+}
+
+METHOD(ha_attribute_t, reserve, void,
+       private_ha_attribute_t *this, char *name, host_t *address)
+{
+       pool_t *pool;
+       int offset;
+
+       this->mutex->lock(this->mutex);
+       pool = get_pool(this, name);
+       if (pool)
+       {
+               offset = host2offset(pool, address);
+               if (offset > 0 && offset < pool->size)
+               {
+                       pool->mask[offset / 8] |= 1 << (offset % 8);
+                       DBG1(DBG_CFG, "reserved address %H in HA pool '%s'", address, name);
+               }
+       }
+       this->mutex->unlock(this->mutex);
+}
+
+METHOD(ha_attribute_t, destroy, void,
+       private_ha_attribute_t *this)
+{
+       this->pools->destroy_function(this->pools, (void*)pool_destroy);
+       this->mutex->destroy(this->mutex);
+       free(this);
+}
+
+/**
+ * Load the configured pools.
+ */
+static void load_pools(private_ha_attribute_t *this)
+{
+       enumerator_t *enumerator;
+       char *name, *net, *bits;
+       host_t *base;
+       int mask, maxbits;
+       pool_t *pool;
+
+       enumerator = lib->settings->create_key_value_enumerator(lib->settings,
+                                                                                                       "charon.plugins.ha.pools");
+       while (enumerator->enumerate(enumerator, &name, &net))
+       {
+               net = strdup(net);
+               bits = strchr(net, '/');
+               if (!bits)
+               {
+                       DBG1(DBG_CFG, "invalid HA pool '%s' subnet, skipped", name);
+                       free(net);
+                       continue;
+               }
+               *bits++ = '\0';
+
+               base = host_create_from_string(net, 0);
+               mask = atoi(bits);
+               free(net);
+               if (!base || !mask)
+               {
+                       DESTROY_IF(base);
+                       DBG1(DBG_CFG, "invalid HA pool '%s', skipped", name);
+                       continue;
+               }
+               maxbits = base->get_family(base) == AF_INET ? 32 : 128;
+               mask = maxbits - mask;
+               if (mask > 24)
+               {
+                       mask = 24;
+                       DBG1(DBG_CFG, "size of HA pool '%s' limited to /%d",
+                                name, maxbits - mask);
+               }
+               if (mask < 3)
+               {
+                       DBG1(DBG_CFG, "HA pool '%s' too small, skipped", name);
+                       base->destroy(base);
+                       continue;
+               }
+
+               INIT(pool,
+                       .name = strdup(name),
+                       .base = base,
+                       .size = (1 << mask),
+               );
+               pool->mask = calloc(pool->size / 8, 1);
+               /* do not use first/last address of pool */
+               pool->mask[0] |= 0x01;
+               pool->mask[pool->size / 8 - 1] |= 0x80;
+
+               DBG1(DBG_CFG, "loaded HA pool '%s' %H/%d (%d addresses)",
+                        pool->name, pool->base, maxbits - mask, pool->size - 2);
+               this->pools->insert_last(this->pools, pool);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * See header
+ */
+ha_attribute_t *ha_attribute_create(ha_kernel_t *kernel, ha_segments_t *segments)
+{
+       private_ha_attribute_t *this;
+
+       INIT(this,
+               .public = {
+                       .provider = {
+                               .acquire_address = _acquire_address,
+                               .release_address = _release_address,
+                               .create_attribute_enumerator = enumerator_create_empty,
+                       },
+                       .reserve = _reserve,
+                       .destroy = _destroy,
+               },
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .pools = linked_list_create(),
+               .kernel = kernel,
+               .segments = segments,
+       );
+
+       load_pools(this);
+
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/ha/ha_attribute.h b/src/libcharon/plugins/ha/ha_attribute.h
new file mode 100644 (file)
index 0000000..d1e4f5e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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_attribute ha_attribute
+ * @{ @ingroup ha
+ */
+
+#ifndef HA_ATTRIBUTE_H_
+#define HA_ATTRIBUTE_H_
+
+#include "ha_kernel.h"
+#include "ha_segments.h"
+
+#include <attributes/attribute_provider.h>
+
+typedef struct ha_attribute_t ha_attribute_t;
+
+/**
+ * A HA enabled in memory address pool attribute provider.
+ */
+struct ha_attribute_t {
+
+       /**
+        * Implements attribute provider interface.
+        */
+       attribute_provider_t provider;
+
+       /**
+        * Reserve an address for a passive IKE_SA.
+        *
+        * @param name                  pool name to reserve address in
+        * @param address               address to reserve
+        */
+       void (*reserve)(ha_attribute_t *this, char *name, host_t *address);
+
+       /**
+        * Destroy a ha_attribute_t.
+        */
+       void (*destroy)(ha_attribute_t *this);
+};
+
+/**
+ * Create a ha_attribute instance.
+ */
+ha_attribute_t *ha_attribute_create(ha_kernel_t *kernel, ha_segments_t *segments);
+
+#endif /** HA_ATTRIBUTE_H_ @}*/
index ecae05f..3bc426e 100644 (file)
@@ -51,6 +51,11 @@ struct private_ha_dispatcher_t {
        ha_kernel_t *kernel;
 
        /**
+        * HA enabled pool
+        */
+       ha_attribute_t *attr;
+
+       /**
         * Dispatcher job
         */
        callback_job_t *job;
@@ -215,6 +220,7 @@ static void process_ike_update(private_ha_dispatcher_t *this,
        ike_sa_t *ike_sa = NULL;
        peer_cfg_t *peer_cfg = NULL;
        auth_cfg_t *auth;
+       bool received_vip = FALSE;
 
        enumerator = message->create_attribute_enumerator(message);
        while (enumerator->enumerate(enumerator, &attribute, &value))
@@ -252,6 +258,7 @@ static void process_ike_update(private_ha_dispatcher_t *this,
                                break;
                        case HA_REMOTE_VIP:
                                ike_sa->set_virtual_ip(ike_sa, FALSE, value.host);
+                               received_vip = TRUE;
                                break;
                        case HA_ADDITIONAL_ADDR:
                                ike_sa->add_additional_address(ike_sa,
@@ -301,6 +308,22 @@ static void process_ike_update(private_ha_dispatcher_t *this,
                                 ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa));
                        ike_sa->set_state(ike_sa, IKE_PASSIVE);
                }
+               if (received_vip)
+               {
+                       host_t *vip;
+                       char *pool;
+
+                       peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+                       vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
+                       if (peer_cfg && vip)
+                       {
+                               pool = peer_cfg->get_pool(peer_cfg);
+                               if (pool)
+                               {
+                                       this->attr->reserve(this->attr, pool, vip);
+                               }
+                       }
+               }
                this->cache->cache(this->cache, ike_sa, message);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
@@ -828,7 +851,8 @@ METHOD(ha_dispatcher_t, destroy, void,
  * See header
  */
 ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
-                       ha_segments_t *segments, ha_cache_t *cache, ha_kernel_t *kernel)
+                                                                       ha_segments_t *segments, ha_cache_t *cache,
+                                                                       ha_kernel_t *kernel, ha_attribute_t *attr)
 {
        private_ha_dispatcher_t *this;
 
@@ -841,6 +865,7 @@ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
                .segments = segments,
                .cache = cache,
                .kernel = kernel,
+               .attr = attr,
        );
        this->job = callback_job_create((callback_job_cb_t)dispatch,
                                                                        this, NULL, NULL);
index 3a5ec79..105a404 100644 (file)
@@ -25,6 +25,7 @@
 #include "ha_segments.h"
 #include "ha_cache.h"
 #include "ha_kernel.h"
+#include "ha_attribute.h"
 
 typedef struct ha_dispatcher_t ha_dispatcher_t;
 
@@ -46,9 +47,11 @@ struct ha_dispatcher_t {
  * @param segments             segments to control based on received messages
  * @param cache                        message cache to use for resynchronization
  * @param kernel               kernel helper
+ * @param attr                 HA enabled pool
  * @return                             dispatcher object
  */
 ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
-                       ha_segments_t *segments, ha_cache_t *cache, ha_kernel_t *kernel);
+                                                                       ha_segments_t *segments, ha_cache_t *cache,
+                                                                       ha_kernel_t *kernel, ha_attribute_t *attr);
 
 #endif /** HA_DISPATCHER_ @}*/
index b20868b..e722b4f 100644 (file)
 #include "ha_segments.h"
 #include "ha_ctl.h"
 #include "ha_cache.h"
+#include "ha_attribute.h"
 
 #include <daemon.h>
+#include <hydra.h>
 #include <config/child_cfg.h>
 
 typedef struct private_ha_plugin_t private_ha_plugin_t;
@@ -82,18 +84,25 @@ struct private_ha_plugin_t {
         * Message cache for resynchronization
         */
        ha_cache_t *cache;
+
+       /**
+        * Attribute provider
+        */
+       ha_attribute_t *attr;
 };
 
 METHOD(plugin_t, destroy, void,
        private_ha_plugin_t *this)
 {
        DESTROY_IF(this->ctl);
+       hydra->attributes->remove_provider(hydra->attributes, &this->attr->provider);
        charon->bus->remove_listener(charon->bus, &this->segments->listener);
        charon->bus->remove_listener(charon->bus, &this->ike->listener);
        charon->bus->remove_listener(charon->bus, &this->child->listener);
        this->ike->destroy(this->ike);
        this->child->destroy(this->child);
        this->dispatcher->destroy(this->dispatcher);
+       this->attr->destroy(this->attr);
        this->cache->destroy(this->cache);
        this->segments->destroy(this->segments);
        this->kernel->destroy(this->kernel);
@@ -155,14 +164,16 @@ plugin_t *ha_plugin_create()
        {
                this->ctl = ha_ctl_create(this->segments, this->cache);
        }
+       this->attr = ha_attribute_create(this->kernel, this->segments);
        this->dispatcher = ha_dispatcher_create(this->socket, this->segments,
-                                                                                       this->cache, this->kernel);
+                                                                               this->cache, this->kernel, this->attr);
        this->ike = ha_ike_create(this->socket, this->tunnel, this->cache);
        this->child = ha_child_create(this->socket, this->tunnel, this->segments,
                                                                  this->kernel);
        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);
+       hydra->attributes->add_provider(hydra->attributes, &this->attr->provider);
 
        return &this->public.plugin;
 }