789 lines
17 KiB
C
789 lines
17 KiB
C
/*
|
|
* Copyright (C) 2014-2016 Tobias Brunner
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* Copyright (C) 2014 Martin Willi
|
|
* Copyright (C) 2014 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 "vici_attribute.h"
|
|
#include "vici_builder.h"
|
|
|
|
#include <daemon.h>
|
|
#include <collections/hashtable.h>
|
|
#include <collections/array.h>
|
|
#include <threading/rwlock.h>
|
|
#include <attributes/mem_pool.h>
|
|
|
|
typedef struct private_vici_attribute_t private_vici_attribute_t;
|
|
|
|
/**
|
|
* private data of vici_attribute
|
|
*/
|
|
struct private_vici_attribute_t {
|
|
|
|
/**
|
|
* public functions
|
|
*/
|
|
vici_attribute_t public;
|
|
|
|
/**
|
|
* vici connection dispatcher
|
|
*/
|
|
vici_dispatcher_t *dispatcher;
|
|
|
|
/**
|
|
* Configured pools, as char* => pool_t
|
|
*/
|
|
hashtable_t *pools;
|
|
|
|
/**
|
|
* rwlock to lock access to pools
|
|
*/
|
|
rwlock_t *lock;
|
|
};
|
|
|
|
/**
|
|
* Single configuration attribute with type
|
|
*/
|
|
typedef struct {
|
|
/** type of attribute */
|
|
configuration_attribute_type_t type;
|
|
/** attribute value */
|
|
chunk_t value;
|
|
} attribute_t;
|
|
|
|
/**
|
|
* Clean up an attribute
|
|
*/
|
|
static void attribute_destroy(attribute_t *attr)
|
|
{
|
|
free(attr->value.ptr);
|
|
free(attr);
|
|
}
|
|
|
|
/**
|
|
* Pool instances with associated attributes
|
|
*/
|
|
typedef struct {
|
|
/** in-memory virtual IP pool */
|
|
mem_pool_t *vips;
|
|
/** configuration attributes, as attribute_t */
|
|
array_t *attrs;
|
|
} pool_t;
|
|
|
|
/**
|
|
* Clean up a pool instance
|
|
*/
|
|
static void pool_destroy(pool_t *pool)
|
|
{
|
|
DESTROY_IF(pool->vips);
|
|
array_destroy_function(pool->attrs, (void*)attribute_destroy, NULL);
|
|
free(pool);
|
|
}
|
|
|
|
/**
|
|
* Find an existing or not yet existing lease
|
|
*/
|
|
static host_t *find_addr(private_vici_attribute_t *this, linked_list_t *pools,
|
|
identification_t *id, host_t *requested,
|
|
mem_pool_op_t op, host_t *peer)
|
|
{
|
|
enumerator_t *enumerator;
|
|
host_t *addr = NULL;
|
|
pool_t *pool;
|
|
char *name;
|
|
|
|
enumerator = pools->create_enumerator(pools);
|
|
while (enumerator->enumerate(enumerator, &name))
|
|
{
|
|
pool = this->pools->get(this->pools, name);
|
|
if (pool)
|
|
{
|
|
addr = pool->vips->acquire_address(pool->vips, id, requested,
|
|
op, peer);
|
|
if (addr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
return addr;
|
|
}
|
|
|
|
METHOD(attribute_provider_t, acquire_address, host_t*,
|
|
private_vici_attribute_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
|
|
host_t *requested)
|
|
{
|
|
identification_t *id;
|
|
host_t *addr, *peer;
|
|
|
|
id = ike_sa->get_other_eap_id(ike_sa);
|
|
peer = ike_sa->get_other_host(ike_sa);
|
|
|
|
this->lock->read_lock(this->lock);
|
|
|
|
addr = find_addr(this, pools, id, requested, MEM_POOL_EXISTING, peer);
|
|
if (!addr)
|
|
{
|
|
addr = find_addr(this, pools, id, requested, MEM_POOL_NEW, peer);
|
|
if (!addr)
|
|
{
|
|
addr = find_addr(this, pools, id, requested, MEM_POOL_REASSIGN, peer);
|
|
}
|
|
}
|
|
|
|
this->lock->unlock(this->lock);
|
|
|
|
return addr;
|
|
}
|
|
|
|
METHOD(attribute_provider_t, release_address, bool,
|
|
private_vici_attribute_t *this, linked_list_t *pools, host_t *address,
|
|
ike_sa_t *ike_sa)
|
|
{
|
|
enumerator_t *enumerator;
|
|
identification_t *id;
|
|
bool found = FALSE;
|
|
pool_t *pool;
|
|
char *name;
|
|
|
|
id = ike_sa->get_other_eap_id(ike_sa);
|
|
|
|
this->lock->read_lock(this->lock);
|
|
|
|
enumerator = pools->create_enumerator(pools);
|
|
while (enumerator->enumerate(enumerator, &name))
|
|
{
|
|
pool = this->pools->get(this->pools, name);
|
|
if (pool)
|
|
{
|
|
found = pool->vips->release_address(pool->vips, address, id);
|
|
if (found)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
this->lock->unlock(this->lock);
|
|
|
|
return found;
|
|
}
|
|
|
|
CALLBACK(attr_filter, bool,
|
|
void *data, enumerator_t *orig, va_list args)
|
|
{
|
|
attribute_t *attr;
|
|
configuration_attribute_type_t *type;
|
|
chunk_t *value;
|
|
|
|
VA_ARGS_VGET(args, type, value);
|
|
|
|
if (orig->enumerate(orig, &attr))
|
|
{
|
|
*type = attr->type;
|
|
*value = attr->value;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Create nested inner enumerator over pool attributes
|
|
*/
|
|
CALLBACK(create_nested, enumerator_t*,
|
|
pool_t *pool, void *this)
|
|
{
|
|
return enumerator_create_filter(array_create_enumerator(pool->attrs),
|
|
attr_filter, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* Data associated to nested enumerator cleanup
|
|
*/
|
|
typedef struct {
|
|
private_vici_attribute_t *this;
|
|
linked_list_t *list;
|
|
} nested_data_t;
|
|
|
|
/**
|
|
* Clean up nested enumerator data
|
|
*/
|
|
CALLBACK(nested_cleanup, void,
|
|
nested_data_t *data)
|
|
{
|
|
data->this->lock->unlock(data->this->lock);
|
|
data->list->destroy(data->list);
|
|
free(data);
|
|
}
|
|
|
|
/**
|
|
* Check if any of vips is from pool
|
|
*/
|
|
static bool have_vips_from_pool(mem_pool_t *pool, linked_list_t *vips)
|
|
{
|
|
enumerator_t *enumerator;
|
|
host_t *host;
|
|
chunk_t start, end, current;
|
|
uint32_t size;
|
|
bool found = FALSE;
|
|
|
|
host = pool->get_base(pool);
|
|
start = host->get_address(host);
|
|
|
|
if (start.len >= sizeof(size))
|
|
{
|
|
end = chunk_clone(start);
|
|
|
|
/* mem_pool is currently limited to 2^31 addresses, so 32-bit
|
|
* calculations should be sufficient. */
|
|
size = untoh32(start.ptr + start.len - sizeof(size));
|
|
htoun32(end.ptr + end.len - sizeof(size), size + pool->get_size(pool));
|
|
|
|
enumerator = vips->create_enumerator(vips);
|
|
while (enumerator->enumerate(enumerator, &host))
|
|
{
|
|
current = host->get_address(host);
|
|
if (chunk_compare(current, start) >= 0 &&
|
|
chunk_compare(current, end) < 0)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
free(end.ptr);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
|
|
private_vici_attribute_t *this, linked_list_t *pools,
|
|
ike_sa_t *ike_sa, linked_list_t *vips)
|
|
{
|
|
enumerator_t *enumerator;
|
|
nested_data_t *data;
|
|
pool_t *pool;
|
|
char *name;
|
|
|
|
INIT(data,
|
|
.this = this,
|
|
.list = linked_list_create(),
|
|
);
|
|
|
|
this->lock->read_lock(this->lock);
|
|
|
|
enumerator = pools->create_enumerator(pools);
|
|
while (enumerator->enumerate(enumerator, &name))
|
|
{
|
|
pool = this->pools->get(this->pools, name);
|
|
if (pool && have_vips_from_pool(pool->vips, vips))
|
|
{
|
|
data->list->insert_last(data->list, pool);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
return enumerator_create_nested(data->list->create_enumerator(data->list),
|
|
create_nested, data, nested_cleanup);
|
|
}
|
|
|
|
/**
|
|
* Merge a pool configuration with existing ones
|
|
*/
|
|
static bool merge_pool(private_vici_attribute_t *this, pool_t *new)
|
|
{
|
|
mem_pool_t *tmp;
|
|
host_t *base;
|
|
pool_t *old;
|
|
const char *name;
|
|
u_int size;
|
|
|
|
name = new->vips->get_name(new->vips);
|
|
base = new->vips->get_base(new->vips);
|
|
size = new->vips->get_size(new->vips);
|
|
|
|
old = this->pools->remove(this->pools, name);
|
|
if (!old)
|
|
{
|
|
this->pools->put(this->pools, name, new);
|
|
DBG1(DBG_CFG, "added vici pool %s: %H, %u entries", name, base, size);
|
|
return TRUE;
|
|
}
|
|
|
|
if (base->ip_equals(base, old->vips->get_base(old->vips)) &&
|
|
size == old->vips->get_size(old->vips))
|
|
{
|
|
/* no changes in pool, so keep existing, but use new attributes */
|
|
DBG1(DBG_CFG, "updated vici pool %s: %H, %u entries", name, base, size);
|
|
tmp = new->vips;
|
|
new->vips = old->vips;
|
|
old->vips = tmp;
|
|
this->pools->put(this->pools, new->vips->get_name(new->vips), new);
|
|
pool_destroy(old);
|
|
return TRUE;
|
|
}
|
|
if (old->vips->get_online(old->vips) == 0)
|
|
{
|
|
/* can replace old pool, no online leases */
|
|
DBG1(DBG_CFG, "replaced vici pool %s: %H, %u entries", name, base, size);
|
|
this->pools->put(this->pools, name, new);
|
|
pool_destroy(old);
|
|
return TRUE;
|
|
}
|
|
/* have online leases, unable to replace, TODO: migrate leases? */
|
|
DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to replace",
|
|
name, old->vips->get_online(old->vips));
|
|
this->pools->put(this->pools, old->vips->get_name(old->vips), old);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Create a (error) reply message
|
|
*/
|
|
static vici_message_t* create_reply(char *fmt, ...)
|
|
{
|
|
vici_builder_t *builder;
|
|
va_list args;
|
|
|
|
builder = vici_builder_create();
|
|
builder->add_kv(builder, "success", fmt ? "no" : "yes");
|
|
if (fmt)
|
|
{
|
|
va_start(args, fmt);
|
|
builder->vadd_kv(builder, "errmsg", fmt, args);
|
|
va_end(args);
|
|
}
|
|
return builder->finalize(builder);
|
|
}
|
|
|
|
/**
|
|
* Parse a range definition of an address pool
|
|
*/
|
|
static mem_pool_t *create_pool_range(char *name, char *buf)
|
|
{
|
|
mem_pool_t *pool;
|
|
host_t *from, *to;
|
|
|
|
if (!host_create_from_range(buf, &from, &to))
|
|
{
|
|
return NULL;
|
|
}
|
|
pool = mem_pool_create_range(name, from, to);
|
|
from->destroy(from);
|
|
to->destroy(to);
|
|
return pool;
|
|
}
|
|
|
|
/**
|
|
* Parse callback data, passed to each callback
|
|
*/
|
|
typedef struct {
|
|
private_vici_attribute_t *this;
|
|
vici_message_t *reply;
|
|
} request_data_t;
|
|
|
|
/**
|
|
* Data associated to a pool load
|
|
*/
|
|
typedef struct {
|
|
request_data_t *request;
|
|
char *name;
|
|
pool_t *pool;
|
|
} load_data_t;
|
|
|
|
CALLBACK(pool_li, bool,
|
|
load_data_t *data, vici_message_t *message, char *name, chunk_t value)
|
|
{
|
|
struct {
|
|
char *name;
|
|
configuration_attribute_type_t v4;
|
|
configuration_attribute_type_t v6;
|
|
} keys[] = {
|
|
{"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS },
|
|
{"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS },
|
|
{"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS },
|
|
{"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP },
|
|
{"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK },
|
|
{"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER },
|
|
{"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET },
|
|
{"split_include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE },
|
|
{"split_exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN },
|
|
};
|
|
char buf[256];
|
|
int i, index = -1, mask = -1, type = 0;
|
|
chunk_t encoding;
|
|
attribute_t *attr;
|
|
host_t *host = NULL;
|
|
|
|
for (i = 0; i < countof(keys); i++)
|
|
{
|
|
if (streq(name, keys[i].name))
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if (index == -1)
|
|
{
|
|
type = atoi(name);
|
|
if (!type)
|
|
{
|
|
data->request->reply = create_reply("invalid attribute: %s", name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (vici_stringify(value, buf, sizeof(buf)))
|
|
{
|
|
if (strchr(buf, '/'))
|
|
{
|
|
host = host_create_from_subnet(buf, &mask);
|
|
}
|
|
else
|
|
{
|
|
host = host_create_from_string(buf, 0);
|
|
}
|
|
}
|
|
if (host)
|
|
{
|
|
if (index != -1)
|
|
{
|
|
switch (host->get_family(host))
|
|
{
|
|
case AF_INET:
|
|
type = keys[index].v4;
|
|
break;
|
|
case AF_INET6:
|
|
default:
|
|
type = keys[index].v6;
|
|
break;
|
|
}
|
|
}
|
|
if (mask == -1)
|
|
{
|
|
encoding = chunk_clone(host->get_address(host));
|
|
}
|
|
else
|
|
{
|
|
if (host->get_family(host) == AF_INET)
|
|
{ /* IPv4 attributes contain a subnet mask */
|
|
uint32_t netmask = 0;
|
|
|
|
if (mask)
|
|
{ /* shifting uint32_t by 32 or more is undefined */
|
|
mask = 32 - mask;
|
|
netmask = htonl((0xFFFFFFFF >> mask) << mask);
|
|
}
|
|
encoding = chunk_cat("cc", host->get_address(host),
|
|
chunk_from_thing(netmask));
|
|
}
|
|
else
|
|
{ /* IPv6 addresses the prefix only */
|
|
encoding = chunk_cat("cc", host->get_address(host),
|
|
chunk_from_chars(mask));
|
|
}
|
|
}
|
|
host->destroy(host);
|
|
}
|
|
else
|
|
{
|
|
if (index != -1)
|
|
{
|
|
data->request->reply = create_reply("invalid attribute value "
|
|
"for %s", name);
|
|
return FALSE;
|
|
}
|
|
/* use raw binary data for numbered attributes */
|
|
encoding = chunk_clone(value);
|
|
}
|
|
INIT(attr,
|
|
.type = type,
|
|
.value = encoding,
|
|
);
|
|
array_insert_create(&data->pool->attrs, ARRAY_TAIL, attr);
|
|
return TRUE;
|
|
}
|
|
|
|
CALLBACK(pool_kv, bool,
|
|
load_data_t *data, vici_message_t *message, char *name, chunk_t value)
|
|
{
|
|
if (streq(name, "addrs"))
|
|
{
|
|
char buf[128];
|
|
mem_pool_t *pool;
|
|
host_t *base = NULL;
|
|
int bits;
|
|
|
|
if (data->pool->vips)
|
|
{
|
|
data->request->reply = create_reply("multiple addrs defined");
|
|
return FALSE;
|
|
}
|
|
if (!vici_stringify(value, buf, sizeof(buf)))
|
|
{
|
|
data->request->reply = create_reply("invalid addrs value");
|
|
return FALSE;
|
|
}
|
|
pool = create_pool_range(data->name, buf);
|
|
if (!pool)
|
|
{
|
|
base = host_create_from_subnet(buf, &bits);
|
|
if (base)
|
|
{
|
|
pool = mem_pool_create(data->name, base, bits);
|
|
base->destroy(base);
|
|
}
|
|
}
|
|
if (!pool)
|
|
{
|
|
data->request->reply = create_reply("invalid addrs value: %s", buf);
|
|
return FALSE;
|
|
}
|
|
data->pool->vips = pool;
|
|
return TRUE;
|
|
}
|
|
data->request->reply = create_reply("invalid attribute: %s", name);
|
|
return FALSE;
|
|
}
|
|
|
|
CALLBACK(pool_sn, bool,
|
|
request_data_t *request, vici_message_t *message,
|
|
vici_parse_context_t *ctx, char *name)
|
|
{
|
|
load_data_t data = {
|
|
.request = request,
|
|
.name = name,
|
|
};
|
|
bool merged;
|
|
|
|
INIT(data.pool);
|
|
|
|
if (!message->parse(message, ctx, NULL, pool_kv, pool_li, &data))
|
|
{
|
|
pool_destroy(data.pool);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!data.pool->vips)
|
|
{
|
|
request->reply = create_reply("missing addrs for pool '%s'", name);
|
|
pool_destroy(data.pool);
|
|
return FALSE;
|
|
}
|
|
|
|
request->this->lock->write_lock(request->this->lock);
|
|
merged = merge_pool(request->this, data.pool);
|
|
request->this->lock->unlock(request->this->lock);
|
|
|
|
if (!merged)
|
|
{
|
|
request->reply = create_reply("vici pool %s has online leases, "
|
|
"unable to replace", name);
|
|
pool_destroy(data.pool);
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
CALLBACK(load_pool, vici_message_t*,
|
|
private_vici_attribute_t *this, char *name, u_int id,
|
|
vici_message_t *message)
|
|
{
|
|
request_data_t request = {
|
|
.this = this,
|
|
};
|
|
|
|
if (!message->parse(message, NULL, pool_sn, NULL, NULL, &request))
|
|
{
|
|
if (request.reply)
|
|
{
|
|
return request.reply;
|
|
}
|
|
return create_reply("parsing request failed");
|
|
}
|
|
return create_reply(NULL);
|
|
}
|
|
|
|
CALLBACK(unload_pool, vici_message_t*,
|
|
private_vici_attribute_t *this, char *name, u_int id,
|
|
vici_message_t *message)
|
|
{
|
|
vici_message_t *reply;
|
|
u_int online;
|
|
pool_t *pool;
|
|
|
|
name = message->get_str(message, NULL, "name");
|
|
if (!name)
|
|
{
|
|
return create_reply("missing pool name to unload");
|
|
}
|
|
|
|
this->lock->write_lock(this->lock);
|
|
|
|
pool = this->pools->remove(this->pools, name);
|
|
if (pool)
|
|
{
|
|
online = pool->vips->get_online(pool->vips);
|
|
if (online)
|
|
{
|
|
DBG1(DBG_CFG, "vici pool %s has %u online leases, unable to unload",
|
|
name, online);
|
|
reply = create_reply("%s has online leases, unable to unload", name);
|
|
this->pools->put(this->pools, pool->vips->get_name(pool->vips), pool);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_CFG, "unloaded vici pool %s", name);
|
|
reply = create_reply(NULL);
|
|
pool_destroy(pool);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
reply = create_reply("%s not found", name);
|
|
}
|
|
|
|
this->lock->unlock(this->lock);
|
|
|
|
return reply;
|
|
}
|
|
|
|
CALLBACK(get_pools, vici_message_t*,
|
|
private_vici_attribute_t *this, char *name, u_int id,
|
|
vici_message_t *message)
|
|
{
|
|
vici_builder_t *builder;
|
|
enumerator_t *enumerator, *leases;
|
|
mem_pool_t *vips;
|
|
pool_t *pool;
|
|
identification_t *uid;
|
|
host_t *lease;
|
|
bool list_leases, on;
|
|
char buf[32], *filter;
|
|
int i;
|
|
|
|
list_leases = message->get_bool(message, FALSE, "leases");
|
|
filter = message->get_str(message, NULL, "name");
|
|
|
|
builder = vici_builder_create();
|
|
|
|
this->lock->read_lock(this->lock);
|
|
enumerator = this->pools->create_enumerator(this->pools);
|
|
while (enumerator->enumerate(enumerator, &name, &pool))
|
|
{
|
|
if (filter && !streq(name, filter))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vips = pool->vips;
|
|
|
|
builder->begin_section(builder, name);
|
|
|
|
builder->add_kv(builder, "base", "%H", vips->get_base(vips));
|
|
builder->add_kv(builder, "size", "%u", vips->get_size(vips));
|
|
builder->add_kv(builder, "online", "%u", vips->get_online(vips));
|
|
builder->add_kv(builder, "offline", "%u", vips->get_offline(vips));
|
|
|
|
if (list_leases)
|
|
{
|
|
i = 0;
|
|
builder->begin_section(builder, "leases");
|
|
leases = vips->create_lease_enumerator(vips);
|
|
while (leases && leases->enumerate(leases, &uid, &lease, &on))
|
|
{
|
|
snprintf(buf, sizeof(buf), "%d", i++);
|
|
builder->begin_section(builder, buf);
|
|
builder->add_kv(builder, "address", "%H", lease);
|
|
builder->add_kv(builder, "identity", "%Y", uid);
|
|
builder->add_kv(builder, "status", on ? "online" : "offline");
|
|
builder->end_section(builder);
|
|
}
|
|
leases->destroy(leases);
|
|
builder->end_section(builder);
|
|
}
|
|
builder->end_section(builder);
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->lock->unlock(this->lock);
|
|
|
|
return builder->finalize(builder);
|
|
}
|
|
|
|
static void manage_command(private_vici_attribute_t *this,
|
|
char *name, vici_command_cb_t cb, bool reg)
|
|
{
|
|
this->dispatcher->manage_command(this->dispatcher, name,
|
|
reg ? cb : NULL, this);
|
|
}
|
|
|
|
/**
|
|
* (Un-)register dispatcher functions
|
|
*/
|
|
static void manage_commands(private_vici_attribute_t *this, bool reg)
|
|
{
|
|
manage_command(this, "load-pool", load_pool, reg);
|
|
manage_command(this, "unload-pool", unload_pool, reg);
|
|
manage_command(this, "get-pools", get_pools, reg);
|
|
}
|
|
|
|
METHOD(vici_attribute_t, destroy, void,
|
|
private_vici_attribute_t *this)
|
|
{
|
|
enumerator_t *enumerator;
|
|
pool_t *pool;
|
|
|
|
manage_commands(this, FALSE);
|
|
|
|
enumerator = this->pools->create_enumerator(this->pools);
|
|
while (enumerator->enumerate(enumerator, NULL, &pool))
|
|
{
|
|
pool_destroy(pool);
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->pools->destroy(this->pools);
|
|
this->lock->destroy(this->lock);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* see header file
|
|
*/
|
|
vici_attribute_t *vici_attribute_create(vici_dispatcher_t *dispatcher)
|
|
{
|
|
private_vici_attribute_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.provider = {
|
|
.acquire_address = _acquire_address,
|
|
.release_address = _release_address,
|
|
.create_attribute_enumerator = _create_attribute_enumerator,
|
|
},
|
|
.destroy = _destroy,
|
|
},
|
|
.dispatcher = dispatcher,
|
|
.pools = hashtable_create(hashtable_hash_str, hashtable_equals_str, 4),
|
|
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
);
|
|
|
|
manage_commands(this, TRUE);
|
|
|
|
return &this->public;
|
|
}
|