aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/plugins/kernel_libipsec
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2013-06-15 16:21:08 +0200
committerTobias Brunner <tobias@strongswan.org>2013-06-21 17:03:21 +0200
commitdcaf8d570c5c17cf1ec82d9d19ca53e1bfdff52b (patch)
tree285c3aefe0dcca0711bb8b2c4489555965f95402 /src/libcharon/plugins/kernel_libipsec
parent7045defbffb2917279b3b8222b700fed4d7af925 (diff)
downloadstrongswan-dcaf8d570c5c17cf1ec82d9d19ca53e1bfdff52b.tar.bz2
strongswan-dcaf8d570c5c17cf1ec82d9d19ca53e1bfdff52b.tar.xz
kernel-libipsec: Router reads packets from multiple TUN devices
These devices are collected via kernel_listener_t interface.
Diffstat (limited to 'src/libcharon/plugins/kernel_libipsec')
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c9
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c2
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c253
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.h20
4 files changed, 268 insertions, 16 deletions
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
index efa519a83..f605501ba 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c
@@ -13,6 +13,7 @@
*/
#include "kernel_libipsec_ipsec.h"
+#include "kernel_libipsec_router.h"
#include <library.h>
#include <ipsec.h>
@@ -36,11 +37,6 @@ struct private_kernel_libipsec_ipsec_t {
ipsec_event_listener_t ipsec_listener;
/**
- * TUN device
- */
- tun_device_t *tun;
-
- /**
* Mutex to lock access to various lists
*/
mutex_t *mutex;
@@ -389,7 +385,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
}
INIT(route,
- .if_name = strdup(this->tun->get_name(this->tun)),
+ .if_name = router->get_tun_name(router, is_virtual ? src_ip : NULL),
.src_ip = src_ip,
.dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)),
.prefixlen = policy->dst.mask,
@@ -634,7 +630,6 @@ kernel_libipsec_ipsec_t *kernel_libipsec_ipsec_create()
.ipsec_listener = {
.expire = expire,
},
- .tun = lib->get(lib, "kernel-libipsec-tun"),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.policies = linked_list_create(),
.excludes = linked_list_create(),
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
index 003ea29c0..d5f3bc248 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
@@ -64,7 +64,7 @@ static bool create_router(private_kernel_libipsec_plugin_t *this,
}
else
{
- this->router->destroy(this->router);
+ DESTROY_IF(this->router);
}
return TRUE;
}
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c
index 9a829f965..6ce1d4eb0 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.c
@@ -13,16 +13,40 @@
* for more details.
*/
+#include <unistd.h>
+#include <fcntl.h>
+
#include "kernel_libipsec_router.h"
#include <daemon.h>
+#include <hydra.h>
#include <ipsec.h>
+#include <collections/hashtable.h>
#include <networking/tun_device.h>
+#include <threading/rwlock.h>
+#include <threading/thread.h>
#include <processing/jobs/callback_job.h>
typedef struct private_kernel_libipsec_router_t private_kernel_libipsec_router_t;
/**
+ * Entry in the TUN device map
+ */
+typedef struct {
+ /** virtual IP (points to internal data of tun) */
+ host_t *addr;
+ /** underlying TUN file descriptor (cached from tun) */
+ int fd;
+ /** TUN device */
+ tun_device_t *tun;
+} tun_entry_t;
+
+/**
+ * Single instance of the router
+ */
+kernel_libipsec_router_t *router;
+
+/**
* Private data
*/
struct private_kernel_libipsec_router_t {
@@ -33,12 +57,44 @@ struct private_kernel_libipsec_router_t {
kernel_libipsec_router_t public;
/**
- * TUN device
+ * Default TUN device if kernel interface does not require separate TUN
+ * devices per VIP or for tunnels without VIP.
*/
- tun_device_t *tun;
+ tun_entry_t tun;
+
+ /**
+ * Hashtable that maps virtual IPs to TUN devices (tun_entry_t).
+ */
+ hashtable_t *tuns;
+
+ /**
+ * Lock for TUN device map
+ */
+ rwlock_t *lock;
+
+ /**
+ * Pipe to signal handle_plain() about changes regarding TUN devices
+ */
+ int notify[2];
};
/**
+ * Hash function for TUN device map
+ */
+static u_int tun_entry_hash(tun_entry_t *entry)
+{
+ return chunk_hash(entry->addr->get_address(entry->addr));
+}
+
+/**
+ * Comparison function for TUN device map
+ */
+static bool tun_entry_equals(tun_entry_t *a, tun_entry_t *b)
+{
+ return a->addr->ip_equals(a->addr, b->addr);
+}
+
+/**
* Outbound callback
*/
static void send_esp(void *data, esp_packet_t *packet)
@@ -61,18 +117,56 @@ static void receiver_esp_cb(void *data, packet_t *packet)
static void deliver_plain(private_kernel_libipsec_router_t *this,
ip_packet_t *packet)
{
- this->tun->write_packet(this->tun, packet->get_encoding(packet));
+ tun_device_t *tun;
+ tun_entry_t *entry, lookup = {
+ .addr = packet->get_destination(packet),
+ };
+
+ this->lock->read_lock(this->lock);
+ entry = this->tuns->get(this->tuns, &lookup);
+ tun = entry ? entry->tun : this->tun.tun;
+ tun->write_packet(tun, packet->get_encoding(packet));
+ this->lock->unlock(this->lock);
packet->destroy(packet);
}
/**
- * Job handling outbound plaintext packets
+ * Create an FD set covering all TUN devices and the read end of the notify pipe
*/
-static job_requeue_t handle_plain(private_kernel_libipsec_router_t *this)
+static int collect_fds(private_kernel_libipsec_router_t *this, fd_set *fds)
+{
+ enumerator_t *enumerator;
+ tun_entry_t *entry;
+ int maxfd;
+
+ FD_ZERO(fds);
+ FD_SET(this->notify[0], fds);
+ maxfd = this->notify[0];
+
+ FD_SET(this->tun.fd, fds);
+ maxfd = max(maxfd, this->tun.fd);
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->tuns->create_enumerator(this->tuns);
+ while (enumerator->enumerate(enumerator, NULL, &entry))
+ {
+ FD_SET(entry->fd, fds);
+ maxfd = max(maxfd, entry->fd);
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+
+ return maxfd + 1;
+}
+
+/**
+ * Read and process outbound plaintext packet for the given TUN device
+ */
+static void process_plain(tun_device_t *tun)
{
chunk_t raw;
- if (this->tun->read_packet(this->tun, &raw))
+ if (tun->read_packet(tun, &raw))
{
ip_packet_t *packet;
@@ -86,9 +180,113 @@ static job_requeue_t handle_plain(private_kernel_libipsec_router_t *this)
DBG1(DBG_KNL, "invalid IP packet read from TUN device");
}
}
+}
+
+/**
+ * Handle waiting data for any TUN device
+ */
+static void handle_tuns(private_kernel_libipsec_router_t *this, fd_set *fds)
+{
+ enumerator_t *enumerator;
+ tun_entry_t *entry;
+
+ if (FD_ISSET(this->tun.fd, fds))
+ {
+ process_plain(this->tun.tun);
+ }
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->tuns->create_enumerator(this->tuns);
+ while (enumerator->enumerate(enumerator, NULL, &entry))
+ {
+ if (FD_ISSET(entry->fd, fds))
+ {
+ process_plain(entry->tun);
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Job handling outbound plaintext packets
+ */
+static job_requeue_t handle_plain(private_kernel_libipsec_router_t *this)
+{
+ bool oldstate;
+ fd_set fds;
+ int maxfd;
+
+ maxfd = collect_fds(this, &fds);
+
+ oldstate = thread_cancelability(TRUE);
+ if (select(maxfd, &fds, NULL, NULL, NULL) <= 0)
+ {
+ thread_cancelability(oldstate);
+ return JOB_REQUEUE_FAIR;
+ }
+ thread_cancelability(oldstate);
+
+ if (FD_ISSET(this->notify[0], &fds))
+ { /* list of TUN devices changed, read notification data, rebuild FDs */
+ char buf[1];
+ while (read(this->notify[0], &buf, sizeof(buf)) == sizeof(buf));
+ return JOB_REQUEUE_DIRECT;
+ }
+
+ handle_tuns(this, &fds);
return JOB_REQUEUE_DIRECT;
}
+METHOD(kernel_listener_t, tun, bool,
+ private_kernel_libipsec_router_t *this, tun_device_t *tun, bool created)
+{
+ tun_entry_t *entry, lookup;
+ char buf[] = {0x01};
+
+ this->lock->write_lock(this->lock);
+ if (created)
+ {
+ INIT(entry,
+ .addr = tun->get_address(tun, NULL),
+ .fd = tun->get_fd(tun),
+ .tun = tun,
+ );
+ this->tuns->put(this->tuns, entry, entry);
+ }
+ else
+ {
+ lookup.addr = tun->get_address(tun, NULL);
+ entry = this->tuns->remove(this->tuns, &lookup);
+ free(entry);
+ }
+ /* notify handler thread to recreate FD set */
+ ignore_result(write(this->notify[1], buf, sizeof(buf)));
+ this->lock->unlock(this->lock);
+ return TRUE;
+}
+
+METHOD(kernel_libipsec_router_t, get_tun_name, char*,
+ private_kernel_libipsec_router_t *this, host_t *vip)
+{
+ tun_entry_t *entry, lookup = {
+ .addr = vip,
+ };
+ tun_device_t *tun;
+ char *name;
+
+ if (!vip)
+ {
+ return strdup(this->tun.tun->get_name(this->tun.tun));
+ }
+ this->lock->read_lock(this->lock);
+ entry = this->tuns->get(this->tuns, &lookup);
+ tun = entry ? entry->tun : this->tun.tun;
+ name = strdup(tun->get_name(tun));
+ this->lock->unlock(this->lock);
+ return name;
+}
+
METHOD(kernel_libipsec_router_t, destroy, void,
private_kernel_libipsec_router_t *this)
{
@@ -98,23 +296,61 @@ METHOD(kernel_libipsec_router_t, destroy, void,
(ipsec_outbound_cb_t)send_esp);
ipsec->processor->unregister_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain);
+ hydra->kernel_interface->remove_listener(hydra->kernel_interface,
+ &this->public.listener);
+ this->lock->destroy(this->lock);
+ this->tuns->destroy(this->tuns);
+ close(this->notify[0]);
+ close(this->notify[1]);
+ router = NULL;
free(this);
}
+/**
+ * Set O_NONBLOCK on the given socket.
+ */
+static bool set_nonblock(int socket)
+{
+ int flags = fcntl(socket, F_GETFL);
+ return flags != -1 && fcntl(socket, F_SETFL, flags | O_NONBLOCK) != -1;
+}
+
/*
* See header file
*/
-kernel_libipsec_router_t *kernel_libipsec_router_create(tun_device_t *tun)
+kernel_libipsec_router_t *kernel_libipsec_router_create()
{
private_kernel_libipsec_router_t *this;
INIT(this,
.public = {
+ .listener = {
+ .tun = _tun,
+ },
+ .get_tun_name = _get_tun_name,
.destroy = _destroy,
},
- .tun = lib->get(lib, "kernel-libipsec-tun"),
+ .tun = {
+ .tun = lib->get(lib, "kernel-libipsec-tun"),
+ }
);
+ if (pipe(this->notify) != 0 ||
+ !set_nonblock(this->notify[0]) || !set_nonblock(this->notify[1]))
+ {
+ DBG1(DBG_KNL, "creating notify pipe for kernel-libipsec router failed");
+ free(this);
+ return NULL;
+ }
+
+ this->tun.fd = this->tun.tun->get_fd(this->tun.tun);
+
+ this->tuns = hashtable_create((hashtable_hash_t)tun_entry_hash,
+ (hashtable_equals_t)tun_entry_equals, 4);
+ this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
+
+ hydra->kernel_interface->add_listener(hydra->kernel_interface,
+ &this->public.listener);
ipsec->processor->register_outbound(ipsec->processor, send_esp, NULL);
ipsec->processor->register_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain, this);
@@ -124,5 +360,6 @@ kernel_libipsec_router_t *kernel_libipsec_router_create(tun_device_t *tun)
(job_t*)callback_job_create((callback_job_cb_t)handle_plain, this,
NULL, (callback_job_cancel_t)return_false));
+ router = &this->public;
return &this->public;
}
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.h b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.h
index a7ef27c0a..7b2f3c6c5 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.h
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_router.h
@@ -21,6 +21,8 @@
#ifndef KERNEL_LIBIPSEC_ROUTER_H_
#define KERNEL_LIBIPSEC_ROUTER_H_
+#include <kernel/kernel_listener.h>
+
typedef struct kernel_libipsec_router_t kernel_libipsec_router_t;
/**
@@ -30,12 +32,30 @@ typedef struct kernel_libipsec_router_t kernel_libipsec_router_t;
struct kernel_libipsec_router_t {
/**
+ * Implements kernel_listener_t interface
+ */
+ kernel_listener_t listener;
+
+ /**
+ * Get the name of the TUN device to be used with the given virtual IP.
+ *
+ * @param vip virtual IP
+ * @return allocated name
+ */
+ char *(*get_tun_name)(kernel_libipsec_router_t *this, host_t *vip);
+
+ /**
* Destroy the given instance
*/
void (*destroy)(kernel_libipsec_router_t *this);
};
/**
+ * Single instance of this class, if created
+ */
+extern kernel_libipsec_router_t *router;
+
+/**
* Create a kernel_libipsec_router_t instance.
*
* @return kernel_libipsec_router_t instance