From: Martin Willi Date: Fri, 12 Apr 2013 13:48:53 +0000 (+0200) Subject: kernel-utun: allocate SPI from kernel, create utun before doing so X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=06aba8839147b66265628af25358f7dd56423a75;p=thirdparty%2Fstrongswan.git kernel-utun: allocate SPI from kernel, create utun before doing so --- diff --git a/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.c b/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.c index a0ccb5b90d..644c686625 100644 --- a/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.c +++ b/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.c @@ -14,7 +14,6 @@ */ #include "kernel_utun_ipsec.h" -#include "kernel_utun_net.h" #include #include @@ -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; } diff --git a/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.h b/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.h index 7c52cf3713..c582059360 100644 --- a/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.h +++ b/src/libhydra/plugins/kernel_utun/kernel_utun_ipsec.h @@ -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_ @}*/ diff --git a/src/libhydra/plugins/kernel_utun/kernel_utun_net.c b/src/libhydra/plugins/kernel_utun/kernel_utun_net.c index 136593131d..5564247068 100644 --- a/src/libhydra/plugins/kernel_utun/kernel_utun_net.c +++ b/src/libhydra/plugins/kernel_utun/kernel_utun_net.c @@ -17,27 +17,12 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "kernel_utun_net.h" +#include "kernel_utun_ipsec.h" #include #include -#include -#include -#include 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(); -} diff --git a/src/libhydra/plugins/kernel_utun/kernel_utun_net.h b/src/libhydra/plugins/kernel_utun/kernel_utun_net.h index b26912c8d9..2bc2f9bd93 100644 --- a/src/libhydra/plugins/kernel_utun/kernel_utun_net.h +++ b/src/libhydra/plugins/kernel_utun/kernel_utun_net.h @@ -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_ @}*/