Added IPsec SA manager
authorTobias Brunner <tobias@strongswan.org>
Fri, 13 Jul 2012 11:21:45 +0000 (13:21 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 8 Aug 2012 13:41:03 +0000 (15:41 +0200)
src/libipsec/Android.mk
src/libipsec/Makefile.am
src/libipsec/ipsec.c
src/libipsec/ipsec.h
src/libipsec/ipsec_sa_mgr.c [new file with mode: 0644]
src/libipsec/ipsec_sa_mgr.h [new file with mode: 0644]

index 2c1c1cc..a5d93df 100644 (file)
@@ -6,7 +6,8 @@ LOCAL_SRC_FILES := \
 ipsec.c ipsec.h \
 esp_context.c esp_context.h \
 esp_packet.c esp_packet.h \
-ipsec_sa.c ipsec_sa.h
+ipsec_sa.c ipsec_sa.h \
+ipsec_sa_mgr.c ipsec_sa_mgr.h
 
 # build libipsec ---------------------------------------------------------------
 
index 6558496..3de8f82 100644 (file)
@@ -4,7 +4,8 @@ libipsec_la_SOURCES = \
 ipsec.c ipsec.h \
 esp_context.c esp_context.h \
 esp_packet.c esp_packet.h \
-ipsec_sa.c ipsec_sa.h
+ipsec_sa.c ipsec_sa.h \
+ipsec_sa_mgr.c ipsec_sa_mgr.h
 
 libipsec_la_LIBADD =
 
index add3b46..5ae6c74 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
  * Copyright (C) 2012 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
@@ -41,6 +43,7 @@ ipsec_t *ipsec;
 void libipsec_deinit()
 {
        private_ipsec_t *this = (private_ipsec_t*)ipsec;
+       DESTROY_IF(this->public.sas);
        free(this);
        ipsec = NULL;
 }
@@ -52,10 +55,7 @@ bool libipsec_init()
 {
        private_ipsec_t *this;
 
-       INIT(this,
-               .public = {
-               },
-       );
+       INIT(this);
        ipsec = &this->public;
 
        if (lib->integrity &&
@@ -64,6 +64,8 @@ bool libipsec_init()
                DBG1(DBG_LIB, "integrity check of libipsec failed");
                return FALSE;
        }
+
+       this->public.sas = ipsec_sa_mgr_create();
        return TRUE;
 }
 
index 80bef54..e4055a8 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
  * Copyright (C) 2012 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
 #ifndef IPSEC_H_
 #define IPSEC_H_
 
-typedef struct ipsec_t ipsec_t;
+#include "ipsec_sa_mgr.h"
 
 #include <library.h>
 
+typedef struct ipsec_t ipsec_t;
+
 /**
  * User space IPsec implementation.
  */
 struct ipsec_t {
 
+       /**
+        * IPsec SA manager instance
+        */
+       ipsec_sa_mgr_t *sas;
+
 };
 
 /**
diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c
new file mode 100644 (file)
index 0000000..74fb2ac
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "ipsec_sa_mgr.h"
+
+#include <debug.h>
+#include <library.h>
+#include <threading/mutex.h>
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+
+typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
+
+/**
+ * Private additions to ipsec_sa_mgr_t.
+ */
+struct private_ipsec_sa_mgr_t {
+
+       /**
+        * Public members of ipsec_sa_mgr_t.
+        */
+       ipsec_sa_mgr_t public;
+
+       /**
+        * Installed SAs
+        */
+       linked_list_t *sas;
+
+       /**
+        * SPIs allocated using get_spi()
+        */
+       hashtable_t *allocated_spis;
+
+       /**
+        * Mutex used to synchronize access to the SA manager
+        */
+       mutex_t *mutex;
+
+       /**
+        * RNG used to generate SPIs
+        */
+       rng_t *rng;
+};
+
+/*
+ * Used for the hash table of allocated SPIs
+ */
+static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi)
+{
+       return *spi == *other_spi;
+}
+
+static u_int spi_hash(u_int32_t *spi)
+{
+       return chunk_hash(chunk_from_thing(*spi));
+}
+
+/**
+ * Flushes all entries
+ * Must be called with this->mutex held.
+ */
+static void flush_entries(private_ipsec_sa_mgr_t *this)
+{
+       enumerator_t *enumerator;
+       ipsec_sa_t *current;
+
+       DBG2(DBG_ESP, "flushing SAD");
+
+       enumerator = this->sas->create_enumerator(this->sas);
+       while (enumerator->enumerate(enumerator, (void**)&current))
+       {
+               this->sas->remove_at(this->sas, enumerator);
+               current->destroy(current);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/*
+ * Different match functions to find SAs in the linked list
+ */
+static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi,
+                                                                          bool inbound)
+{
+       return sa->get_spi(sa) == spi && sa->is_inbound(sa) == inbound;
+}
+
+static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi,
+                                                                          host_t *src, host_t *dst)
+{
+       return sa->match_by_spi_src_dst(sa, spi, src, dst);
+}
+
+/**
+ * Remove all allocated SPIs
+ */
+static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
+{
+       enumerator_t *enumerator;
+       u_int32_t *current;
+
+       DBG2(DBG_ESP, "flushing allocated SPIs");
+       enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
+       while (enumerator->enumerate(enumerator, NULL, (void**)&current))
+       {
+               this->allocated_spis->remove_at(this->allocated_spis, enumerator);
+               DBG2(DBG_ESP, "  removed allocated SPI %.8x", ntohl(*current));
+               free(current);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * Pre-allocate an SPI for an inbound SA
+ */
+static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
+{
+       u_int32_t *spi_alloc;
+
+       if (this->allocated_spis->get(this->allocated_spis, &spi) ||
+               this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
+                                                         NULL, spi, TRUE) == SUCCESS)
+       {
+               return FALSE;
+       }
+       spi_alloc = malloc_thing(u_int32_t);
+       *spi_alloc = spi;
+       this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
+       return TRUE;
+}
+
+METHOD(ipsec_sa_mgr_t, get_spi, status_t,
+       private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol,
+       u_int32_t reqid, u_int32_t *spi)
+{
+       u_int32_t spi_new;
+
+       DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid);
+
+       this->mutex->lock(this->mutex);
+       if (!this->rng)
+       {
+               this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+               if (!this->rng)
+               {
+                       this->mutex->unlock(this->mutex);
+                       DBG1(DBG_ESP, "failed to create RNG for SPI generation");
+                       return FAILED;
+               }
+       }
+
+       do
+       {
+               if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
+                                                                (u_int8_t*)&spi_new))
+               {
+                       this->mutex->unlock(this->mutex);
+                       DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid);
+                       return FAILED;
+               }
+               /* make sure the SPI is valid (not in range 0-255) */
+               spi_new |= 0x00000100;
+               spi_new = htonl(spi_new);
+       }
+       while (!allocate_spi(this, spi_new));
+       this->mutex->unlock(this->mutex);
+
+       *spi = spi_new;
+
+       DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
+       return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, add_sa, status_t,
+       private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
+       u_int8_t protocol, u_int32_t reqid,     mark_t mark, u_int32_t tfc,
+       lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
+       u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
+       u_int16_t cpi, bool encap, bool esn, bool inbound,
+       traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
+{
+       ipsec_sa_t *sa_new;
+
+       DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
+                ntohl(spi), reqid);
+       DBG2(DBG_ESP, "  using encryption algorithm %N with key size %d",
+                encryption_algorithm_names, enc_alg, enc_key.len * 8);
+       DBG2(DBG_ESP, "  using integrity algorithm %N with key size %d",
+                integrity_algorithm_names, int_alg, int_key.len * 8);
+
+       sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
+                                                        lifetime, enc_alg, enc_key, int_alg, int_key, mode,
+                                                        ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts);
+       if (!sa_new)
+       {
+               DBG1(DBG_ESP, "failed to create SAD entry");
+               return FAILED;
+       }
+
+       this->mutex->lock(this->mutex);
+
+       if (inbound)
+       {       /* remove any pre-allocated SPIs */
+               u_int32_t *spi_alloc;
+
+               spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
+               free(spi_alloc);
+       }
+
+       if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
+                                                         NULL, spi, src, dst) == SUCCESS)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG1(DBG_ESP, "failed to install SAD entry: already installed");
+               sa_new->destroy(sa_new);
+               return FAILED;
+       }
+       this->sas->insert_last(this->sas, sa_new);
+       this->mutex->unlock(this->mutex);
+       return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, del_sa, status_t,
+       private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
+       u_int8_t protocol, u_int16_t cpi, mark_t mark)
+{
+       ipsec_sa_t *current, *found = NULL;
+       enumerator_t *enumerator;
+
+       this->mutex->lock(this->mutex);
+       enumerator = this->sas->create_enumerator(this->sas);
+       while (enumerator->enumerate(enumerator, (void**)&current))
+       {
+               if (match_entry_by_spi_src_dst(current, spi, src, dst))
+               {
+                       this->sas->remove_at(this->sas, enumerator);
+                       found = current;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->mutex->unlock(this->mutex);
+
+       if (found)
+       {
+               DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
+                        found->is_inbound(found) ? "in" : "out", ntohl(spi));
+               found->destroy(found);
+               return SUCCESS;
+       }
+       return FAILED;
+}
+
+METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
+       private_ipsec_sa_mgr_t *this)
+{
+       this->mutex->lock(this->mutex);
+       flush_entries(this);
+       this->mutex->unlock(this->mutex);
+       return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, destroy, void,
+       private_ipsec_sa_mgr_t *this)
+{
+       this->mutex->lock(this->mutex);
+       flush_entries(this);
+       flush_allocated_spis(this);
+       this->mutex->unlock(this->mutex);
+
+       this->allocated_spis->destroy(this->allocated_spis);
+       this->sas->destroy(this->sas);
+
+       this->mutex->destroy(this->mutex);
+       DESTROY_IF(this->rng);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_sa_mgr_t *ipsec_sa_mgr_create()
+{
+       private_ipsec_sa_mgr_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_spi = _get_spi,
+                       .add_sa = _add_sa,
+                       .del_sa = _del_sa,
+                       .flush_sas = _flush_sas,
+                       .destroy = _destroy,
+               },
+               .sas = linked_list_create(),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
+                                                                                  (hashtable_equals_t)spi_equals, 16),
+       );
+
+       return &this->public;
+}
diff --git a/src/libipsec/ipsec_sa_mgr.h b/src/libipsec/ipsec_sa_mgr.h
new file mode 100644 (file)
index 0000000..0acb0c1
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 ipsec_sa_mgr ipsec_sa_mgr
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_SA_MGR_H_
+#define IPSEC_SA_MGR_H_
+
+#include "ipsec_sa.h"
+
+#include <library.h>
+#include <ipsec/ipsec_types.h>
+#include <selectors/traffic_selector.h>
+#include <utils/host.h>
+
+typedef struct ipsec_sa_mgr_t ipsec_sa_mgr_t;
+
+/**
+ * IPsec SA manager
+ *
+ * The first methods are modeled after those in kernel_ipsec_t.
+ */
+struct ipsec_sa_mgr_t {
+
+       /**
+        * Allocate an SPI for an inbound IPsec SA
+        *
+        * @param src                   source address of the SA
+        * @param dst                   destination address of the SA
+        * @param protocol              protocol of the SA (only ESP supported)
+        * @param reqid                 reqid for the SA
+        * @param spi                   the allocated SPI
+        * @return                              SUCCESS of operation successful
+        */
+       status_t (*get_spi)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+                                               u_int8_t protocol, u_int32_t reqid, u_int32_t *spi);
+
+       /**
+        * Add a new SA
+        *
+        * @param src                   source address for this SA (gets cloned)
+        * @param dst                   destination address for this SA (gets cloned)
+        * @param spi                   SPI for this SA
+        * @param protocol              protocol for this SA (only ESP is supported)
+        * @param reqid                 reqid for this SA
+        * @param mark                  mark for this SA (ignored)
+        * @param tfc                   Traffic Flow Confidentiality (not yet supported)
+        * @param lifetime              lifetime for this SA
+        * @param enc_alg               encryption algorithm for this SA
+        * @param enc_key               encryption key for this SA
+        * @param int_alg               integrity protection algorithm
+        * @param int_key               integrity protection key
+        * @param mode                  mode for this SA (only tunnel mode is supported)
+        * @param ipcomp                IPcomp transform (not supported, use IPCOMP_NONE)
+        * @param cpi                   CPI for IPcomp (ignored)
+        * @param encap                 enable UDP encapsulation (must be TRUE)
+        * @param esn                   Extended Sequence Numbers (currently not supported)
+        * @param inbound               TRUE if this is an inbound SA, FALSE otherwise
+        * @param src_ts                source traffic selector
+        * @param dst_ts                destination traffic selector
+        * @return                              SUCCESS if operation completed
+        */
+       status_t (*add_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+                                          u_int32_t spi, u_int8_t protocol, u_int32_t reqid,
+                                          mark_t mark, u_int32_t tfc,  lifetime_cfg_t *lifetime,
+                                          u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg,
+                                          chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
+                                          u_int16_t cpi, bool encap, bool esn, bool inbound,
+                                          traffic_selector_t *src_ts, traffic_selector_t *dst_ts);
+
+       /**
+        * Delete a previously added SA
+        *
+        * @param spi                   SPI of the SA
+        * @param src                   source address of the SA
+        * @param dst                   destination address of the SA
+        * @param protocol              protocol of the SA
+        * @param cpi                   CPI for IPcomp
+        * @param mark                  optional mark
+        * @return                              SUCCESS if operation completed
+        */
+       status_t (*del_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+                                          u_int32_t spi, u_int8_t protocol, u_int16_t cpi,
+                                          mark_t mark);
+
+       /**
+        * Flush all SAs
+        *
+        * @return                              SUCCESS if operation completed
+        */
+       status_t (*flush_sas)(ipsec_sa_mgr_t *this);
+
+       /**
+        * Destroy an ipsec_sa_mgr_t
+        */
+       void (*destroy)(ipsec_sa_mgr_t *this);
+
+};
+
+/**
+ * Create an ipsec_sa_mgr instance
+ *
+ * @return                                     IPsec SA manager instance
+ */
+ipsec_sa_mgr_t *ipsec_sa_mgr_create();
+
+#endif /** IPSEC_SA_MGR_H_ @}*/