Implemented a HA enabled in-memory address pool

This commit is contained in:
Martin Willi 2010-07-28 09:51:41 +02:00
parent 7455ab063f
commit 98d0343870
6 changed files with 468 additions and 4 deletions

View 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

View File

@ -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;
}

View File

@ -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_ @}*/

View File

@ -50,6 +50,11 @@ struct private_ha_dispatcher_t {
*/
ha_kernel_t *kernel;
/**
* HA enabled pool
*/
ha_attribute_t *attr;
/**
* Dispatcher 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);

View 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_ @}*/

View File

@ -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;
}