Files
strongswan-ext/src/libcharon/plugins/ha/ha_attribute.c
T
Martin Willi 594c58e111 Pass the full list of pools to acquire_address, enumerate in providers
If the provider has access to the full pool list, it can enumerate
them twice, for example to search for existing leases first, and
only search for new leases in a second step.

Fixes lease enumeration in attr-sql using multiple pools.
2012-09-11 16:18:28 +02:00

379 lines
7.8 KiB
C

/*
* 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, linked_list_t *pools, identification_t *id,
host_t *requested)
{
enumerator_t *enumerator;
pool_t *pool;
int offset = -1, byte, bit;
host_t *address;
char *name;
enumerator = pools->create_enumerator(pools);
this->mutex->lock(this->mutex);
while (enumerator->enumerate(enumerator, &name))
{
pool = get_pool(this, name);
if (!pool)
{
continue;
}
if (pool->base->get_family(pool->base) !=
requested->get_family(requested))
{
continue;
}
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);
enumerator->destroy(enumerator);
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,
"%s.plugins.ha.pools", charon->name);
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;
}