vici: Add an IKE virtual IP and attribute backend
authorMartin Willi <martin@revosec.ch>
Wed, 16 Apr 2014 08:55:40 +0000 (10:55 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 7 May 2014 12:13:38 +0000 (14:13 +0200)
src/libcharon/plugins/vici/Makefile.am
src/libcharon/plugins/vici/vici_attribute.c [new file with mode: 0644]
src/libcharon/plugins/vici/vici_attribute.h [new file with mode: 0644]
src/libcharon/plugins/vici/vici_plugin.c

index df241dc..2a99b13 100644 (file)
@@ -22,6 +22,7 @@ libstrongswan_vici_la_SOURCES = \
        vici_control.h vici_control.c \
        vici_config.h vici_config.c \
        vici_cred.h vici_cred.c \
+       vici_attribute.h vici_attribute.c \
        vici_logger.h vici_logger.c \
        vici_plugin.h vici_plugin.c
 
diff --git a/src/libcharon/plugins/vici/vici_attribute.c b/src/libcharon/plugins/vici/vici_attribute.c
new file mode 100644 (file)
index 0000000..2178116
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * 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)
+{
+       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);
+                       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, identification_t *id,
+       host_t *requested)
+{
+       host_t *addr;
+
+       this->lock->read_lock(this->lock);
+
+       addr = find_addr(this, pools, id, requested, MEM_POOL_EXISTING);
+       if (!addr)
+       {
+               addr = find_addr(this, pools, id, requested, MEM_POOL_NEW);
+               if (!addr)
+               {
+                       addr = find_addr(this, pools, id, requested, MEM_POOL_REASSIGN);
+               }
+       }
+
+       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,
+       identification_t *id)
+{
+       enumerator_t *enumerator;
+       bool found = FALSE;
+       pool_t *pool;
+       char *name;
+
+       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;
+}
+
+/**
+ * Filter mapping attribute_t to enumerated type/value arguments
+ */
+static bool attr_filter(void *data, attribute_t **attr,
+                                               configuration_attribute_type_t *type,
+                                               void *in, chunk_t *value)
+{
+       *type = (*attr)->type;
+       *value = (*attr)->value;
+       return TRUE;
+}
+
+/**
+ * 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),
+                                                                       (void*)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;
+       u_int32_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 currenty 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,
+       identification_t *id, 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 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 */
+                               u_int32_t netmask = 0;
+
+                               if (mask)
+                               {       /* shifting u_int32_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];
+               host_t *base;
+               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;
+               }
+               base = host_create_from_subnet(buf, &bits);
+               if (!base)
+               {
+                       data->request->reply = create_reply("invalid addrs value: %s", buf);
+                       return FALSE;
+               }
+               data->pool->vips = mem_pool_create(data->name, base, bits);
+               base->destroy(base);
+               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;
+       mem_pool_t *vips;
+       pool_t *pool;
+
+       builder = vici_builder_create();
+
+       this->lock->read_lock(this->lock);
+       enumerator = this->pools->create_enumerator(this->pools);
+       while (enumerator->enumerate(enumerator, &name, &pool))
+       {
+               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));
+
+               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;
+}
diff --git a/src/libcharon/plugins/vici/vici_attribute.h b/src/libcharon/plugins/vici/vici_attribute.h
new file mode 100644 (file)
index 0000000..652a96d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup vici_attribute vici_attribute
+ * @{ @ingroup vici
+ */
+
+#ifndef VICI_ATTRIBUTE_H_
+#define VICI_ATTRIBUTE_H_
+
+#include "vici_dispatcher.h"
+
+#include <attributes/attribute_provider.h>
+
+typedef struct vici_attribute_t vici_attribute_t;
+
+/**
+ * IKE configuration attribute backend for vici.
+ */
+struct vici_attribute_t {
+
+       /**
+        * Implements attribute provider interface
+        */
+       attribute_provider_t provider;
+
+       /**
+        * Destroy a vici_attribute_t.
+        */
+       void (*destroy)(vici_attribute_t *this);
+};
+
+/**
+ * Create a vici_attribute instance.
+ *
+ * @param dispatcher           vici dispatcher context
+ * @return                                     vici attribute handler
+ */
+vici_attribute_t *vici_attribute_create(vici_dispatcher_t *dispatcher);
+
+#endif /** VICI_ATTRIBUTE_H_ @}*/
index fcd95ed..8881fec 100644 (file)
 #include "vici_control.h"
 #include "vici_cred.h"
 #include "vici_config.h"
+#include "vici_attribute.h"
 #include "vici_logger.h"
 
 #include <library.h>
+#include <hydra.h>
 #include <daemon.h>
 
 typedef struct private_vici_plugin_t private_vici_plugin_t;
@@ -62,6 +64,11 @@ struct private_vici_plugin_t {
        vici_config_t *config;
 
        /**
+        * IKE attribute backend
+        */
+       vici_attribute_t *attrs;
+
+       /**
         * Generic debug logger
         */
        vici_logger_t *logger;
@@ -92,10 +99,13 @@ static bool register_vici(private_vici_plugin_t *this,
                        this->control = vici_control_create(this->dispatcher);
                        this->cred = vici_cred_create(this->dispatcher);
                        this->config = vici_config_create(this->dispatcher);
+                       this->attrs = vici_attribute_create(this->dispatcher);
                        this->logger = vici_logger_create(this->dispatcher);
 
                        charon->backends->add_backend(charon->backends,
                                                                                  &this->config->backend);
+                       hydra->attributes->add_provider(hydra->attributes,
+                                                                                       &this->attrs->provider);
                        charon->bus->add_logger(charon->bus, &this->logger->logger);
                        return TRUE;
                }
@@ -104,10 +114,13 @@ static bool register_vici(private_vici_plugin_t *this,
        else
        {
                charon->bus->remove_logger(charon->bus, &this->logger->logger);
+               hydra->attributes->remove_provider(hydra->attributes,
+                                                                                  &this->attrs->provider);
                charon->backends->remove_backend(charon->backends,
                                                                                 &this->config->backend);
 
                this->logger->destroy(this->logger);
+               this->attrs->destroy(this->attrs);
                this->config->destroy(this->config);
                this->cred->destroy(this->cred);
                this->control->destroy(this->control);