From: Guus Sliepen Date: Wed, 16 Sep 2009 17:55:47 +0000 (+0200) Subject: Merge branch 'master' into 1.1 X-Git-Tag: import-tinc-1.1~592 X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=075e6828a7533e7daa790225f17aa6bb39703278;hp=-c Merge branch 'master' into 1.1 Conflicts: have.h lib/dropin.c lib/fake-getaddrinfo.c lib/pidfile.c src/Makefile.am src/bsd/device.c src/conf.c src/connection.c src/connection.h src/graph.c src/mingw/device.c src/net.c src/net_setup.c src/node.c src/protocol_key.c src/protocol_misc.c src/tincd.c --- 075e6828a7533e7daa790225f17aa6bb39703278 diff --combined configure.in index 456b9b23,1ff36b3f..0561af8b --- a/configure.in +++ b/configure.in @@@ -2,23 -2,26 +2,23 @@@ dnl Process this file with autoconf to dnl $Id$ -AC_PREREQ(2.61) +AC_PREREQ(2.59) AC_INIT AC_CONFIG_SRCDIR([src/tincd.c]) -AM_INIT_AUTOMAKE(tinc, 1.0-cvs) +AC_GNU_SOURCE +AM_INIT_AUTOMAKE(tinc, 1.1-cvs) AC_CONFIG_HEADERS([config.h]) AM_MAINTAINER_MODE AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION(0.14.1) -# Enable GNU extensions. -# Define this here, not in acconfig's @TOP@ section, since definitions -# in the latter don't make it into the configure-time tests. -AC_GNU_SOURCE AC_DEFINE([__USE_BSD], 1, [Enable BSD extensions]) ALL_LINGUAS="nl" dnl Checks for programs. - AC_PROG_CC + AC_PROG_CC_C99 AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S @@@ -72,6 -75,20 +72,20 @@@ case $host_os i ;; esac + AC_ARG_ENABLE(tunemu, + AS_HELP_STRING([--enable-tunemu], [enable support for the tunemu driver]), + [ AC_DEFINE(ENABLE_TUNEMU, 1, [Support for tunemu]) + tunemu=true + ] + ) + + AC_ARG_WITH(windows2000, + AS_HELP_STRING([--with-windows2000], [compile with support for Windows 2000. This disables support for tunneling over existing IPv6 networks.]), + [AC_DEFINE(WITH_WINDOWS2000, 1, [Compile with support for Windows 2000])] + ) + + AM_CONDITIONAL(TUNEMU, test "$tunemu" = true) + AC_CACHE_SAVE if test -d /sw/include ; then @@@ -83,15 -100,12 +97,15 @@@ f dnl Checks for libraries. +AC_CHECK_LIB(event, event_init, + [], [AC_MSG_ERROR(libevent is required)]) + dnl Checks for header files. dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies. AC_HEADER_STDC AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/socket.h sys/time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h]) -AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h], +AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h], [], [], [#include "have.h"] ) AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h], @@@ -119,7 -133,7 +133,7 @@@ dnl Checks for library functions AC_FUNC_MEMCMP AC_FUNC_ALLOCA AC_TYPE_SIGNAL -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system unsetenv vsyslog writev], +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time unsetenv vsyslog writev], [], [], [#include "have.h"] ) AC_FUNC_MALLOC @@@ -134,7 -148,7 +148,7 @@@ AC_CHECK_FUNC(gethostbyname, [], AC_CHECK_LIB(nsl, gethostbyname) ]) - AC_CHECK_FUNCS([freeaddrinfo gai_strerror getaddrinfo getnameinfo inet_aton], + AC_CHECK_DECLS([freeaddrinfo, gai_strerror, getaddrinfo, getnameinfo], [], [], [#include "have.h"] ) @@@ -142,20 -156,10 +156,20 @@@ AC_CACHE_SAV dnl These are defined in files in m4/ -tinc_OPENSSL +AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], []) + tinc_ZLIB tinc_LZO +if test "$with_libgcrypt" = yes; then + AM_PATH_LIBGCRYPT([1.4.0], [], []) + ln -sf gcrypt/cipher.c gcrypt/cipher.h gcrypt/crypto.c gcrypt/crypto.h gcrypt/digest.c gcrypt/digest.h gcrypt/rsa.c gcrypt/rsa.h gcrypt/rsagen.c gcrypt/rsagen.h src/ +else + tinc_OPENSSL + ln -sf openssl/cipher.c openssl/cipher.h openssl/crypto.c openssl/crypto.h openssl/digest.c openssl/digest.h openssl/rsa.c openssl/rsa.h openssl/rsagen.c openssl/rsagen.h src/ +fi + + dnl Check if support for jumbograms is requested AC_ARG_ENABLE(jumbograms, AS_HELP_STRING([--enable-jumbograms], [enable support for jumbograms (packets up to 9000 bytes)]), diff --combined have.h index e0652a9d,7e2930dc..43a9147a --- a/have.h +++ b/have.h @@@ -31,6 -31,17 +31,17 @@@ #include #include + #ifdef HAVE_MINGW + #ifdef WITH_WINDOWS2000 + #define WINVER Windows2000 + #else + #define WINVER WindowsXP + #endif + #include + #include + #include + #endif + #ifdef HAVE_STDBOOL_H #include #endif @@@ -57,10 -68,6 +68,10 @@@ #include #endif +#ifdef HAVE_TIME_H +#include +#endif + #ifdef HAVE_SYS_TYPES_H #include #endif @@@ -164,13 -171,4 +175,4 @@@ #include #endif - #ifdef HAVE_MINGW - #include - #include - #endif - - #ifdef HAVE_LIBEVENT - #include - #endif - #endif /* __TINC_SYSTEM_H__ */ diff --combined lib/dropin.c index 773fe488,1c2592df..4ad32378 --- a/lib/dropin.c +++ b/lib/dropin.c @@@ -38,7 -38,8 +38,7 @@@ Unless the argument noclose is non-zero, daemon() will redirect standard input, standard output and standard error to /dev/null. */ -int daemon(int nochdir, int noclose) -{ +int daemon(int nochdir, int noclose) { #ifdef HAVE_FORK pid_t pid; int fd; @@@ -96,7 -97,8 +96,7 @@@ current directory name. If the environment variable PWD is set, and its value is correct, then that value will be returned. */ -char *get_current_dir_name(void) -{ +char *get_current_dir_name(void) { size_t size; char *buf; char *r; @@@ -124,25 -126,34 +124,34 @@@ #ifndef HAVE_ASPRINTF int asprintf(char **buf, const char *fmt, ...) { - int status; + int result; va_list ap; + va_start(ap, fmt); + result = vasprintf(buf, fmt, ap); + va_end(ap); + return result; + } + + int vasprintf(char **buf, const char *fmt, va_list ap) { + int status; + va_list aq; int len; len = 4096; *buf = xmalloc(len); - va_start(ap, fmt); - status = vsnprintf(*buf, len, fmt, ap); - va_end(ap); + va_copy(aq, ap); + status = vsnprintf(*buf, len, fmt, aq); + va_end(aq); if(status >= 0) *buf = xrealloc(*buf, status + 1); if(status > len - 1) { len = status; - va_start(ap, fmt); - status = vsnprintf(*buf, len, fmt, ap); - va_end(ap); + va_copy(aq, ap); + status = vsnprintf(*buf, len, fmt, aq); + va_end(aq); } return status; @@@ -156,15 -167,3 +165,3 @@@ int gettimeofday(struct timeval *tv, vo return 0; } #endif - - #ifndef HAVE_RANDOM - #include - - long int random(void) { - long int x; - - RAND_pseudo_bytes((unsigned char *)&x, sizeof(x)); - - return x; - } - #endif diff --combined lib/fake-getaddrinfo.c index 0c27025b,10672b7e..df3d3476 --- a/lib/fake-getaddrinfo.c +++ b/lib/fake-getaddrinfo.c @@@ -16,8 -16,9 +16,9 @@@ #include "fake-getaddrinfo.h" #include "xalloc.h" - #ifndef HAVE_GAI_STRERROR ++ + #if !HAVE_DECL_GAI_STRERROR -char *gai_strerror(int ecode) -{ +char *gai_strerror(int ecode) { switch (ecode) { case EAI_NODATA: return "No address associated with hostname"; @@@ -31,8 -32,9 +32,8 @@@ } #endif /* !HAVE_GAI_STRERROR */ - #ifndef HAVE_FREEADDRINFO + #if !HAVE_DECL_FREEADDRINFO -void freeaddrinfo(struct addrinfo *ai) -{ +void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *next; while(ai) { @@@ -43,8 -45,9 +44,8 @@@ } #endif /* !HAVE_FREEADDRINFO */ - #ifndef HAVE_GETADDRINFO + #if !HAVE_DECL_GETADDRINFO -static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr) -{ +static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr) { struct addrinfo *ai; ai = xmalloc_and_zero(sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); @@@ -59,7 -62,8 +60,7 @@@ return ai; } -int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) -{ +int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo *prev = NULL; struct hostent *hp; struct in_addr in = {0}; diff --combined lib/fake-getnameinfo.c index 05f0875a,80471730..1eba4925 --- a/lib/fake-getnameinfo.c +++ b/lib/fake-getnameinfo.c @@@ -14,9 -14,10 +14,9 @@@ #include "fake-getnameinfo.h" #include "fake-getaddrinfo.h" - #ifndef HAVE_GETNAMEINFO + #if !HAVE_DECL_GETNAMEINFO -int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) -{ +int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; struct hostent *hp; int len; diff --combined lib/utils.c index beabfeaf,02b7b344..1d46630b --- a/lib/utils.c +++ b/lib/utils.c @@@ -31,7 -31,8 +31,7 @@@ volatile int cp_index = 0 const char hexadecimals[] = "0123456789ABCDEF"; -int charhex2bin(char c) -{ +int charhex2bin(char c) { if(isdigit(c)) return c - '0'; else @@@ -39,13 -40,15 +39,13 @@@ } -void hex2bin(char *src, char *dst, int length) -{ +void hex2bin(char *src, char *dst, int length) { int i; for(i = 0; i < length; i++) dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]); } -void bin2hex(char *src, char *dst, int length) -{ +void bin2hex(char *src, char *dst, int length) { int i; for(i = length - 1; i >= 0; i--) { dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15]; @@@ -54,7 -57,8 +54,7 @@@ } #ifdef ENABLE_TRACING -void cp_trace() -{ +void cp_trace() { logger(LOG_DEBUG, "Checkpoint trace: %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d...", cp_file[(cp_index + 15) % 16], cp_line[(cp_index + 15) % 16], cp_file[(cp_index + 14) % 16], cp_line[(cp_index + 14) % 16], @@@ -96,3 -100,10 +96,10 @@@ const char *winerror(int err) } #endif + unsigned int bitfield_to_int(void *bitfield, size_t size) { + unsigned int value = 0; + if(size > sizeof value) + size = sizeof value; + memcpy(&value, bitfield, size); + return value; + } diff --combined src/Makefile.am index ed110e9f,501fdf66..db90897f --- a/src/Makefile.am +++ b/src/Makefile.am @@@ -1,36 -1,39 +1,44 @@@ ## Produce this file with automake to get Makefile.in # $Id: Makefile.am,v 1.4.4.33 2003/08/02 15:13:08 guus Exp $ -sbin_PROGRAMS = tincd +sbin_PROGRAMS = tincd tincctl EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h raw_socket/device.c uml_socket/device.c -tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ +tincd_SOURCES = cipher.c conf.c connection.c control.c crypto.c digest.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \ - protocol_key.c protocol_subnet.c route.c subnet.c tincd.c + protocol_key.c protocol_subnet.c route.c rsa.c subnet.c tincd.c + +tincctl_SOURCES = tincctl.c rsagen.c + if TUNEMU + tincd_SOURCES += bsd/tunemu.c + endif + nodist_tincd_SOURCES = device.c DEFAULT_INCLUDES = INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ - protocol.h route.h subnet.h bsd/tunemu.h +noinst_HEADERS = cipher.h conf.h connection.h control.h crypto.h device.h digest.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ - protocol.h route.h rsa.h rsagen.h subnet.h ++ protocol.h route.h rsa.h rsagen.h subnet.h bsd/tunemu.h -LIBS = @LIBS@ @LIBINTL@ +LIBS = @LIBS@ @LIBGCRYPT_LIBS@ @LIBINTL@ + if TUNEMU + LIBS += -lpcap + endif + tincd_LDADD = \ $(top_builddir)/lib/libvpn.a +tincctl_LDADD = \ + $(top_builddir)/lib/libvpn.a + localedir = $(datadir)/locale -AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" +AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\" dist-hook: rm -f `find . -type l` diff --combined src/bsd/device.c index 1066858b,fe85d10f..872ad92f --- a/src/bsd/device.c +++ b/src/bsd/device.c @@@ -29,12 -29,19 +29,19 @@@ #include "utils.h" #include "xalloc.h" + #ifdef HAVE_TUNEMU + #include "bsd/tunemu.h" + #endif + #define DEFAULT_DEVICE "/dev/tun0" typedef enum device_type { DEVICE_TYPE_TUN, DEVICE_TYPE_TUNIFHEAD, DEVICE_TYPE_TAP, + #ifdef HAVE_TUNEMU + DEVICE_TYPE_TUNEMU, + #endif } device_type_t; int device_fd = -1; @@@ -43,7 -50,9 +50,9 @@@ char *iface = NULL static char *device_info = NULL; static int device_total_in = 0; static int device_total_out = 0; - #if defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) + #if defined(TUNEMU) + static device_type_t device_type = DEVICE_TYPE_TUNEMU; + #elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD; #else static device_type_t device_type = DEVICE_TYPE_TUN; @@@ -60,14 -69,13 +69,13 @@@ bool setup_device(void) if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); - if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) { - logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno)); - return false; - } - if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) { if(!strcasecmp(type, "tun")) /* use default */; + #ifdef HAVE_TUNEMU + else if(!strcasecmp(type, "tunemu")) + device_type = DEVICE_TYPE_TUNEMU; + #endif else if(!strcasecmp(type, "tunnohead")) device_type = DEVICE_TYPE_TUN; else if(!strcasecmp(type, "tunifhead")) @@@ -83,6 -91,23 +91,23 @@@ device_type = DEVICE_TYPE_TAP; } + switch(device_type) { + #ifdef HAVE_TUNEMU + case DEVICE_TYPE_TUNEMU: { + char dynamic_name[256] = ""; + device_fd = tunemu_open(dynamic_name); + } + break; + #endif + default: + device_fd = open(device, O_RDWR | O_NONBLOCK); + } + + if(device_fd < 0) { + logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno)); + return false; + } + switch(device_type) { default: device_type = DEVICE_TYPE_TUN; @@@ -129,6 -154,11 +154,11 @@@ overwrite_mac = true; device_info = _("Generic BSD tap device"); break; + #ifdef HAVE_TUNEMU + case DEVICE_TYPE_TUNEMU: + device_info = _("BSD tunemu device"); + break; + #endif } logger(LOG_INFO, _("%s is a %s"), device, device_info); @@@ -139,20 -169,37 +169,37 @@@ void close_device(void) { cp(); - close(device_fd); + switch(device_type) { + #ifdef HAVE_TUNEMU + case DEVICE_TYPE_TUNEMU: + tunemu_close(device_fd); + break; + #endif + default: + close(device_fd); + } free(device); free(iface); } bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; cp(); switch(device_type) { case DEVICE_TYPE_TUN: - if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { + #ifdef HAVE_TUNEMU + case DEVICE_TYPE_TUNEMU: + if(device_type == DEVICE_TYPE_TUNEMU) - lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14); ++ inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14); + else + #else - lenin = read(device_fd, packet->data + 14, MTU - 14); ++ inlen = read(device_fd, packet->data + 14, MTU - 14); + #endif + - if(lenin <= 0) { ++ if(inlen <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -174,14 -221,14 +221,14 @@@ return false; } - packet->len = lenin + 14; + packet->len = inlen + 14; break; case DEVICE_TYPE_TUNIFHEAD: { u_int32_t type; - struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}}; + struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}}; - if((lenin = readv(device_fd, vector, 2)) <= 0) { + if((inlen = readv(device_fd, vector, 2)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -205,18 -252,18 +252,18 @@@ return false; } - packet->len = lenin + 10; + packet->len = inlen + 10; break; } case DEVICE_TYPE_TAP: - if((lenin = read(device_fd, packet->data, MTU)) <= 0) { + if((inlen = read(device_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin; + packet->len = inlen; break; default: @@@ -228,10 -275,12 +275,11 @@@ ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, device_info); + logger(LOG_INFO, "E:fd_read"); return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -248,7 -297,7 +296,7 @@@ case DEVICE_TYPE_TUNIFHEAD: { u_int32_t type; - struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, packet->len - 14}}; + struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}}; int af; af = (packet->data[12] << 8) + packet->data[13]; @@@ -283,6 -332,16 +331,16 @@@ } break; + #ifdef HAVE_TUNEMU + case DEVICE_TYPE_TUNEMU: + if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) { + logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, + device, strerror(errno)); + return false; + } + break; + #endif + default: return false; } @@@ -292,7 -351,8 +350,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/conf.c index 77942597,d8a8d832..827da681 --- a/src/conf.c +++ b/src/conf.c @@@ -24,21 -24,22 +24,21 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "logger.h" #include "netutl.h" /* for str2address */ #include "utils.h" /* for cp */ #include "xalloc.h" -avl_tree_t *config_tree; +splay_tree_t *config_tree; int pinginterval = 0; /* seconds between pings */ int pingtimeout = 0; /* seconds to wait for response */ char *confbase = NULL; /* directory in which all config files are */ char *netname = NULL; /* name of the vpn network */ -static int config_compare(const config_t *a, const config_t *b) -{ +static int config_compare(const config_t *a, const config_t *b) { int result; result = strcasecmp(a->variable, b->variable); @@@ -54,26 -55,30 +54,26 @@@ return strcmp(a->file, b->file); } -void init_configuration(avl_tree_t ** config_tree) -{ +void init_configuration(splay_tree_t ** config_tree) { cp(); - *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config); + *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config); } -void exit_configuration(avl_tree_t ** config_tree) -{ +void exit_configuration(splay_tree_t ** config_tree) { cp(); - avl_delete_tree(*config_tree); + splay_delete_tree(*config_tree); *config_tree = NULL; } -config_t *new_config(void) -{ +config_t *new_config(void) { cp(); return xmalloc_and_zero(sizeof(config_t)); } -void free_config(config_t *cfg) -{ +void free_config(config_t *cfg) { cp(); if(cfg->variable) @@@ -88,13 -93,15 +88,13 @@@ free(cfg); } -void config_add(avl_tree_t *config_tree, config_t *cfg) -{ +void config_add(splay_tree_t *config_tree, config_t *cfg) { cp(); - avl_insert(config_tree, cfg); + splay_insert(config_tree, cfg); } -config_t *lookup_config(avl_tree_t *config_tree, char *variable) -{ +config_t *lookup_config(splay_tree_t *config_tree, char *variable) { config_t cfg, *found; cp(); @@@ -103,7 -110,7 +103,7 @@@ cfg.file = ""; cfg.line = 0; - found = avl_search_closest_greater(config_tree, &cfg); + found = splay_search_closest_greater(config_tree, &cfg); if(!found) return NULL; @@@ -114,13 -121,14 +114,13 @@@ return found; } -config_t *lookup_config_next(avl_tree_t *config_tree, const config_t *cfg) -{ - avl_node_t *node; +config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) { + splay_node_t *node; config_t *found; cp(); - node = avl_search_node(config_tree, cfg); + node = splay_search_node(config_tree, cfg); if(node) { if(node->next) { @@@ -134,7 -142,8 +134,7 @@@ return NULL; } -bool get_config_bool(const config_t *cfg, bool *result) -{ +bool get_config_bool(const config_t *cfg, bool *result) { cp(); if(!cfg) @@@ -154,7 -163,8 +154,7 @@@ return false; } -bool get_config_int(const config_t *cfg, int *result) -{ +bool get_config_int(const config_t *cfg, int *result) { cp(); if(!cfg) @@@ -169,7 -179,8 +169,7 @@@ return false; } -bool get_config_string(const config_t *cfg, char **result) -{ +bool get_config_string(const config_t *cfg, char **result) { cp(); if(!cfg) @@@ -180,7 -191,8 +180,7 @@@ return true; } -bool get_config_address(const config_t *cfg, struct addrinfo **result) -{ +bool get_config_address(const config_t *cfg, struct addrinfo **result) { struct addrinfo *ai; cp(); @@@ -201,7 -213,8 +201,7 @@@ return false; } -bool get_config_subnet(const config_t *cfg, subnet_t ** result) -{ +bool get_config_subnet(const config_t *cfg, subnet_t ** result) { subnet_t subnet = {0}; cp(); @@@ -218,9 -231,9 +218,9 @@@ /* Teach newbies what subnets are... */ if(((subnet.type == SUBNET_IPV4) - && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t))) + && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address)) || ((subnet.type == SUBNET_IPV6) - && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) { + && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) { logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"), cfg->variable, cfg->file, cfg->line); return false; @@@ -241,7 -254,8 +241,7 @@@ given, and buf needs to be expanded, the var pointed to by buflen will be increased. */ -static char *readline(FILE * fp, char **buf, size_t *buflen) -{ +static char *readline(FILE * fp, char **buf, size_t *buflen) { char *newline = NULL; char *p; char *line; /* The array that contains everything that has been read so far */ @@@ -305,7 -319,8 +305,7 @@@ Parse a configuration file and put the results in the configuration tree starting at *base. */ -int read_config_file(avl_tree_t *config_tree, const char *fname) -{ +int read_config_file(splay_tree_t *config_tree, const char *fname) { int err = -2; /* Parse error */ FILE *fp; char *buffer, *line; @@@ -395,13 -410,14 +395,13 @@@ return err; } -bool read_server_config() -{ +bool read_server_config() { char *fname; int x; cp(); - asprintf(&fname, "%s/tinc.conf", confbase); + xasprintf(&fname, "%s/tinc.conf", confbase); x = read_config_file(config_tree, fname); if(x == -1) { /* System error: complain */ @@@ -412,3 -428,98 +412,3 @@@ return x == 0; } - -FILE *ask_and_open(const char *filename, const char *what) -{ - FILE *r; - char *directory; - char *fn; - - /* Check stdin and stdout */ - if(!isatty(0) || !isatty(1)) { - /* Argh, they are running us from a script or something. Write - the files to the current directory and let them burn in hell - for ever. */ - fn = xstrdup(filename); - } else { - /* Ask for a file and/or directory name. */ - fprintf(stdout, _("Please enter a file to save %s to [%s]: "), - what, filename); - fflush(stdout); - - fn = readline(stdin, NULL, NULL); - - if(!fn) { - fprintf(stderr, _("Error while reading stdin: %s\n"), - strerror(errno)); - return NULL; - } - - if(!strlen(fn)) - /* User just pressed enter. */ - fn = xstrdup(filename); - } - -#ifdef HAVE_MINGW - if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) { -#else - if(fn[0] != '/') { -#endif - /* The directory is a relative path or a filename. */ - char *p; - - directory = get_current_dir_name(); - xasprintf(&p, "%s/%s", directory, fn); - free(fn); - free(directory); - fn = p; - } - - umask(0077); /* Disallow everything for group and other */ - - /* Open it first to keep the inode busy */ - - r = fopen(fn, "r+") ?: fopen(fn, "w+"); - - if(!r) { - fprintf(stderr, _("Error opening file `%s': %s\n"), - fn, strerror(errno)); - free(fn); - return NULL; - } - - free(fn); - - return r; -} - -bool disable_old_keys(FILE *f) { - char buf[100]; - long pos; - bool disabled = false; - - rewind(f); - pos = ftell(f); - - while(fgets(buf, sizeof buf, f)) { - if(!strncmp(buf, "-----BEGIN RSA", 14)) { - buf[11] = 'O'; - buf[12] = 'L'; - buf[13] = 'D'; - fseek(f, pos, SEEK_SET); - fputs(buf, f); - disabled = true; - } - else if(!strncmp(buf, "-----END RSA", 12)) { - buf[ 9] = 'O'; - buf[10] = 'L'; - buf[11] = 'D'; - fseek(f, pos, SEEK_SET); - fputs(buf, f); - disabled = true; - } - pos = ftell(f); - } - - return disabled; -} diff --combined src/connection.c index 230ec4ed,283ebd71..4a2ba4c0 --- a/src/connection.c +++ b/src/connection.c @@@ -22,8 -22,7 +22,8 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "list.h" #include "logger.h" @@@ -33,49 -32,76 +33,49 @@@ #include "utils.h" #include "xalloc.h" -avl_tree_t *connection_tree; /* Meta connections */ +splay_tree_t *connection_tree; /* Meta connections */ connection_t *broadcast; -static int connection_compare(const connection_t *a, const connection_t *b) -{ +static int connection_compare(const connection_t *a, const connection_t *b) { - return (void *)a - (void *)b; + return a < b ? -1 : a == b ? 0 : 1; } -void init_connections(void) -{ +void init_connections(void) { cp(); - connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection); + connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection); broadcast = new_connection(); broadcast->name = xstrdup(_("everyone")); broadcast->hostname = xstrdup(_("BROADCAST")); } -void exit_connections(void) -{ +void exit_connections(void) { cp(); - avl_delete_tree(connection_tree); + splay_delete_tree(connection_tree); free_connection(broadcast); } -connection_t *new_connection(void) -{ - connection_t *c; - +connection_t *new_connection(void) { cp(); - c = xmalloc_and_zero(sizeof(connection_t)); - - if(!c) - return NULL; - - gettimeofday(&c->start, NULL); - - return c; + return xmalloc_and_zero(sizeof(connection_t)); } -void free_connection(connection_t *c) -{ +void free_connection(connection_t *c) { cp(); + if(!c) + return; + if(c->name) free(c->name); if(c->hostname) free(c->hostname); - if(c->inkey) - free(c->inkey); - - if(c->outkey) - free(c->outkey); - - if(c->inctx) { - EVP_CIPHER_CTX_cleanup(c->inctx); - free(c->inctx); - } - - if(c->outctx) { - EVP_CIPHER_CTX_cleanup(c->outctx); - free(c->outctx); - } - - if(c->mychallenge) - free(c->mychallenge); + cipher_close(&c->incipher); + cipher_close(&c->outcipher); if(c->hischallenge) free(c->hischallenge); @@@ -83,52 -109,56 +83,52 @@@ if(c->config_tree) exit_configuration(&c->config_tree); - if(c->outbuf) - free(c->outbuf); - - if(c->rsa_key) - RSA_free(c->rsa_key); + if(c->buffer) + bufferevent_free(c->buffer); + + if(event_initialized(&c->inevent)) + event_del(&c->inevent); free(c); } -void connection_add(connection_t *c) -{ +void connection_add(connection_t *c) { cp(); - avl_insert(connection_tree, c); + splay_insert(connection_tree, c); } -void connection_del(connection_t *c) -{ +void connection_del(connection_t *c) { cp(); - avl_delete(connection_tree, c); + splay_delete(connection_tree, c); } -void dump_connections(void) -{ - avl_node_t *node; +int dump_connections(struct evbuffer *out) { + splay_node_t *node; connection_t *c; cp(); - logger(LOG_DEBUG, _("Connections:")); - for(node = connection_tree->head; node; node = node->next) { c = node->data; - logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x outbuf %d/%d/%d"), - c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status), - c->outbufsize, c->outbufstart, c->outbuflen); + if(evbuffer_add_printf(out, + _(" %s at %s options %lx socket %d status %04x\n"), + c->name, c->hostname, c->options, c->socket, - c->status.value) == -1) ++ bitfield_to_int(&c->status, sizeof c->status)) == -1) + return errno; } - logger(LOG_DEBUG, _("End of connections.")); + return 0; } -bool read_connection_config(connection_t *c) -{ +bool read_connection_config(connection_t *c) { char *fname; int x; cp(); - asprintf(&fname, "%s/hosts/%s", confbase, c->name); + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); x = read_config_file(c->config_tree, fname); free(fname); diff --combined src/connection.h index 08778bf3,8948d4fa..e44af0b0 --- a/src/connection.h +++ b/src/connection.h @@@ -23,29 -23,26 +23,26 @@@ #ifndef __TINC_CONNECTION_H__ #define __TINC_CONNECTION_H__ -#include -#include - -#include "avl_tree.h" +#include "cipher.h" +#include "digest.h" +#include "rsa.h" +#include "splay_tree.h" #define OPTION_INDIRECT 0x0001 #define OPTION_TCPONLY 0x0002 #define OPTION_PMTU_DISCOVERY 0x0004 - typedef union connection_status_t { - struct { + typedef struct connection_status_t { - int pinged:1; /* sent ping */ - int active:1; /* 1 if active.. */ - int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */ - int termreq:1; /* the termination of this connection was requested */ - int remove:1; /* Set to 1 if you want this connection removed */ - int timeout:1; /* 1 if gotten timeout */ - int encryptout:1; /* 1 if we can encrypt outgoing traffic */ - int decryptin:1; /* 1 if we have to decrypt incoming traffic */ - int mst:1; /* 1 if this connection is part of a minimum spanning tree */ - int unused:23; + int pinged:1; /* sent ping */ + int active:1; /* 1 if active.. */ + int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */ + int termreq:1; /* the termination of this connection was requested */ + int remove_unused:1; /* Set to 1 if you want this connection removed */ + int timeout_unused:1; /* 1 if gotten timeout */ + int encryptout:1; /* 1 if we can encrypt outgoing traffic */ + int decryptin:1; /* 1 if we have to decrypt incoming traffic */ + int mst:1; /* 1 if this connection is part of a minimum spanning tree */ + int unused:23; - }; - uint32_t value; } connection_status_t; #include "edge.h" @@@ -70,30 -67,42 +67,30 @@@ typedef struct connection_t struct node_t *node; /* node associated with the other end */ struct edge_t *edge; /* edge associated with this connection */ - RSA *rsa_key; /* his public/private key */ - const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */ - const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */ - EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */ - EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */ - char *inkey; /* His symmetric meta key + iv */ - char *outkey; /* Our symmetric meta key + iv */ - int inkeylength; /* Length of his key + iv */ - int outkeylength; /* Length of our key + iv */ - const EVP_MD *indigest; - const EVP_MD *outdigest; + rsa_t rsa; /* his public/private key */ + cipher_t incipher; /* Cipher he will use to send data to us */ + cipher_t outcipher; /* Cipher we will use to send data to him */ + digest_t indigest; + digest_t outdigest; + int inmaclength; int outmaclength; int incompression; int outcompression; - char *mychallenge; /* challenge we received from him */ - char *hischallenge; /* challenge we sent to him */ - char buffer[MAXBUFSIZE]; /* metadata input buffer */ - int buflen; /* bytes read into buffer */ - int reqlen; /* length of incoming request */ + char *hischallenge; /* The challenge we sent to him */ + + struct bufferevent *buffer; /* buffer events on this metadata connection */ + struct event inevent; /* input event on this metadata connection */ int tcplen; /* length of incoming TCPpacket */ int allow_request; /* defined if there's only one request possible */ - char *outbuf; /* metadata output buffer */ - int outbufstart; /* index of first meaningful byte in output buffer */ - int outbuflen; /* number of meaningful bytes in output buffer */ - int outbufsize; /* number of bytes allocated to output buffer */ - time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */ - time_t last_flushed_time; /* last time buffer was empty. Only meaningful if outbuflen > 0 */ - avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */ + splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */ } connection_t; -extern avl_tree_t *connection_tree; +extern splay_tree_t *connection_tree; extern connection_t *broadcast; extern void init_connections(void); @@@ -102,7 -111,7 +99,7 @@@ extern connection_t *new_connection(voi extern void free_connection(connection_t *); extern void connection_add(connection_t *); extern void connection_del(connection_t *); -extern void dump_connections(void); +extern int dump_connections(struct evbuffer *); extern bool read_connection_config(connection_t *); #endif /* __TINC_CONNECTION_H__ */ diff --combined src/graph.c index 4e060b7f,a267f052..f2e546ee --- a/src/graph.c +++ b/src/graph.c @@@ -46,7 -46,7 +46,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "config.h" #include "connection.h" #include "device.h" @@@ -57,17 -57,24 +57,18 @@@ #include "process.h" #include "subnet.h" #include "utils.h" + #include "xalloc.h" -static bool graph_changed = true; - /* Implementation of Kruskal's algorithm. - Running time: O(EN) + Running time: O(E) Please note that sorting on weight is already done by add_edge(). */ -void mst_kruskal(void) -{ - avl_node_t *node, *next; +void mst_kruskal(void) { + splay_node_t *node, *next; edge_t *e; node_t *n; connection_t *c; - int nodes = 0; - int safe_edges = 0; - bool skipped; cp(); @@@ -78,6 -85,11 +79,6 @@@ c->status.mst = false; } - /* Do we have something to do at all? */ - - if(!edge_weight_tree->head) - return; - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:"); /* Clear visited status on nodes */ @@@ -85,16 -97,29 +86,16 @@@ for(node = node_tree->head; node; node = node->next) { n = node->data; n->status.visited = false; - nodes++; - } - - /* Starting point */ - - for(node = edge_weight_tree->head; node; node = node->next) { - e = node->data; - if(e->from->status.reachable) { - e->from->status.visited = true; - break; - } } /* Add safe edges */ - for(skipped = false, node = edge_weight_tree->head; node; node = next) { + for(node = edge_weight_tree->head; node; node = next) { next = node->next; e = node->data; - if(!e->reverse || e->from->status.visited == e->to->status.visited) { - skipped = true; + if(!e->reverse || (e->from->status.visited && e->to->status.visited)) continue; - } e->from->status.visited = true; e->to->status.visited = true; @@@ -105,148 -130,38 +106,148 @@@ if(e->reverse->connection) e->reverse->connection->status.mst = true; - safe_edges++; - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name, e->to->name, e->weight); + } +} - if(skipped) { - skipped = false; - next = edge_weight_tree->head; - continue; +/* Implementation of Dijkstra's algorithm. + Running time: O(N^2) +*/ + +void sssp_dijkstra(void) { + splay_node_t *node, *to; + edge_t *e; + node_t *n, *m; + list_t *todo_list; + list_node_t *lnode, *nnode; + bool indirect; + + cp(); + + todo_list = list_alloc(NULL); + + ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Dijkstra's algorithm:"); + + /* Clear visited status on nodes */ + + for(node = node_tree->head; node; node = node->next) { + n = node->data; + n->status.visited = false; + n->status.indirect = true; + n->distance = -1; + } + + /* Begin with myself */ + + myself->status.indirect = false; + myself->nexthop = myself; + myself->via = myself; + myself->distance = 0; + list_insert_head(todo_list, myself); + + /* Loop while todo_list is filled */ + + while(todo_list->head) { + n = NULL; + nnode = NULL; + + /* Select node from todo_list with smallest distance */ + + for(lnode = todo_list->head; lnode; lnode = lnode->next) { + m = lnode->data; + if(!n || m->status.indirect < n->status.indirect || m->distance < n->distance) { + n = m; + nnode = lnode; + } + } + + /* Mark this node as visited and remove it from the todo_list */ + + n->status.visited = true; + list_unlink_node(todo_list, nnode); + + /* Update distance of neighbours and add them to the todo_list */ + + for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */ + e = to->data; + + if(e->to->status.visited || !e->reverse) + continue; + + /* Situation: + + / + / + ----->(n)---e-->(e->to) + \ + \ + + Where e is an edge, (n) and (e->to) are nodes. + n->address is set to the e->address of the edge left of n to n. + We are currently examining the edge e right of n from n: + + - If e->reverse->address != n->address, then e->to is probably + not reachable for the nodes left of n. We do as if the indirectdata + flag is set on edge e. + - If edge e provides for better reachability of e->to, update e->to. + */ + + if(e->to->distance < 0) + list_insert_tail(todo_list, e->to); + + indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address)); + + if(e->to->distance >= 0 && (!e->to->status.indirect || indirect) && e->to->distance <= n->distance + e->weight) + continue; + + e->to->distance = n->distance + e->weight; + e->to->status.indirect = indirect; + e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop; + e->to->via = indirect ? n->via : e->to; + e->to->options = e->options; + + if(sockaddrcmp(&e->to->address, &e->address)) { + node = splay_unlink(node_udp_tree, e->to); + sockaddrfree(&e->to->address); + sockaddrcpy(&e->to->address, &e->address); + + if(e->to->hostname) + free(e->to->hostname); + + e->to->hostname = sockaddr2hostname(&e->to->address); + + if(node) + splay_insert_node(node_udp_tree, node); + + if(e->to->options & OPTION_PMTU_DISCOVERY) { + e->to->mtuprobes = 0; + e->to->minmtu = 0; + e->to->maxmtu = MTU; + if(e->to->status.validkey) + send_mtu_probe(e->to); + } + } + + ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Updating edge %s - %s weight %d distance %d", e->from->name, + e->to->name, e->weight, e->to->distance); } } - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, - safe_edges); + list_free(todo_list); } /* Implementation of a simple breadth-first search algorithm. Running time: O(E) */ -void sssp_bfs(void) -{ - avl_node_t *node, *next, *to; +void sssp_bfs(void) { + splay_node_t *node, *to; edge_t *e; node_t *n; list_t *todo_list; list_node_t *from, *todonext; bool indirect; - char *name; - char *address, *port; - char *envp[7]; - int i; cp(); @@@ -323,15 -238,6 +324,15 @@@ } list_free(todo_list); +} + +void check_reachability() { + splay_node_t *node, *next; + node_t *n; + char *name; + char *address, *port; + char *envp[7]; + int i; /* Check reachability status. */ @@@ -359,18 -265,23 +360,20 @@@ n->minmtu = 0; n->mtuprobes = 0; - asprintf(&envp[0], "NETNAME=%s", netname ? : ""); - asprintf(&envp[1], "DEVICE=%s", device ? : ""); - asprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - asprintf(&envp[3], "NODE=%s", n->name); - if(n->mtuevent) { - event_del(n->mtuevent); - n->mtuevent = NULL; - } ++ event_del(&n->mtuevent); + + xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); + xasprintf(&envp[1], "DEVICE=%s", device ? : ""); + xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); + xasprintf(&envp[3], "NODE=%s", n->name); sockaddr2str(&n->address, &address, &port); - asprintf(&envp[4], "REMOTEADDRESS=%s", address); - asprintf(&envp[5], "REMOTEPORT=%s", port); + xasprintf(&envp[4], "REMOTEADDRESS=%s", address); + xasprintf(&envp[5], "REMOTEPORT=%s", port); envp[6] = NULL; execute_script(n->status.reachable ? "host-up" : "host-down", envp); - asprintf(&name, + xasprintf(&name, n->status.reachable ? "hosts/%s-up" : "hosts/%s-down", n->name); execute_script(name, envp); @@@ -387,45 -298,74 +390,45 @@@ } } -void graph(void) -{ - subnet_cache_flush(); - sssp_bfs(); - mst_kruskal(); - graph_changed = true; -} - - - /* Dump nodes and edges to a graphviz file. The file can be converted to an image with dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true */ -void dump_graph(void) -{ - avl_node_t *node; +int dump_graph(struct evbuffer *out) { + splay_node_t *node; node_t *n; edge_t *e; - char *filename = NULL, *tmpname = NULL; - FILE *file; - - if(!graph_changed || !get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename)) - return; - - graph_changed = false; - - ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph"); - - if(filename[0] == '|') { - file = popen(filename + 1, "w"); - } else { - xasprintf(&tmpname, "%s.new", filename); - file = fopen(tmpname, "w"); - } - - if(!file) { - logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno)); - free(tmpname); - return; - } - fprintf(file, "digraph {\n"); + if(evbuffer_add_printf(out, "digraph {\n") == -1) + return errno; /* dump all nodes first */ for(node = node_tree->head; node; node = node->next) { n = node->data; - fprintf(file, " %s [label = \"%s\"];\n", n->name, n->name); + if(evbuffer_add_printf(out, " %s [label = \"%s\"];\n", + n->name, n->name) == -1) + return errno; } /* now dump all edges */ for(node = edge_weight_tree->head; node; node = node->next) { e = node->data; - fprintf(file, " %s -> %s;\n", e->from->name, e->to->name); + if(evbuffer_add_printf(out, " %s -> %s;\n", + e->from->name, e->to->name) == -1) + return errno; } - fprintf(file, "}\n"); - - if(filename[0] == '|') { - pclose(file); - } else { - fclose(file); -#ifdef HAVE_MINGW - unlink(filename); -#endif - rename(tmpname, filename); - free(tmpname); - } + if(evbuffer_add_printf(out, "}\n") == -1) + return errno; + + return 0; +} + +void graph(void) { + subnet_cache_flush(); + sssp_dijkstra(); + check_reachability(); + mst_kruskal(); } diff --combined src/meta.c index 82dde3a5,b59f15b0..6054427c --- a/src/meta.c +++ b/src/meta.c @@@ -22,8 -22,10 +22,8 @@@ #include "system.h" -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "connection.h" #include "logger.h" #include "meta.h" @@@ -32,37 -34,96 +32,42 @@@ #include "utils.h" #include "xalloc.h" -bool send_meta(connection_t *c, const char *buffer, int length) -{ - int outlen; - int result; - +bool send_meta(connection_t *c, const char *buffer, int length) { cp(); + if(!c) { + logger(LOG_ERR, _("send_meta() called with NULL pointer!")); + abort(); + } + ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length, c->name, c->hostname); - if(!c->outbuflen) - c->last_flushed_time = now; - - /* Find room in connection's buffer */ - if(length + c->outbuflen > c->outbufsize) { - c->outbufsize = length + c->outbuflen; - c->outbuf = xrealloc(c->outbuf, c->outbufsize); - } - - if(length + c->outbuflen + c->outbufstart > c->outbufsize) { - memmove(c->outbuf, c->outbuf + c->outbufstart, c->outbuflen); - c->outbufstart = 0; - } - /* Add our data to buffer */ if(c->status.encryptout) { - result = EVP_EncryptUpdate(c->outctx, (unsigned char *)c->outbuf + c->outbufstart + c->outbuflen, - &outlen, (unsigned char *)buffer, length); - if(!result || outlen < length) { - logger(LOG_ERR, _("Error while encrypting metadata to %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } else if(outlen > length) { - logger(LOG_EMERG, _("Encrypted data too long! Heap corrupted!")); - abort(); - } - c->outbuflen += outlen; - } else { - memcpy(c->outbuf + c->outbufstart + c->outbuflen, buffer, length); - c->outbuflen += length; - } - - return true; -} - -bool flush_meta(connection_t *c) -{ - int result; - - ifdebug(META) logger(LOG_DEBUG, _("Flushing %d bytes to %s (%s)"), - c->outbuflen, c->name, c->hostname); - - while(c->outbuflen) { - result = send(c->socket, c->outbuf + c->outbufstart, c->outbuflen, 0); - if(result <= 0) { - if(!errno || errno == EPIPE) { - ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection closed by %s (%s)"), - c->name, c->hostname); - } else if(errno == EINTR) { - continue; -#ifdef EWOULDBLOCK - } else if(errno == EWOULDBLOCK) { - ifdebug(CONNECTIONS) logger(LOG_DEBUG, _("Flushing %d bytes to %s (%s) would block"), - c->outbuflen, c->name, c->hostname); - return true; -#endif - } else { - logger(LOG_ERR, _("Flushing meta data to %s (%s) failed: %s"), c->name, - c->hostname, strerror(errno)); - } + char outbuf[length]; + size_t outlen = length; + if(!cipher_encrypt(&c->outcipher, buffer, length, outbuf, &outlen, false) || outlen != length) { + logger(LOG_ERR, _("Error while encrypting metadata to %s (%s)"), + c->name, c->hostname); return false; } - - c->outbufstart += result; - c->outbuflen -= result; + + ifdebug(META) logger(LOG_DEBUG, _("Encrypted write %p %p %p %d"), c, c->buffer, outbuf, length); + bufferevent_write(c->buffer, (void *)outbuf, length); + ifdebug(META) logger(LOG_DEBUG, _("Done.")); + } else { + ifdebug(META) logger(LOG_DEBUG, _("Unencrypted write %p %p %p %d"), c, c->buffer, buffer, length); + bufferevent_write(c->buffer, (void *)buffer, length); + ifdebug(META) logger(LOG_DEBUG, _("Done.")); } - c->outbufstart = 0; /* avoid unnecessary memmoves */ return true; } -void broadcast_meta(connection_t *from, const char *buffer, int length) -{ - avl_node_t *node; +void broadcast_meta(connection_t *from, const char *buffer, int length) { + splay_node_t *node; connection_t *c; cp(); @@@ -75,10 -136,12 +80,10 @@@ } } -bool receive_meta(connection_t *c) -{ - int oldlen, i, result; - int lenin, lenout, reqlen; - bool decrypted = false; +bool receive_meta(connection_t *c) { + size_t inlen; char inbuf[MAXBUFSIZE]; + char *bufp = inbuf, *endp; cp(); @@@ -91,70 -154,89 +96,70 @@@ - If not, keep stuff in buffer and exit. */ - lenin = recv(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen, 0); - - if(lenin <= 0) { - if(!lenin || !errno) { - ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection closed by %s (%s)"), - c->name, c->hostname); - } else if(errno == EINTR) - return true; - else - logger(LOG_ERR, _("Metadata socket read error for %s (%s): %s"), - c->name, c->hostname, strerror(errno)); + inlen = recv(c->socket, inbuf, sizeof inbuf, 0); + if(inlen <= 0) { + logger(LOG_ERR, _("Receive callback called for %s (%s) but no data to receive: %s"), c->name, c->hostname, strerror(errno)); return false; } - oldlen = c->buflen; - c->buflen += lenin; + do { + if(!c->status.decryptin) { + endp = memchr(bufp, '\n', inlen); + if(endp) + endp++; + else + endp = bufp + inlen; - while(lenin > 0) { - /* Decrypt */ + evbuffer_add(c->buffer->input, bufp, endp - bufp); - if(c->status.decryptin && !decrypted) { - result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin); - if(!result || lenout != lenin) { - logger(LOG_ERR, _("Error while decrypting metadata from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); + inlen -= endp - bufp; + bufp = endp; + } else { + size_t outlen = inlen; + ifdebug(META) logger(LOG_DEBUG, _("Received encrypted %zu bytes"), inlen); + evbuffer_expand(c->buffer->input, c->buffer->input->off + inlen); + + if(!cipher_decrypt(&c->incipher, bufp, inlen, c->buffer->input->buffer + c->buffer->input->off, &outlen, false) || inlen != outlen) { + logger(LOG_ERR, _("Error while decrypting metadata from %s (%s)"), + c->name, c->hostname); return false; } - memcpy(c->buffer + oldlen, inbuf, lenin); - decrypted = true; + c->buffer->input->off += inlen; + + inlen = 0; } - /* Are we receiving a TCPpacket? */ + while(c->buffer->input->off) { + /* Are we receiving a TCPpacket? */ + + if(c->tcplen) { + if(c->tcplen <= c->buffer->input->off) { + receive_tcppacket(c, (char *)c->buffer->input->buffer, c->tcplen); + evbuffer_drain(c->buffer->input, c->tcplen); + c->tcplen = 0; + continue; + } else { + break; + } + } - if(c->tcplen) { - if(c->tcplen <= c->buflen) { - receive_tcppacket(c, c->buffer, c->tcplen); + /* Otherwise we are waiting for a request */ - c->buflen -= c->tcplen; - lenin -= c->tcplen - oldlen; - memmove(c->buffer, c->buffer + c->tcplen, c->buflen); - oldlen = 0; - c->tcplen = 0; + char *request = evbuffer_readline(c->buffer->input); + if(request) { + bool result = receive_request(c, request); + free(request); + if(!result) + return false; continue; } else { break; } } + } while(inlen); - /* Otherwise we are waiting for a request */ - - reqlen = 0; - - for(i = oldlen; i < c->buflen; i++) { - if(c->buffer[i] == '\n') { - c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */ - reqlen = i + 1; - break; - } - } - - if(reqlen) { - c->reqlen = reqlen; - if(!receive_request(c)) - return false; - - c->buflen -= reqlen; - lenin -= reqlen - oldlen; - memmove(c->buffer, c->buffer + reqlen, c->buflen); - oldlen = 0; - continue; - } else { - break; - } - } - - if(c->buflen >= MAXBUFSIZE) { - logger(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"), - c->name, c->hostname); - return false; - } - - c->last_ping_time = now; + c->last_ping_time = time(NULL); return true; } diff --combined src/mingw/device.c index 915256f5,fa134332..2334af39 --- a/src/mingw/device.c +++ b/src/mingw/device.c @@@ -34,7 -34,7 +34,7 @@@ #include "mingw/common.h" - int device_fd = 0; + int device_fd = -1; static HANDLE device_handle = INVALID_HANDLE_VALUE; char *device = NULL; char *iface = NULL; @@@ -45,63 -45,24 +45,24 @@@ static int device_total_out = 0 extern char *myport; - static struct packetbuf { - uint8_t data[MTU]; - length_t len; - } *bufs; - - static int nbufs = 64; - static DWORD WINAPI tapreader(void *bla) { - int sock, err, status; - struct addrinfo *ai; - struct addrinfo hint = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - .ai_flags = 0, - }; - unsigned char bufno = 0; + int status; long len; OVERLAPPED overlapped; - - /* Open a socket to the parent process */ - - err = getaddrinfo(NULL, myport, &hint, &ai); - - if(err || !ai) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(errno)); - return -1; - } - - sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP); - - if(sock < 0) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "socket", strerror(errno)); - freeaddrinfo(ai); - return -1; - } - - if(connect(sock, ai->ai_addr, ai->ai_addrlen)) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "connect", strerror(errno)); - freeaddrinfo(ai); - return -1; - } - - freeaddrinfo(ai); + vpn_packet_t packet; logger(LOG_DEBUG, _("Tap reader running")); /* Read from tap device and send to parent */ overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - + for(;;) { overlapped.Offset = 0; overlapped.OffsetHigh = 0; ResetEvent(overlapped.hEvent); - status = ReadFile(device_handle, bufs[bufno].data, MTU, &len, &overlapped); + status = ReadFile(device_handle, packet.data, MTU, &len, &overlapped); if(!status) { if(GetLastError() == ERROR_IO_PENDING) { @@@ -115,15 -76,16 +76,15 @@@ } } - bufs[bufno].len = len; - if(send(sock, &bufno, 1, 0) <= 0) - return -1; - if(++bufno >= nbufs) - bufno = 0; + EnterCriticalSection(&mutex); + packet.len = len; + packet.priority = 0; + route(myself, &packet); + LeaveCriticalSection(&mutex); } } -bool setup_device(void) -{ +bool setup_device(void) { HKEY key, key2; int i; @@@ -160,18 -122,18 +121,18 @@@ } for (i = 0; ; i++) { - len = sizeof(adapterid); + len = sizeof adapterid; if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) break; /* Find out more about this adapter */ - snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); + snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) continue; - len = sizeof(adaptername); + len = sizeof adaptername; err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len); RegCloseKey(key2); @@@ -195,7 -157,7 +156,7 @@@ continue; } - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if(device_handle != INVALID_HANDLE_VALUE) { found = true; @@@ -219,7 -181,7 +180,7 @@@ /* Try to open the corresponding tap device */ if(device_handle == INVALID_HANDLE_VALUE) { - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device); device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); } @@@ -230,7 -192,7 +191,7 @@@ /* Get MAC address from tap device */ - if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) { + if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) { logger(LOG_ERR, _("Could not get MAC address from Windows tap device %s (%s): %s"), device, iface, winerror(GetLastError())); return false; } @@@ -239,44 -201,6 +200,6 @@@ overwrite_mac = 1; } - /* Set up ringbuffer */ - - get_config_int(lookup_config(config_tree, "RingBufferSize"), &nbufs); - if(nbufs <= 1) - nbufs = 1; - else if(nbufs > 256) - nbufs = 256; - - bufs = xmalloc_and_zero(nbufs * sizeof *bufs); - - /* Create a listening socket */ - - err = getaddrinfo(NULL, myport, &hint, &ai); - - if(err || !ai) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(errno)); - return false; - } - - sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP); - - if(sock < 0) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "socket", strerror(errno)); - return false; - } - - if(bind(sock, ai->ai_addr, ai->ai_addrlen)) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "bind", strerror(errno)); - return false; - } - - freeaddrinfo(ai); - - if(listen(sock, 1)) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno)); - return false; - } - /* Start the tap reader */ thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL); @@@ -286,19 -210,10 +209,10 @@@ return false; } - /* Wait for the tap reader to connect back to us */ - - if((device_fd = accept(sock, NULL, 0)) == -1) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "accept", strerror(errno)); - return false; - } - - closesocket(sock); - /* Set media status for newer TAP-Win32 devices */ status = true; - DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL); + DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL); device_info = _("Windows tap device"); @@@ -307,7 -222,8 +221,7 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); CloseHandle(device_handle); @@@ -316,30 -232,14 +230,12 @@@ free(iface); } -bool read_packet(vpn_packet_t *packet) -{ +bool read_packet(vpn_packet_t *packet) { - unsigned char bufno; - - cp(); - - if((recv(device_fd, &bufno, 1, 0)) <= 0) { - logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, - device, strerror(errno)); - return false; - } - - packet->len = bufs[bufno].len; - memcpy(packet->data, bufs[bufno].data, bufs[bufno].len); - - device_total_in += packet->len; - - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, - device_info); - - return true; + return false; } -bool write_packet(vpn_packet_t *packet) -{ - long lenout; +bool write_packet(vpn_packet_t *packet) { + long outlen; OVERLAPPED overlapped = {0}; cp(); @@@ -347,7 -247,7 +243,7 @@@ ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), packet->len, device_info); - if(!WriteFile(device_handle, packet->data, packet->len, &lenout, &overlapped)) { + if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) { logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, winerror(GetLastError())); return false; } @@@ -357,7 -257,8 +253,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/net.h index a28f4785,ff32b73c..57e9d8db --- a/src/net.h +++ b/src/net.h @@@ -23,9 -23,9 +23,9 @@@ #ifndef __TINC_NET_H__ #define __TINC_NET_H__ -#include - #include "ipv6.h" +#include "cipher.h" +#include "digest.h" #ifdef ENABLE_JUMBOGRAMS #define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */ @@@ -33,10 -33,10 +33,10 @@@ #define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */ #endif -#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */ +#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */ #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */ -#define MAXSOCKETS 128 /* Overkill... */ +#define MAXSOCKETS 8 /* Probably overkill... */ typedef struct mac_t { uint8_t x[6]; @@@ -86,8 -86,6 +86,8 @@@ typedef struct vpn_packet_t } vpn_packet_t; typedef struct listen_socket_t { + struct event ev_tcp; + struct event ev_udp; int tcp; int udp; sockaddr_t sa; @@@ -102,7 -100,6 +102,7 @@@ typedef struct outgoing_t struct config_t *cfg; struct addrinfo *ai; struct addrinfo *aip; + struct event ev; } outgoing_t; extern list_t *outgoing_list; @@@ -113,19 -110,22 +113,19 @@@ extern int addressfamily extern listen_socket_t listen_socket[MAXSOCKETS]; extern int listen_sockets; -extern int keyexpires; extern int keylifetime; extern bool do_prune; -extern bool do_purge; extern char *myport; -extern time_t now; /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ #include "connection.h" #include "node.h" extern void retry_outgoing(outgoing_t *); -extern void handle_incoming_vpn_data(int); +extern void handle_incoming_vpn_data(int, short, void *); extern void finish_connecting(struct connection_t *); extern void do_outgoing_connection(struct connection_t *); -extern bool handle_new_meta_connection(int); +extern void handle_new_meta_connection(int, short, void *); extern int setup_listen_socket(const sockaddr_t *); extern int setup_vpn_in_socket(const sockaddr_t *); extern void send_packet(const struct node_t *, vpn_packet_t *); @@@ -140,15 -140,11 +140,17 @@@ extern void terminate_connection(struc extern void flush_queue(struct node_t *); extern bool read_rsa_public_key(struct connection_t *); extern void send_mtu_probe(struct node_t *); +extern void handle_device_data(int, short, void *); +extern void handle_meta_connection_data(int, short, void *); +extern void regenerate_key(); +extern void purge(void); +extern void retry(void); +extern int reload_configuration(void); #ifndef HAVE_MINGW #define closesocket(s) close(s) + #else + extern CRITICAL_SECTION mutex; #endif #endif /* __TINC_NET_H__ */ diff --combined src/net_packet.c index 8bf41c39,aca84683..77e29c0a --- a/src/net_packet.c +++ b/src/net_packet.c @@@ -22,17 -22,21 +22,17 @@@ #include "system.h" -#include -#include -#include -#include -#include - #include #include LZO1X_H -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" +#include "crypto.h" +#include "digest.h" #include "device.h" #include "ethernet.h" -#include "event.h" #include "graph.h" #include "list.h" #include "logger.h" @@@ -56,15 -60,21 +56,20 @@@ static void send_udppacket(node_t *, vp #define MAX_SEQNO 1073741824 -void send_mtu_probe(node_t *n) -{ +static void send_mtu_probe_handler(int fd, short events, void *data) { + node_t *n = data; vpn_packet_t packet; int len, i; cp(); n->mtuprobes++; - n->mtuevent = NULL; + if(!n->status.reachable) { + logger(LOG_DEBUG, _("Trying to send MTU probe to unreachable node %s (%s)"), n->name, n->hostname); + return; + } + if(n->mtuprobes >= 10 && !n->minmtu) { ifdebug(TRAFFIC) logger(LOG_INFO, _("No response to MTU probes from %s (%s)"), n->name, n->hostname); return; @@@ -77,12 -87,12 +82,12 @@@ return; } - len = n->minmtu + 1 + random() % (n->maxmtu - n->minmtu); + len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu); if(len < 64) len = 64; memset(packet.data, 0, 14); - RAND_pseudo_bytes(packet.data + 14, len - 14); + randomize(packet.data + 14, len - 14); packet.len = len; packet.priority = 0; @@@ -91,13 -101,11 +96,13 @@@ send_udppacket(n, &packet); } - n->mtuevent = new_event(); - n->mtuevent->handler = (event_handler_t)send_mtu_probe; - n->mtuevent->data = n; - n->mtuevent->time = now + 1; - event_add(n->mtuevent); + event_add(&n->mtuevent, &(struct timeval){1, 0}); +} + +void send_mtu_probe(node_t *n) { + if(!timeout_initialized(&n->mtuevent)) + timeout_set(&n->mtuevent, send_mtu_probe_handler, n); + send_mtu_probe_handler(0, 0, n); } void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) { @@@ -112,7 -120,8 +117,7 @@@ } } -static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) -{ +static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) { if(level == 10) { lzo_uint lzolen = MAXSIZE; lzo1x_1_compress(source, len, dest, &lzolen, lzo_wrkmem); @@@ -132,7 -141,8 +137,7 @@@ return -1; } -static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) -{ +static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) { if(level > 9) { lzo_uint lzolen = MAXSIZE; if(lzo1x_decompress_safe(source, len, dest, &lzolen, NULL) == LZO_E_OK) @@@ -152,7 -162,8 +157,7 @@@ /* VPN packet I/O */ -static void receive_packet(node_t *n, vpn_packet_t *packet) -{ +static void receive_packet(node_t *n, vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), @@@ -161,12 -172,16 +166,12 @@@ route(n, packet); } -static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) +static bool try_mac(node_t *n, const vpn_packet_t *inpkt) { - unsigned char hmac[EVP_MAX_MD_SIZE]; - - if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength) + if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) return false; - HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL); - - return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength); + return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len); } static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) @@@ -175,12 -190,13 +180,12 @@@ vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; int nextpkt = 0; vpn_packet_t *outpkt = pkt[0]; - int outlen, outpad; - unsigned char hmac[EVP_MAX_MD_SIZE]; + size_t outlen; int i; cp(); - if(!n->inkey) { + if(!cipher_active(&n->incipher)) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got packet from %s (%s) but he hasn't got our key yet"), n->name, n->hostname); return; @@@ -188,7 -204,7 +193,7 @@@ /* Check packet length */ - if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) { + if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"), n->name, n->hostname); return; @@@ -196,56 -212,66 +201,56 @@@ /* Check the message authentication code */ - if(n->indigest && n->inmaclength) { - inpkt->len -= n->inmaclength; - HMAC(n->indigest, n->inkey, n->inkeylength, - (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL); - - if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), - n->name, n->hostname); - return; - } + if(digest_active(&n->indigest) && !digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname); + return; } /* Decrypt the packet */ - if(n->incipher) { + if(cipher_active(&n->incipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL) - || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname); return; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } /* Check the sequence number */ - inpkt->len -= sizeof(inpkt->seqno); + inpkt->len -= sizeof inpkt->seqno; inpkt->seqno = ntohl(inpkt->seqno); if(inpkt->seqno != n->received_seqno + 1) { - if(inpkt->seqno >= n->received_seqno + sizeof(n->late) * 8) { + if(inpkt->seqno >= n->received_seqno + sizeof n->late * 8) { logger(LOG_WARNING, _("Lost %d packets from %s (%s)"), inpkt->seqno - n->received_seqno - 1, n->name, n->hostname); - memset(n->late, 0, sizeof(n->late)); + memset(n->late, 0, sizeof n->late); } else if (inpkt->seqno <= n->received_seqno) { - if((n->received_seqno >= sizeof(n->late) * 8 && inpkt->seqno <= n->received_seqno - sizeof(n->late) * 8) || !(n->late[(inpkt->seqno / 8) % sizeof(n->late)] & (1 << inpkt->seqno % 8))) { + if((n->received_seqno >= sizeof n->late * 8 && inpkt->seqno <= n->received_seqno - sizeof n->late * 8) || !(n->late[(inpkt->seqno / 8) % sizeof n->late] & (1 << inpkt->seqno % 8))) { logger(LOG_WARNING, _("Got late or replayed packet from %s (%s), seqno %d, last received %d"), n->name, n->hostname, inpkt->seqno, n->received_seqno); return; } } else { for(i = n->received_seqno + 1; i < inpkt->seqno; i++) - n->late[(i / 8) % sizeof(n->late)] |= 1 << i % 8; + n->late[(i / 8) % sizeof n->late] |= 1 << i % 8; } } - n->late[(inpkt->seqno / 8) % sizeof(n->late)] &= ~(1 << inpkt->seqno % 8); + n->late[(inpkt->seqno / 8) % sizeof n->late] &= ~(1 << inpkt->seqno % 8); if(inpkt->seqno > n->received_seqno) n->received_seqno = inpkt->seqno; if(n->received_seqno > MAX_SEQNO) - keyexpires = 0; + regenerate_key(); /* Decompress the packet */ @@@ -267,13 -293,17 +272,13 @@@ inpkt->priority = 0; - if(n->connection) - n->connection->last_ping_time = now; - if(!inpkt->data[12] && !inpkt->data[13]) mtu_probe_h(n, inpkt, origlen); else receive_packet(n, inpkt); } -void receive_tcppacket(connection_t *c, char *buffer, int len) -{ +void receive_tcppacket(connection_t *c, char *buffer, int len) { vpn_packet_t outpkt; cp(); @@@ -288,20 -318,26 +293,25 @@@ receive_packet(c->node, &outpkt); } -static void send_udppacket(node_t *n, vpn_packet_t *origpkt) -{ +static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { vpn_packet_t pkt1, pkt2; vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; vpn_packet_t *inpkt = origpkt; int nextpkt = 0; vpn_packet_t *outpkt; int origlen; - int outlen, outpad; + size_t outlen; static int priority = 0; int origpriority; int sock; cp(); + if(!n->status.reachable) { + ifdebug(TRAFFIC) logger(LOG_INFO, _("Trying to send UDP packet to unreachable node %s (%s)"), n->name, n->hostname); + return; + } + /* Make sure we have a valid key */ if(!n->status.validkey) { @@@ -319,9 -355,9 +329,9 @@@ return; } - if(n->options & OPTION_PMTU_DISCOVERY && !n->minmtu && (inpkt->data[12] | inpkt->data[13])) { + if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) { ifdebug(TRAFFIC) logger(LOG_INFO, - _("No minimum MTU established yet for %s (%s), forwarding via TCP"), + _("Packet for %s (%s) larger than minimum MTU, forwarding via TCP"), n->name, n->hostname); send_tcppacket(n->nexthop->connection, origpkt); @@@ -349,28 -385,32 +359,28 @@@ /* Add sequence number */ inpkt->seqno = htonl(++(n->sent_seqno)); - inpkt->len += sizeof(inpkt->seqno); + inpkt->len += sizeof inpkt->seqno; /* Encrypt the packet */ - if(n->outcipher) { + if(cipher_active(&n->outcipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL) - || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname); goto end; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } /* Add the message authentication code */ - if(n->outdigest && n->outmaclength) { - HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno, - inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL); - inpkt->len += n->outmaclength; + if(digest_active(&n->outdigest)) { + digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len); + inpkt->len += digest_length(&n->outdigest); } /* Determine which socket we have to use */ @@@ -389,7 -429,7 +399,7 @@@ && listen_socket[sock].sa.sa.sa_family == AF_INET) { priority = origpriority; ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority); - if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */ + if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof priority)) /* SO_PRIORITY doesn't seem to work */ logger(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno)); } #endif @@@ -411,7 -451,8 +421,7 @@@ end /* send a packet to the given vpn ip. */ -void send_packet(const node_t *n, vpn_packet_t *packet) -{ +void send_packet(const node_t *n, vpn_packet_t *packet) { node_t *via; cp(); @@@ -447,8 -488,9 +457,8 @@@ /* Broadcast a packet using the minimum spanning tree */ -void broadcast_packet(const node_t *from, vpn_packet_t *packet) -{ - avl_node_t *node; +void broadcast_packet(const node_t *from, vpn_packet_t *packet) { + splay_node_t *node; connection_t *c; cp(); @@@ -474,7 -516,7 +484,7 @@@ } static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) { - avl_node_t *node; + splay_node_t *node; edge_t *e; node_t *n = NULL; @@@ -497,12 -539,12 +507,12 @@@ return n; } -void handle_incoming_vpn_data(int sock) +void handle_incoming_vpn_data(int sock, short events, void *data) { vpn_packet_t pkt; char *hostname; sockaddr_t from; - socklen_t fromlen = sizeof(from); + socklen_t fromlen = sizeof from; node_t *n; cp(); @@@ -510,7 -552,8 +520,8 @@@ pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen); if(pkt.len < 0) { - logger(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno)); + if(errno != EAGAIN && errno != EINTR) + logger(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno)); return; } @@@ -534,10 -577,3 +545,10 @@@ receive_udppacket(n, &pkt); } + +void handle_device_data(int sock, short events, void *data) { + vpn_packet_t packet; + + if(read_packet(&packet)) + route(myself, &packet); +} diff --combined src/net_setup.c index 224bdf61,3c4bf48c..623eb185 --- a/src/net_setup.c +++ b/src/net_setup.c @@@ -22,13 -22,17 +22,13 @@@ #include "system.h" -#include -#include -#include -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" +#include "control.h" #include "device.h" -#include "event.h" +#include "digest.h" #include "graph.h" #include "logger.h" #include "net.h" @@@ -36,79 -40,138 +36,79 @@@ #include "process.h" #include "protocol.h" #include "route.h" +#include "rsa.h" #include "subnet.h" #include "utils.h" #include "xalloc.h" char *myport; +static struct event device_ev; -bool read_rsa_public_key(connection_t *c) -{ +bool read_rsa_public_key(connection_t *c) { FILE *fp; char *fname; - char *key; + char *n; + bool result; cp(); - if(!c->rsa_key) { - c->rsa_key = RSA_new(); -// RSA_blinding_on(c->rsa_key, NULL); - } - /* First, check for simple PublicKey statement */ - if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) { - BN_hex2bn(&c->rsa_key->n, key); - BN_hex2bn(&c->rsa_key->e, "FFFF"); - free(key); - return true; + if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) { + result = rsa_set_hex_public_key(&c->rsa, n, "FFFF"); + free(n); + return result; } /* Else, check for PublicKeyFile statement and read it */ - if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) { - fp = fopen(fname, "r"); - - if(!fp) { - logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), - fname, strerror(errno)); - free(fname); - return false; - } - - free(fname); - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - - if(c->rsa_key) - return true; /* Woohoo. */ + if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) + asprintf(&fname, "%s/hosts/%s", confbase, c->name); - /* If it fails, try PEM_read_RSA_PUBKEY. */ - fp = fopen(fname, "r"); - - if(!fp) { - logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), - fname, strerror(errno)); - free(fname); - return false; - } - - free(fname); - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - - if(c->rsa_key) { -// RSA_blinding_on(c->rsa_key, NULL); - return true; - } + fp = fopen(fname, "r"); - logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), + if(!fp) { + logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), fname, strerror(errno)); + free(fname); return false; } - /* Else, check if a harnessed public key is in the config file */ - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - } - - free(fname); - - if(c->rsa_key) - return true; - - /* Try again with PEM_read_RSA_PUBKEY. */ - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); -// RSA_blinding_on(c->rsa_key, NULL); - fclose(fp); - } + result = rsa_read_pem_public_key(&c->rsa, fp); + fclose(fp); + if(!result) + logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), fname, strerror(errno)); free(fname); - - if(c->rsa_key) - return true; - - logger(LOG_ERR, _("No public key for %s specified!"), c->name); - - return false; + return result; } -bool read_rsa_private_key(void) -{ +bool read_rsa_private_key() { FILE *fp; - char *fname, *key, *pubkey; - struct stat s; + char *fname; + char *n, *d; + bool result; cp(); - if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { - if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &pubkey)) { + /* First, check for simple PrivateKey statement */ + + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) { + if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) { logger(LOG_ERR, _("PrivateKey used but no PublicKey found!")); + free(d); return false; } - myself->connection->rsa_key = RSA_new(); -// RSA_blinding_on(myself->connection->rsa_key, NULL); - BN_hex2bn(&myself->connection->rsa_key->d, key); - BN_hex2bn(&myself->connection->rsa_key->n, pubkey); - BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); - free(key); - free(pubkey); + result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d); + free(n); + free(d); return true; } + /* Else, check for PrivateKeyFile statement and read it */ + if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname)) - asprintf(&fname, "%s/rsa_key.priv", confbase); + xasprintf(&fname, "%s/rsa_key.priv", confbase); fp = fopen(fname, "r"); @@@ -120,10 -183,9 +120,10 @@@ } #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) + struct stat s; + if(fstat(fileno(fp), &s)) { - logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), - fname, strerror(errno)); + logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), fname, strerror(errno)); free(fname); return false; } @@@ -132,37 -194,25 +132,37 @@@ logger(LOG_WARNING, _("Warning: insecure file permissions for RSA private key file `%s'!"), fname); #endif - myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + result = rsa_read_pem_private_key(&myself->connection->rsa, fp); fclose(fp); - if(!myself->connection->rsa_key) { - logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), - fname, strerror(errno)); - free(fname); - return false; + if(!result) + logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), fname, strerror(errno)); + free(fname); + return result; +} + +static struct event keyexpire_event; + +static void keyexpire_handler(int fd, short events, void *data) { + regenerate_key(); +} + +void regenerate_key() { + if(timeout_initialized(&keyexpire_event)) { + ifdebug(STATUS) logger(LOG_INFO, _("Expiring symmetric keys")); + event_del(&keyexpire_event); + send_key_changed(broadcast, myself); + } else { + timeout_set(&keyexpire_event, keyexpire_handler, NULL); } - free(fname); - return true; + event_add(&keyexpire_event, &(struct timeval){keylifetime, 0}); } /* Configure node_t myself and set up the local sockets (listen only) */ -bool setup_myself(void) -{ +bool setup_myself(void) { config_t *cfg; subnet_t *subnet; char *name, *hostname, *mode, *afname, *cipher, *digest; @@@ -178,8 -228,8 +178,8 @@@ myself->connection = new_connection(); init_configuration(&myself->connection->config_tree); - asprintf(&myself->hostname, _("MYSELF")); - asprintf(&myself->connection->hostname, _("MYSELF")); + xasprintf(&myself->hostname, _("MYSELF")); + xasprintf(&myself->connection->hostname, _("MYSELF")); myself->connection->options = 0; myself->connection->protocol_version = PROT_CURRENT; @@@ -207,7 -257,7 +207,7 @@@ return false; if(!get_config_string(lookup_config(myself->connection->config_tree, "Port"), &myport)) - asprintf(&myport, "655"); + xasprintf(&myport, "655"); /* Read in all the subnets specified in the host configuration file */ @@@ -256,9 -306,12 +256,12 @@@ } else routing_mode = RMODE_ROUTER; - if(routing_mode == RMODE_ROUTER) - if(!get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) || choice) - myself->options |= OPTION_PMTU_DISCOVERY; + // Enable PMTUDiscovery by default if we are in router mode. + + choice = routing_mode == RMODE_ROUTER; + get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice); + if(choice) + myself->options |= OPTION_PMTU_DISCOVERY; get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance); @@@ -296,36 -349,65 +299,36 @@@ /* Generate packet encryption key */ - if(get_config_string - (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) { - if(!strcasecmp(cipher, "none")) { - myself->incipher = NULL; - } else { - myself->incipher = EVP_get_cipherbyname(cipher); - - if(!myself->incipher) { - logger(LOG_ERR, _("Unrecognized cipher type!")); - return false; - } - } - } else - myself->incipher = EVP_aes_256_cbc(); + if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) - cipher = xstrdup("blowfish"); ++ cipher = xstrdup("aes256"); - if(myself->incipher) - myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len; - else - myself->inkeylength = 1; - - myself->connection->outcipher = EVP_aes_256_ofb(); + if(!cipher_open_by_name(&myself->incipher, cipher)) { + logger(LOG_ERR, _("Unrecognized cipher type!")); + return false; + } if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; - keyexpires = now + keylifetime; - + regenerate_key(); + /* Check if we want to use message authentication codes... */ - if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) { - if(!strcasecmp(digest, "none")) { - myself->indigest = NULL; - } else { - myself->indigest = EVP_get_digestbyname(digest); + if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) - digest = xstrdup("sha1"); ++ digest = xstrdup("sha256"); - if(!myself->indigest) { - logger(LOG_ERR, _("Unrecognized digest type!")); - return false; - } - } - } else - myself->indigest = EVP_sha256(); - - myself->connection->outdigest = EVP_sha256(); - - if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength)) { - if(myself->indigest) { - if(myself->inmaclength > myself->indigest->md_size) { - logger(LOG_ERR, _("MAC length exceeds size of digest!")); - return false; - } else if(myself->inmaclength < 0) { - logger(LOG_ERR, _("Bogus MAC length!")); - return false; - } - } - } else - myself->inmaclength = 4; + int maclength = 4; + get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength); - myself->connection->outmaclength = 0; + if(maclength < 0) { + logger(LOG_ERR, _("Bogus MAC length!")); + return false; + } + + if(!digest_open_by_name(&myself->indigest, digest, maclength)) { + logger(LOG_ERR, _("Unrecognized digest type!")); + return false; + } /* Compression */ @@@ -353,19 -435,11 +356,19 @@@ if(!setup_device()) return false; + event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL); + + if (event_add(&device_ev, NULL) < 0) { + logger(LOG_ERR, _("event_add failed: %s"), strerror(errno)); + close_device(); + return false; + } + /* Run tinc-up script to further initialize the tap interface */ - asprintf(&envp[0], "NETNAME=%s", netname ? : ""); - asprintf(&envp[1], "DEVICE=%s", device ? : ""); - asprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - asprintf(&envp[3], "NAME=%s", myself->name); + xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); + xasprintf(&envp[1], "DEVICE=%s", device ? : ""); + xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); + xasprintf(&envp[3], "NAME=%s", myself->name); envp[4] = NULL; execute_script("tinc-up", envp); @@@ -406,28 -480,8 +409,28 @@@ listen_socket[listen_sockets].udp = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr); - if(listen_socket[listen_sockets].udp < 0) + if(listen_socket[listen_sockets].udp < 0) { + close(listen_socket[listen_sockets].tcp); continue; + } + + event_set(&listen_socket[listen_sockets].ev_tcp, + listen_socket[listen_sockets].tcp, + EV_READ|EV_PERSIST, + handle_new_meta_connection, NULL); + if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) { + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); + } + + event_set(&listen_socket[listen_sockets].ev_udp, + listen_socket[listen_sockets].udp, + EV_READ|EV_PERSIST, + handle_incoming_vpn_data, NULL); + if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) { + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); + } ifdebug(CONNECTIONS) { hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr); @@@ -437,11 -491,6 +440,11 @@@ memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen); listen_sockets++; + + if(listen_sockets >= MAXSOCKETS) { + logger(LOG_WARNING, _("Maximum of %d listening sockets reached"), MAXSOCKETS); + break; + } } freeaddrinfo(ai); @@@ -463,6 -512,9 +466,6 @@@ bool setup_network(void { cp(); - now = time(NULL); - - init_events(); init_connections(); init_subnets(); init_nodes(); @@@ -493,8 -545,9 +496,8 @@@ /* close all open network connections */ -void close_network_connections(void) -{ - avl_node_t *node, *next; +void close_network_connections(void) { + splay_node_t *node, *next; connection_t *c; char *envp[5]; int i; @@@ -517,16 -570,14 +520,16 @@@ } for(i = 0; i < listen_sockets; i++) { + event_del(&listen_socket[i].ev_tcp); + event_del(&listen_socket[i].ev_udp); close(listen_socket[i].tcp); close(listen_socket[i].udp); } - asprintf(&envp[0], "NETNAME=%s", netname ? : ""); - asprintf(&envp[1], "DEVICE=%s", device ? : ""); - asprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - asprintf(&envp[3], "NAME=%s", myself->name); + xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); + xasprintf(&envp[1], "DEVICE=%s", device ? : ""); + xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); + xasprintf(&envp[3], "NAME=%s", myself->name); envp[4] = NULL; exit_requests(); @@@ -534,6 -585,7 +537,6 @@@ exit_subnets(); exit_nodes(); exit_connections(); - exit_events(); execute_script("tinc-down", envp); diff --combined src/net_socket.c index 2dfaa2f0,8df9f380..47a41684 --- a/src/net_socket.c +++ b/src/net_socket.c @@@ -22,9 -22,10 +22,9 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "connection.h" -#include "event.h" #include "logger.h" #include "meta.h" #include "net.h" @@@ -54,7 -55,8 +54,7 @@@ list_t *outgoing_list = NULL /* Setup sockets */ -static void configure_tcp(connection_t *c) -{ +static void configure_tcp(connection_t *c) { int option; #ifdef O_NONBLOCK @@@ -73,12 -75,12 +73,12 @@@ #if defined(SOL_TCP) && defined(TCP_NODELAY) option = 1; - setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option)); + setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option); #endif #if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY) option = IPTOS_LOWDELAY; - setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option)); + setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option); #endif } @@@ -188,7 -190,7 +188,7 @@@ int setup_listen_socket(const sockaddr_ /* Optimize TCP settings */ option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) @@@ -200,10 -202,10 +200,10 @@@ #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); - if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) { + if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) { closesocket(nfd); logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface, strerror(errno)); @@@ -233,7 -235,8 +233,7 @@@ return nfd; } -int setup_vpn_in_socket(const sockaddr_t *sa) -{ +int setup_vpn_in_socket(const sockaddr_t *sa) { int nfd; char *addrstr; int option; @@@ -271,7 -274,7 +271,7 @@@ #endif option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) @@@ -309,11 -312,10 +309,11 @@@ return nfd; } /* int setup_vpn_in_socket */ -void retry_outgoing(outgoing_t *outgoing) -{ - event_t *event; +static void retry_outgoing_handler(int fd, short events, void *data) { + setup_outgoing_connection(data); +} +void retry_outgoing(outgoing_t *outgoing) { cp(); outgoing->timeout += 5; @@@ -321,28 -323,32 +321,28 @@@ if(outgoing->timeout > maxtimeout) outgoing->timeout = maxtimeout; - event = new_event(); - event->handler = (event_handler_t) setup_outgoing_connection; - event->time = now + outgoing->timeout; - event->data = outgoing; - event_add(event); + timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing); + event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0}); ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout); } -void finish_connecting(connection_t *c) -{ +void finish_connecting(connection_t *c) { cp(); ifdebug(CONNECTIONS) logger(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname); configure_tcp(c); - c->last_ping_time = now; + c->last_ping_time = time(NULL); + c->status.connecting = false; send_id(c); } -void do_outgoing_connection(connection_t *c) -{ +void do_outgoing_connection(connection_t *c) { char *address, *port; int result; @@@ -353,16 -359,15 +353,16 @@@ begin if(!c->outgoing->cfg) { ifdebug(CONNECTIONS) logger(LOG_ERR, _("Could not set up a meta connection to %s"), c->name); - c->status.remove = true; retry_outgoing(c->outgoing); + c->outgoing = NULL; + connection_del(c); return; } get_config_string(c->outgoing->cfg, &address); if(!get_config_string(lookup_config(c->config_tree, "Port"), &port)) - asprintf(&port, "655"); + xasprintf(&port, "655"); c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM); free(address); @@@ -438,22 -443,8 +438,22 @@@ return; } -void setup_outgoing_connection(outgoing_t *outgoing) -{ +void handle_meta_read(struct bufferevent *event, void *data) { + logger(LOG_EMERG, _("handle_meta_read() called")); + abort(); +} + +void handle_meta_write(struct bufferevent *event, void *data) { + ifdebug(META) logger(LOG_DEBUG, _("handle_meta_write() called")); +} + +void handle_meta_connection_error(struct bufferevent *event, short what, void *data) { + connection_t *c = data; + logger(LOG_EMERG, _("handle_meta_connection_error() called: %d: %s"), what, strerror(errno)); + terminate_connection(c, c->status.active); +} + +void setup_outgoing_connection(outgoing_t *outgoing) { connection_t *c; node_t *n; @@@ -488,39 -479,32 +488,39 @@@ } c->outgoing = outgoing; - c->last_ping_time = now; + c->last_ping_time = time(NULL); connection_add(c); do_outgoing_connection(c); + + event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c); + event_add(&c->inevent, NULL); + c->buffer = bufferevent_new(c->socket, handle_meta_read, handle_meta_write, handle_meta_connection_error, c); + if(!c->buffer) { + logger(LOG_EMERG, _("bufferevent_new() failed: %s"), strerror(errno)); + abort(); + } + bufferevent_disable(c->buffer, EV_READ); } /* accept a new tcp connect and create a new connection */ -bool handle_new_meta_connection(int sock) -{ +void handle_new_meta_connection(int sock, short events, void *data) { connection_t *c; sockaddr_t sa; int fd; - socklen_t len = sizeof(sa); + socklen_t len = sizeof sa; cp(); fd = accept(sock, &sa.sa, &len); if(fd < 0) { - logger(LOG_ERR, _("Accepting a new connection failed: %s"), - strerror(errno)); - return false; + logger(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno)); + return; } sockaddrunmap(&sa); @@@ -535,25 -519,18 +535,25 @@@ c->address = sa; c->hostname = sockaddr2hostname(&sa); c->socket = fd; - c->last_ping_time = now; + c->last_ping_time = time(NULL); ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection from %s"), c->hostname); + event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c); + event_add(&c->inevent, NULL); + c->buffer = bufferevent_new(c->socket, NULL, handle_meta_write, handle_meta_connection_error, c); + if(!c->buffer) { + logger(LOG_EMERG, _("bufferevent_new() failed: %s"), strerror(errno)); + abort(); + } + bufferevent_disable(c->buffer, EV_READ); + configure_tcp(c); connection_add(c); c->allow_request = ID; send_id(c); - - return true; } void free_outgoing(outgoing_t *outgoing) { @@@ -572,7 -549,7 +572,7 @@@ void try_outgoing_connections(void char *name; outgoing_t *outgoing; connection_t *c; - avl_node_t *node; + splay_node_t *node; cp(); @@@ -598,7 -575,7 +598,7 @@@ continue; } - outgoing = xmalloc_and_zero(sizeof(*outgoing)); + outgoing = xmalloc_and_zero(sizeof *outgoing); outgoing->name = name; list_insert_tail(outgoing_list, outgoing); setup_outgoing_connection(outgoing); diff --combined src/netutl.c index 39cd7907,909441e1..480b716b --- a/src/netutl.c +++ b/src/netutl.c @@@ -34,7 -34,8 +34,7 @@@ bool hostnames = false Turn a string into a struct addrinfo. Return NULL on failure. */ -struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) -{ +struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) { struct addrinfo *ai, hint = {0}; int err; @@@ -54,7 -55,8 +54,7 @@@ return ai; } -sockaddr_t str2sockaddr(const char *address, const char *port) -{ +sockaddr_t str2sockaddr(const char *address, const char *port) { struct addrinfo *ai, hint = {0}; sockaddr_t result; int err; @@@ -82,7 -84,8 +82,7 @@@ return result; } -void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) -{ +void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) { char address[NI_MAXHOST]; char port[NI_MAXSERV]; char *scopeid; @@@ -96,7 -99,7 +96,7 @@@ return; } - err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV); if(err) { logger(LOG_ERR, _("Error while translating addresses: %s"), @@@ -115,7 -118,8 +115,7 @@@ *portstr = xstrdup(port); } -char *sockaddr2hostname(const sockaddr_t *sa) -{ +char *sockaddr2hostname(const sockaddr_t *sa) { char *str; char address[NI_MAXHOST] = "unknown"; char port[NI_MAXSERV] = "unknown"; @@@ -124,18 -128,18 +124,18 @@@ cp(); if(sa->sa.sa_family == AF_UNKNOWN) { - asprintf(&str, _("%s port %s"), sa->unknown.address, sa->unknown.port); + xasprintf(&str, _("%s port %s"), sa->unknown.address, sa->unknown.port); return str; } - err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), + err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV)); if(err) { logger(LOG_ERR, _("Error while looking up hostname: %s"), gai_strerror(err)); } - asprintf(&str, _("%s port %s"), address, port); + xasprintf(&str, _("%s port %s"), address, port); return str; } @@@ -197,20 -201,20 +197,20 @@@ int sockaddrcmp(const sockaddr_t *a, co return strcmp(a->unknown.port, b->unknown.port); case AF_INET: - result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr)); + result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof a->in.sin_addr); if(result) return result; - return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port)); + return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof a->in.sin_port); case AF_INET6: - result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)); + result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof a->in6.sin6_addr); if(result) return result; - return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port)); + return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof a->in6.sin6_port); default: logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"), @@@ -242,7 -246,8 +242,7 @@@ void sockaddrfree(sockaddr_t *a) } } -void sockaddrunmap(sockaddr_t *sa) -{ +void sockaddrunmap(sockaddr_t *sa) { cp(); if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr)) { @@@ -253,7 -258,8 +253,7 @@@ /* Subnet mask handling */ -int maskcmp(const void *va, const void *vb, int masklen) -{ +int maskcmp(const void *va, const void *vb, int masklen) { int i, m, result; const char *a = va; const char *b = vb; @@@ -273,7 -279,8 +273,7 @@@ return 0; } -void mask(void *va, int masklen, int len) -{ +void mask(void *va, int masklen, int len) { int i; char *a = va; @@@ -289,7 -296,8 +289,7 @@@ a[i] = 0; } -void maskcpy(void *va, const void *vb, int masklen, int len) -{ +void maskcpy(void *va, const void *vb, int masklen, int len) { int i, m; char *a = va; const char *b = vb; @@@ -308,7 -316,8 +308,7 @@@ a[i] = 0; } -bool maskcheck(const void *va, int masklen, int len) -{ +bool maskcheck(const void *va, int masklen, int len) { int i; const char *a = va; diff --combined src/node.c index 6df236a1,4fbec08f..ebd2c7c2 --- a/src/node.c +++ b/src/node.c @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "logger.h" #include "net.h" #include "netutl.h" @@@ -30,58 -30,63 +30,58 @@@ #include "utils.h" #include "xalloc.h" -avl_tree_t *node_tree; /* Known nodes, sorted by name */ -avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ +splay_tree_t *node_tree; /* Known nodes, sorted by name */ +splay_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ node_t *myself; -static int node_compare(const node_t *a, const node_t *b) -{ +static int node_compare(const node_t *a, const node_t *b) { return strcmp(a->name, b->name); } -static int node_udp_compare(const node_t *a, const node_t *b) -{ - return sockaddrcmp(&a->address, &b->address); +static int node_udp_compare(const node_t *a, const node_t *b) { + int result; + + cp(); + + result = sockaddrcmp(&a->address, &b->address); + + if(result) + return result; + + return (a->name && b->name) ? strcmp(a->name, b->name) : 0; } -void init_nodes(void) -{ +void init_nodes(void) { cp(); - node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node); - node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL); + node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node); + node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL); } -void exit_nodes(void) -{ +void exit_nodes(void) { cp(); - avl_delete_tree(node_udp_tree); - avl_delete_tree(node_tree); + splay_delete_tree(node_udp_tree); + splay_delete_tree(node_tree); } -node_t *new_node(void) -{ - node_t *n = xmalloc_and_zero(sizeof(*n)); +node_t *new_node(void) { + node_t *n = xmalloc_and_zero(sizeof *n); cp(); n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); - EVP_CIPHER_CTX_init(&n->inctx); - EVP_CIPHER_CTX_init(&n->outctx); n->mtu = MTU; n->maxmtu = MTU; return n; } -void free_node(node_t *n) -{ +void free_node(node_t *n) { cp(); - if(n->inkey) - free(n->inkey); - - if(n->outkey) - free(n->outkey); - if(n->subnet_tree) free_subnet_tree(n->subnet_tree); @@@ -90,12 -95,11 +90,12 @@@ sockaddrfree(&n->address); - EVP_CIPHER_CTX_cleanup(&n->inctx); - EVP_CIPHER_CTX_cleanup(&n->outctx); + cipher_close(&n->incipher); + digest_close(&n->indigest); + cipher_close(&n->outcipher); + digest_close(&n->outdigest); - if(n->mtuevent) - event_del(n->mtuevent); + event_del(&n->mtuevent); if(n->hostname) free(n->hostname); @@@ -106,14 -110,16 +106,14 @@@ free(n); } -void node_add(node_t *n) -{ +void node_add(node_t *n) { cp(); - avl_insert(node_tree, n); + splay_insert(node_tree, n); } -void node_del(node_t *n) -{ - avl_node_t *node, *next; +void node_del(node_t *n) { + splay_node_t *node, *next; edge_t *e; subnet_t *s; @@@ -131,21 -137,23 +131,21 @@@ edge_del(e); } - splay_delete(node_tree, n); - avl_delete(node_udp_tree, n); - avl_delete(node_tree, n); + splay_delete(node_udp_tree, n); ++ splay_delete(node_tree, n); } -node_t *lookup_node(char *name) -{ +node_t *lookup_node(char *name) { node_t n = {0}; cp(); n.name = name; - return avl_search(node_tree, &n); + return splay_search(node_tree, &n); } -node_t *lookup_node_udp(const sockaddr_t *sa) -{ +node_t *lookup_node_udp(const sockaddr_t *sa) { node_t n = {0}; cp(); @@@ -153,12 -161,12 +153,12 @@@ n.address = *sa; n.name = NULL; - return avl_search(node_udp_tree, &n); + return splay_search(node_udp_tree, &n); } void update_node_udp(node_t *n, const sockaddr_t *sa) { - avl_delete(node_udp_tree, n); + splay_delete(node_udp_tree, n); if(n->hostname) free(n->hostname); @@@ -166,30 -174,32 +166,30 @@@ if(sa) { n->address = *sa; n->hostname = sockaddr2hostname(&n->address); - splay_delete(node_udp_tree, n); - avl_insert(node_udp_tree, n); - ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname); + splay_insert(node_udp_tree, n); + logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname); } else { memset(&n->address, 0, sizeof n->address); - logger(LOG_DEBUG, "UDP address of %s cleared", n->name); + n->hostname = 0; + ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s cleared", n->name); } } -void dump_nodes(void) -{ - avl_node_t *node; +int dump_nodes(struct evbuffer *out) { + splay_node_t *node; node_t *n; cp(); - logger(LOG_DEBUG, _("Nodes:")); - for(node = node_tree->head; node; node = node->next) { n = node->data; - logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"), - n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, - n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, + if(evbuffer_add_printf(out, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)\n"), + n->name, n->hostname, cipher_get_nid(&n->outcipher), + digest_get_nid(&n->outdigest), digest_length(&n->outdigest), n->outcompression, - n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-", + n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-", - n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); + n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1) + return errno; } - logger(LOG_DEBUG, _("End of nodes.")); + return 0; } diff --combined src/node.h index 82294a7f,528716de..37ba073e --- a/src/node.h +++ b/src/node.h @@@ -23,26 -23,20 +23,23 @@@ #ifndef __TINC_NODE_H__ #define __TINC_NODE_H__ -#include "avl_tree.h" +#include + +#include "splay_tree.h" +#include "cipher.h" #include "connection.h" -#include "event.h" +#include "digest.h" #include "list.h" #include "subnet.h" - typedef union node_status_t { - struct { - int unused_active:1; /* 1 if active (not used for nodes) */ - int validkey:1; /* 1 if we currently have a valid key for him */ - int waitingforkey:1; /* 1 if we already sent out a request */ - int visited:1; /* 1 if this node has been visited by one of the graph algorithms */ - int reachable:1; /* 1 if this node is reachable in the graph */ - int indirect:1; /* 1 if this node is not directly reachable by us */ - int unused:26; - }; - uint32_t value; + typedef struct node_status_t { + int unused_active:1; /* 1 if active (not used for nodes) */ + int validkey:1; /* 1 if we currently have a valid key for him */ + int waitingforkey:1; /* 1 if we already sent out a request */ + int visited:1; /* 1 if this node has been visited by one of the graph algorithms */ + int reachable:1; /* 1 if this node is reachable in the graph */ + int indirect:1; /* 1 if this node is not directly reachable by us */ + int unused:26; } node_status_t; typedef struct node_t { @@@ -54,22 -48,31 +51,22 @@@ node_status_t status; - const EVP_CIPHER *incipher; /* Cipher type for UDP packets received from him */ - char *inkey; /* Cipher key and iv */ - int inkeylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX inctx; /* Cipher context */ - - const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/ - char *outkey; /* Cipher key and iv */ - int outkeylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX outctx; /* Cipher context */ - - const EVP_MD *indigest; /* Digest type for MAC of packets received from him */ - int inmaclength; /* Length of MAC */ - - const EVP_MD *outdigest; /* Digest type for MAC of packets sent to him*/ - int outmaclength; /* Length of MAC */ + cipher_t incipher; /* Cipher for UDP packets */ + digest_t indigest; /* Digest for UDP packets */ + + cipher_t outcipher; /* Cipher for UDP packets */ + digest_t outdigest; /* Digest for UDP packets */ int incompression; /* Compressionlevel, 0 = no compression */ int outcompression; /* Compressionlevel, 0 = no compression */ + int distance; struct node_t *nexthop; /* nearest node from us to him */ struct node_t *via; /* next hop for UDP packets */ - avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */ + splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */ - avl_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ + splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */ @@@ -81,12 -84,12 +78,12 @@@ length_t minmtu; /* Probed minimum MTU */ length_t maxmtu; /* Probed maximum MTU */ int mtuprobes; /* Number of probes */ - event_t *mtuevent; /* Probe event */ + struct event mtuevent; /* Probe event */ } node_t; extern struct node_t *myself; -extern avl_tree_t *node_tree; -extern avl_tree_t *node_udp_tree; +extern splay_tree_t *node_tree; +extern splay_tree_t *node_udp_tree; extern void init_nodes(void); extern void exit_nodes(void); @@@ -96,7 -99,7 +93,7 @@@ extern void node_add(node_t *) extern void node_del(node_t *); extern node_t *lookup_node(char *); extern node_t *lookup_node_udp(const sockaddr_t *); +extern int dump_nodes(struct evbuffer *); extern void update_node_udp(node_t *, const sockaddr_t *); -extern void dump_nodes(void); #endif /* __TINC_NODE_H__ */ diff --combined src/process.c index 29cd486d,544c2242..546570aa --- a/src/process.c +++ b/src/process.c @@@ -24,11 -24,11 +24,11 @@@ #include "conf.h" #include "connection.h" +#include "control.h" #include "device.h" #include "edge.h" #include "logger.h" #include "node.h" -#include "pidfile.h" #include "process.h" #include "subnet.h" #include "utils.h" @@@ -36,15 -36,21 +36,15 @@@ /* If zero, don't detach from the terminal. */ bool do_detach = true; -bool sighup = false; bool sigalrm = false; extern char *identname; -extern char *pidfilename; extern char **g_argv; extern bool use_logfile; -extern volatile bool running; sigset_t emptysigset; -static int saved_debug_level = -1; - -static void memory_full(int size) -{ +static void memory_full(int size) { logger(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size); cp_trace(); exit(1); @@@ -162,14 -168,23 +162,14 @@@ DWORD WINAPI controlhandler(DWORD reque return ERROR_CALL_NOT_IMPLEMENTED; } - if(running) { - running = false; - status.dwWaitHint = 30000; - status.dwCurrentState = SERVICE_STOP_PENDING; - SetServiceStatus(statushandle, &status); - return NO_ERROR; - } else { - status.dwWaitHint = 0; - status.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(statushandle, &status); - exit(1); - } - + event_loopexit(NULL); + status.dwWaitHint = 30000; + status.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(statushandle, &status); + return NO_ERROR; } -VOID WINAPI run_service(DWORD argc, LPTSTR* argv) -{ +VOID WINAPI run_service(DWORD argc, LPTSTR* argv) { int err = 1; extern int main2(int argc, char **argv); @@@ -223,15 -238,95 +223,15 @@@ bool init_service(void) } #endif -#ifndef HAVE_MINGW -/* - check for an existing tinc for this net, and write pid to pidfile -*/ -static bool write_pidfile(void) -{ - pid_t pid; - - cp(); - - pid = check_pid(pidfilename); - - if(pid) { - if(netname) - fprintf(stderr, _("A tincd is already running for net `%s' with pid %ld.\n"), - netname, (long)pid); - else - fprintf(stderr, _("A tincd is already running with pid %ld.\n"), (long)pid); - return false; - } - - /* if it's locked, write-protected, or whatever */ - if(!write_pid(pidfilename)) { - fprintf(stderr, _("Could write pid file %s: %s\n"), pidfilename, strerror(errno)); - return false; - } - - return true; -} -#endif - -/* - kill older tincd for this net -*/ -bool kill_other(int signal) -{ -#ifndef HAVE_MINGW - pid_t pid; - - cp(); - - pid = read_pid(pidfilename); - - if(!pid) { - if(netname) - fprintf(stderr, _("No other tincd is running for net `%s'.\n"), - netname); - else - fprintf(stderr, _("No other tincd is running.\n")); - return false; - } - - errno = 0; /* No error, sometimes errno is only changed on error */ - - /* ESRCH is returned when no process with that pid is found */ - if(kill(pid, signal) && errno == ESRCH) { - if(netname) - fprintf(stderr, _("The tincd for net `%s' is no longer running. "), - netname); - else - fprintf(stderr, _("The tincd is no longer running. ")); - - fprintf(stderr, _("Removing stale lock file.\n")); - remove_pid(pidfilename); - } - - return true; -#else - return remove_service(); -#endif -} - /* - Detach from current terminal, write pidfile, kill parent + Detach from current terminal */ -bool detach(void) -{ +bool detach(void) { cp(); setup_signals(); - /* First check if we can open a fresh new pidfile */ - #ifndef HAVE_MINGW - if(!write_pidfile()) - return false; - - /* If we succeeded in doing that, detach */ - closelogger(); #endif @@@ -242,6 -337,13 +242,6 @@@ strerror(errno)); return false; } - - /* Now UPDATE the pid in the pidfile, because we changed it... */ - - if(!write_pid(pidfilename)) { - fprintf(stderr, _("Could not write pid file %s: %s\n"), pidfilename, strerror(errno)); - return false; - } #else if(!statushandle) exit(install_service()); @@@ -258,31 -360,33 +258,32 @@@ return true; } -bool execute_script(const char *name, char **envp) -{ +bool execute_script(const char *name, char **envp) { #ifdef HAVE_SYSTEM int status, len; - struct stat s; char *scriptname, *p; int i; cp(); #ifndef HAVE_MINGW - len = asprintf(&scriptname, "\"%s/%s\"", confbase, name); + len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name); #else - len = asprintf(&scriptname, "\"%s/%s.bat\"", confbase, name); + len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name); #endif if(len < 0) return false; scriptname[len - 1] = '\0'; + #ifndef HAVE_TUNEMU /* First check if there is a script */ - if(stat(scriptname + 1, &s)) { + if(access(scriptname + 1, F_OK)) { free(scriptname); return true; } + #endif ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name); @@@ -341,14 -445,34 +342,14 @@@ */ #ifndef HAVE_MINGW -static RETSIGTYPE sigterm_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "TERM"); - if(running) - running = false; - else - exit(1); -} - -static RETSIGTYPE sigquit_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "QUIT"); - if(running) - running = false; - else - exit(1); -} - -static RETSIGTYPE fatal_signal_square(int a) -{ +static RETSIGTYPE fatal_signal_square(int a) { logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a)); cp_trace(); exit(1); } -static RETSIGTYPE fatal_signal_handler(int a) -{ +static RETSIGTYPE fatal_signal_handler(int a) { struct sigaction act; logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a)); cp_trace(); @@@ -363,7 -487,7 +364,7 @@@ close_network_connections(); sleep(5); - remove_pid(pidfilename); + exit_control(); execvp(g_argv[0], g_argv); } else { logger(LOG_NOTICE, _("Not restarting.")); @@@ -371,12 -495,62 +372,12 @@@ } } -static RETSIGTYPE sighup_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "HUP"); - sighup = true; -} - -static RETSIGTYPE sigint_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "INT"); - - if(saved_debug_level != -1) { - logger(LOG_NOTICE, _("Reverting to old debug level (%d)"), - saved_debug_level); - debug_level = saved_debug_level; - saved_debug_level = -1; - } else { - logger(LOG_NOTICE, - _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."), - debug_level); - saved_debug_level = debug_level; - debug_level = 5; - } -} - -static RETSIGTYPE sigalrm_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "ALRM"); - sigalrm = true; -} - -static RETSIGTYPE sigusr1_handler(int a) -{ - dump_connections(); -} - -static RETSIGTYPE sigusr2_handler(int a) -{ - dump_device_stats(); - dump_nodes(); - dump_edges(); - dump_subnets(); -} - -static RETSIGTYPE sigwinch_handler(int a) -{ - do_purge = true; -} - -static RETSIGTYPE unexpected_signal_handler(int a) -{ +static RETSIGTYPE unexpected_signal_handler(int a) { logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a)); cp_trace(); } -static RETSIGTYPE ignore_signal_handler(int a) -{ +static RETSIGTYPE ignore_signal_handler(int a) { ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a)); } @@@ -384,16 -558,25 +385,16 @@@ static struct int signal; void (*handler)(int); } sighandlers[] = { - {SIGHUP, sighup_handler}, - {SIGTERM, sigterm_handler}, - {SIGQUIT, sigquit_handler}, {SIGSEGV, fatal_signal_handler}, {SIGBUS, fatal_signal_handler}, {SIGILL, fatal_signal_handler}, {SIGPIPE, ignore_signal_handler}, - {SIGINT, sigint_handler}, - {SIGUSR1, sigusr1_handler}, - {SIGUSR2, sigusr2_handler}, {SIGCHLD, ignore_signal_handler}, - {SIGALRM, sigalrm_handler}, - {SIGWINCH, sigwinch_handler}, {0, NULL} }; #endif -void setup_signals(void) -{ +void setup_signals(void) { #ifndef HAVE_MINGW int i; struct sigaction act; @@@ -405,7 -588,7 +406,7 @@@ /* Set a default signal handler for every signal, errors will be ignored. */ - for(i = 0; i < NSIG; i++) { + for(i = 1; i < NSIG; i++) { if(!do_detach) act.sa_handler = SIG_DFL; else @@@ -415,7 -598,7 +416,7 @@@ /* If we didn't detach, allow coredumps */ if(!do_detach) - sighandlers[3].handler = SIG_DFL; + sighandlers[0].handler = SIG_DFL; /* Then, for each known signal that we want to catch, assign a handler to the signal, with error checking this time. */ diff --combined src/protocol.c index 24ba88b9,85133954..50f6fe73 --- a/src/protocol.c +++ b/src/protocol.c @@@ -34,7 -34,7 +34,7 @@@ bool tunnelserver = false /* Jumptable for the request handlers */ -static bool (*request_handlers[])(connection_t *) = { +static bool (*request_handlers[])(connection_t *, char *) = { id_h, metakey_h, challenge_h, chal_reply_h, ack_h, status_h, error_h, termreq_h, ping_h, pong_h, @@@ -53,9 -53,10 +53,9 @@@ static char (*request_name[]) = "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", }; -static avl_tree_t *past_request_tree; +static splay_tree_t *past_request_tree; -bool check_id(const char *id) -{ +bool check_id(const char *id) { for(; *id; id++) if(!isalnum(*id) && *id != '_') return false; @@@ -66,19 -67,20 +66,19 @@@ /* Generic request routines - takes care of logging and error detection as well */ -bool send_request(connection_t *c, const char *format, ...) -{ +bool send_request(connection_t *c, const char *format, ...) { va_list args; - char buffer[MAXBUFSIZE]; - int len, request; + char request[MAXBUFSIZE]; + int len; cp(); - /* Use vsnprintf instead of vasprintf: faster, no memory + /* Use vsnprintf instead of vxasprintf: faster, no memory fragmentation, cleanup is automatic, and there is a limit on the input buffer anyway */ va_start(args, format); - len = vsnprintf(buffer, MAXBUFSIZE, format, args); + len = vsnprintf(request, MAXBUFSIZE, format, args); va_end(args); if(len < 0 || len > MAXBUFSIZE - 1) { @@@ -88,50 -90,57 +88,50 @@@ } ifdebug(PROTOCOL) { - sscanf(buffer, "%d", &request); ifdebug(META) logger(LOG_DEBUG, _("Sending %s to %s (%s): %s"), - request_name[request], c->name, c->hostname, buffer); + request_name[atoi(request)], c->name, c->hostname, request); else - logger(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], + logger(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[atoi(request)], c->name, c->hostname); } - buffer[len++] = '\n'; + request[len++] = '\n'; if(c == broadcast) { - broadcast_meta(NULL, buffer, len); + broadcast_meta(NULL, request, len); return true; } else - return send_meta(c, buffer, len); + return send_meta(c, request, len); } -void forward_request(connection_t *from) -{ - int request; - +void forward_request(connection_t *from, char *request) { cp(); ifdebug(PROTOCOL) { - sscanf(from->buffer, "%d", &request); ifdebug(META) logger(LOG_DEBUG, _("Forwarding %s from %s (%s): %s"), - request_name[request], from->name, from->hostname, - from->buffer); + request_name[atoi(request)], from->name, from->hostname, request); else logger(LOG_DEBUG, _("Forwarding %s from %s (%s)"), - request_name[request], from->name, from->hostname); + request_name[atoi(request)], from->name, from->hostname); } - from->buffer[from->reqlen - 1] = '\n'; - - broadcast_meta(from, from->buffer, from->reqlen); + int len = strlen(request); + request[len] = '\n'; + broadcast_meta(from, request, len); } -bool receive_request(connection_t *c) -{ - int request; +bool receive_request(connection_t *c, char *request) { + int reqno = atoi(request); cp(); - if(sscanf(c->buffer, "%d", &request) == 1) { - if((request < 0) || (request >= LAST) || !request_handlers[request]) { + if(reqno || *request == '0') { + if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) { ifdebug(META) logger(LOG_DEBUG, _("Unknown request from %s (%s): %s"), - c->name, c->hostname, c->buffer); + c->name, c->hostname, request); else logger(LOG_ERR, _("Unknown request from %s (%s)"), c->name, c->hostname); @@@ -141,24 -150,25 +141,24 @@@ ifdebug(PROTOCOL) { ifdebug(META) logger(LOG_DEBUG, _("Got %s from %s (%s): %s"), - request_name[request], c->name, c->hostname, - c->buffer); + request_name[reqno], c->name, c->hostname, request); else logger(LOG_DEBUG, _("Got %s from %s (%s)"), - request_name[request], c->name, c->hostname); + request_name[reqno], c->name, c->hostname); } } - if((c->allow_request != ALL) && (c->allow_request != request)) { + if((c->allow_request != ALL) && (c->allow_request != reqno)) { logger(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name, c->hostname); return false; } - if(!request_handlers[request](c)) { + if(!request_handlers[reqno](c, request)) { /* Something went wrong. Probably scriptkiddies. Terminate. */ logger(LOG_ERR, _("Error while processing %s from %s (%s)"), - request_name[request], c->name, c->hostname); + request_name[reqno], c->name, c->hostname); return false; } } else { @@@ -170,11 -180,13 +170,11 @@@ return true; } -static int past_request_compare(const past_request_t *a, const past_request_t *b) -{ +static int past_request_compare(const past_request_t *a, const past_request_t *b) { return strcmp(a->request, b->request); } -static void free_past_request(past_request_t *r) -{ +static void free_past_request(past_request_t *r) { cp(); if(r->request) @@@ -183,33 -195,45 +183,33 @@@ free(r); } -void init_requests(void) -{ - cp(); - - past_request_tree = avl_alloc_tree((avl_compare_t) past_request_compare, (avl_action_t) free_past_request); -} - -void exit_requests(void) -{ - cp(); +static struct event past_request_event; - avl_delete_tree(past_request_tree); -} - -bool seen_request(char *request) -{ +bool seen_request(char *request) { past_request_t *new, p = {0}; cp(); p.request = request; - if(avl_search(past_request_tree, &p)) { + if(splay_search(past_request_tree, &p)) { ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Already seen request")); return true; } else { - new = xmalloc(sizeof(*new)); + new = xmalloc(sizeof *new); new->request = xstrdup(request); - new->firstseen = now; - avl_insert(past_request_tree, new); + new->firstseen = time(NULL); + splay_insert(past_request_tree, new); + event_add(&past_request_event, &(struct timeval){10, 0}); return false; } } -void age_past_requests(void) -{ - avl_node_t *node, *next; +void age_past_requests(int fd, short events, void *data) { + splay_node_t *node, *next; past_request_t *p; int left = 0, deleted = 0; + time_t now = time(NULL); cp(); @@@ -218,7 -242,7 +218,7 @@@ p = node->data; if(p->firstseen + pinginterval < now) - avl_delete_node(past_request_tree, node), deleted++; + splay_delete_node(past_request_tree, node), deleted++; else left++; } @@@ -226,23 -250,4 +226,23 @@@ if(left || deleted) ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Aging past requests: deleted %d, left %d"), deleted, left); + + if(left) + event_add(&past_request_event, &(struct timeval){10, 0}); +} + +void init_requests(void) { + cp(); + + past_request_tree = splay_alloc_tree((splay_compare_t) past_request_compare, (splay_action_t) free_past_request); + + timeout_set(&past_request_event, age_past_requests, NULL); +} + +void exit_requests(void) { + cp(); + + splay_delete_tree(past_request_tree); + + event_del(&past_request_event); } diff --combined src/protocol_edge.c index 35776116,4066a30d..92d31dbd --- a/src/protocol_edge.c +++ b/src/protocol_edge.c @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "connection.h" #include "edge.h" @@@ -36,7 -36,8 +36,7 @@@ #include "utils.h" #include "xalloc.h" -bool send_add_edge(connection_t *c, const edge_t *e) -{ +bool send_add_edge(connection_t *c, const edge_t *e) { bool x; char *address, *port; @@@ -44,7 -45,7 +44,7 @@@ sockaddr2str(&e->address, &address, &port); - x = send_request(c, "%d %lx %s %s %s %s %lx %d", ADD_EDGE, random(), + x = send_request(c, "%d %x %s %s %s %s %lx %d", ADD_EDGE, rand(), e->from->name, e->to->name, address, port, e->options, e->weight); free(address); @@@ -53,7 -54,8 +53,7 @@@ return x; } -bool add_edge_h(connection_t *c) -{ +bool add_edge_h(connection_t *c, char *request) { edge_t *e; node_t *from, *to; char from_name[MAX_STRING_SIZE]; @@@ -66,7 -68,7 +66,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d", + if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d", from_name, to_name, to_address, to_port, &options, &weight) != 6) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name, c->hostname); @@@ -87,7 -89,7 +87,7 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* Lookup nodes */ @@@ -163,7 -165,7 +163,7 @@@ /* Tell the rest about the new edge */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); /* Run MST before or after we tell the rest? */ @@@ -172,14 -174,16 +172,14 @@@ return true; } -bool send_del_edge(connection_t *c, const edge_t *e) -{ +bool send_del_edge(connection_t *c, const edge_t *e) { cp(); - return send_request(c, "%d %lx %s %s", DEL_EDGE, random(), + return send_request(c, "%d %x %s %s", DEL_EDGE, rand(), e->from->name, e->to->name); } -bool del_edge_h(connection_t *c) -{ +bool del_edge_h(connection_t *c, char *request) { edge_t *e; char from_name[MAX_STRING_SIZE]; char to_name[MAX_STRING_SIZE]; @@@ -187,7 -191,7 +187,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) { + if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE", c->name, c->hostname); return false; @@@ -207,7 -211,7 +207,7 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* Lookup nodes */ @@@ -257,7 -261,7 +257,7 @@@ /* Tell the rest about the deleted edge */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); /* Delete the edge */ diff --combined src/protocol_key.c index 2a0f2301,7ae98036..60e9d736 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@@ -22,10 -22,12 +22,10 @@@ #include "system.h" -#include -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "connection.h" +#include "crypto.h" #include "logger.h" #include "net.h" #include "netutl.h" @@@ -34,9 -36,10 +34,9 @@@ #include "utils.h" #include "xalloc.h" -bool mykeyused = false; +static bool mykeyused = false; -bool send_key_changed() -{ +bool send_key_changed() { cp(); /* Only send this message if some other daemon requested our key previously. @@@ -46,22 -49,23 +46,22 @@@ if(!mykeyused) return true; - return send_request(broadcast, "%d %lx %s", KEY_CHANGED, random(), myself->name); + return send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name); } -bool key_changed_h(connection_t *c) -{ +bool key_changed_h(connection_t *c, char *request) { char name[MAX_STRING_SIZE]; node_t *n; cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) { + if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED", c->name, c->hostname); return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; n = lookup_node(name); @@@ -78,25 -82,27 +78,25 @@@ /* Tell the others */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); return true; } -bool send_req_key(node_t *to) -{ +bool send_req_key(node_t *to) { cp(); return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name); } -bool req_key_h(connection_t *c) -{ +bool req_key_h(connection_t *c, char *request) { char from_name[MAX_STRING_SIZE]; char to_name[MAX_STRING_SIZE]; node_t *from, *to; cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) { + if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name, c->hostname); return false; @@@ -121,7 -127,6 +121,7 @@@ /* Check if this key request is for us */ if(to == myself) { /* Yes, send our own key back */ + send_ans_key(from); } else { if(tunnelserver) @@@ -133,42 -138,52 +133,42 @@@ return true; } - send_request(to->nexthop->connection, "%s", c->buffer); + send_request(to->nexthop->connection, "%s", request); } return true; } -bool send_ans_key(node_t *to) -{ - char *key; +bool send_ans_key(node_t *to) { + size_t keylen = cipher_keylength(&myself->incipher); + char key[keylen * 2 + 1]; cp(); - // Set key parameters - to->incipher = myself->incipher; - to->inkeylength = myself->inkeylength; - to->indigest = myself->indigest; - to->inmaclength = myself->inmaclength; + cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher)); + digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest)); to->incompression = myself->incompression; - // Allocate memory for key - to->inkey = xrealloc(to->inkey, to->inkeylength); + randomize(key, keylen); + cipher_set_key(&to->incipher, key, true); - // Create a new key - RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength); - if(to->incipher) - EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len); + bin2hex(key, key, keylen); + key[keylen * 2] = '\0'; // Reset sequence number and late packet window mykeyused = true; to->received_seqno = 0; memset(to->late, 0, sizeof(to->late)); - // Convert to hexadecimal and send - key = alloca(2 * to->inkeylength + 1); - bin2hex(to->inkey, key, to->inkeylength); - key[to->inkeylength * 2] = '\0'; - return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY, - myself->name, to->name, key, - to->incipher ? to->incipher->nid : 0, - to->indigest ? to->indigest->type : 0, to->inmaclength, - to->incompression); + myself->name, to->name, key, + cipher_get_nid(&to->incipher), + digest_get_nid(&to->indigest), + digest_length(&to->indigest), + to->incompression); } -bool ans_key_h(connection_t *c) -{ +bool ans_key_h(connection_t *c, char *request) { char from_name[MAX_STRING_SIZE]; char to_name[MAX_STRING_SIZE]; char key[MAX_STRING_SIZE]; @@@ -177,7 -192,7 +177,7 @@@ cp(); - if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", + if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name, @@@ -209,33 -224,59 +209,33 @@@ if(!to->status.reachable) { logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"), - "ANS_KEY", c->name, c->hostname, to_name); + "ANS_KEY", c->name, c->hostname, to_name); return true; } - return send_request(to->nexthop->connection, "%s", c->buffer); + return send_request(to->nexthop->connection, "%s", request); } - /* Update our copy of the origin's packet key */ - from->outkey = xrealloc(from->outkey, strlen(key) / 2); - - from->outkey = xstrdup(key); - from->outkeylength = strlen(key) / 2; - hex2bin(key, from->outkey, from->outkeylength); - - from->status.waitingforkey = false; /* Check and lookup cipher and digest algorithms */ - if(cipher) { - from->outcipher = EVP_get_cipherbynid(cipher); - - if(!from->outcipher) { - logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, - from->hostname); - return false; - } - - if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) { - logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, - from->hostname); - return false; - } - } else { - from->outcipher = NULL; + if(!cipher_open_by_nid(&from->outcipher, cipher)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname); + return false; } - from->outmaclength = maclength; - - if(digest) { - from->outdigest = EVP_get_digestbynid(digest); + if(strlen(key) / 2 != cipher_keylength(&from->outcipher)) { + logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname); + return false; + } - if(!from->outdigest) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, - from->hostname); - return false; - } + if(!digest_open_by_nid(&from->outdigest, digest, maclength)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname); + return false; + } - if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) { - logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), - from->name, from->hostname); - return false; - } - } else { - from->outdigest = NULL; + if(maclength != digest_length(&from->outdigest)) { + logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname); + return false; } if(compression < 0 || compression > 11) { @@@ -245,13 -286,14 +245,13 @@@ from->outcompression = compression; - if(from->outcipher) - if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"), - from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + /* Update our copy of the origin's packet key */ + + hex2bin(key, key, cipher_keylength(&from->outcipher)); + cipher_set_key(&from->outcipher, key, false); from->status.validkey = true; + from->status.waitingforkey = false; from->sent_seqno = 0; if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes) diff --combined src/protocol_misc.c index 02e38598,18ff13c8..b8d2f67d --- a/src/protocol_misc.c +++ b/src/protocol_misc.c @@@ -45,14 -45,14 +45,14 @@@ bool send_status(connection_t *c, int s return send_request(c, "%d %d %s", STATUS, statusno, statusstring); } -bool status_h(connection_t *c) +bool status_h(connection_t *c, char *request) { int statusno; char statusstring[MAX_STRING_SIZE]; cp(); - if(sscanf(c->buffer, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) { + if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS", c->name, c->hostname); return false; @@@ -74,14 -74,14 +74,14 @@@ bool send_error(connection_t *c, int er return send_request(c, "%d %d %s", ERROR, err, errstring); } -bool error_h(connection_t *c) +bool error_h(connection_t *c, char *request) { int err; char errorstring[MAX_STRING_SIZE]; cp(); - if(sscanf(c->buffer, "%*d %d " MAX_STRING, &err, errorstring) != 2) { + if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR", c->name, c->hostname); return false; @@@ -90,7 -90,9 +90,7 @@@ ifdebug(ERROR) logger(LOG_NOTICE, _("Error message from %s (%s): %d: %s"), c->name, c->hostname, err, errorstring); - terminate_connection(c, c->status.active); - - return true; + return false; } bool send_termreq(connection_t *c) @@@ -100,11 -102,13 +100,11 @@@ return send_request(c, "%d", TERMREQ); } -bool termreq_h(connection_t *c) +bool termreq_h(connection_t *c, char *request) { cp(); - terminate_connection(c, c->status.active); - - return true; + return false; } bool send_ping(connection_t *c) @@@ -112,12 -116,12 +112,12 @@@ cp(); c->status.pinged = true; - c->last_ping_time = now; + c->last_ping_time = time(NULL); return send_request(c, "%d", PING); } -bool ping_h(connection_t *c) +bool ping_h(connection_t *c, char *request) { cp(); @@@ -131,7 -135,7 +131,7 @@@ bool send_pong(connection_t *c return send_request(c, "%d", PONG); } -bool pong_h(connection_t *c) +bool pong_h(connection_t *c, char *request) { cp(); @@@ -154,7 -158,7 +154,7 @@@ bool send_tcppacket(connection_t *c, vp /* If there already is a lot of data in the outbuf buffer, discard this packet. We use a very simple Random Early Drop algorithm. */ - if(2.0 * c->buffer->output->off / (double)maxoutbufsize - 1 > drand48()) - if(2.0 * c->outbuflen / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX) ++ if(2.0 * c->buffer->output->off / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX) return true; if(!send_request(c, "%d %hd", PACKET, packet->len)) @@@ -163,13 -167,13 +163,13 @@@ return send_meta(c, (char *)packet->data, packet->len); } -bool tcppacket_h(connection_t *c) +bool tcppacket_h(connection_t *c, char *request) { short int len; cp(); - if(sscanf(c->buffer, "%*d %hd", &len) != 1) { + if(sscanf(request, "%*d %hd", &len) != 1) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name, c->hostname); return false; diff --combined src/protocol_subnet.c index e5927213,b50cf6a3..9c5b04c8 --- a/src/protocol_subnet.c +++ b/src/protocol_subnet.c @@@ -42,10 -42,10 +42,10 @@@ bool send_add_subnet(connection_t *c, c if(!net2str(netstr, sizeof netstr, subnet)) return false; - return send_request(c, "%d %lx %s %s", ADD_SUBNET, random(), subnet->owner->name, netstr); + return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr); } -bool add_subnet_h(connection_t *c) +bool add_subnet_h(connection_t *c, char *request) { char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; @@@ -54,7 -54,7 +54,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { + if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname); return false; @@@ -76,7 -76,7 +76,7 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* Check if the owner of the new subnet is in the connection list */ @@@ -147,7 -147,7 +147,7 @@@ /* Tell the rest */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); return true; } @@@ -161,10 -161,10 +161,10 @@@ bool send_del_subnet(connection_t *c, c if(!net2str(netstr, sizeof netstr, s)) return false; - return send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr); + return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr); } -bool del_subnet_h(connection_t *c) +bool del_subnet_h(connection_t *c, char *request) { char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; @@@ -173,7 -173,7 +173,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { + if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname); return false; @@@ -195,7 -195,7 +195,7 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* Check if the owner of the subnet being deleted is in the connection list */ @@@ -239,7 -239,7 +239,7 @@@ /* Tell the rest */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); /* Finally, delete it. */ diff --combined src/route.c index 8acabb1b,9b689039..da37473c --- a/src/route.c +++ b/src/route.c @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "connection.h" #include "ethernet.h" #include "ipv4.h" @@@ -51,8 -51,6 +51,8 @@@ static const size_t icmp6_size = sizeof static const size_t ns_size = sizeof(struct nd_neighbor_solicit); static const size_t opt_size = sizeof(struct nd_opt_hdr); +static struct event age_subnets_event; + /* RFC 1071 */ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) @@@ -77,7 -75,6 +77,7 @@@ static bool ratelimit(int frequency) { static time_t lasttime = 0; static int count = 0; + time_t now = time(NULL); if(lasttime == now) { if(++count > frequency) @@@ -97,48 -94,18 +97,55 @@@ static bool checklength(node_t *source } else return true; } + + static void swap_mac_addresses(vpn_packet_t *packet) { + mac_t tmp; + memcpy(&tmp, &packet->data[0], sizeof tmp); + memcpy(&packet->data[0], &packet->data[6], sizeof tmp); + memcpy(&packet->data[6], &tmp, sizeof tmp); + } +static void age_subnets(int fd, short events, void *data) +{ + subnet_t *s; + connection_t *c; + splay_node_t *node, *next, *node2; + bool left = false; + time_t now = time(NULL); + + cp(); + + for(node = myself->subnet_tree->head; node; node = next) { + next = node->next; + s = node->data; + if(s->expires && s->expires < now) { + ifdebug(TRAFFIC) { + char netstr[MAXNETSTR]; + if(net2str(netstr, sizeof netstr, s)) + logger(LOG_INFO, _("Subnet %s expired"), netstr); + } + + for(node2 = connection_tree->head; node2; node2 = node2->next) { + c = node2->data; + if(c->status.active) + send_del_subnet(c, s); + } + + subnet_del(myself, s); + } else { + if(s->expires) + left = true; + } + } + + if(left) + event_add(&age_subnets_event, &(struct timeval){10, 0}); +} + static void learn_mac(mac_t *address) { subnet_t *subnet; - avl_node_t *node; + splay_node_t *node; connection_t *c; cp(); @@@ -154,7 -121,7 +161,7 @@@ subnet = new_subnet(); subnet->type = SUBNET_MAC; - subnet->expires = now + macexpire; + subnet->expires = time(NULL) + macexpire; subnet->net.mac.address = *address; subnet_add(myself, subnet); @@@ -165,50 -132,41 +172,16 @@@ if(c->status.active) send_add_subnet(c, subnet); } - } - - if(subnet->expires) - subnet->expires = now + macexpire; -} - -void age_subnets(void) -{ - subnet_t *s; - connection_t *c; - avl_node_t *node, *next, *node2; - cp(); - - for(node = myself->subnet_tree->head; node; node = next) { - next = node->next; - s = node->data; - if(s->expires && s->expires < now) { - ifdebug(TRAFFIC) { - char netstr[MAXNETSTR]; - if(net2str(netstr, sizeof netstr, s)) - logger(LOG_INFO, _("Subnet %s expired"), netstr); - } - - for(node2 = connection_tree->head; node2; node2 = node2->next) { - c = node2->data; - if(c->status.active) - send_del_subnet(c, s); - } - - subnet_del(myself, s); - } + if(!timeout_initialized(&age_subnets_event)) + timeout_set(&age_subnets_event, age_subnets, NULL); + event_add(&age_subnets_event, &(struct timeval){10, 0}); + } else { + if(subnet->expires) + subnet->expires = time(NULL) + macexpire; } } - static void route_mac(node_t *source, vpn_packet_t *packet) - { - subnet_t *subnet; - mac_t dest; - - cp(); - - - /* Learn source address */ - - if(source == myself) { - mac_t src; - memcpy(&src, &packet->data[6], sizeof src); - learn_mac(&src); - } - - /* Lookup destination address */ - - memcpy(&dest, &packet->data[0], sizeof dest); - subnet = lookup_subnet_mac(&dest); - - if(!subnet) { - broadcast_packet(source, packet); - return; - } - - if(subnet->owner == source) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Packet looping back to %s (%s)!"), source->name, source->hostname); - return; - } - - send_packet(subnet->owner, packet); - } - /* RFC 792 */ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) @@@ -225,6 -183,10 +198,10 @@@ cp(); + /* Swap Ethernet source and destination addresses */ + + swap_mac_addresses(packet); + /* Copy headers from packet into properly aligned structs on the stack */ memcpy(&ip, packet->data + ether_size, ip_size); @@@ -421,6 -383,10 +398,10 @@@ static void route_ipv6_unreachable(node cp(); + /* Swap Ethernet source and destination addresses */ + + swap_mac_addresses(packet); + /* Copy headers from packet to structs on the stack */ memcpy(&ip6, packet->data + ether_size, ip6_size); @@@ -464,7 -430,7 +445,7 @@@ /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&icmp6, icmp6_size, checksum); checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum); @@@ -589,7 -555,7 +570,7 @@@ static void route_neighborsol(node_t *s /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&ns, ns_size, checksum); if(has_opt) { checksum = inet_checksum(&opt, opt_size, checksum); @@@ -652,7 -618,7 +633,7 @@@ /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&ns, ns_size, checksum); if(has_opt) { checksum = inet_checksum(&opt, opt_size, checksum); @@@ -719,7 -685,7 +700,7 @@@ static void route_arp(node_t *source, v /* Check if this is a valid ARP request */ if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP || - arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) { + arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) { ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: received unknown type ARP request")); return; } @@@ -743,9 -709,9 +724,9 @@@ memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */ packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */ - memcpy(&addr, arp.arp_tpa, sizeof(addr)); /* save protocol addr */ - memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */ - memcpy(arp.arp_spa, &addr, sizeof(addr)); /* ... */ + memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */ + memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */ + memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */ memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */ memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */ @@@ -758,6 -724,63 +739,63 @@@ send_packet(source, packet); } + static void route_mac(node_t *source, vpn_packet_t *packet) + { + subnet_t *subnet; + mac_t dest; + + cp(); + + + /* Learn source address */ + + if(source == myself) { + mac_t src; + memcpy(&src, &packet->data[6], sizeof src); + learn_mac(&src); + } + + /* Lookup destination address */ + + memcpy(&dest, &packet->data[0], sizeof dest); + subnet = lookup_subnet_mac(&dest); + + if(!subnet) { + broadcast_packet(source, packet); + return; + } + + if(subnet->owner == source) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Packet looping back to %s (%s)!"), source->name, source->hostname); + return; + } + + // Handle packets larger than PMTU + + node_t *via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via; + + if(via && packet->len > via->mtu && via != myself) { + ifdebug(TRAFFIC) logger(LOG_INFO, _("Packet for %s (%s) length %d larger than MTU %d"), subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu); + uint16_t type = packet->data[12] << 8 | packet->data[13]; + if(type == ETH_P_IP) { + if(packet->data[20] & 0x40) { + packet->len = via->mtu; + route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED); + } else { + fragment_ipv4_packet(via, packet); + } + return; + } else if(type == ETH_P_IPV6) { + packet->len = via->mtu; + route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0); + return; + } + } + + send_packet(subnet->owner, packet); + } + + void route(node_t *source, vpn_packet_t *packet) { cp(); @@@ -768,9 -791,8 +806,8 @@@ switch (routing_mode) { case RMODE_ROUTER: { - uint16_t type; + uint16_t type = packet->data[12] << 8 | packet->data[13]; - type = ntohs(*((uint16_t *)(&packet->data[12]))); switch (type) { case ETH_P_ARP: route_arp(source, packet); diff --combined src/solaris/device.c index 295a2f88,f76bbbd5..a258e34e --- a/src/solaris/device.c +++ b/src/solaris/device.c @@@ -43,7 -43,8 +43,7 @@@ static char *device_info = NULL static int device_total_in = 0; static int device_total_out = 0; -bool setup_device(void) -{ +bool setup_device(void) { int ip_fd = -1, if_fd = -1; int ppa; char *ptr; @@@ -99,7 -100,7 +99,7 @@@ } if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) - asprintf(&iface, "tun%d", ppa); + xasprintf(&iface, "tun%d", ppa); device_info = _("Solaris tun device"); @@@ -108,7 -109,8 +108,7 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); close(device_fd); @@@ -117,12 -119,13 +117,12 @@@ free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); - if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { + if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -144,7 -147,7 +144,7 @@@ return false; } - packet->len = lenin + 14; + packet->len = inlen + 14; device_total_in += packet->len; @@@ -154,7 -157,8 +154,7 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -171,7 -175,8 +171,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/subnet.c index 75fe31f8,140b6145..057550ab --- a/src/subnet.c +++ b/src/subnet.c @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "device.h" #include "logger.h" #include "net.h" @@@ -35,7 -35,7 +35,7 @@@ /* lists type of subnet */ -avl_tree_t *subnet_tree; +splay_tree_t *subnet_tree; /* Subnet lookup cache */ @@@ -60,7 -60,7 +60,7 @@@ static int subnet_compare_mac(const sub { int result; - result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t)); + result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address); if(result) return result; @@@ -149,7 -149,7 +149,7 @@@ void init_subnets(void { cp(); - subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet); + subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet); subnet_cache_flush(); } @@@ -158,21 -158,21 +158,21 @@@ void exit_subnets(void { cp(); - avl_delete_tree(subnet_tree); + splay_delete_tree(subnet_tree); } -avl_tree_t *new_subnet_tree(void) +splay_tree_t *new_subnet_tree(void) { cp(); - return avl_alloc_tree((avl_compare_t) subnet_compare, NULL); + return splay_alloc_tree((splay_compare_t) subnet_compare, NULL); } -void free_subnet_tree(avl_tree_t *subnet_tree) +void free_subnet_tree(splay_tree_t *subnet_tree) { cp(); - avl_delete_tree(subnet_tree); + splay_delete_tree(subnet_tree); } /* Allocating and freeing space for subnets */ @@@ -199,8 -199,8 +199,8 @@@ void subnet_add(node_t *n, subnet_t *su subnet->owner = n; - avl_insert(subnet_tree, subnet); - avl_insert(n->subnet_tree, subnet); + splay_insert(subnet_tree, subnet); + splay_insert(n->subnet_tree, subnet); subnet_cache_flush(); } @@@ -209,8 -209,8 +209,8 @@@ void subnet_del(node_t *n, subnet_t *su { cp(); - avl_delete(n->subnet_tree, subnet); - avl_delete(subnet_tree, subnet); + splay_delete(n->subnet_tree, subnet); + splay_delete(subnet_tree, subnet); subnet_cache_flush(); } @@@ -361,7 -361,7 +361,7 @@@ subnet_t *lookup_subnet(const node_t *o { cp(); - return avl_search(owner->subnet_tree, subnet); + return splay_search(owner->subnet_tree, subnet); } subnet_t *lookup_subnet_mac(const mac_t *address) @@@ -374,7 -374,7 +374,7 @@@ subnet.net.mac.address = *address; subnet.owner = NULL; - p = avl_search(subnet_tree, &subnet); + p = splay_search(subnet_tree, &subnet); return p; } @@@ -382,7 -382,7 +382,7 @@@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) { subnet_t *p, *r = NULL, subnet = {0}; - avl_node_t *n; + splay_node_t *n; int i; cp(); @@@ -429,7 -429,7 +429,7 @@@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) { subnet_t *p, *r = NULL, subnet = {0}; - avl_node_t *n; + splay_node_t *n; int i; cp(); @@@ -474,26 -474,25 +474,25 @@@ } void subnet_update(node_t *owner, subnet_t *subnet, bool up) { - avl_node_t *node; + splay_node_t *node; int i; - char *envp[8]; - char netstr[MAXNETSTR + 7] = "SUBNET="; + char *envp[9] = {0}; + char netstr[MAXNETSTR]; char *name, *address, *port; + char empty[] = ""; + + // Prepare environment variables to be passed to the script - asprintf(&envp[0], "NETNAME=%s", netname ? : ""); - asprintf(&envp[1], "DEVICE=%s", device ? : ""); - asprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - asprintf(&envp[3], "NODE=%s", owner->name); + xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); + xasprintf(&envp[1], "DEVICE=%s", device ? : ""); + xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); + xasprintf(&envp[3], "NODE=%s", owner->name); if(owner != myself) { sockaddr2str(&owner->address, &address, &port); - asprintf(&envp[4], "REMOTEADDRESS=%s", address); - asprintf(&envp[5], "REMOTEPORT=%s", port); - envp[6] = netstr; - envp[7] = NULL; - } else { - envp[4] = netstr; - envp[5] = NULL; + // 4 and 5 are reserved for SUBNET and WEIGHT + xasprintf(&envp[6], "REMOTEADDRESS=%s", address); + xasprintf(&envp[7], "REMOTEPORT=%s", port); } name = up ? "subnet-up" : "subnet-down"; @@@ -501,40 -500,62 +500,62 @@@ if(!subnet) { for(node = owner->subnet_tree->head; node; node = node->next) { subnet = node->data; - if(!net2str(netstr + 7, sizeof netstr - 7, subnet)) + if(!net2str(netstr, sizeof netstr, subnet)) continue; + // Strip the weight from the subnet, and put it in its own environment variable + char *weight = strchr(netstr + 7, '#'); + if(weight) + *weight++ = 0; + else + weight = empty; + + // Prepare the SUBNET and WEIGHT variables + if(envp[4]) + free(envp[4]); + if(envp[5]) + free(envp[5]); + xasprintf(&envp[4], "SUBNET=%s", netstr); + xasprintf(&envp[5], "WEIGHT=%s", weight); + execute_script(name, envp); } } else { - if(net2str(netstr + 7, sizeof netstr - 7, subnet)) + if(net2str(netstr + 7, sizeof netstr - 7, subnet)) { + // Strip the weight from the subnet, and put it in its own environment variable + char *weight = strchr(netstr + 7, '#'); + if(weight) + *weight++ = 0; + else + weight = empty; + + // Prepare the SUBNET and WEIGHT variables + xasprintf(&envp[4], "SUBNET=%s", netstr); + xasprintf(&envp[5], "WEIGHT=%s", weight); + execute_script(name, envp); + } } - for(i = 0; i < (owner != myself ? 6 : 4); i++) + for(i = 0; envp[i] && i < 9; i++) free(envp[i]); - - if(owner != myself) { - free(address); - free(port); - } } -void dump_subnets(void) +int dump_subnets(struct evbuffer *out) { char netstr[MAXNETSTR]; subnet_t *subnet; - avl_node_t *node; + splay_node_t *node; cp(); - logger(LOG_DEBUG, _("Subnet list:")); - for(node = subnet_tree->head; node; node = node->next) { subnet = node->data; if(!net2str(netstr, sizeof netstr, subnet)) continue; - logger(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name); + if(evbuffer_add_printf(out, _(" %s owner %s\n"), + netstr, subnet->owner->name) == -1) + return errno; } - logger(LOG_DEBUG, _("End of subnet list.")); + return 0; } diff --combined src/tincctl.c index 814800e8,00000000..92796b6b mode 100644,000000..100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@@ -1,591 -1,0 +1,591 @@@ +/* + tincctl.c -- Controlling a running tincd + Copyright (C) 2007 Guus Sliepen + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include + +#include "xalloc.h" +#include "protocol.h" +#include "control_common.h" +#include "rsagen.h" + +/* The name this program was run with. */ +char *program_name = NULL; + +/* If nonzero, display usage information and exit. */ +bool show_help = false; + +/* If nonzero, print the version on standard output and exit. */ +bool show_version = false; + +/* If nonzero, it will attempt to kill a running tincd and exit. */ +int kill_tincd = 0; + +/* If nonzero, generate public/private keypair for this host/net. */ +int generate_keys = 0; + +static char *identname = NULL; /* program name for syslog */ +static char *controlsocketname = NULL; /* pid file location */ +char *netname = NULL; +char *confbase = NULL; + +static struct option const long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"net", required_argument, NULL, 'n'}, + {"help", no_argument, NULL, 1}, + {"version", no_argument, NULL, 2}, + {"controlsocket", required_argument, NULL, 5}, + {NULL, 0, NULL, 0} +}; + +static void usage(bool status) { + if(status) + fprintf(stderr, _("Try `%s --help\' for more information.\n"), + program_name); + else { + printf(_("Usage: %s [options] command\n\n"), program_name); + printf(_("Valid options are:\n" + " -c, --config=DIR Read configuration options from DIR.\n" + " -n, --net=NETNAME Connect to net NETNAME.\n" + " --controlsocket=FILENAME Open control socket at FILENAME.\n" + " --help Display this help and exit.\n" + " --version Output version information and exit.\n" + "\n" + "Valid commands are:\n" + " start Start tincd.\n" + " stop Stop tincd.\n" + " restart Restart tincd.\n" + " reload Reload configuration of running tincd.\n" + " pid Show PID of currently running tincd.\n" + " generate-keys [bits] Generate a new public/private keypair.\n" + " dump Dump a list of one of the following things:\n" + " nodes - all known nodes in the VPN\n" + " edges - all known connections in the VPN\n" + " subnets - all known subnets in the VPN\n" + " connections - all meta connections with ourself\n" + " graph - graph of the VPN in dotty format\n" + " purge Purge unreachable nodes\n" + " debug N Set debug level\n" + " retry Retry all outgoing connections\n" + " reload Partial reload of configuration\n" + "\n")); + printf(_("Report bugs to tinc@tinc-vpn.org.\n")); + } +} + +static bool parse_options(int argc, char **argv) { + int r; + int option_index = 0; + + while((r = getopt_long(argc, argv, "c:n:", long_options, &option_index)) != EOF) { + switch (r) { + case 0: /* long option */ + break; + + case 'c': /* config file */ + confbase = xstrdup(optarg); + break; + + case 'n': /* net name given */ + netname = xstrdup(optarg); + break; + + case 1: /* show help */ + show_help = true; + break; + + case 2: /* show version */ + show_version = true; + break; + + case 5: /* open control socket here */ + controlsocketname = xstrdup(optarg); + break; + + case '?': + usage(true); + return false; + + default: + break; + } + } + + return true; +} + +FILE *ask_and_open(const char *filename, const char *what, const char *mode) { + FILE *r; + char *directory; + char buf[PATH_MAX]; + char buf2[PATH_MAX]; + size_t len; + + /* Check stdin and stdout */ + if(isatty(0) && isatty(1)) { + /* Ask for a file and/or directory name. */ + fprintf(stdout, _("Please enter a file to save %s to [%s]: "), + what, filename); + fflush(stdout); + + if(fgets(buf, sizeof buf, stdin) < 0) { + fprintf(stderr, _("Error while reading stdin: %s\n"), + strerror(errno)); + return NULL; + } + + len = strlen(buf); + if(len) + buf[--len] = 0; + + if(len) + filename = buf; + } + +#ifdef HAVE_MINGW + if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) { +#else + if(filename[0] != '/') { +#endif + /* The directory is a relative path or a filename. */ + directory = get_current_dir_name(); + snprintf(buf2, sizeof buf2, "%s/%s", directory, filename); + filename = buf2; + } + + umask(0077); /* Disallow everything for group and other */ + + /* Open it first to keep the inode busy */ + + r = fopen(filename, mode); + + if(!r) { + fprintf(stderr, _("Error opening file `%s': %s\n"), filename, strerror(errno)); + return NULL; + } + + return r; +} + +/* + Generate a public/private RSA keypair, and ask for a file to store + them in. +*/ +static bool keygen(int bits) { + rsa_t key; + FILE *f; + char *name = NULL; + char *filename; + + fprintf(stderr, _("Generating %d bits keys:\n"), bits); + + if(!rsa_generate(&key, bits, 0x10001)) { + fprintf(stderr, _("Error during key generation!\n")); + return false; + } else + fprintf(stderr, _("Done.\n")); + + asprintf(&filename, "%s/rsa_key.priv", confbase); + f = ask_and_open(filename, _("private RSA key"), "a"); + + if(!f) + return false; + +#ifdef HAVE_FCHMOD + /* Make it unreadable for others. */ + fchmod(fileno(f), 0600); +#endif + + if(ftell(f)) + fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n")); + + rsa_write_pem_private_key(&key, f); + + fclose(f); + free(filename); + + if(name) + asprintf(&filename, "%s/hosts/%s", confbase, name); + else + asprintf(&filename, "%s/rsa_key.pub", confbase); + + f = ask_and_open(filename, _("public RSA key"), "a"); + + if(!f) + return false; + + if(ftell(f)) + fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n")); + + rsa_write_pem_public_key(&key, f); + + fclose(f); + free(filename); + + return true; +} + +/* + Set all files and paths according to netname +*/ +static void make_names(void) { +#ifdef HAVE_MINGW + HKEY key; + char installdir[1024] = ""; + long len = sizeof installdir; +#endif + + if(netname) + asprintf(&identname, "tinc.%s", netname); + else + identname = xstrdup("tinc"); + +#ifdef HAVE_MINGW + if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) { + if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) { + if(!logfilename) + asprintf(&logfilename, "%s/log/%s.log", identname); + if(!confbase) { + if(netname) + asprintf(&confbase, "%s/%s", installdir, netname); + else + asprintf(&confbase, "%s", installdir); + } + } + RegCloseKey(key); + if(*installdir) + return; + } +#endif + + if(!controlsocketname) + asprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname); + + if(netname) { + if(!confbase) + asprintf(&confbase, CONFDIR "/tinc/%s", netname); + else + fprintf(stderr, _("Both netname and configuration directory given, using the latter...\n")); + } else { + if(!confbase) + asprintf(&confbase, CONFDIR "/tinc"); + } +} + +static int fullread(int fd, void *data, size_t datalen) { + int rv, len = 0; + + while(len < datalen) { + rv = read(fd, data + len, datalen - len); + if(rv == -1 && errno == EINTR) + continue; + else if(rv == -1) + return rv; + else if(rv == 0) { + errno = ENODATA; + return -1; + } + len += rv; + } + return 0; +} + +/* + Send a request (raw) +*/ +static int send_ctl_request(int fd, enum request_type type, + void const *outdata, size_t outdatalen, + int *res_errno_p, void **indata_p, + size_t *indatalen_p) { + tinc_ctl_request_t req; + int rv; + struct iovec vector[2] = { + {&req, sizeof req}, + {(void*) outdata, outdatalen} + }; + void *indata; + + if(res_errno_p == NULL) + return -1; + + memset(&req, 0, sizeof req); + req.length = sizeof req + outdatalen; + req.type = type; + req.res_errno = 0; + + while((rv = writev(fd, vector, 2)) == -1 && errno == EINTR) ; + if(rv != req.length) + return -1; + + if(fullread(fd, &req, sizeof req) == -1) + return -1; + + if(req.length < sizeof req) { + errno = EINVAL; + return -1; + } + + if(req.length > sizeof req) { + if(indata_p == NULL) { + errno = EINVAL; + return -1; + } + + indata = xmalloc(req.length - sizeof req); + + if(fullread(fd, indata, req.length - sizeof req) == -1) { + free(indata); + return -1; + } + + *indata_p = indata; + if(indatalen_p != NULL) + *indatalen_p = req.length - sizeof req; + } + + *res_errno_p = req.res_errno; + + return 0; +} + +/* + Send a request (with printfs) +*/ +static int send_ctl_request_cooked(int fd, enum request_type type, + void const *outdata, size_t outdatalen) +{ + int res_errno = -1; + char *buf = NULL; + size_t buflen = 0; + + if(send_ctl_request(fd, type, outdata, outdatalen, &res_errno, + (void**) &buf, &buflen)) { + fprintf(stderr, _("Error sending request: %s\n"), strerror(errno)); + return -1; + } + + if(buf != NULL) { + printf("%*s", (int)buflen, buf); + free(buf); + } + + if(res_errno != 0) { + fprintf(stderr, _("Server reported error: %s\n"), strerror(res_errno)); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[], char *envp[]) { + struct sockaddr_un addr; + tinc_ctl_greeting_t greeting; + int fd; + int result; + + program_name = argv[0]; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + if(!parse_options(argc, argv)) + return 1; + + make_names(); + + if(show_version) { + printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, + VERSION, __DATE__, __TIME__, PROT_CURRENT); + printf(_("Copyright (C) 1998-2007 Ivo Timmermans, Guus Sliepen and others.\n" + "See the AUTHORS file for a complete list.\n\n" + "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n" + "and you are welcome to redistribute it under certain conditions;\n" + "see the file COPYING for details.\n")); + + return 0; + } + + if(show_help) { + usage(false); + return 0; + } + + if(optind >= argc) { + fprintf(stderr, _("Not enough arguments.\n")); + usage(true); + return 1; + } + + // First handle commands that don't involve connecting to a running tinc daemon. + + if(!strcasecmp(argv[optind], "generate-keys")) { - return !keygen(optind > argc ? atoi(argv[optind + 1]) : 1024); ++ return !keygen(optind > argc ? atoi(argv[optind + 1]) : 2048); + } + + if(!strcasecmp(argv[optind], "start")) { + argv[optind] = NULL; + execve(SBINDIR "/tincd", argv, envp); + fprintf(stderr, _("Could not start tincd: %s"), strerror(errno)); + return 1; + } + + /* + * Now handle commands that do involve connecting to a running tinc daemon. + * Authenticate the server by ensuring the parent directory can be + * traversed only by root. Note this is not totally race-free unless all + * ancestors are writable only by trusted users, which we don't verify. + */ + + struct stat statbuf; + char *lastslash = strrchr(controlsocketname, '/'); + if(lastslash != NULL) { + /* control socket is not in cwd; stat its parent */ + *lastslash = 0; + result = stat(controlsocketname, &statbuf); + *lastslash = '/'; + } else + result = stat(".", &statbuf); + + if(result < 0) { + fprintf(stderr, _("Unable to check control socket directory permissions: %s\n"), strerror(errno)); + return 1; + } + + if(statbuf.st_uid != 0 || (statbuf.st_mode & S_IXOTH) != 0 || (statbuf.st_gid != 0 && (statbuf.st_mode & S_IXGRP)) != 0) { + fprintf(stderr, _("Insecure permissions on control socket directory\n")); + return 1; + } + + if(strlen(controlsocketname) >= sizeof addr.sun_path) { + fprintf(stderr, _("Control socket filename too long!\n")); + return 1; + } + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd < 0) { + fprintf(stderr, _("Cannot create UNIX socket: %s\n"), strerror(errno)); + return 1; + } + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, controlsocketname, sizeof addr.sun_path - 1); + + if(connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) { + fprintf(stderr, _("Cannot connect to %s: %s\n"), controlsocketname, strerror(errno)); + return 1; + } + + if(fullread(fd, &greeting, sizeof greeting) == -1) { + fprintf(stderr, _("Cannot read greeting from control socket: %s\n"), + strerror(errno)); + return 1; + } + + if(greeting.version != TINC_CTL_VERSION_CURRENT) { + fprintf(stderr, _("Version mismatch: server %d, client %d\n"), + greeting.version, TINC_CTL_VERSION_CURRENT); + return 1; + } + + if(!strcasecmp(argv[optind], "pid")) { + printf("%d\n", greeting.pid); + return 0; + } + + if(!strcasecmp(argv[optind], "stop")) { + return send_ctl_request_cooked(fd, REQ_STOP, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind], "reload")) { + return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind], "restart")) { + return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind], "dump")) { + if(argc < optind + 2) { + fprintf(stderr, _("Not enough arguments.\n")); + usage(true); + return 1; + } + + if(!strcasecmp(argv[optind+1], "nodes")) { + return send_ctl_request_cooked(fd, REQ_DUMP_NODES, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind+1], "edges")) { + return send_ctl_request_cooked(fd, REQ_DUMP_EDGES, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind+1], "subnets")) { + return send_ctl_request_cooked(fd, REQ_DUMP_SUBNETS, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind+1], "connections")) { + return send_ctl_request_cooked(fd, REQ_DUMP_CONNECTIONS, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind+1], "graph")) { + return send_ctl_request_cooked(fd, REQ_DUMP_GRAPH, NULL, 0) != -1; + } + + fprintf(stderr, _("Unknown dump type '%s'.\n"), argv[optind+1]); + usage(true); + return 1; + } + + if(!strcasecmp(argv[optind], "purge")) { + return send_ctl_request_cooked(fd, REQ_PURGE, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind], "debug")) { + int debuglevel; + + if(argc != optind + 2) { + fprintf(stderr, "Invalid arguments.\n"); + return 1; + } + debuglevel = atoi(argv[optind+1]); + return send_ctl_request_cooked(fd, REQ_SET_DEBUG, &debuglevel, + sizeof debuglevel) != -1; + } + + if(!strcasecmp(argv[optind], "retry")) { + return send_ctl_request_cooked(fd, REQ_RETRY, NULL, 0) != -1; + } + + if(!strcasecmp(argv[optind], "reload")) { + return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1; + } + + fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]); + usage(true); + + close(fd); + + return 0; +} diff --combined src/tincd.c index 76053a00,602f18b1..48f5faf8 --- a/src/tincd.c +++ b/src/tincd.c @@@ -31,6 -31,12 +31,6 @@@ #include #endif -#include -#include -#include -#include -#include - #include LZO1X_H #ifndef HAVE_MINGW @@@ -40,10 -46,9 +40,10 @@@ #endif #include -#include "pidfile.h" #include "conf.h" +#include "control.h" +#include "crypto.h" #include "device.h" #include "logger.h" #include "net.h" @@@ -62,6 -67,12 +62,6 @@@ bool show_help = false /* If nonzero, print the version on standard output and exit. */ bool show_version = false; -/* If nonzero, it will attempt to kill a running tincd and exit. */ -int kill_tincd = 0; - -/* If nonzero, generate public/private keypair for this host/net. */ -int generate_keys = 0; - /* If nonzero, use null ciphers and skip all key exchanges. */ bool bypass_security = false; @@@ -78,7 -89,7 +78,7 @@@ static const char *switchuser = NULL bool use_logfile = false; char *identname = NULL; /* program name for syslog */ -char *pidfilename = NULL; /* pid file location */ +char *controlsocketname = NULL; /* control socket location */ char *logfilename = NULL; /* log file location */ char **g_argv; /* a copy of the cmdline arguments */ @@@ -86,22 -97,25 +86,23 @@@ static int status static struct option const long_options[] = { {"config", required_argument, NULL, 'c'}, - {"kill", optional_argument, NULL, 'k'}, {"net", required_argument, NULL, 'n'}, {"help", no_argument, NULL, 1}, {"version", no_argument, NULL, 2}, {"no-detach", no_argument, NULL, 'D'}, - {"generate-keys", optional_argument, NULL, 'K'}, {"debug", optional_argument, NULL, 'd'}, {"bypass-security", no_argument, NULL, 3}, {"mlock", no_argument, NULL, 'L'}, {"chroot", no_argument, NULL, 'R'}, {"user", required_argument, NULL, 'U'}, {"logfile", optional_argument, NULL, 4}, - {"pidfile", required_argument, NULL, 5}, + {"controlsocket", required_argument, NULL, 5}, {NULL, 0, NULL, 0} }; #ifdef HAVE_MINGW static struct WSAData wsa_state; + CRITICAL_SECTION mutex; #endif static void usage(bool status) @@@ -111,17 -125,19 +112,17 @@@ program_name); else { printf(_("Usage: %s [option]...\n\n"), program_name); - printf(_(" -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID to FILENAME.\n" - " -R, --chroot chroot to NET dir at startup.\n" - " -U, --user=USER setuid to given USER at startup.\n" - " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n")); + printf(_( " -c, --config=DIR Read configuration options from DIR.\n" + " -D, --no-detach Don't fork and detach.\n" + " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" + " -n, --net=NETNAME Connect to net NETNAME.\n" + " -L, --mlock Lock tinc into main memory.\n" + " --logfile[=FILENAME] Write log entries to a logfile.\n" + " --controlsocket=FILENAME Open control socket at FILENAME.\n" + " --bypass-security Disables meta protocol security, for debugging.\n" + " -R, --chroot chroot to NET dir at startup.\n" + " -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n" + " --version Output version information and exit.\n\n")); printf(_("Report bugs to tinc@tinc-vpn.org.\n")); } } @@@ -131,7 -147,7 +132,7 @@@ static bool parse_options(int argc, cha int r; int option_index = 0; - while((r = getopt_long(argc, argv, "c:DLd::k::n:K::RU:", long_options, &option_index)) != EOF) { + while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) { switch (r) { case 0: /* long option */ break; @@@ -160,10 -176,62 +161,10 @@@ debug_level++; break; - case 'k': /* kill old tincds */ -#ifndef HAVE_MINGW - if(optarg) { - if(!strcasecmp(optarg, "HUP")) - kill_tincd = SIGHUP; - else if(!strcasecmp(optarg, "TERM")) - kill_tincd = SIGTERM; - else if(!strcasecmp(optarg, "KILL")) - kill_tincd = SIGKILL; - else if(!strcasecmp(optarg, "USR1")) - kill_tincd = SIGUSR1; - else if(!strcasecmp(optarg, "USR2")) - kill_tincd = SIGUSR2; - else if(!strcasecmp(optarg, "WINCH")) - kill_tincd = SIGWINCH; - else if(!strcasecmp(optarg, "INT")) - kill_tincd = SIGINT; - else if(!strcasecmp(optarg, "ALRM")) - kill_tincd = SIGALRM; - else { - kill_tincd = atoi(optarg); - - if(!kill_tincd) { - fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), - optarg); - usage(true); - return false; - } - } - } else - kill_tincd = SIGTERM; -#else - kill_tincd = 1; -#endif - break; - case 'n': /* net name given */ netname = xstrdup(optarg); break; - case 'K': /* generate public/private keypair */ - if(optarg) { - generate_keys = atoi(optarg); - - if(generate_keys < 512) { - fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"), - optarg); - usage(true); - return false; - } - - generate_keys &= ~7; /* Round it to bytes */ - } else - generate_keys = 2048; - break; - case 'R': /* chroot to NETNAME dir */ do_chroot = true; break; @@@ -190,8 -258,8 +191,8 @@@ logfilename = xstrdup(optarg); break; - case 5: /* write PID to a file */ - pidfilename = xstrdup(optarg); + case 5: /* open control socket here */ + controlsocketname = xstrdup(optarg); break; case '?': @@@ -206,6 -274,110 +207,6 @@@ return true; } -/* This function prettyprints the key generation process */ - -static void indicator(int a, int b, void *p) -{ - switch (a) { - case 0: - fprintf(stderr, "."); - break; - - case 1: - fprintf(stderr, "+"); - break; - - case 2: - fprintf(stderr, "-"); - break; - - case 3: - switch (b) { - case 0: - fprintf(stderr, " p\n"); - break; - - case 1: - fprintf(stderr, " q\n"); - break; - - default: - fprintf(stderr, "?"); - } - break; - - default: - fprintf(stderr, "?"); - } -} - -/* - Generate a public/private RSA keypair, and ask for a file to store - them in. -*/ -static bool keygen(int bits) -{ - RSA *rsa_key; - FILE *f; - char *name = NULL; - char *filename; - - get_config_string(lookup_config(config_tree, "Name"), &name); - - if(name && !check_id(name)) { - fprintf(stderr, _("Invalid name for myself!\n")); - return false; - } - - fprintf(stderr, _("Generating %d bits keys:\n"), bits); - rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL); - - if(!rsa_key) { - fprintf(stderr, _("Error during key generation!\n")); - return false; - } else - fprintf(stderr, _("Done.\n")); - - xasprintf(&filename, "%s/rsa_key.priv", confbase); - f = ask_and_open(filename, _("private RSA key")); - - if(!f) - return false; - - if(disable_old_keys(f)) - fprintf(stderr, _("Warning: old key(s) found and disabled.\n")); - -#ifdef HAVE_FCHMOD - /* Make it unreadable for others. */ - fchmod(fileno(f), 0600); -#endif - - PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL); - fclose(f); - free(filename); - - if(name) - xasprintf(&filename, "%s/hosts/%s", confbase, name); - else - xasprintf(&filename, "%s/rsa_key.pub", confbase); - - f = ask_and_open(filename, _("public RSA key")); - - if(!f) - return false; - - if(disable_old_keys(f)) - fprintf(stderr, _("Warning: old key(s) found and disabled.\n")); - - PEM_write_RSAPublicKey(f, rsa_key); - fclose(f); - free(filename); - if(name) - free(name); - - return true; -} - /* Set all files and paths according to netname */ @@@ -214,11 -386,11 +215,11 @@@ static void make_names(void #ifdef HAVE_MINGW HKEY key; char installdir[1024] = ""; - long len = sizeof(installdir); + long len = sizeof installdir; #endif if(netname) - asprintf(&identname, "tinc.%s", netname); + xasprintf(&identname, "tinc.%s", netname); else identname = xstrdup("tinc"); @@@ -226,12 -398,12 +227,12 @@@ if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) { if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) { if(!logfilename) - asprintf(&logfilename, "%s/log/%s.log", identname); + xasprintf(&logfilename, "%s/log/%s.log", identname); if(!confbase) { if(netname) - asprintf(&confbase, "%s/%s", installdir, netname); + xasprintf(&confbase, "%s/%s", installdir, netname); else - asprintf(&confbase, "%s", installdir); + xasprintf(&confbase, "%s", installdir); } } RegCloseKey(key); @@@ -240,27 -412,27 +241,27 @@@ } #endif - if(!pidfilename) - xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname); + if(!controlsocketname) - asprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname); ++ xasprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname); if(!logfilename) - asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname); + xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname); if(netname) { if(!confbase) - asprintf(&confbase, CONFDIR "/tinc/%s", netname); + xasprintf(&confbase, CONFDIR "/tinc/%s", netname); else logger(LOG_INFO, _("Both netname and configuration directory given, using the latter...")); } else { if(!confbase) - asprintf(&confbase, CONFDIR "/tinc"); + xasprintf(&confbase, CONFDIR "/tinc"); } } static void free_names() { if (identname) free(identname); if (netname) free(netname); - if (pidfilename) free(pidfilename); + if (controlsocketname) free(controlsocketname); if (logfilename) free(logfilename); if (confbase) free(confbase); } @@@ -314,7 -486,7 +315,7 @@@ static bool drop_privs() } #ifdef HAVE_MINGW - # define setpriority(level) SetPriorityClass(GetCurrentProcess(), level); + # define setpriority(level) SetPriorityClass(GetCurrentProcess(), level) #else # define NORMAL_PRIORITY_CLASS 0 # define BELOW_NORMAL_PRIORITY_CLASS 10 @@@ -352,24 -524,28 +353,24 @@@ int main(int argc, char **argv return 0; } - if(kill_tincd) - return !kill_other(kill_tincd); - openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR); + if(!event_init()) { + logger(LOG_ERR, _("Error initializing libevent!")); + return 1; + } + + if(!init_control()) + return 1; + g_argv = argv; init_configuration(&config_tree); /* Slllluuuuuuurrrrp! */ - RAND_load_file("/dev/urandom", 1024); - - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - OpenSSL_add_all_algorithms(); - - if(generate_keys) { - read_server_config(); - return !keygen(generate_keys); - } + srand(time(NULL)); + crypto_init(); if(!read_server_config()) return 1; @@@ -393,6 -569,8 +394,8 @@@ int main2(int argc, char **argv) { + InitializeCriticalSection(&mutex); + EnterCriticalSection(&mutex); #endif if(!detach()) @@@ -454,10 -632,14 +457,10 @@@ end logger(LOG_NOTICE, _("Terminating")); #ifndef HAVE_MINGW - remove_pid(pidfilename); + exit_control(); #endif - EVP_cleanup(); - ENGINE_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); + crypto_exit(); exit_configuration(&config_tree); free_names(); diff --combined src/uml_socket/device.c index 30194d9c,de0b4a55..ec9dcb1e --- a/src/uml_socket/device.c +++ b/src/uml_socket/device.c @@@ -57,7 -57,8 +57,7 @@@ static struct request static struct sockaddr_un data_sun; -bool setup_device(void) -{ +bool setup_device(void) { struct sockaddr_un listen_sun; static const int one = 1; struct { @@@ -70,7 -71,7 +70,7 @@@ cp(); if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - asprintf(&device, LOCALSTATEDIR "/run/%s.umlsocket", identname); + xasprintf(&device, LOCALSTATEDIR "/run/%s.umlsocket", identname); get_config_string(lookup_config(config_tree, "Interface"), &iface); @@@ -153,7 -154,8 +153,7 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); if(listen_fd >= 0) @@@ -174,8 -176,9 +174,8 @@@ if(iface) free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); @@@ -205,7 -208,7 +205,7 @@@ } case 1: { - if((lenin = read(request_fd, &request, sizeof request)) != sizeof request) { + if((inlen = read(request_fd, &request, sizeof request)) != sizeof request) { logger(LOG_ERR, _("Error while reading request from %s %s: %s"), device_info, device, strerror(errno)); running = false; @@@ -235,14 -238,14 +235,14 @@@ } case 2: { - if((lenin = read(data_fd, packet->data, MTU)) <= 0) { + if((inlen = read(data_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); running = false; return false; } - packet->len = lenin; + packet->len = inlen; device_total_in += packet->len; @@@ -254,7 -257,8 +254,7 @@@ } } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); if(state != 2) { @@@ -280,7 -284,8 +280,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);