diff options
Diffstat (limited to 'src/libstrongswan/networking')
-rw-r--r-- | src/libstrongswan/networking/host.c | 597 | ||||
-rw-r--r-- | src/libstrongswan/networking/host.h | 231 | ||||
-rw-r--r-- | src/libstrongswan/networking/host_resolver.c | 351 | ||||
-rw-r--r-- | src/libstrongswan/networking/host_resolver.h | 60 | ||||
-rw-r--r-- | src/libstrongswan/networking/packet.c | 163 | ||||
-rw-r--r-- | src/libstrongswan/networking/packet.h | 121 | ||||
-rw-r--r-- | src/libstrongswan/networking/tun_device.c | 461 | ||||
-rw-r--r-- | src/libstrongswan/networking/tun_device.h | 112 |
8 files changed, 2096 insertions, 0 deletions
diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c new file mode 100644 index 000000000..bffa96064 --- /dev/null +++ b/src/libstrongswan/networking/host.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2006-2012 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 "host.h" + +#include <utils/debug.h> +#include <library.h> + +#define IPV4_LEN 4 +#define IPV6_LEN 16 + +typedef struct private_host_t private_host_t; + +/** + * Private Data of a host object. + */ +struct private_host_t { + /** + * Public data + */ + host_t public; + + /** + * low-lewel structure, which stores the address + */ + union { + /** generic type */ + struct sockaddr address; + /** maximum sockaddr size */ + struct sockaddr_storage address_max; + /** IPv4 address */ + struct sockaddr_in address4; + /** IPv6 address */ + struct sockaddr_in6 address6; + }; + /** + * length of address structure + */ + socklen_t socklen; +}; + + +METHOD(host_t, get_sockaddr, sockaddr_t*, + private_host_t *this) +{ + return &(this->address); +} + +METHOD(host_t, get_sockaddr_len, socklen_t*, + private_host_t *this) +{ + return &(this->socklen); +} + +METHOD(host_t, is_anyaddr, bool, + private_host_t *this) +{ + static const u_int8_t zeroes[IPV6_LEN]; + + switch (this->address.sa_family) + { + case AF_INET: + { + return memeq(zeroes, &(this->address4.sin_addr.s_addr), IPV4_LEN); + } + case AF_INET6: + { + return memeq(zeroes, &(this->address6.sin6_addr.s6_addr), IPV6_LEN); + } + default: + { + return FALSE; + } + } +} + +/** + * Described in header. + */ +int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + private_host_t *this = *((private_host_t**)(args[0])); + char buffer[INET6_ADDRSTRLEN + 16]; + + if (this == NULL) + { + snprintf(buffer, sizeof(buffer), "(null)"); + } + else if (is_anyaddr(this) && !spec->plus) + { + snprintf(buffer, sizeof(buffer), "%%any%s", + this->address.sa_family == AF_INET6 ? "6" : ""); + } + else + { + void *address; + u_int16_t port; + int len; + + address = &this->address6.sin6_addr; + port = this->address6.sin6_port; + + switch (this->address.sa_family) + { + case AF_INET: + address = &this->address4.sin_addr; + port = this->address4.sin_port; + /* fall */ + case AF_INET6: + + if (inet_ntop(this->address.sa_family, address, + buffer, sizeof(buffer)) == NULL) + { + snprintf(buffer, sizeof(buffer), + "(address conversion failed)"); + } + else if (spec->hash) + { + len = strlen(buffer); + snprintf(buffer + len, sizeof(buffer) - len, + "[%d]", ntohs(port)); + } + break; + default: + snprintf(buffer, sizeof(buffer), "(family not supported)"); + break; + } + } + if (spec->minus) + { + return print_in_hook(data, "%-*s", spec->width, buffer); + } + return print_in_hook(data, "%*s", spec->width, buffer); +} + +METHOD(host_t, get_address, chunk_t, + private_host_t *this) +{ + chunk_t address = chunk_empty; + + switch (this->address.sa_family) + { + case AF_INET: + { + address.ptr = (char*)&(this->address4.sin_addr.s_addr); + address.len = IPV4_LEN; + return address; + } + case AF_INET6: + { + address.ptr = (char*)&(this->address6.sin6_addr.s6_addr); + address.len = IPV6_LEN; + return address; + } + default: + { + /* return empty chunk */ + return address; + } + } +} + +METHOD(host_t, get_family, int, + private_host_t *this) +{ + return this->address.sa_family; +} + +METHOD(host_t, get_port, u_int16_t, + private_host_t *this) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + return ntohs(this->address4.sin_port); + } + case AF_INET6: + { + return ntohs(this->address6.sin6_port); + } + default: + { + return 0; + } + } +} + +METHOD(host_t, set_port, void, + private_host_t *this, u_int16_t port) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + this->address4.sin_port = htons(port); + break; + } + case AF_INET6: + { + this->address6.sin6_port = htons(port); + break; + } + default: + { + break; + } + } +} + +METHOD(host_t, clone_, host_t*, + private_host_t *this) +{ + private_host_t *new; + + new = malloc_thing(private_host_t); + memcpy(new, this, sizeof(private_host_t)); + + return &new->public; +} + +/** + * Implements host_t.ip_equals + */ +static bool ip_equals(private_host_t *this, private_host_t *other) +{ + if (this->address.sa_family != other->address.sa_family) + { + /* 0.0.0.0 and 0::0 are equal */ + return (is_anyaddr(this) && is_anyaddr(other)); + } + + switch (this->address.sa_family) + { + case AF_INET: + { + return memeq(&this->address4.sin_addr, &other->address4.sin_addr, + sizeof(this->address4.sin_addr)); + } + case AF_INET6: + { + return memeq(&this->address6.sin6_addr, &other->address6.sin6_addr, + sizeof(this->address6.sin6_addr)); + } + default: + break; + } + return FALSE; +} + +/** + * Implements host_t.get_differences + */ +static host_diff_t get_differences(host_t *this, host_t *other) +{ + host_diff_t ret = HOST_DIFF_NONE; + + if (!this->ip_equals(this, other)) + { + ret |= HOST_DIFF_ADDR; + } + + if (this->get_port(this) != other->get_port(other)) + { + ret |= HOST_DIFF_PORT; + } + + return ret; +} + +/** + * Implements host_t.equals + */ +static bool equals(private_host_t *this, private_host_t *other) +{ + if (!ip_equals(this, other)) + { + return FALSE; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + return (this->address4.sin_port == other->address4.sin_port); + } + case AF_INET6: + { + return (this->address6.sin6_port == other->address6.sin6_port); + } + default: + break; + } + return FALSE; +} + +METHOD(host_t, destroy, void, + private_host_t *this) +{ + free(this); +} + +/** + * Creates an empty host_t object + */ +static private_host_t *host_create_empty(void) +{ + private_host_t *this; + + INIT(this, + .public = { + .get_sockaddr = _get_sockaddr, + .get_sockaddr_len = _get_sockaddr_len, + .clone = _clone_, + .get_family = _get_family, + .get_address = _get_address, + .get_port = _get_port, + .set_port = _set_port, + .get_differences = get_differences, + .ip_equals = (bool (*)(host_t *,host_t *))ip_equals, + .equals = (bool (*)(host_t *,host_t *)) equals, + .is_anyaddr = _is_anyaddr, + .destroy = _destroy, + }, + ); + + return this; +} + +/* + * Create a %any host with port + */ +static host_t *host_create_any_port(int family, u_int16_t port) +{ + host_t *this; + + this = host_create_any(family); + this->set_port(this, port); + return this; +} + +/* + * Described in header. + */ +host_t *host_create_from_string_and_family(char *string, int family, + u_int16_t port) +{ + union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } addr; + + if (streq(string, "%any")) + { + return host_create_any_port(family ? family : AF_INET, port); + } + if (family == AF_UNSPEC || family == AF_INET) + { + if (streq(string, "%any4") || streq(string, "0.0.0.0")) + { + return host_create_any_port(AF_INET, port); + } + } + if (family == AF_UNSPEC || family == AF_INET6) + { + if (streq(string, "%any6") || streq(string, "::")) + { + return host_create_any_port(AF_INET6, port); + } + } + switch (family) + { + case AF_UNSPEC: + if (strchr(string, '.')) + { + goto af_inet; + } + /* FALL */ + case AF_INET6: + if (inet_pton(AF_INET6, string, &addr.v6.sin6_addr) != 1) + { + return NULL; + } + addr.v6.sin6_port = htons(port); + addr.v6.sin6_family = AF_INET6; + return host_create_from_sockaddr((sockaddr_t*)&addr); + case AF_INET: + if (strchr(string, ':')) + { /* do not try to convert v6 addresses for v4 family */ + return NULL; + } + af_inet: + if (inet_pton(AF_INET, string, &addr.v4.sin_addr) != 1) + { + return NULL; + } + addr.v4.sin_port = htons(port); + addr.v4.sin_family = AF_INET; + return host_create_from_sockaddr((sockaddr_t*)&addr); + default: + return NULL; + } +} + +/* + * Described in header. + */ +host_t *host_create_from_string(char *string, u_int16_t port) +{ + return host_create_from_string_and_family(string, AF_UNSPEC, port); +} + +/* + * Described in header. + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) +{ + private_host_t *this = host_create_empty(); + + switch (sockaddr->sa_family) + { + case AF_INET: + { + memcpy(&this->address4, (struct sockaddr_in*)sockaddr, + sizeof(struct sockaddr_in)); + this->socklen = sizeof(struct sockaddr_in); + return &this->public; + } + case AF_INET6: + { + memcpy(&this->address6, (struct sockaddr_in6*)sockaddr, + sizeof(struct sockaddr_in6)); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_dns(char *string, int af, u_int16_t port) +{ + host_t *this; + + this = host_create_from_string_and_family(string, af, port); + if (!this) + { + this = lib->hosts->resolve(lib->hosts, string, af); + } + if (this) + { + this->set_port(this, port); + } + return this; +} + +/* + * Described in header. + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port) +{ + private_host_t *this; + + switch (family) + { + case AF_INET: + if (address.len < IPV4_LEN) + { + return NULL; + } + address.len = IPV4_LEN; + break; + case AF_INET6: + if (address.len < IPV6_LEN) + { + return NULL; + } + address.len = IPV6_LEN; + break; + case AF_UNSPEC: + switch (address.len) + { + case IPV4_LEN: + family = AF_INET; + break; + case IPV6_LEN: + family = AF_INET6; + break; + default: + return NULL; + } + break; + default: + return NULL; + } + this = host_create_empty(); + this->address.sa_family = family; + switch (family) + { + case AF_INET: + memcpy(&this->address4.sin_addr.s_addr, address.ptr, address.len); + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + memcpy(&this->address6.sin6_addr.s6_addr, address.ptr, address.len); + this->address6.sin6_port = htons(port); + this->socklen = sizeof(struct sockaddr_in6); + break; + } + return &this->public; +} + +/* + * Described in header. + */ +host_t *host_create_from_subnet(char *string, int *bits) +{ + char *pos, buf[64]; + host_t *net; + + pos = strchr(string, '/'); + if (pos) + { + if (pos - string >= sizeof(buf)) + { + return NULL; + } + strncpy(buf, string, pos - string); + buf[pos - string] = '\0'; + *bits = atoi(pos + 1); + return host_create_from_string(buf, 0); + } + net = host_create_from_string(string, 0); + if (net) + { + if (net->get_family(net) == AF_INET) + { + *bits = 32; + } + else + { + *bits = 128; + } + } + return net; +} + +/* + * Described in header. + */ +host_t *host_create_any(int family) +{ + private_host_t *this = host_create_empty(); + + memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); + this->address.sa_family = family; + + switch (family) + { + case AF_INET: + { + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h new file mode 100644 index 000000000..25f334779 --- /dev/null +++ b/src/libstrongswan/networking/host.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2006-2009 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2008 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 host host + * @{ @ingroup networking + */ + +#ifndef HOST_H_ +#define HOST_H_ + +typedef enum host_diff_t host_diff_t; +typedef struct host_t host_t; + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <utils/chunk.h> + +/** + * Differences between two hosts. They differ in + * address, port, or both. + */ +enum host_diff_t { + HOST_DIFF_NONE = 0, + HOST_DIFF_ADDR = 1, + HOST_DIFF_PORT = 2, +}; + +/** + * Representates a Host + * + * Host object, identifies a address:port pair and defines some + * useful functions on it. + */ +struct host_t { + + /** + * Build a clone of this host object. + * + * @return cloned host + */ + host_t *(*clone) (host_t *this); + + /** + * Get a pointer to the internal sockaddr struct. + * + * This is used for sending and receiving via sockets. + * + * @return pointer to the internal sockaddr structure + */ + sockaddr_t *(*get_sockaddr) (host_t *this); + + /** + * Get the length of the sockaddr struct. + * + * Depending on the family, the length of the sockaddr struct + * is different. Use this function to get the length of the sockaddr + * struct returned by get_sock_addr. + * + * This is used for sending and receiving via sockets. + * + * @return length of the sockaddr struct + */ + socklen_t *(*get_sockaddr_len) (host_t *this); + + /** + * Gets the family of the address + * + * @return family + */ + int (*get_family) (host_t *this); + + /** + * Checks if the ip address of host is set to default route. + * + * @return TRUE if host is 0.0.0.0 or 0::0, FALSE otherwise + */ + bool (*is_anyaddr) (host_t *this); + + /** + * Get the address of this host as chunk_t + * + * Returned chunk points to internal data. + * + * @return address string, + */ + chunk_t (*get_address) (host_t *this); + + /** + * Get the port of this host + * + * @return port number + */ + u_int16_t (*get_port) (host_t *this); + + /** + * Set the port of this host + * + * @param port port numer + */ + void (*set_port) (host_t *this, u_int16_t port); + + /** + * Compare the ips of two hosts hosts. + * + * @param other the other to compare + * @return TRUE if addresses are equal. + */ + bool (*ip_equals) (host_t *this, host_t *other); + + /** + * Compare two hosts, with port. + * + * @param other the other to compare + * @return TRUE if addresses and ports are equal. + */ + bool (*equals) (host_t *this, host_t *other); + + /** + * Compare two hosts and return the differences. + * + * @param other the other to compare + * @return differences in a combination of host_diff_t's + */ + host_diff_t (*get_differences) (host_t *this, host_t *other); + + /** + * Destroy this host object. + */ + void (*destroy) (host_t *this); +}; + +/** + * Constructor to create a host_t object from an address string. + * + * @param string string of an address, such as "152.96.193.130" + * @param port port number + * @return host_t, NULL if string not an address. + */ +host_t *host_create_from_string(char *string, u_int16_t port); + +/** + * Same as host_create_from_string(), but with the option to enforce a family. + * + * @param string string of an address + * @param family address family, or AF_UNSPEC + * @param port port number + * @return host_t, NULL if string not an address. + */ +host_t *host_create_from_string_and_family(char *string, int family, + u_int16_t port); + +/** + * Constructor to create a host_t from a DNS name. + * + * @param string hostname to resolve + * @param family family to prefer, 0 for first match + * @param port port number + * @return host_t, NULL lookup failed + */ +host_t *host_create_from_dns(char *string, int family, u_int16_t port); + +/** + * Constructor to create a host_t object from an address chunk. + * + * If family is AF_UNSPEC, it is guessed using address.len. + * + * @param family Address family, such as AF_INET or AF_INET6 + * @param address address as chunk_t in network order + * @param port port number + * @return host_t, NULL if family not supported/chunk invalid + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port); + +/** + * Constructor to create a host_t object from a sockaddr struct + * + * @param sockaddr sockaddr struct which contains family, address and port + * @return host_t, NULL if family not supported + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); + +/** + * Create a host from a CIDR subnet definition (1.2.3.0/24), return bits. + * + * @param string string to parse + * @param bits gets the number of network bits in CIDR notation + * @return network start address, NULL on error + */ +host_t *host_create_from_subnet(char *string, int *bits); + +/** + * Create a host without an address, a "any" host. + * + * @param family family of the any host + * @return host_t, NULL if family not supported + */ +host_t *host_create_any(int family); + +/** + * printf hook function for host_t. + * + * Arguments are: + * host_t *host + * Use #-modifier to include port number + * Use +-modifier to force numeric representation (instead of e.g. %any) + */ +int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +#endif /** HOST_H_ @}*/ diff --git a/src/libstrongswan/networking/host_resolver.c b/src/libstrongswan/networking/host_resolver.c new file mode 100644 index 000000000..5e244f114 --- /dev/null +++ b/src/libstrongswan/networking/host_resolver.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * 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 <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#include "host_resolver.h" + +#include <library.h> +#include <utils/debug.h> +#include <threading/condvar.h> +#include <threading/mutex.h> +#include <threading/thread.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> + +/** + * Default minimum and maximum number of threads + */ +#define MIN_THREADS_DEFAULT 0 +#define MAX_THREADS_DEFAULT 3 + +/** + * Timeout in seconds to wait for new queries until a thread may be stopped + */ +#define NEW_QUERY_WAIT_TIMEOUT 30 + +typedef struct private_host_resolver_t private_host_resolver_t; + +/** + * Private data of host_resolver_t + */ +struct private_host_resolver_t { + + /** + * Public interface + */ + host_resolver_t public; + + /** + * Hashtable to check for queued queries, query_t* + */ + hashtable_t *queries; + + /** + * Queue for queries, query_t* + */ + linked_list_t *queue; + + /** + * Mutex to safely access private data + */ + mutex_t *mutex; + + /** + * Condvar to signal arrival of new queries + */ + condvar_t *new_query; + + /** + * Minimum number of resolver threads + */ + u_int min_threads; + + /** + * Maximum number of resolver threads + */ + u_int max_threads; + + /** + * Current number of threads + */ + u_int threads; + + /** + * Current number of busy threads + */ + u_int busy_threads; + + /** + * Pool of threads, thread_t* + */ + linked_list_t *pool; + + /** + * TRUE if no new queries are accepted + */ + bool disabled; + +}; + +typedef struct { + /** DNS name we are looking for */ + char *name; + /** address family we request */ + int family; + /** Condvar to signal completion of a query */ + condvar_t *done; + /** refcount */ + refcount_t refcount; + /** the result if successful */ + host_t *result; +} query_t; + +/** + * Destroy the given query_t object if refcount is zero + */ +static void query_destroy(query_t *this) +{ + if (ref_put(&this->refcount)) + { + DESTROY_IF(this->result); + this->done->destroy(this->done); + free(this->name); + free(this); + } +} + +/** + * Signals all waiting threads and destroys the query + */ +static void query_signal_and_destroy(query_t *this) +{ + this->done->broadcast(this->done); + query_destroy(this); +} + +/** + * Hash a queued query + */ +static u_int query_hash(query_t *this) +{ + return chunk_hash_inc(chunk_create(this->name, strlen(this->name)), + chunk_hash(chunk_from_thing(this->family))); +} + +/** + * Compare two queued queries + */ +static bool query_equals(query_t *this, query_t *other) +{ + return this->family == other->family && streq(this->name, other->name); +} + +/** + * Main function of resolver threads + */ +static void *resolve_hosts(private_host_resolver_t *this) +{ + struct addrinfo hints, *result; + query_t *query; + int error; + bool old, timed_out; + + while (TRUE) + { + this->mutex->lock(this->mutex); + thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); + while (this->queue->remove_first(this->queue, + (void**)&query) != SUCCESS) + { + old = thread_cancelability(TRUE); + timed_out = this->new_query->timed_wait(this->new_query, + this->mutex, NEW_QUERY_WAIT_TIMEOUT * 1000); + thread_cancelability(old); + if (this->disabled) + { + thread_cleanup_pop(TRUE); + return NULL; + } + else if (timed_out && (this->threads > this->min_threads)) + { /* terminate this thread by detaching it */ + thread_t *thread = thread_current(); + + this->threads--; + this->pool->remove(this->pool, thread, NULL); + thread_cleanup_pop(TRUE); + thread->detach(thread); + return NULL; + } + } + this->busy_threads++; + thread_cleanup_pop(TRUE); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = query->family; + hints.ai_socktype = SOCK_DGRAM; + + thread_cleanup_push((thread_cleanup_t)query_signal_and_destroy, query); + old = thread_cancelability(TRUE); + error = getaddrinfo(query->name, NULL, &hints, &result); + thread_cancelability(old); + thread_cleanup_pop(FALSE); + + this->mutex->lock(this->mutex); + this->busy_threads--; + if (error != 0) + { + DBG1(DBG_LIB, "resolving '%s' failed: %s", query->name, + gai_strerror(error)); + } + else + { /* result is a linked list, but we use only the first address */ + query->result = host_create_from_sockaddr(result->ai_addr); + freeaddrinfo(result); + } + this->queries->remove(this->queries, query); + query->done->broadcast(query->done); + this->mutex->unlock(this->mutex); + query_destroy(query); + } + return NULL; +} + +METHOD(host_resolver_t, resolve, host_t*, + private_host_resolver_t *this, char *name, int family) +{ + query_t *query, lookup = { + .name = name, + .family = family, + }; + host_t *result; + + if (family == AF_INET && strchr(name, ':')) + { /* do not try to convert v6 addresses for v4 family */ + return NULL; + } + this->mutex->lock(this->mutex); + if (this->disabled) + { + this->mutex->unlock(this->mutex); + return NULL; + } + query = this->queries->get(this->queries, &lookup); + if (!query) + { + INIT(query, + .name = strdup(name), + .family = family, + .done = condvar_create(CONDVAR_TYPE_DEFAULT), + .refcount = 1, + ); + this->queries->put(this->queries, query, query); + this->queue->insert_last(this->queue, query); + this->new_query->signal(this->new_query); + } + ref_get(&query->refcount); + if (this->busy_threads == this->threads && + this->threads < this->max_threads) + { + thread_t *thread; + + thread = thread_create((thread_main_t)resolve_hosts, this); + if (thread) + { + this->threads++; + this->pool->insert_last(this->pool, thread); + } + } + query->done->wait(query->done, this->mutex); + this->mutex->unlock(this->mutex); + + result = query->result ? query->result->clone(query->result) : NULL; + query_destroy(query); + return result; +} + +METHOD(host_resolver_t, flush, void, + private_host_resolver_t *this) +{ + enumerator_t *enumerator; + query_t *query; + + this->mutex->lock(this->mutex); + enumerator = this->queries->create_enumerator(this->queries); + while (enumerator->enumerate(enumerator, &query, NULL)) + { /* use the hashtable here as we also want to signal dequeued queries */ + this->queries->remove_at(this->queries, enumerator); + query->done->broadcast(query->done); + } + enumerator->destroy(enumerator); + this->queue->destroy_function(this->queue, (void*)query_destroy); + this->queue = linked_list_create(); + this->disabled = TRUE; + /* this will already terminate most idle threads */ + this->new_query->broadcast(this->new_query); + this->mutex->unlock(this->mutex); +} + +METHOD(host_resolver_t, destroy, void, + private_host_resolver_t *this) +{ + thread_t *thread; + + flush(this); + this->pool->invoke_offset(this->pool, offsetof(thread_t, cancel)); + while (this->pool->remove_first(this->pool, (void**)&thread) == SUCCESS) + { + thread->join(thread); + } + this->pool->destroy(this->pool); + this->queue->destroy(this->queue); + this->queries->destroy(this->queries); + this->new_query->destroy(this->new_query); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * Described in header + */ +host_resolver_t *host_resolver_create() +{ + private_host_resolver_t *this; + + INIT(this, + .public = { + .resolve = _resolve, + .flush = _flush, + .destroy = _destroy, + }, + .queries = hashtable_create((hashtable_hash_t)query_hash, + (hashtable_equals_t)query_equals, 8), + .queue = linked_list_create(), + .pool = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .new_query = condvar_create(CONDVAR_TYPE_DEFAULT), + ); + + this->min_threads = max(0, lib->settings->get_int(lib->settings, + "libstrongswan.host_resolver.min_threads", + MIN_THREADS_DEFAULT)); + this->max_threads = max(this->min_threads ?: 1, + lib->settings->get_int(lib->settings, + "libstrongswan.host_resolver.max_threads", + MAX_THREADS_DEFAULT)); + return &this->public; +} diff --git a/src/libstrongswan/networking/host_resolver.h b/src/libstrongswan/networking/host_resolver.h new file mode 100644 index 000000000..f944a9cdf --- /dev/null +++ b/src/libstrongswan/networking/host_resolver.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * 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 host_resolver host_resolver + * @{ @ingroup networking + */ + +#ifndef HOST_RESOLVER_H_ +#define HOST_RESOLVER_H_ + +#include "host.h" + +typedef struct host_resolver_t host_resolver_t; + +/** + * Resolve hosts by DNS name but do so in a separate thread (calling + * getaddrinfo(3) directly might block indefinitely, or at least a very long + * time if no DNS servers are reachable). + */ +struct host_resolver_t { + + /** + * Resolve host from the given DNS name. + * + * @param name name to lookup + * @param family requested address family + * @return resolved host or NULL if failed or canceled + */ + host_t *(*resolve)(host_resolver_t *this, char *name, int family); + + /** + * Flush the queue of queries. No new queries will be accepted afterwards. + */ + void (*flush)(host_resolver_t *this); + + /** + * Destroy a host_resolver_t. + */ + void (*destroy)(host_resolver_t *this); +}; + +/** + * Create a host_resolver_t instance. + */ +host_resolver_t *host_resolver_create(); + +#endif /** HOST_RESOLVER_H_ @}*/ diff --git a/src/libstrongswan/networking/packet.c b/src/libstrongswan/networking/packet.c new file mode 100644 index 000000000..a2c329d60 --- /dev/null +++ b/src/libstrongswan/networking/packet.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 "packet.h" + +typedef struct private_packet_t private_packet_t; + +/** + * Private data of an packet_t object. + */ +struct private_packet_t { + + /** + * Public part of a packet_t object. + */ + packet_t public; + + /** + * source address + */ + host_t *source; + + /** + * destination address + */ + host_t *destination; + + /** + * message data + */ + chunk_t data; + + /** + * actual chunk returned from get_data, adjusted when skip_bytes is called + */ + chunk_t adjusted_data; +}; + +METHOD(packet_t, set_source, void, + private_packet_t *this, host_t *source) +{ + DESTROY_IF(this->source); + this->source = source; +} + +METHOD(packet_t, set_destination, void, + private_packet_t *this, host_t *destination) +{ + DESTROY_IF(this->destination); + this->destination = destination; +} + +METHOD(packet_t, get_source, host_t*, + private_packet_t *this) +{ + return this->source; +} + +METHOD(packet_t, get_destination, host_t*, + private_packet_t *this) +{ + return this->destination; +} + +METHOD(packet_t, get_data, chunk_t, + private_packet_t *this) +{ + return this->adjusted_data; +} + +METHOD(packet_t, set_data, void, + private_packet_t *this, chunk_t data) +{ + free(this->data.ptr); + this->adjusted_data = this->data = data; +} + +METHOD(packet_t, skip_bytes, void, + private_packet_t *this, size_t bytes) +{ + this->adjusted_data = chunk_skip(this->adjusted_data, bytes); +} + +METHOD(packet_t, destroy, void, + private_packet_t *this) +{ + DESTROY_IF(this->source); + DESTROY_IF(this->destination); + free(this->data.ptr); + free(this); +} + +METHOD(packet_t, clone_, packet_t*, + private_packet_t *this) +{ + packet_t *other; + + other = packet_create(); + if (this->destination) + { + other->set_destination(other, + this->destination->clone(this->destination)); + } + if (this->source) + { + other->set_source(other, this->source->clone(this->source)); + } + if (this->data.ptr) + { + other->set_data(other, chunk_clone(this->adjusted_data)); + } + return other; +} + +/** + * Described in header. + */ +packet_t *packet_create_from_data(host_t *src, host_t *dst, chunk_t data) +{ + private_packet_t *this; + + INIT(this, + .public = { + .set_data = _set_data, + .get_data = _get_data, + .set_source = _set_source, + .get_source = _get_source, + .set_destination = _set_destination, + .get_destination = _get_destination, + .skip_bytes = _skip_bytes, + .clone = _clone_, + .destroy = _destroy, + }, + .source = src, + .destination = dst, + .adjusted_data = data, + .data = data, + ); + + return &this->public; +} + +/* + * Described in header. + */ +packet_t *packet_create() +{ + return packet_create_from_data(NULL, NULL, chunk_empty); +} diff --git a/src/libstrongswan/networking/packet.h b/src/libstrongswan/networking/packet.h new file mode 100644 index 000000000..6fb9cece2 --- /dev/null +++ b/src/libstrongswan/networking/packet.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 packet packet + * @{ @ingroup networking + */ + +#ifndef PACKET_H_ +#define PACKET_H_ + +typedef struct packet_t packet_t; + +#include <library.h> +#include <networking/host.h> + +/** + * Abstraction of an IP/UDP-Packet, contains data, sender and receiver. + */ +struct packet_t { + + /** + * Set the source address. + * + * @param source address to set as source (gets owned) + */ + void (*set_source)(packet_t *packet, host_t *source); + + /** + * Set the destination address. + * + * @param source address to set as destination (gets owned) + */ + void (*set_destination)(packet_t *packet, host_t *destination); + + /** + * Get the source address. + * + * @return source address (internal data) + */ + host_t *(*get_source)(packet_t *packet); + + /** + * Get the destination address. + * + * @return destination address (internal data) + */ + host_t *(*get_destination)(packet_t *packet); + + /** + * Get the data from the packet. + * + * @return chunk containing the data (internal data) + */ + chunk_t (*get_data)(packet_t *packet); + + /** + * Set the data in the packet. + * + * @param data chunk with data to set (gets owned) + */ + void (*set_data)(packet_t *packet, chunk_t data); + + /** + * Increase the offset where the actual packet data starts. + * + * The total offset applies to future calls of get_data() and clone(). + * + * @note The offset is reset to 0 when set_data() is called. + * + * @param bytes the number of additional bytes to skip + */ + void (*skip_bytes)(packet_t *packet, size_t bytes); + + /** + * Clones a packet_t object. + * + * @note Data is cloned without skipped bytes. + * + * @param clone clone of the packet + */ + packet_t* (*clone)(packet_t *packet); + + /** + * Destroy the packet, freeing contained data. + */ + void (*destroy)(packet_t *packet); +}; + +/** + * Create an empty packet + * + * @return packet_t object + */ +packet_t *packet_create(); + +/** + * Create a packet from the supplied data + * + * @param src source address (gets owned) + * @param dst destination address (gets owned) + * @param data packet data (gets owned) + * @return packet_t object + */ +packet_t *packet_create_from_data(host_t *src, host_t *dst, chunk_t data); + +#endif /** PACKET_H_ @}*/ diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c new file mode 100644 index 000000000..d07327e5c --- /dev/null +++ b/src/libstrongswan/networking/tun_device.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012 Martin Willi + * + * 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 <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <unistd.h> +#include <net/if.h> + +#ifdef __APPLE__ +#include <net/if_utun.h> +#include <netinet/in_var.h> +#include <sys/kern_control.h> +#elif defined(__linux__) +#include <linux/if_tun.h> +#else +#include <net/if_tun.h> +#endif + +#include "tun_device.h" + +#include <library.h> +#include <utils/debug.h> +#include <threading/thread.h> + +#define TUN_DEFAULT_MTU 1500 + +typedef struct private_tun_device_t private_tun_device_t; + +struct private_tun_device_t { + + /** + * Public interface + */ + tun_device_t public; + + /** + * The TUN device's file descriptor + */ + int tunfd; + + /** + * Name of the TUN device + */ + char if_name[IFNAMSIZ]; + + /** + * Socket used for ioctl() to set interface addr, ... + */ + int sock; + + /** + * The current MTU + */ + int mtu; +}; + +/** + * Set the sockaddr_t from the given netmask + */ +static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) +{ + int len, bytes, bits; + char *target; + + switch (family) + { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr; + addr->sin_family = AF_INET; + target = (char*)&addr->sin_addr; + len = 4; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr; + addr->sin6_family = AF_INET6; + target = (char*)&addr->sin6_addr; + len = 16; + break; + } + default: + return; + } + + bytes = (netmask + 7) / 8; + bits = (bytes * 8) - netmask; + + memset(target, 0xff, bytes); + memset(target + bytes, 0x00, len - bytes); + target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff; +} + +METHOD(tun_device_t, set_address, bool, + private_tun_device_t *this, host_t *addr, u_int8_t netmask) +{ + struct ifreq ifr; + int family; + + family = addr->get_family(addr); + if ((netmask > 32 && family == AF_INET) || netmask > 128) + { + DBG1(DBG_LIB, "failed to set address on %s: invalid netmask", + this->if_name); + return FALSE; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), sizeof(sockaddr_t)); + + if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set address on %s: %s", + this->if_name, strerror(errno)); + return FALSE; + } +#ifdef __APPLE__ + if (ioctl(this->sock, SIOCSIFDSTADDR, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set dest address on %s: %s", + this->if_name, strerror(errno)); + return FALSE; + } +#endif /* __APPLE__ */ + + set_netmask(&ifr, family, netmask); + + if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set netmask on %s: %s", + this->if_name, strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(tun_device_t, up, bool, + private_tun_device_t *this) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + + if (ioctl(this->sock, SIOCGIFFLAGS, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to get interface flags for %s: %s", this->if_name, + strerror(errno)); + return FALSE; + } + + ifr.ifr_flags |= IFF_RUNNING | IFF_UP; + + if (ioctl(this->sock, SIOCSIFFLAGS, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set interface flags on %s: %s", this->if_name, + strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(tun_device_t, set_mtu, bool, + private_tun_device_t *this, int mtu) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + ifr.ifr_mtu = mtu; + + if (ioctl(this->sock, SIOCSIFMTU, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set MTU on %s: %s", this->if_name, + strerror(errno)); + return FALSE; + } + this->mtu = mtu; + return TRUE; +} + +METHOD(tun_device_t, get_mtu, int, + private_tun_device_t *this) +{ + struct ifreq ifr; + + if (this->mtu > 0) + { + return this->mtu; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + this->mtu = TUN_DEFAULT_MTU; + + if (ioctl(this->sock, SIOCGIFMTU, &ifr) == 0) + { + this->mtu = ifr.ifr_mtu; + } + return this->mtu; +} + +METHOD(tun_device_t, get_name, char*, + private_tun_device_t *this) +{ + return this->if_name; +} + +METHOD(tun_device_t, write_packet, bool, + private_tun_device_t *this, chunk_t packet) +{ + ssize_t s; + + s = write(this->tunfd, packet.ptr, packet.len); + if (s < 0) + { + DBG1(DBG_LIB, "failed to write packet to TUN device %s: %s", + this->if_name, strerror(errno)); + return FALSE; + } + else if (s != packet.len) + { + return FALSE; + } + return TRUE; +} + +METHOD(tun_device_t, read_packet, bool, + private_tun_device_t *this, chunk_t *packet) +{ + ssize_t len; + fd_set set; + bool old; + + FD_ZERO(&set); + FD_SET(this->tunfd, &set); + + old = thread_cancelability(TRUE); + len = select(this->tunfd + 1, &set, NULL, NULL, NULL); + thread_cancelability(old); + + if (len < 0) + { + DBG1(DBG_LIB, "select on TUN device %s failed: %s", this->if_name, + strerror(errno)); + return FALSE; + } + /* FIXME: this is quite expensive for lots of small packets, copy from + * local buffer instead? */ + *packet = chunk_alloc(get_mtu(this)); + len = read(this->tunfd, packet->ptr, packet->len); + if (len < 0) + { + DBG1(DBG_LIB, "reading from TUN device %s failed: %s", this->if_name, + strerror(errno)); + chunk_free(packet); + return FALSE; + } + packet->len = len; + return TRUE; +} + +METHOD(tun_device_t, destroy, void, + private_tun_device_t *this) +{ + if (this->tunfd > 0) + { + close(this->tunfd); +#ifdef __FreeBSD__ + /* tun(4) says the following: "These network interfaces persist until + * the if_tun.ko module is unloaded, or until removed with the + * ifconfig(8) command." So simply closing the FD is not enough. */ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to destroy %s: %s", this->if_name, + strerror(errno)); + } +#endif /* __FreeBSD__ */ + } + if (this->sock > 0) + { + close(this->sock); + } + free(this); +} + +/** + * Initialize the tun device + */ +static bool init_tun(private_tun_device_t *this, const char *name_tmpl) +{ +#ifdef __APPLE__ + + struct ctl_info info; + struct sockaddr_ctl addr; + socklen_t size = IFNAMSIZ; + + memset(&info, 0, sizeof(info)); + memset(&addr, 0, sizeof(addr)); + + this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (this->tunfd < 0) + { + DBG1(DBG_LIB, "failed to open tundevice PF_SYSTEM socket: %s", + strerror(errno)); + return FALSE; + } + + /* get a control identifier for the utun kernel extension */ + strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME)); + if (ioctl(this->tunfd, CTLIOCGINFO, &info) < 0) + { + DBG1(DBG_LIB, "failed to ioctl tundevice: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + + addr.sc_id = info.ctl_id; + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + /* allocate identifier dynamically */ + addr.sc_unit = 0; + + if (connect(this->tunfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + DBG1(DBG_LIB, "failed to connect tundevice: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + if (getsockopt(this->tunfd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, + this->if_name, &size) < 0) + { + DBG1(DBG_LIB, "getting tundevice name failed: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + return TRUE; + +#elif defined(IFF_TUN) + + struct ifreq ifr; + + strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ); + this->if_name[IFNAMSIZ-1] = '\0'; + + this->tunfd = open("/dev/net/tun", O_RDWR); + if (this->tunfd < 0) + { + DBG1(DBG_LIB, "failed to open /dev/net/tun: %s", strerror(errno)); + return FALSE; + } + + memset(&ifr, 0, sizeof(ifr)); + + /* TUN device, no packet info */ + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + if (ioctl(this->tunfd, TUNSETIFF, (void*)&ifr) < 0) + { + DBG1(DBG_LIB, "failed to configure TUN device: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + strncpy(this->if_name, ifr.ifr_name, IFNAMSIZ); + return TRUE; + +#else /* !IFF_TUN */ + + /* this works on FreeBSD and might also work on Linux with older TUN + * driver versions (no IFF_TUN) */ + char devname[IFNAMSIZ]; + int i; + + if (name_tmpl) + { + DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported"); + } + + for (i = 0; i < 256; i++) + { + snprintf(devname, IFNAMSIZ, "/dev/tun%d", i); + this->tunfd = open(devname, O_RDWR); + if (this->tunfd > 0) + { /* for ioctl(2) calls only the interface name is used */ + snprintf(this->if_name, IFNAMSIZ, "tun%d", i); + break; + } + DBG1(DBG_LIB, "failed to open %s: %s", this->if_name, strerror(errno)); + } + return this->tunfd > 0; + +#endif /* !__APPLE__ */ +} + +/* + * Described in header + */ +tun_device_t *tun_device_create(const char *name_tmpl) +{ + private_tun_device_t *this; + + INIT(this, + .public = { + .read_packet = _read_packet, + .write_packet = _write_packet, + .get_mtu = _get_mtu, + .set_mtu = _set_mtu, + .get_name = _get_name, + .set_address = _set_address, + .up = _up, + .destroy = _destroy, + }, + .tunfd = -1, + .sock = -1, + ); + + if (!init_tun(this, name_tmpl)) + { + free(this); + return NULL; + } + DBG1(DBG_LIB, "created TUN device: %s", this->if_name); + + this->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (this->sock < 0) + { + DBG1(DBG_LIB, "failed to open socket to configure TUN device"); + destroy(this); + return NULL; + } + return &this->public; +} diff --git a/src/libstrongswan/networking/tun_device.h b/src/libstrongswan/networking/tun_device.h new file mode 100644 index 000000000..b22a5d170 --- /dev/null +++ b/src/libstrongswan/networking/tun_device.h @@ -0,0 +1,112 @@ +/* + * 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 tun_device tun_device + * @{ @ingroup networking + */ + +#ifndef TUN_DEVICE_H_ +#define TUN_DEVICE_H_ + +#include <library.h> +#include <networking/host.h> + +typedef struct tun_device_t tun_device_t; + +/** + * Class to create TUN devices + * + * Creating such a device requires the CAP_NET_ADMIN capability. + * + * @note The implementation is currently very Linux specific + */ +struct tun_device_t { + + /** + * Read a packet from the TUN device + * + * @note This call blocks until a packet is available. It is a thread + * cancellation point. + * + * @param packet the packet read from the device + * @return TRUE if successful + */ + bool (*read_packet)(tun_device_t *this, chunk_t *packet); + + /** + * Write a packet to the TUN device + * + * @param packet the packet to write to the TUN device + * @return TRUE if successful + */ + bool (*write_packet)(tun_device_t *this, chunk_t packet); + + /** + * Set the IP address of the device + * + * @param addr the desired interface address + * @param netmask the netmask to use + * @return TRUE if operation successful + */ + bool (*set_address)(tun_device_t *this, host_t *addr, u_int8_t netmask); + + /** + * Bring the TUN device up + * + * @return TRUE if operation successful + */ + bool (*up)(tun_device_t *this); + + /** + * Set the MTU for this TUN device + * + * @param mtu new MTU + * @return TRUE if operation successful + */ + bool (*set_mtu)(tun_device_t *this, int mtu); + + /** + * Get the current MTU for this TUN device + * + * @return current MTU + */ + int (*get_mtu)(tun_device_t *this); + + /** + * Get the interface name of this device + * + * @return interface name + */ + char *(*get_name)(tun_device_t *this); + + /** + * Destroy a tun_device_t + */ + void (*destroy)(tun_device_t *this); + +}; + +/** + * Create a TUN device using the given name template. + * + * @param name_tmpl name template, defaults to "tun%d" if not given + * @return TUN device + */ +tun_device_t *tun_device_create(const char *name_tmpl); + +#endif /** TUN_DEVICE_H_ @}*/ |