diff options
Diffstat (limited to 'src/libcharon/plugins')
-rw-r--r-- | src/libcharon/plugins/ha/Makefile.am | 3 | ||||
-rw-r--r-- | src/libcharon/plugins/ha/ha_attribute.c | 364 | ||||
-rw-r--r-- | src/libcharon/plugins/ha/ha_attribute.h | 60 | ||||
-rw-r--r-- | src/libcharon/plugins/ha/ha_dispatcher.c | 27 | ||||
-rw-r--r-- | src/libcharon/plugins/ha/ha_dispatcher.h | 5 | ||||
-rw-r--r-- | src/libcharon/plugins/ha/ha_plugin.c | 13 |
6 files changed, 468 insertions, 4 deletions
diff --git a/src/libcharon/plugins/ha/Makefile.am b/src/libcharon/plugins/ha/Makefile.am index 165f8c9dc..0df1b8d91 100644 --- a/src/libcharon/plugins/ha/Makefile.am +++ b/src/libcharon/plugins/ha/Makefile.am @@ -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 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; +} diff --git a/src/libcharon/plugins/ha/ha_attribute.h b/src/libcharon/plugins/ha/ha_attribute.h new file mode 100644 index 000000000..d1e4f5e89 --- /dev/null +++ b/src/libcharon/plugins/ha/ha_attribute.h @@ -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_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index ecae05fd5..3bc426ea0 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -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); diff --git a/src/libcharon/plugins/ha/ha_dispatcher.h b/src/libcharon/plugins/ha/ha_dispatcher.h index 3a5ec7985..105a40473 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.h +++ b/src/libcharon/plugins/ha/ha_dispatcher.h @@ -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_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_plugin.c b/src/libcharon/plugins/ha/ha_plugin.c index b20868b71..e722b4f3a 100644 --- a/src/libcharon/plugins/ha/ha_plugin.c +++ b/src/libcharon/plugins/ha/ha_plugin.c @@ -22,8 +22,10 @@ #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; } |