kernel-libipsec: Router reads packets from multiple TUN devices
These devices are collected via kernel_listener_t interface.
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "kernel_libipsec_ipsec.h"
|
#include "kernel_libipsec_ipsec.h"
|
||||||
|
#include "kernel_libipsec_router.h"
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
#include <ipsec.h>
|
#include <ipsec.h>
|
||||||
@@ -35,11 +36,6 @@ struct private_kernel_libipsec_ipsec_t {
|
|||||||
*/
|
*/
|
||||||
ipsec_event_listener_t ipsec_listener;
|
ipsec_event_listener_t ipsec_listener;
|
||||||
|
|
||||||
/**
|
|
||||||
* TUN device
|
|
||||||
*/
|
|
||||||
tun_device_t *tun;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutex to lock access to various lists
|
* Mutex to lock access to various lists
|
||||||
*/
|
*/
|
||||||
@@ -389,7 +385,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this,
|
|||||||
}
|
}
|
||||||
|
|
||||||
INIT(route,
|
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,
|
.src_ip = src_ip,
|
||||||
.dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)),
|
.dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)),
|
||||||
.prefixlen = policy->dst.mask,
|
.prefixlen = policy->dst.mask,
|
||||||
@@ -634,7 +630,6 @@ kernel_libipsec_ipsec_t *kernel_libipsec_ipsec_create()
|
|||||||
.ipsec_listener = {
|
.ipsec_listener = {
|
||||||
.expire = expire,
|
.expire = expire,
|
||||||
},
|
},
|
||||||
.tun = lib->get(lib, "kernel-libipsec-tun"),
|
|
||||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||||
.policies = linked_list_create(),
|
.policies = linked_list_create(),
|
||||||
.excludes = linked_list_create(),
|
.excludes = linked_list_create(),
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ static bool create_router(private_kernel_libipsec_plugin_t *this,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->router->destroy(this->router);
|
DESTROY_IF(this->router);
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,15 +13,39 @@
|
|||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "kernel_libipsec_router.h"
|
#include "kernel_libipsec_router.h"
|
||||||
|
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
|
#include <hydra.h>
|
||||||
#include <ipsec.h>
|
#include <ipsec.h>
|
||||||
|
#include <collections/hashtable.h>
|
||||||
#include <networking/tun_device.h>
|
#include <networking/tun_device.h>
|
||||||
|
#include <threading/rwlock.h>
|
||||||
|
#include <threading/thread.h>
|
||||||
#include <processing/jobs/callback_job.h>
|
#include <processing/jobs/callback_job.h>
|
||||||
|
|
||||||
typedef struct private_kernel_libipsec_router_t private_kernel_libipsec_router_t;
|
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
|
* Private data
|
||||||
*/
|
*/
|
||||||
@@ -33,11 +57,43 @@ struct private_kernel_libipsec_router_t {
|
|||||||
kernel_libipsec_router_t public;
|
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
|
* Outbound callback
|
||||||
*/
|
*/
|
||||||
@@ -61,18 +117,56 @@ static void receiver_esp_cb(void *data, packet_t *packet)
|
|||||||
static void deliver_plain(private_kernel_libipsec_router_t *this,
|
static void deliver_plain(private_kernel_libipsec_router_t *this,
|
||||||
ip_packet_t *packet)
|
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);
|
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;
|
chunk_t raw;
|
||||||
|
|
||||||
if (this->tun->read_packet(this->tun, &raw))
|
if (tun->read_packet(tun, &raw))
|
||||||
{
|
{
|
||||||
ip_packet_t *packet;
|
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");
|
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;
|
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,
|
METHOD(kernel_libipsec_router_t, destroy, void,
|
||||||
private_kernel_libipsec_router_t *this)
|
private_kernel_libipsec_router_t *this)
|
||||||
{
|
{
|
||||||
@@ -98,23 +296,61 @@ METHOD(kernel_libipsec_router_t, destroy, void,
|
|||||||
(ipsec_outbound_cb_t)send_esp);
|
(ipsec_outbound_cb_t)send_esp);
|
||||||
ipsec->processor->unregister_inbound(ipsec->processor,
|
ipsec->processor->unregister_inbound(ipsec->processor,
|
||||||
(ipsec_inbound_cb_t)deliver_plain);
|
(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);
|
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
|
* 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;
|
private_kernel_libipsec_router_t *this;
|
||||||
|
|
||||||
INIT(this,
|
INIT(this,
|
||||||
.public = {
|
.public = {
|
||||||
|
.listener = {
|
||||||
|
.tun = _tun,
|
||||||
|
},
|
||||||
|
.get_tun_name = _get_tun_name,
|
||||||
.destroy = _destroy,
|
.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_outbound(ipsec->processor, send_esp, NULL);
|
||||||
ipsec->processor->register_inbound(ipsec->processor,
|
ipsec->processor->register_inbound(ipsec->processor,
|
||||||
(ipsec_inbound_cb_t)deliver_plain, this);
|
(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,
|
(job_t*)callback_job_create((callback_job_cb_t)handle_plain, this,
|
||||||
NULL, (callback_job_cancel_t)return_false));
|
NULL, (callback_job_cancel_t)return_false));
|
||||||
|
|
||||||
|
router = &this->public;
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#ifndef KERNEL_LIBIPSEC_ROUTER_H_
|
#ifndef KERNEL_LIBIPSEC_ROUTER_H_
|
||||||
#define KERNEL_LIBIPSEC_ROUTER_H_
|
#define KERNEL_LIBIPSEC_ROUTER_H_
|
||||||
|
|
||||||
|
#include <kernel/kernel_listener.h>
|
||||||
|
|
||||||
typedef struct kernel_libipsec_router_t kernel_libipsec_router_t;
|
typedef struct kernel_libipsec_router_t kernel_libipsec_router_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,12 +31,30 @@ typedef struct kernel_libipsec_router_t kernel_libipsec_router_t;
|
|||||||
*/
|
*/
|
||||||
struct 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
|
* Destroy the given instance
|
||||||
*/
|
*/
|
||||||
void (*destroy)(kernel_libipsec_router_t *this);
|
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.
|
* Create a kernel_libipsec_router_t instance.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user