child-sa-manager: Add a global manager storing CHILD_SA relations
authorMartin Willi <martin@revosec.ch>
Thu, 23 Oct 2014 13:42:21 +0000 (15:42 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 20 Feb 2015 12:34:49 +0000 (13:34 +0100)
To quickly check out IKE_SAs and find associated CHILD_SAs, the
child_sa_manager stores relations between CHILD_SAs and IKE_SAs. It provides
CHILD_SA specific IKE_SA checkout functions wrapping the ike_sa_manager.

src/libcharon/Android.mk
src/libcharon/Makefile.am
src/libcharon/daemon.c
src/libcharon/daemon.h
src/libcharon/sa/child_sa_manager.c [new file with mode: 0644]
src/libcharon/sa/child_sa_manager.h [new file with mode: 0644]

index 4212ee8..ab71d40 100644 (file)
@@ -72,6 +72,7 @@ sa/ike_sa.c sa/ike_sa.h \
 sa/ike_sa_id.c sa/ike_sa_id.h \
 sa/keymat.h sa/keymat.c \
 sa/ike_sa_manager.c sa/ike_sa_manager.h \
 sa/ike_sa_id.c sa/ike_sa_id.h \
 sa/keymat.h sa/keymat.c \
 sa/ike_sa_manager.c sa/ike_sa_manager.h \
+sa/child_sa_manager.c sa/child_sa_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
@@ -238,4 +239,3 @@ LOCAL_PRELINK_MODULE := false
 LOCAL_SHARED_LIBRARIES += libstrongswan libhydra
 
 include $(BUILD_SHARED_LIBRARY)
 LOCAL_SHARED_LIBRARIES += libstrongswan libhydra
 
 include $(BUILD_SHARED_LIBRARY)
-
index e98f5e1..e666950 100644 (file)
@@ -70,6 +70,7 @@ sa/ike_sa.c sa/ike_sa.h \
 sa/ike_sa_id.c sa/ike_sa_id.h \
 sa/keymat.h sa/keymat.c \
 sa/ike_sa_manager.c sa/ike_sa_manager.h \
 sa/ike_sa_id.c sa/ike_sa_id.h \
 sa/keymat.h sa/keymat.c \
 sa/ike_sa_manager.c sa/ike_sa_manager.h \
+sa/child_sa_manager.c sa/child_sa_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
index 3ae7c4e..f3859f9 100644 (file)
@@ -480,6 +480,7 @@ static void destroy(private_daemon_t *this)
        DESTROY_IF(this->kernel_handler);
        DESTROY_IF(this->public.traps);
        DESTROY_IF(this->public.shunts);
        DESTROY_IF(this->kernel_handler);
        DESTROY_IF(this->public.traps);
        DESTROY_IF(this->public.shunts);
+       DESTROY_IF(this->public.child_sa_manager);
        DESTROY_IF(this->public.ike_sa_manager);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
        DESTROY_IF(this->public.ike_sa_manager);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
@@ -606,6 +607,7 @@ METHOD(daemon_t, initialize, bool,
        {
                return FALSE;
        }
        {
                return FALSE;
        }
+       this->public.child_sa_manager = child_sa_manager_create();
 
        /* Queue start_action job */
        lib->processor->queue_job(lib->processor, (job_t*)start_action_job_create());
 
        /* Queue start_action job */
        lib->processor->queue_job(lib->processor, (job_t*)start_action_job_create());
index 36242bb..8ec1ec2 100644 (file)
@@ -158,6 +158,7 @@ typedef struct daemon_t daemon_t;
 #include <control/controller.h>
 #include <bus/bus.h>
 #include <sa/ike_sa_manager.h>
 #include <control/controller.h>
 #include <bus/bus.h>
 #include <sa/ike_sa_manager.h>
+#include <sa/child_sa_manager.h>
 #include <sa/trap_manager.h>
 #include <sa/shunt_manager.h>
 #include <config/backend_manager.h>
 #include <sa/trap_manager.h>
 #include <sa/shunt_manager.h>
 #include <config/backend_manager.h>
@@ -215,6 +216,11 @@ struct daemon_t {
        ike_sa_manager_t *ike_sa_manager;
 
        /**
        ike_sa_manager_t *ike_sa_manager;
 
        /**
+        * A child_sa_manager_t instance.
+        */
+       child_sa_manager_t *child_sa_manager;
+
+       /**
         * Manager for triggering policies, called traps
         */
        trap_manager_t *traps;
         * Manager for triggering policies, called traps
         */
        trap_manager_t *traps;
diff --git a/src/libcharon/sa/child_sa_manager.c b/src/libcharon/sa/child_sa_manager.c
new file mode 100644 (file)
index 0000000..071a119
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * 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 "child_sa_manager.h"
+
+#include <daemon.h>
+#include <threading/mutex.h>
+#include <collections/hashtable.h>
+
+typedef struct private_child_sa_manager_t private_child_sa_manager_t;
+
+/**
+ * Private data of an child_sa_manager_t object.
+ */
+struct private_child_sa_manager_t {
+
+       /**
+        * Public child_sa_manager_t interface.
+        */
+       child_sa_manager_t public;
+
+       /**
+        * CHILD_SAs by inbound SPI/dst, child_entry_t => child_entry_t
+        */
+       hashtable_t *in;
+
+       /**
+        * CHILD_SAs by outbound SPI/dst, child_entry_t => child_entry_t
+        */
+       hashtable_t *out;
+
+       /**
+        * CHILD_SAs by unique ID, child_entry_t => child_entry_t
+        */
+       hashtable_t *ids;
+
+       /**
+        * Mutex to access any hashtable
+        */
+       mutex_t *mutex;
+};
+
+/**
+ * Hashtable entry for a known CHILD_SA
+ */
+typedef struct {
+       /** the associated IKE_SA */
+       ike_sa_id_t *ike_id;
+       /** unique CHILD_SA identifier */
+       u_int32_t unique_id;
+       /** inbound SPI */
+       u_int32_t spi_in;
+       /** outbound SPI */
+       u_int32_t spi_out;
+       /** inbound host address */
+       host_t *host_in;
+       /** outbound host address and port */
+       host_t *host_out;
+       /** IPsec protocol, AH|ESP */
+       protocol_id_t proto;
+} child_entry_t;
+
+/**
+ * Destroy a CHILD_SA entry
+ */
+static void child_entry_destroy(child_entry_t *entry)
+{
+       entry->ike_id->destroy(entry->ike_id);
+       entry->host_in->destroy(entry->host_in);
+       entry->host_out->destroy(entry->host_out);
+       free(entry);
+}
+
+/**
+ * Hashtable hash function for inbound SAs
+ */
+static u_int hash_in(child_entry_t *entry)
+{
+       return chunk_hash_inc(chunk_from_thing(entry->spi_in),
+                       chunk_hash_inc(entry->host_in->get_address(entry->host_in),
+                        chunk_hash(chunk_from_thing(entry->proto))));
+}
+
+/**
+ * Hashtable equals function for inbound SAs
+ */
+static bool equals_in(child_entry_t *a, child_entry_t *b)
+{
+       return a->spi_in == b->spi_in &&
+                  a->proto == b->proto &&
+                  a->host_in->ip_equals(a->host_in, b->host_in);
+}
+
+/**
+ * Hashtable hash function for outbound SAs
+ */
+static u_int hash_out(child_entry_t *entry)
+{
+       return chunk_hash_inc(chunk_from_thing(entry->spi_out),
+                       chunk_hash_inc(entry->host_out->get_address(entry->host_out),
+                        chunk_hash(chunk_from_thing(entry->proto))));
+}
+
+/**
+ * Hashtable equals function for outbound SAs
+ */
+static bool equals_out(child_entry_t *a, child_entry_t *b)
+{
+       return a->spi_out == b->spi_out &&
+                  a->proto == b->proto &&
+                  a->host_out->ip_equals(a->host_out, b->host_out);
+}
+
+/**
+ * Hashtable hash function for SAs by unique ID
+ */
+static u_int hash_id(child_entry_t *entry)
+{
+       return chunk_hash(chunk_from_thing(entry->unique_id));
+}
+
+/**
+ * Hashtable equals function for SAs by unique ID
+ */
+static bool equals_id(child_entry_t *a, child_entry_t *b)
+{
+       return a->unique_id == b->unique_id;
+}
+
+METHOD(child_sa_manager_t, add, void,
+       private_child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa)
+{
+       child_entry_t *entry;
+       host_t *in, *out;
+       ike_sa_id_t *id;
+
+       id = ike_sa->get_id(ike_sa);
+       in = ike_sa->get_my_host(ike_sa);
+       out = ike_sa->get_other_host(ike_sa);
+
+       INIT(entry,
+               .ike_id = id->clone(id),
+               .unique_id = child_sa->get_unique_id(child_sa),
+               .proto = child_sa->get_protocol(child_sa),
+               .spi_in = child_sa->get_spi(child_sa, TRUE),
+               .spi_out = child_sa->get_spi(child_sa, FALSE),
+               .host_in = in->clone(in),
+               .host_out = out->clone(out),
+       );
+
+       this->mutex->lock(this->mutex);
+       if (!this->in->get(this->in, entry) &&
+               !this->out->get(this->out, entry))
+       {
+               this->in->put(this->in, entry, entry);
+               this->out->put(this->out, entry, entry);
+               entry = this->ids->put(this->ids, entry, entry);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (entry)
+       {
+               child_entry_destroy(entry);
+       }
+}
+
+METHOD(child_sa_manager_t, remove_, void,
+       private_child_sa_manager_t *this, child_sa_t *child_sa)
+{
+       child_entry_t *entry, key = {
+               .unique_id = child_sa->get_unique_id(child_sa),
+       };
+
+       this->mutex->lock(this->mutex);
+       entry = this->ids->remove(this->ids, &key);
+       if (entry)
+       {
+               this->in->remove(this->in, entry);
+               this->out->remove(this->out, entry);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (entry)
+       {
+               child_entry_destroy(entry);
+       }
+}
+
+/**
+ * Check out an IKE_SA for a given CHILD_SA
+ */
+static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this,
+                                       ike_sa_id_t *id, u_int32_t unique_id, child_sa_t **child_sa)
+{
+       enumerator_t *enumerator;
+       child_sa_t *current;
+       ike_sa_t *ike_sa;
+       bool found = FALSE;
+
+       ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+       id->destroy(id);
+       if (ike_sa)
+       {
+               enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
+               while (enumerator->enumerate(enumerator, &current))
+               {
+                       found = current->get_unique_id(current) == unique_id;
+                       if (found)
+                       {
+                               if (child_sa)
+                               {
+                                       *child_sa = current;
+                               }
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+
+               if (found)
+               {
+                       return ike_sa;
+               }
+               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+       }
+       return NULL;
+}
+
+METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*,
+       private_child_sa_manager_t *this, u_int32_t unique_id,
+       child_sa_t **child_sa)
+{
+       ike_sa_id_t *id;
+       child_entry_t *entry, key = {
+               .unique_id = unique_id,
+       };
+
+       this->mutex->lock(this->mutex);
+       entry = this->ids->get(this->ids, &key);
+       if (entry)
+       {
+               id = entry->ike_id->clone(entry->ike_id);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (entry)
+       {
+               return checkout_ikesa(this, id, unique_id, child_sa);
+       }
+       return NULL;
+}
+
+METHOD(child_sa_manager_t, checkout, ike_sa_t*,
+       private_child_sa_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+       host_t *dst, child_sa_t **child_sa)
+{
+       ike_sa_id_t *id;
+       u_int32_t unique_id;
+       child_entry_t *entry, key = {
+               .spi_in = spi,
+               .spi_out = spi,
+               .host_in = dst,
+               .host_out = dst,
+               .proto = protocol,
+       };
+
+       this->mutex->lock(this->mutex);
+       entry = this->in->get(this->in, &key);
+       if (!entry)
+       {
+               entry = this->out->get(this->out, &key);
+       }
+       if (entry)
+       {
+               unique_id = entry->unique_id;
+               id = entry->ike_id->clone(entry->ike_id);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (entry)
+       {
+               return checkout_ikesa(this, id, unique_id, child_sa);
+       }
+       return NULL;
+}
+
+METHOD(child_sa_manager_t, destroy, void,
+       private_child_sa_manager_t *this)
+{
+       this->in->destroy(this->in);
+       this->out->destroy(this->out);
+       this->ids->destroy(this->ids);
+       this->mutex->destroy(this->mutex);
+       free(this);
+}
+
+/**
+ * See header
+ */
+child_sa_manager_t *child_sa_manager_create()
+{
+       private_child_sa_manager_t *this;
+
+       INIT(this,
+               .public = {
+                       .add = _add,
+                       .remove = _remove_,
+                       .checkout = _checkout,
+                       .checkout_by_id = _checkout_by_id,
+                       .destroy = _destroy,
+               },
+               .in = hashtable_create((hashtable_hash_t)hash_in,
+                                                          (hashtable_equals_t)equals_in, 8),
+               .out = hashtable_create((hashtable_hash_t)hash_out,
+                                                          (hashtable_equals_t)equals_out, 8),
+               .ids = hashtable_create((hashtable_hash_t)hash_id,
+                                                          (hashtable_equals_t)equals_id, 8),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+       );
+
+       return &this->public;
+}
diff --git a/src/libcharon/sa/child_sa_manager.h b/src/libcharon/sa/child_sa_manager.h
new file mode 100644 (file)
index 0000000..4d57528
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 child_sa_manager child_sa_manager
+ * @{ @ingroup sa
+ */
+
+#ifndef CHILD_SA_MANAGER_H_
+#define CHILD_SA_MANAGER_H_
+
+#include <sa/ike_sa.h>
+#include <sa/child_sa.h>
+
+typedef struct child_sa_manager_t child_sa_manager_t;
+
+/**
+ * Handle CHILD_SA to IKE_SA relations
+ */
+struct child_sa_manager_t {
+
+       /**
+        * Register a CHILD_SA/IKE_SA relation.
+        *
+        * @param child_sa              CHILD_SA to register
+        * @param ike_sa                IKE_SA owning the CHILD_SA
+        */
+       void (*add)(child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa);
+
+       /**
+        * Unregister a CHILD_SA/IKE_SA relation.
+        *
+        * @param child_sa              CHILD_SA to unregister
+        */
+       void (*remove)(child_sa_manager_t *this, child_sa_t *child_sa);
+
+       /**
+        * Find a CHILD_SA and check out the associated IKE_SA by SPI.
+        *
+        * On success, the returned IKE_SA must be checked in after use to
+        * the IKE_SA manager.
+        *
+        * @param protocol              IPsec protocol, AH|ESP
+        * @param spi                   SPI of CHILD_SA to check out
+        * @param dst                   SA destination host related to SPI
+        * @param child_sa              returns CHILD_SA managed by IKE_SA
+        * @return                              IKE_SA, NULL if not found
+        */
+       ike_sa_t *(*checkout)(child_sa_manager_t *this,
+                                                 protocol_id_t protocol, u_int32_t spi, host_t *dst,
+                                                 child_sa_t **child_sa);
+
+       /**
+        * Find a CHILD_SA and check out the associated IKE_SA by unique_id.
+        *
+        * On success, the returned IKE_SA must be checked in after use to
+        * the IKE_SA manager.
+        *
+        * @param unique_id             unique ID of CHILD_SA to check out
+        * @param child_sa              returns CHILD_SA managed by IKE_SA
+        * @return                              IKE_SA, NULL if not found
+        */
+       ike_sa_t *(*checkout_by_id)(child_sa_manager_t *this, u_int32_t unique_id,
+                                                               child_sa_t **child_sa);
+
+       /**
+        * Destroy a child_sa_manager_t.
+        */
+       void (*destroy)(child_sa_manager_t *this);
+};
+
+/**
+ * Create a child_sa_manager instance.
+ */
+child_sa_manager_t *child_sa_manager_create();
+
+#endif /** CHILD_SA_MANAGER_H_ @}*/