]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-utun: allocate SPI from kernel, create utun before doing so
authorMartin Willi <martin@revosec.ch>
Fri, 12 Apr 2013 13:48:53 +0000 (15:48 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 18 Apr 2013 12:43:56 +0000 (14:43 +0200)
src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.c
src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.h
src/libhydra/plugins/kernel_utun/kernel_utun_net.c
src/libhydra/plugins/kernel_utun/kernel_utun_net.h

index a0ccb5b90da3ef468d9989943bfdac5277a41b6c..644c686625037cc79ff40087ce9df53e95818f22 100644 (file)
@@ -14,7 +14,6 @@
  */
 
 #include "kernel_utun_ipsec.h"
-#include "kernel_utun_net.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -55,9 +54,9 @@ struct private_kernel_utun_ipsec_t {
        mutex_t *mutex;
 
        /**
-        * Next SPI to allocate
+        * List of tun devices, as tun_device_t
         */
-       u_int32_t spi;
+       linked_list_t *tuns;
 };
 
 METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
@@ -66,12 +65,90 @@ METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
        return 0;
 }
 
+/**
+ * Enable IPsec crypt extension on utun device
+ */
+static bool enable_crypto(tun_device_t *tun)
+{
+       utun_crypto_args_t args = {
+               .ver = UTUN_CRYPTO_VER_1,
+               .type = UTUN_CRYPTO_TYPE_IPSEC,
+               .args_ulen = sizeof(utun_crypto_ipsec_args_v1_t),
+               .u = {
+                       .ipsec_v1 = {
+                               /* nothing to set */
+                       },
+               },
+       };
+       if (setsockopt(tun->get_fd(tun), SYSPROTO_CONTROL, UTUN_OPT_ENABLE_CRYPTO,
+                                  &args, sizeof(args)) < 0)
+       {
+               DBG1(DBG_KNL, "enabling crypto on %s failed: %s",
+                        tun->get_name(tun), strerror(errno));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Allocate an SPI on the given tun device
+ */
+static bool alloc_spi(tun_device_t *tun, host_t *src, host_t *dst,
+                                         u_int32_t reqid, u_int32_t *spi)
+{
+       utun_crypto_keys_idx_args_t args = {
+               .ver = UTUN_CRYPTO_VER_1,
+               .type = UTUN_CRYPTO_TYPE_IPSEC,
+               .dir = UTUN_CRYPTO_DIR_IN,
+               .args_ulen = sizeof(utun_crypto_keys_idx_ipsec_args_v1_t),
+               .u = {
+                       .ipsec_v1 = {
+                               .proto = IF_UTUN_CRYPTO_IPSEC_PROTO_ESP,
+                               .mode = IF_UTUN_CRYPTO_IPSEC_MODE_TUNNEL,
+                               .reqid = reqid,
+                               .spirange_min = 0xd0000000,
+                               .spirange_max = 0xdfffffff,
+                       },
+               },
+       };
+       socklen_t len;
+
+       len = sizeof(args);
+       if (getsockopt(tun->get_fd(tun), SYSPROTO_CONTROL,
+                                  UTUN_OPT_GENERATE_CRYPTO_KEYS_IDX, &args, &len) < 0 ||
+               len != sizeof(args))
+       {
+               DBG1(DBG_KNL, "allocating SPI on %s failed: %s",
+                        tun->get_name(tun), strerror(errno));
+               return FALSE;
+       }
+       *spi = htonl(args.u.ipsec_v1.spi);
+       return TRUE;
+}
+
 METHOD(kernel_ipsec_t, get_spi, status_t,
        private_kernel_utun_ipsec_t *this, host_t *src, host_t *dst,
        u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
 {
+       tun_device_t *tun;
+
+       if (protocol != IPPROTO_ESP)
+       {
+               return NOT_SUPPORTED;
+       }
+       tun = tun_device_create(NULL);
+       if (!tun)
+       {
+               return FAILED;
+       }
+       if (!enable_crypto(tun) || !alloc_spi(tun, src, dst, reqid, spi))
+       {
+               tun->destroy(tun);
+               return FAILED;
+       }
+
        this->mutex->lock(this->mutex);
-       *spi = this->spi++;
+       this->tuns->insert_last(this->tuns, tun);
        this->mutex->unlock(this->mutex);
 
        return SUCCESS;
@@ -182,7 +259,7 @@ static status_t add_sa_tun(private_kernel_utun_ipsec_t *this, tun_device_t *tun,
                                .replay = 32,
                                .key_auth_len = int_key.len * 8,
                                .key_enc_len = enc_key.len * 8,
-                               .spi = spi,
+                               .spi = ntohl(spi),
                                .pid = getpid(),
                                .reqid = reqid,
                                .lifetime_hard = hard,
@@ -246,7 +323,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
        }
 
        ts = inbound ? dst_ts : src_ts;
-       enumerator = kernel_utun_create_enumerator();
+       this->mutex->lock(this->mutex);
+       enumerator = this->tuns->create_enumerator(this->tuns);
        while (enumerator->enumerate(enumerator, &tun))
        {
                host = tun->get_address(tun, NULL);
@@ -259,6 +337,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                }
        }
        enumerator->destroy(enumerator);
+       this->mutex->unlock(this->mutex);
 
        return status;
 }
@@ -336,9 +415,89 @@ METHOD(kernel_ipsec_t, enable_udp_decap, bool,
        return FALSE;
 }
 
+METHOD(kernel_utun_ipsec_t, add_ip, status_t,
+       private_kernel_utun_ipsec_t *this, host_t *vip, int prefix)
+{
+       tun_device_t *tun;
+       bool added = FALSE;
+
+       if (prefix == -1)
+       {
+               switch (vip->get_family(vip))
+               {
+                       case AF_INET:
+                               prefix = 32;
+                               break;
+                       case AF_INET6:
+                               prefix = 128;
+                               break;
+                       default:
+                               return NOT_SUPPORTED;
+               }
+       }
+       this->mutex->lock(this->mutex);
+       if (this->tuns->get_last(this->tuns, (void**)&tun) == SUCCESS)
+       {
+               added = tun->set_address(tun, vip, prefix);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (added)
+       {
+               return SUCCESS;
+       }
+       return FAILED;
+}
+
+METHOD(kernel_utun_ipsec_t, del_ip, status_t,
+       private_kernel_utun_ipsec_t *this, host_t *vip, int prefix)
+{
+       enumerator_t *enumerator;
+       tun_device_t *tun;
+       host_t *host;
+       bool found;
+
+       this->mutex->lock(this->mutex);
+       enumerator = this->tuns->create_enumerator(this->tuns);
+       while (enumerator->enumerate(enumerator, &tun))
+       {
+               host = tun->get_address(tun, NULL);
+               if (host && host->ip_equals(host, vip))
+               {
+                       this->tuns->remove_at(this->tuns, enumerator);
+                       tun->destroy(tun);
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->mutex->unlock(this->mutex);
+
+       if (found)
+       {
+               return SUCCESS;
+       }
+       return NOT_FOUND;
+}
+
+/**
+ * Globally referencable instance of kernek_utun_ipsec instance
+ */
+static kernel_utun_ipsec_t *singleton;
+
+/**
+ * See header.
+ */
+kernel_utun_ipsec_t *kernel_utun_ipsec_get()
+{
+       return singleton;
+}
+
 METHOD(kernel_ipsec_t, destroy, void,
        private_kernel_utun_ipsec_t *this)
 {
+       singleton = NULL;
+       this->tuns->destroy_offset(this->tuns, offsetof(tun_device_t, destroy));
        this->mutex->destroy(this->mutex);
        free(this);
 }
@@ -369,10 +528,12 @@ kernel_utun_ipsec_t *kernel_utun_ipsec_create()
                                .enable_udp_decap = _enable_udp_decap,
                                .destroy = _destroy,
                        },
+                       .add_ip = _add_ip,
+                       .del_ip = _del_ip,
                },
+               .tuns = linked_list_create(),
                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
-               /* initialize to "charon-style" SPIs with a leading "c" */
-               .spi = 0xc0000000,
        );
+       singleton = &this->public;
        return &this->public;
 }
index 7c52cf371308075dd181dc738733c3ead68680eb..c582059360bc4960cafd7f273c25cb8e239da0fa 100644 (file)
@@ -34,6 +34,24 @@ struct kernel_utun_ipsec_t {
         * Implements kernel_ipsec_t interface
         */
        kernel_ipsec_t interface;
+
+       /**
+        * Add a virtual IP to the prefiously created utun device.
+        *
+        * @param vip                   virtual IP
+        * @param prefix                network prefix
+        * @reuturn                             SUCCESS if IP added
+        */
+       status_t (*add_ip)(kernel_utun_ipsec_t *this, host_t *vip, int prefix);
+
+       /**
+        * Remove a virtual IP from an utun device.
+        *
+        * @param vip                   virtual IP
+        * @param prefix                network prefix
+        * @reuturn                             SUCCESS if IP deleted
+        */
+       status_t (*del_ip)(kernel_utun_ipsec_t *this, host_t *vip, int prefix);
 };
 
 /**
@@ -43,4 +61,11 @@ struct kernel_utun_ipsec_t {
  */
 kernel_utun_ipsec_t *kernel_utun_ipsec_create();
 
+/**
+ * Get the single instance of kernel_utun_ipsec_t, once created.
+ *
+ * @return                     single instance of utun_ipsec
+ */
+kernel_utun_ipsec_t *kernel_utun_ipsec_get();
+
 #endif /** KERNEL_UTUN_IPSEC_H_ @}*/
index 136593131db20dc4feef671bdf6e2e35e442110c..5564247068ffa5a0ac232af048ff05ea9fc2be02 100644 (file)
 #include <sys/socket.h>
 #include <ifaddrs.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <net/if.h>
-#include <net/if.h>
-#include <net/if_utun.h>
-#include <net/if_utun_crypto.h>
-#include <net/if_utun_crypto_ipsec.h>
-#include <netinet/in_var.h>
-#include <sys/kern_control.h>
 
 #include "kernel_utun_net.h"
+#include "kernel_utun_ipsec.h"
 
 #include <hydra.h>
 #include <utils/debug.h>
-#include <collections/linked_list.h>
-#include <threading/mutex.h>
-#include <networking/tun_device.h>
 
 typedef struct private_kernel_utun_net_t private_kernel_utun_net_t;
 
@@ -50,16 +35,6 @@ struct private_kernel_utun_net_t {
         * Public part of the kernel_utun_net_t object.
         */
        kernel_utun_net_t public;
-
-       /**
-        * Mutex to access tun list
-        */
-       mutex_t *mutex;
-
-       /**
-        * List of tun devices, as tun_device_t
-        */
-       linked_list_t *tuns;
 };
 
 METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
@@ -116,97 +91,32 @@ METHOD(kernel_net_t, get_nexthop, host_t*,
        return NULL;
 }
 
-/**
- * Enable IPsec crypt extension on utun device
- */
-static bool enable_crypto(tun_device_t *tun)
-{
-       utun_crypto_args_t args = {
-               .ver = UTUN_CRYPTO_VER_1,
-               .type = UTUN_CRYPTO_TYPE_IPSEC,
-               .args_ulen = sizeof(utun_crypto_ipsec_args_v1_t),
-               .u = {
-                       .ipsec_v1 = {
-                               /* nothing to set */
-                       },
-               },
-       };
-       if (setsockopt(tun->get_fd(tun), SYSPROTO_CONTROL, UTUN_OPT_ENABLE_CRYPTO,
-                                  &args, sizeof(args)) < 0)
-       {
-               DBG1(DBG_KNL, "enabling crypto on %s failed: %s",
-                        tun->get_name(tun), strerror(errno));
-               return FALSE;
-       }
-       return TRUE;
-}
-
 METHOD(kernel_net_t, add_ip, status_t,
        private_kernel_utun_net_t *this, host_t *virtual_ip, int prefix,
        char *iface_name)
 {
-       tun_device_t *tun;
+       kernel_utun_ipsec_t *ipsec;
 
-       if (prefix == -1)
-       {
-               switch (virtual_ip->get_family(virtual_ip))
-               {
-                       case AF_INET:
-                               prefix = 32;
-                               break;
-                       case AF_INET6:
-                               prefix = 128;
-                               break;
-                       default:
-                               return NOT_SUPPORTED;
-               }
-       }
-       tun = tun_device_create(NULL);
-       if (!tun)
-       {
-               return FAILED;
-       }
-       if (!tun->set_address(tun, virtual_ip, prefix) || !enable_crypto(tun))
+       ipsec = kernel_utun_ipsec_get();
+       if (ipsec)
        {
-               tun->destroy(tun);
-               return FAILED;
+               return ipsec->add_ip(ipsec, virtual_ip, prefix);
        }
-       this->mutex->lock(this->mutex);
-       this->tuns->insert_last(this->tuns, tun);
-       this->mutex->unlock(this->mutex);
-       return SUCCESS;
+       return FAILED;
 }
 
 METHOD(kernel_net_t, del_ip, status_t,
        private_kernel_utun_net_t *this, host_t *virtual_ip, int prefix,
        bool wait)
 {
-       enumerator_t *enumerator;
-       tun_device_t *tun;
-       host_t *host;
-       bool found;
+       kernel_utun_ipsec_t *ipsec;
 
-       this->mutex->lock(this->mutex);
-       enumerator = this->tuns->create_enumerator(this->tuns);
-       while (enumerator->enumerate(enumerator, &tun))
+       ipsec = kernel_utun_ipsec_get();
+       if (ipsec)
        {
-               host = tun->get_address(tun, NULL);
-               if (host && host->ip_equals(host, virtual_ip))
-               {
-                       this->tuns->remove_at(this->tuns, enumerator);
-                       tun->destroy(tun);
-                       found = TRUE;
-                       break;
-               }
+               return ipsec->del_ip(ipsec, virtual_ip, prefix);
        }
-       enumerator->destroy(enumerator);
-       this->mutex->unlock(this->mutex);
-
-       if (found)
-       {
-               return SUCCESS;
-       }
-       return NOT_FOUND;
+       return FAILED;
 }
 
 METHOD(kernel_net_t, add_route, status_t,
@@ -223,21 +133,12 @@ METHOD(kernel_net_t, del_route, status_t,
        return FAILED;
 }
 
-/**
- * Globally referencable instance of kernek_utun_net instance
- */
-static private_kernel_utun_net_t *singleton;
-
 METHOD(kernel_net_t, destroy, void,
        private_kernel_utun_net_t *this)
 {
-       singleton = NULL;
-       this->tuns->destroy_offset(this->tuns, offsetof(tun_device_t, destroy));
-       this->mutex->destroy(this->mutex);
        free(this);
 }
 
-
 /*
  * Described in header.
  */
@@ -259,25 +160,7 @@ kernel_utun_net_t *kernel_utun_net_create()
                                .destroy = _destroy,
                        },
                },
-               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
-               .tuns = linked_list_create(),
        );
 
-       singleton = this;
        return &this->public;
 }
-
-/**
- * See header.
- */
-enumerator_t *kernel_utun_create_enumerator()
-{
-       if (singleton)
-       {
-               singleton->mutex->lock(singleton->mutex);
-               return enumerator_create_cleaner(
-                                       singleton->tuns->create_enumerator(singleton->tuns),
-                                       (void*)singleton->mutex->unlock, singleton->mutex);
-       }
-       return enumerator_create_empty();
-}
index b26912c8d90fb179a518781e6f29abcd9a2a9a2f..2bc2f9bd937e45403e991b711219897f9ee237f2 100644 (file)
@@ -43,11 +43,4 @@ struct kernel_utun_net_t {
  */
 kernel_utun_net_t *kernel_utun_net_create();
 
-/**
- * Create an enumerator over all known utun interfaces.
- *
- * @return                     enumerator over tun_device_t
- */
-enumerator_t *kernel_utun_create_enumerator();
-
 #endif /** KERNEL_UTUN_NET_H_ @}*/