summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/ha/ha_attribute.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/ha/ha_attribute.c')
-rw-r--r--src/libcharon/plugins/ha/ha_attribute.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/libcharon/plugins/ha/ha_attribute.c b/src/libcharon/plugins/ha/ha_attribute.c
new file mode 100644
index 000000000..b08abe1a9
--- /dev/null
+++ b/src/libcharon/plugins/ha/ha_attribute.c
@@ -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;
+}