2010-07-28 07:51:41 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
2012-10-16 12:54:16 +00:00
|
|
|
#include <collections/linked_list.h>
|
2010-07-28 07:51:41 +00:00
|
|
|
#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;
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t *pos;
|
2010-07-28 07:51:41 +00:00
|
|
|
|
|
|
|
if (offset > pool->size)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = chunk_clone(pool->base->get_address(pool->base));
|
|
|
|
if (pool->base->get_family(pool->base) == AF_INET6)
|
|
|
|
{
|
2016-03-22 12:22:01 +00:00
|
|
|
pos = (uint32_t*)(addr.ptr + 12);
|
2010-07-28 07:51:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-03-22 12:22:01 +00:00
|
|
|
pos = (uint32_t*)addr.ptr;
|
2010-07-28 07:51:41 +00:00
|
|
|
}
|
|
|
|
*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;
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t hosti, basei;
|
2010-07-28 07:51:41 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2016-03-22 12:22:01 +00:00
|
|
|
hosti = ntohl(*(uint32_t*)(host.ptr));
|
|
|
|
basei = ntohl(*(uint32_t*)(base.ptr));
|
2010-07-28 07:51:41 +00:00
|
|
|
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*,
|
2014-11-04 14:38:07 +00:00
|
|
|
private_ha_attribute_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
|
2010-07-28 07:51:41 +00:00
|
|
|
host_t *requested)
|
|
|
|
{
|
2012-09-11 08:41:11 +00:00
|
|
|
enumerator_t *enumerator;
|
2013-03-19 14:16:06 +00:00
|
|
|
pool_t *pool = NULL;
|
2010-07-28 07:51:41 +00:00
|
|
|
int offset = -1, byte, bit;
|
|
|
|
host_t *address;
|
2012-09-11 08:41:11 +00:00
|
|
|
char *name;
|
2010-07-28 07:51:41 +00:00
|
|
|
|
2012-09-11 08:41:11 +00:00
|
|
|
enumerator = pools->create_enumerator(pools);
|
2010-07-28 07:51:41 +00:00
|
|
|
this->mutex->lock(this->mutex);
|
2012-09-11 08:41:11 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &name))
|
2010-07-28 07:51:41 +00:00
|
|
|
{
|
2012-09-11 08:41:11 +00:00
|
|
|
pool = get_pool(this, name);
|
|
|
|
if (!pool)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2012-08-27 14:26:01 +00:00
|
|
|
if (pool->base->get_family(pool->base) !=
|
|
|
|
requested->get_family(requested))
|
|
|
|
{
|
2012-09-11 08:41:11 +00:00
|
|
|
continue;
|
2012-08-27 14:26:01 +00:00
|
|
|
}
|
2010-07-28 07:51:41 +00:00
|
|
|
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);
|
2012-09-11 08:41:11 +00:00
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
2010-07-28 07:51:41 +00:00
|
|
|
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,
|
2012-09-11 09:19:56 +00:00
|
|
|
private_ha_attribute_t *this, linked_list_t *pools, host_t *address,
|
2014-11-04 14:38:07 +00:00
|
|
|
ike_sa_t *ike_sa)
|
2010-07-28 07:51:41 +00:00
|
|
|
{
|
2012-09-11 09:19:56 +00:00
|
|
|
enumerator_t *enumerator;
|
2010-07-28 07:51:41 +00:00
|
|
|
pool_t *pool;
|
|
|
|
int offset;
|
2012-09-11 09:19:56 +00:00
|
|
|
char *name;
|
2010-07-28 07:51:41 +00:00
|
|
|
bool found = FALSE;
|
|
|
|
|
2012-09-11 09:19:56 +00:00
|
|
|
enumerator = pools->create_enumerator(pools);
|
2010-07-28 07:51:41 +00:00
|
|
|
this->mutex->lock(this->mutex);
|
2012-09-11 09:19:56 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &name))
|
2010-07-28 07:51:41 +00:00
|
|
|
{
|
2012-09-11 09:19:56 +00:00
|
|
|
pool = get_pool(this, name);
|
|
|
|
if (!pool)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (pool->base->get_family(pool->base) != address->get_family(address))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2010-07-28 07:51:41 +00:00
|
|
|
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;
|
2012-09-11 09:19:56 +00:00
|
|
|
break;
|
2010-07-28 07:51:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this->mutex->unlock(this->mutex);
|
2012-09-11 09:19:56 +00:00
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
2010-07-28 07:51:41 +00:00
|
|
|
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,
|
2014-01-22 14:18:58 +00:00
|
|
|
"%s.plugins.ha.pools", lib->ns);
|
2010-07-28 07:51:41 +00:00
|
|
|
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;
|
|
|
|
}
|