From: Guus Sliepen Date: Mon, 22 Mar 2004 12:30:39 +0000 (+0000) Subject: Imported start of brand new codebase for tinc 2.0. X-Git-Url: https://git.tinc-vpn.org/git/browse?a=commitdiff_plain;h=7d12cbb6e6acebbe8f9bcab75f5ec878a3360eb9;p=tinc Imported start of brand new codebase for tinc 2.0. --- 7d12cbb6e6acebbe8f9bcab75f5ec878a3360eb9 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..e64caa03 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,26 @@ +AUTOMAKE_OPTIONS = gnu + +EXTRA_DIST = config.rpath mkinstalldirs system.h depcomp + +SUBDIRS = cfg fd logger support tnl vnd + +sbin_PROGRAMS = tincd + +tincd_SOURCES = tincd.c + +tincd_LDADD = cfg/libcfg.a fd/libfd.a logger/liblogger.a support/libsupport.a tnl/libtnl.a vnd/libvnd.a -lgnutls + +ACLOCAL_AMFLAGS = -I m4 + +ChangeLog: + svn log > ChangeLog + +svn-clean: maintainer-clean + svn status --no-ignore | sed -n 's/^[?I] \+//p' | tr '\012' '\0' | xargs -r0 rm -rf + +release: + rm -f ChangeLog + $(MAKE) ChangeLog + echo "Please edit the NEWS file now..." + /usr/bin/editor NEWS + $(MAKE) dist diff --git a/cfg/Makefile.am b/cfg/Makefile.am new file mode 100644 index 00000000..49d353a6 --- /dev/null +++ b/cfg/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libcfg.a + +noinst_HEADERS = cfg.h + +libcfg_a_SOURCES = cfg.c + diff --git a/cfg/cfg.c b/cfg/cfg.c new file mode 100644 index 00000000..f0c8dad8 --- /dev/null +++ b/cfg/cfg.c @@ -0,0 +1,338 @@ +/* + cfg.c -- cfguration code + + Copyright (C) 1998 Robert van der Meulen + 1998-2004 Ivo Timmermans + 2000-2004 Guus Sliepen + 2000 Cris van Pelt + + 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 "cfg/cfg.h" +#include "support/avl.h" +#include "support/xalloc.h" + +static int cfg_compare(const cfg_t *a, const cfg_t *b) { + return strcasecmp(a->variable, b->variable) ?: (a->line - b->line) ?: strcmp(a->file, b->file); +} + +avl_tree_t *cfg_tree_new(void) { + return avl_tree_new((avl_compare_t)cfg_compare, (avl_action_t)cfg_free); +} + +void cfg_tree_del(avl_tree_t *cfgs) { + avl_tree_del(cfgs); +} + +cfg_t *cfg_new(void) { + cfg_t *cfg; + + return clear(new(cfg)); +} + +void cfg_free(cfg_t *cfg) { + replace(cfg->variable, NULL); + replace(cfg->value, NULL); + replace(cfg->file, NULL); + free(cfg); +} + +void cfg_add(avl_tree_t *cfgs, cfg_t *cfg) { + avl_add(cfgs, cfg); +} + +cfg_t *cfg_get(const avl_tree_t *cfgs, char *variable) { + cfg_t search, *cfg; + + search.variable = variable; + search.file = ""; + search.line = 0; + + cfg = avl_get_closest_greater(cfgs, &search); + + if(!cfg || strcasecmp(cfg->variable, variable)) + return NULL; + + return cfg; +} + +cfg_t *cfg_get_next(const avl_tree_t *cfgs, const cfg_t *cfg) { + avl_node_t *avl; + cfg_t *next; + + avl = avl_get_node(cfgs, cfg); + + if(avl && avl->next) { + next = avl->next->data; + + if(!strcasecmp(next->variable, cfg->variable)) + return next; + } + + return NULL; +} + +bool cfg_bool(const cfg_t *cfg, const bool def, bool *result) { + if(!cfg) { + *result = def; + return true; + } + + if(!strcasecmp(cfg->value, "yes")) { + *result = true; + return true; + } else if(!strcasecmp(cfg->value, "no")) { + *result = false; + return true; + } + + logger(LOG_ERR, _("cfg: \"yes\" or \"no\" expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_int(const cfg_t *cfg, const int def, int *result) { + if(!cfg) { + *result = def; + return true; + } + + if(sscanf(cfg->value, "%d", result) == 1) + return true; + + logger(LOG_ERR, _("cfg: integer expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_string(const cfg_t *cfg, const char *def, char **result) { + if(!cfg) { + *result = def ? xstrdup(def) : NULL; + return true; + } + + *result = xstrdup(cfg->value); + + return true; +} + +bool cfg_choice(const cfg_t *cfg, const cfg_choice_t *choice, const int def, int *result) { + int i; + + if(!cfg) { + *result = def; + return true; + } + + for(i = 0; choice[i].key; i++) { + if(!strcasecmp(cfg->variable, choice[i].key)) { + *result = choice[i].value; + return true; + } + } + + logger(LOG_ERR, _("cfg: invalid choice for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_period(const cfg_t *cfg, const int def, int *result) { + char unit; + + if(!cfg) { + *result = def; + return true; + } + + if(sscanf(cfg->value, "%d%c", result, &unit) == 2) { + switch(unit) { + case 's': + break; + case 'm': + *result *= 60; + break; + case 'h': + *result *= 60 * 60; + break; + case 'd': + *result *= 60 * 60 * 24; + break; + case 'W': + *result *= 60 * 60 * 24 * 7; + break; + case 'M': + *result *= 60 * 60 * 24 * 30; + break; + case 'Y': + *result *= 60 * 60 * 24 * 365; + break; + default: + logger(LOG_ERR, _("cfg: invalid period for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + return false; + } + return true; + } + + if(sscanf(cfg->value, "%d", result) == 1) + return true; + + logger(LOG_ERR, _("cfg: period expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +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 */ + char *idx; /* Read into this pointer, which points to an offset within line */ + size_t size, newsize; /* The size of the current array pointed to by line */ + size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */ + + if(feof(fp)) + return NULL; + + if(buf && buflen) { + size = *buflen; + line = *buf; + } else { + dim(line, size = 100); + } + + maxlen = size; + idx = line; + *idx = 0; + + for(;;) { + errno = 0; + p = fgets(idx, maxlen, fp); + + if(!p) { + if(feof(fp)) + break; + + logger(LOG_ERR, _("cfg: error while reading: %s"), strerror(errno)); + free(line); + return NULL; + } + + newline = strchr(p, '\n'); + + if(!newline) { + idx = &line[size - 1]; + maxlen = size + 1; + redim(line, size *= 2); + } else { + *newline = '\0'; + break; + } + } + + if(buf && buflen) { + *buflen = size; + *buf = line; + } + + return line; +} + +bool cfg_read_file(avl_tree_t *cfgs, const char *fname) { + FILE *fp; + char *buffer, *line; + char *variable, *value; + int lineno = 0; + int len; + bool result = false; + bool ignore = false; + cfg_t *cfg; + size_t bufsize; + + fp = fopen(fname, "r"); + + if(!fp) { + logger(LOG_ERR, _("cfg: error opening %s: %s"), fname, strerror(errno)); + return false; + } + + dim(buffer, bufsize = 100); + + for(;;) { + line = readline(fp, &buffer, &bufsize); + + if(!line) + break; + + if(feof(fp)) { + result = true; + break; + } + + lineno++; + + if(!*line || *line == '#') + continue; + + if(ignore) { + if(!strncmp(line, "-----END", 8)) + ignore = false; + continue; + } + + if(!strncmp(line, "-----BEGIN", 10)) { + ignore = true; + continue; + } + + variable = value = line; + + len = strcspn(value, "\t ="); + value += len; + value += strspn(value, "\t "); + if(*value == '=') { + value++; + value += strspn(value, "\t "); + } + variable[len] = '\0'; + + if(!*value) { + logger(LOG_ERR, _("cfg: no value for variable %s on line %d while reading cfg file %s"), + variable, lineno, fname); + break; + } + + cfg = cfg_new(); + replace(cfg->variable, variable); + replace(cfg->value, value); + replace(cfg->file, fname); + cfg->line = lineno; + + cfg_add(cfgs, cfg); + } + + free(buffer); + fclose(fp); + + return result; +} diff --git a/cfg/cfg.h b/cfg/cfg.h new file mode 100644 index 00000000..bbc01392 --- /dev/null +++ b/cfg/cfg.h @@ -0,0 +1,64 @@ +/* + conf.h -- header for conf.c + + Copyright (C) 1998-2004 Ivo Timmermans + 2000-2004 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$ +*/ + +#ifndef __TINC_CONF_H__ +#define __TINC_CONF_H__ + +#include "support/avl.h" + +typedef struct cfg { + char *variable; + char *value; + char *file; + int line; +} cfg_t; + +typedef struct cfg_choice { + char *key; + int value; +} cfg_choice_t; + +extern avl_tree_t *cfgs; + +extern avl_tree_t *cfg_tree_new(void); +extern void cfg_tree_free(avl_tree_t *); +extern cfg_t *cfg_new(void) __attribute__ ((__malloc__)); +extern void cfg_free(cfg_t *); +extern void cfg_add(avl_tree_t *, cfg_t *); +extern void cfg_del(avl_tree_t *, cfg_t *); +extern cfg_t *cfg_get(const avl_tree_t *, char *); +extern cfg_t *cfg_get_next(const avl_tree_t *, const cfg_t *); +extern bool cfg_bool(const cfg_t *, const bool, bool *); +extern bool cfg_int(const cfg_t *, const int, int *); +extern bool cfg_string(const cfg_t *, const char *, char **); +extern bool cfg_choice(const cfg_t *, const cfg_choice_t *, const int, int *); +extern bool cfg_period(const cfg_t *, const int, int *); +#define cfg_get_bool(tree, var, def, result) cfg_bool(cfg_get(tree, var), def, result) +#define cfg_get_int(tree, var, def, result) cfg_int(cfg_get(tree, var), def, result) +#define cfg_get_string(tree, var, def, result) cfg_string(cfg_get(tree, var), def, result) +#define cfg_get_choice(tree, var, choice, def, result) cfg_choice(cfg_get(tree, var), choice, def, (int *)result) +#define cfg_get_period(tree, var, def, result) cfg_period(cfg_get(tree, var), def, (int *)result) + +extern bool cfg_read_file(avl_tree_t *, const char *); + +#endif diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..c26b0496 --- /dev/null +++ b/configure.ac @@ -0,0 +1,288 @@ +AC_PREREQ(2.59) +AC_INIT +AC_CONFIG_SRCDIR([tincd.c]) +AM_INIT_AUTOMAKE(tinc, 2.0-svn) +AC_CONFIG_HEADERS([config.h]) +AM_MAINTAINER_MODE + +dnl Include the macros from the m4/ directory +dnl AM_ACLOCAL_INCLUDE(m4) + +dnl AM_GNU_GETTEXT([external]) +dnl AM_GNU_GETTEXT_VERSION(0.14.1) + +AC_DEFINE([_GNU_SOURCE], 1, [Enable GNU extenstions]) +AC_DEFINE([__USE_BSD], 1, [Enable BSD extensions]) + +dnl ALL_LINGUAS="nl" + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_GCC_TRADITIONAL +AC_PROG_AWK +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_RANLIB + +AC_ISC_POSIX + +dnl Check and set OS + +AC_CANONICAL_HOST + +case $host_os in + *linux*) + AC_DEFINE(HAVE_LINUX, 1, [Linux]) + ;; + *freebsd*) + AC_DEFINE(HAVE_FREEBSD, 1, [FreeBSD]) + ;; + *darwin*) + AC_DEFINE(HAVE_DARWIN, 1, [Darwin (MacOS/X)]) + ;; + *solaris*) + AC_DEFINE(HAVE_SOLARIS, 1, [Solaris/SunOS]) + ;; + *openbsd*) + AC_DEFINE(HAVE_OPENBSD, 1, [OpenBSD]) + ;; + *netbsd*) + AC_DEFINE(HAVE_NETBSD, 1, [NetBSD]) + ;; + *cygwin*) + AC_DEFINE(HAVE_CYGWIN, 1, [Cygwin]) + ;; + *mingw*) + AC_DEFINE(HAVE_MINGW, 1, [MinGW]) + LIBS="$LIBS -lws2_32" + ;; + *) + AC_MSG_ERROR("Unknown operating system.") + ;; +esac + +AC_CACHE_SAVE + +if test -d /sw/include ; then + CPPFLAGS="$CPPFLAGS -I/sw/include" +fi +if test -d /sw/lib ; then + LIBS="$LIBS -L/sw/lib" +fi + +dnl Checks for libraries. + +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/param.h sys/time.h sys/socket.h sys/wait.h sys/mman.h netdb.h arpa/inet.h]) +AC_CHECK_HEADERS([net/if.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h], + [], [], + [#ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_NETDB_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + ] +) +AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h], + [], [], + [#ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_NETDB_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_NET_IF_H + #include + #endif + #ifdef HAVE_NETINET_IN_SYSTM_H + #include + #endif + #ifdef HAVE_NETINET_IN_H + #include + #endif + #ifdef HAVE_NETINET_IN6_H + #include + #endif + #ifdef HAVE_NET_ETHERNET_H + #include + #endif + #ifdef HAVE_NET_IF_ARP_H + #include + #endif + ] +) +AC_CHECK_HEADERS([netinet/tcp.h netinet/ip_icmp.h netinet/icmp6.h], + [], [], + [#ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_NETDB_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_NET_IF_H + #include + #endif + #ifdef HAVE_NETINET_IN_SYSTM_H + #include + #endif + #ifdef HAVE_NETINET_IN_H + #include + #endif + #ifdef HAVE_NETINET_IP_H + #include + #endif + #ifdef HAVE_NETINET_IN6_H + #include + #endif + #ifdef HAVE_NETINET_IP6_H + #include + #endif + #ifdef HAVE_NET_ETHERNET_H + #include + #endif + #ifdef HAVE_NET_IF_ARP_H + #include + #endif + #ifdef HAVE_NETINET_IF_ETHER_H + #include + #endif + ] +) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_VOLATILE +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl tinc_ATTRIBUTE(__malloc__) + +AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp, struct in_addr, struct addrinfo, struct ip, struct icmp, struct in6_addr, struct sockaddr_in6, struct ip6_hdr, struct icmp6_hdr, struct nd_neighbor_solicit, struct nd_opt_hdr], , , + [#ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_NETDB_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_NET_IF_H + #include + #endif + #ifdef HAVE_NETINET_IN_SYSTM_H + #include + #endif + #ifdef HAVE_NETINET_IN_H + #include + #endif + #ifdef HAVE_NETINET_IP_H + #include + #endif + #ifdef HAVE_NETINET_TCP_H + #include + #endif + #ifdef HAVE_NETINET_IN6_H + #include + #endif + #ifdef HAVE_NETINET_IP6_H + #include + #endif + #ifdef HAVE_NET_ETHERNET_H + #include + #endif + #ifdef HAVE_NET_IF_ARP_H + #include + #endif + #ifdef HAVE_NETINET_IF_ETHER_H + #include + #endif + #ifdef HAVE_NETINET_IP_ICMP_H + #include + #endif + #ifdef HAVE_NETINET_ICMP6_H + #include + #endif + ] +) + +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]) +dnl jm_FUNC_MALLOC +dnl jm_FUNC_REALLOC + +dnl Support for SunOS + +AC_CHECK_FUNC(socket, [], [ + AC_CHECK_LIB(socket, connect) +]) +AC_CHECK_FUNC(gethostbyname, [], [ + AC_CHECK_LIB(nsl, gethostbyname) +]) + +AC_CHECK_FUNCS([freeaddrinfo gai_strerror getaddrinfo getnameinfo inet_aton]) + +AC_CACHE_SAVE + +dnl These are defined in files in m4/ + +dnl case $host_os in +dnl *linux*) +dnl tinc_TUNTAP +dnl ;; +dnl esac + +dnl tinc_OPENSSL +dnl tinc_ZLIB +dnl tinc_LZO + +AM_PATH_LIBGNUTLS([1.0.4], [], [AC_MSG_ERROR([GNUTLS library not found.]); break]) + +dnl Check if support for jumbograms is requested +dnl AC_ARG_ENABLE(jumbograms, +dnl AS_HELP_STRING(--enable-jumbograms,enable support for jumbograms (packets up to 9000 bytes)), +dnl [ AC_DEFINE(ENABLE_JUMBOGRAMS, 1, [Support for jumbograms (packets up to 9000 bytes)]) ] +dnl ) + +dnl Check if checkpoint tracing has to be enabled +dnl AC_ARG_ENABLE(tracing, +dnl AS_HELP_STRING(--enable-tracing,enable checkpoint tracing (debugging only)), +dnl [ AC_DEFINE(ENABLE_TRACING, 1, [Checkpoint tracing]) ] +dnl ) + +dnl AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile doc/tincd.8 doc/tinc.conf.5 doc/tincinclude.texi lib/Makefile po/Makefile.in m4/Makefile]) +AC_CONFIG_FILES([Makefile cfg/Makefile fd/Makefile logger/Makefile rt/Makefile support/Makefile tnl/Makefile vnd/Makefile]) + +AC_OUTPUT diff --git a/fd/Makefile.am b/fd/Makefile.am new file mode 100644 index 00000000..511a1e6c --- /dev/null +++ b/fd/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libfd.a + +noinst_HEADERS = event.h fd.h + +libfd_a_SOURCES = event.c fd.c + diff --git a/fd/event.c b/fd/event.c new file mode 100644 index 00000000..56d70374 --- /dev/null +++ b/fd/event.c @@ -0,0 +1,137 @@ +/* + event.c -- event queue + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "support/avl.h" +#include "support/xalloc.h" +#include "fd/event.h" + +avl_tree_t *events; + +static event_id_t id; + +static int timevalcmp(struct timeval a, struct timeval b) { + return a.tv_sec - b.tv_sec ?: a.tv_usec - b.tv_usec; +} + +static int event_compare(const event_t *a, const event_t *b) { + return timevalcmp(a->tv, b->tv) ?: a->id - b->id; +} + +bool event_init(void) { + events = avl_tree_new((avl_compare_t)event_compare, (avl_action_t)event_free); + + return true; +} + +bool event_exit(void) { + avl_tree_del(events); + + return true; +} + +event_t *event_new(void) { + event_t *event; + + return clear(new(event)); +} + +void event_free(event_t *event) { + free(event); +} + +void event_set(event_t *event, struct timeval timeout, event_handler_t handler, void *data) { + gettimeofday(&event->tv, NULL); + event_update(event, timeout); + event->interval = timeout; + event->handler = handler; + event->data = data; +} + +void event_update(event_t *event, struct timeval timeout) { + event->tv.tv_sec += timeout.tv_sec; + event->tv.tv_usec += timeout.tv_usec; + event->tv.tv_sec += event->tv.tv_usec / 1000000; + event->tv.tv_usec %= 1000000; +} + +bool event_add(event_t *event) { + event->id = ++id; + return avl_add(events, event); +} + +bool event_del(event_t *event) { + return avl_del(events, event); +} + +void event_handle(void) { + struct timeval now; + event_t *event; + avl_node_t *avl; + + gettimeofday(&now, NULL); + + avl_foreach_node(events, avl, { + event = avl->data; + + if(timercmp(&event->tv, &now, <)) { + avl_unlink_node(events, avl); + if(event->handler(event)) + avl_add_node(events, avl); + else + avl_node_free(events, avl); + } else { + break; + } + }); +} + +struct timeval event_timeout(void) { + struct timeval tv, now; + event_t *event; + + gettimeofday(&now, NULL); + + if(events->head) { + event = events->head->data; + + tv.tv_sec = event->tv.tv_sec - now.tv_sec; + tv.tv_usec = event->tv.tv_usec - now.tv_usec; + + if(tv.tv_usec < 0) { + tv.tv_usec += 1e6; + tv.tv_sec--; + } + + if(tv.tv_sec < 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } else { + tv.tv_sec = -1; + tv.tv_usec = -1; + } + + return tv; +} diff --git a/fd/event.h b/fd/event.h new file mode 100644 index 00000000..e54b6787 --- /dev/null +++ b/fd/event.h @@ -0,0 +1,51 @@ +/* + event.h -- event queue + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __EVENT_H__ +#define __EVENT_H__ + +typedef int event_id_t; + +struct event; + +typedef bool (*event_handler_t)(struct event *event); + +typedef struct event { + struct timeval tv; + struct timeval interval; + event_id_t id; + event_handler_t handler; + void *data; +} event_t; + +extern bool event_init(void); +extern bool event_exit(void); +extern bool event_add(struct event *event); +extern bool event_del(struct event *event); +extern struct event *event_new(void); +extern void event_free(struct event *); +extern void event_set(struct event *, struct timeval, event_handler_t, void *); +extern void event_update(struct event *, struct timeval); +extern void event_handle(void); +extern struct timeval event_timeout(void); + +#endif /* __EVENT_H__ */ diff --git a/fd/fd.c b/fd/fd.c new file mode 100644 index 00000000..966fe357 --- /dev/null +++ b/fd/fd.c @@ -0,0 +1,121 @@ +/* + fd.c -- I/O and event multiplexing + + Copyright (C) 2003-2004 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 "support/avl.h" +#include "support/xalloc.h" +#include "fd/event.h" +#include "fd/fd.h" + +static fd_set fd_sets[FD_MODES]; +static int max_fd; +static avl_tree_t *fds; + +volatile bool fd_running = false; + +int fd_compare(struct fd *a, struct fd *b) { + return (a->fd - b->fd) ?: (a->mode - b->mode); +}; + +bool fd_init(void) { + int i; + + for(i = 0; i < FD_MODES; i++) + FD_ZERO(&fd_sets[i]); + + fds = avl_tree_new((avl_compare_t)fd_compare, NULL); + + event_init(); +} + +bool fd_exit(void) { + event_exit(); + + avl_tree_del(fds); +} + +bool fd_add(struct fd *fd) { + if(!avl_add(fds, fd)) + return false; + + FD_SET(fd->fd, &fd_sets[fd->mode]); + + if(fd->fd > max_fd) + max_fd = fd->fd; + + return true; +}; + +bool fd_del(struct fd *fd) { + FD_CLR(fd->fd, &fd_sets[fd->mode]); + + if(fd->fd >= max_fd) + max_fd = ((struct fd *)fds->tail)->fd; + + return avl_del(fds, fd); +}; + +bool fd_run(void) { + struct timeval tv; + int result; + fd_set fd_cur[FD_MODES]; + + fd_running = true; + + logger(LOG_INFO, "fd: running"); + + while(fd_running) { + memcpy(fd_cur, fd_sets, sizeof(fd_cur)); + tv = event_timeout(); + + result = select(max_fd + 1, &fd_cur[0], &fd_cur[1], &fd_cur[2], tv.tv_sec >= 0 ? &tv : NULL); + + if(result < 0) { + if(errno != EINTR && errno != EAGAIN) { + logger(LOG_ERR, _("fd: error while waiting for input: %s"), strerror(errno)); + return false; + } + + continue; + } + + if(result) { + struct fd *fd; + + avl_foreach(fds, fd, { + if(FD_ISSET(fd->fd, &fd_cur[fd->mode])) + fd->handler(fd); + }); + } else { + event_handle(); + } + } + + logger(LOG_INFO, "fd: stopping"); + + return true; +} + +void fd_stop(void) { + fd_running = false; +} diff --git a/fd/fd.h b/fd/fd.h new file mode 100644 index 00000000..13714a47 --- /dev/null +++ b/fd/fd.h @@ -0,0 +1,51 @@ +/* + fd.h -- I/O and event multiplexing + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __FD_H__ +#define __FD_H__ + +enum fd_mode { + FD_MODE_READ = 0, + FD_MODE_WRITE, + FD_MODE_EXCEPT, + FD_MODES, +} fd_mode_t; + +struct fd; + +typedef bool (*fd_handler_t)(struct fd *); + +typedef struct fd { + int fd; + enum fd_mode mode; + fd_handler_t handler; + void *data; +} fd_t; + +extern bool fd_init(void); +extern bool fd_exit(void); +extern bool fd_add(struct fd *fd); +extern bool fd_del(struct fd *fd); +extern bool fd_run(void); +extern void fd_stop(void); + +#endif diff --git a/logger/Makefile.am b/logger/Makefile.am new file mode 100644 index 00000000..6fe30478 --- /dev/null +++ b/logger/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = liblogger.a + +noinst_HEADERS = logger.h + +liblogger_a_SOURCES = logger.c + diff --git a/logger/log.h b/logger/log.h new file mode 100644 index 00000000..e178d69a --- /dev/null +++ b/logger/log.h @@ -0,0 +1,77 @@ +/* + log.h -- logging + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +typedef enum debug_t { + DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */ + DEBUG_ALWAYS = 0, + DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */ + DEBUG_ERROR = 2, /* Show error messages received from other hosts */ + DEBUG_STATUS = 2, /* Show status messages received from other hosts */ + DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */ + DEBUG_META = 4, /* Show contents of every request that is sent/received */ + DEBUG_TRAFFIC = 5, /* Show network traffic information */ + DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */ + DEBUG_SCARY_THINGS = 10 /* You have been warned */ +} debug_t; + +typedef enum logmode_t { + LOGMODE_NULL, + LOGMODE_STDERR, + LOGMODE_FILE, + LOGMODE_SYSLOG +} logmode_t; + +#ifdef HAVE_MINGW +#define LOG_EMERG EVENTLOG_ERROR_TYPE +#define LOG_ALERT EVENTLOG_ERROR_TYPE +#define LOG_CRIT EVENTLOG_ERROR_TYPE +#define LOG_ERR EVENTLOG_ERROR_TYPE +#define LOG_WARNING EVENTLOG_WARNING_TYPE +#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE +#define LOG_INFO EVENTLOG_INFORMATION_TYPE +#define LOG_DEBUG EVENTLOG_INFORMATION_TYPE +#else +#ifndef HAVE_SYSLOG_H +enum { + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG, +}; +#endif +#endif + +extern debug_t debug_level; +extern void openlogger(const char *, logmode_t); +extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); +extern void closelogger(void); + +#define ifdebug(l) if(debug_level >= DEBUG_##l) + +#endif /* __LOGGER_H__ */ diff --git a/logger/logger.c b/logger/logger.c new file mode 100644 index 00000000..9bbd8fe7 --- /dev/null +++ b/logger/logger.c @@ -0,0 +1,141 @@ +/* + logger.c -- logging + + Copyright (C) 2003-2004 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 "logger/logger.h" + +logger_level_t logger_level = LOGGER_LEVEL_NONE; + +static logger_mode_t logger_mode = LOGGER_MODE_STDERR; +static pid_t logger_pid; +char *logger_filename; +static FILE *logger_file = NULL; +#ifdef HAVE_MINGW +static HANDLE logger_handle = NULL; +#endif +static const char *logger_ident = NULL; + +bool logger_init(const char *ident, logger_mode_t mode) { + logger_ident = ident; + logger_mode = mode; + + switch(mode) { + case LOGGER_MODE_STDERR: + logger_pid = getpid(); + break; + case LOGGER_MODE_FILE: + logger_pid = getpid(); + logger_file = fopen(logger_filename, "a"); + if(!logger_file) + logger_mode = LOGGER_MODE_NULL; + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + logger_handle = RegisterEventSource(NULL, logger_ident); + if(!logger_handle) + logger_mode = LOGGER_MODE_NULL; + break; +#else +#ifdef HAVE_SYSLOG_H + openlog(logger_ident, LOG_CONS | LOG_PID, LOG_DAEMON); + break; +#endif +#endif + case LOGGER_MODE_NULL: + break; + } + + return true; +} + +bool logger_exit(void) { + switch(logger_mode) { + case LOGGER_MODE_FILE: + fclose(logger_file); + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + DeregisterEventSource(logger_handle); + break; +#else +#ifdef HAVE_SYSLOG_H + closelog(); + break; +#endif +#endif + case LOGGER_MODE_NULL: + case LOGGER_MODE_STDERR: + break; + break; + } + + return true; +} + +void logger(int priority, const char *format, ...) { + va_list ap; + + va_start(ap, format); + + switch(logger_mode) { + case LOGGER_MODE_STDERR: + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + fflush(stderr); + break; + case LOGGER_MODE_FILE: + fprintf(logger_file, "%ld %s[%ld]: ", time(NULL), logger_ident, (long)logger_pid); + vfprintf(logger_file, format, ap); + fprintf(logger_file, "\n"); + fflush(logger_file); + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + { + char message[4096]; + char *messages[] = {message}; + vsnprintf(message, sizeof(message), format, ap); + ReportEvent(logger_handle, priority, 0, 0, NULL, 1, 0, messages, NULL); + } +#else +#ifdef HAVE_SYSLOG_H +#ifdef HAVE_VSYSLOG + vsyslog(priority, format, ap); +#else + { + char message[4096]; + vsnprintf(message, sizeof(message), format, ap); + syslog(priority, "%s", message); + } +#endif + break; +#endif +#endif + case LOGGER_MODE_NULL: + break; + } + + va_end(ap); +} + + diff --git a/logger/logger.h b/logger/logger.h new file mode 100644 index 00000000..afcf1e5c --- /dev/null +++ b/logger/logger.h @@ -0,0 +1,45 @@ +/* + logger.h -- logging + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +typedef enum logger_level { + LOGGER_LEVEL_NONE, + LOGGER_LEVEL_NOTICE, + LOGGER_LEVEL_WARNING, + LOGGER_LEVEL_ERROR, + LOGGER_LEVEL_DEBUG, +} logger_level_t; + +typedef enum logger_mode { + LOGGER_MODE_NULL, + LOGGER_MODE_STDERR, + LOGGER_MODE_FILE, + LOGGER_MODE_SYSLOG, +} logger_mode_t; + +extern bool logger_init(const char *, logger_mode_t); +extern bool logger_exit(void); +extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); + +#endif diff --git a/rt/Makefile.am b/rt/Makefile.am new file mode 100644 index 00000000..9c5c4326 --- /dev/null +++ b/rt/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = librt.a + +noinst_HEADERS = edge.h graph.h node.h rt.h subnet.h + +librt_a_SOURCES = edge.c graph.c node.c rt.c subnet.c + diff --git a/rt/edge.c b/rt/edge.c new file mode 100644 index 00000000..bf78d1fc --- /dev/null +++ b/rt/edge.c @@ -0,0 +1,97 @@ +/* + edge.c -- edge management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "rt/edge.h" +#include "rt/node.h" +#include "support/avl.h" +#include "support/xalloc.h" + +avl_tree_t *edges; + +static int edge_compare(const edge_t *a, const edge_t *b) { + return strcmp(a->to->name, b->to->name); +} + +static int edge_weight_compare(const edge_t *a, const edge_t *b) { + return (a->weight - b->weight) ?: strcmp(a->from->name, b->from->name) ?: strcmp(a->to->name, b->to->name); +} + +bool edge_init(void) { + edges = avl_tree_new((avl_compare_t)edge_weight_compare, NULL); + + return true; +} + +bool edge_exit(void) { + avl_tree_free(edges); + + return true; +} + +avl_tree_t *edge_tree_new(void) { + return avl_tree_new((avl_compare_t)edge_compare, (avl_action_t)edge_free); +} + +void edge_tree_free(avl_tree_t *edge_tree) { + avl_tree_free(edge_tree); +} + +edge_t *edge_new(void) { + edge_t *edge; + + return clear(new(edge)); +} + +void edge_free(edge_t *edge) { + free(edge); +} + +void edge_add(edge_t *edge) { + avl_add(edge->from->edges, edge); + avl_add(edges, edge); + + edge->reverse = edge_get(edge->to, edge->from); + + if(edge->reverse) + edge->reverse->reverse = edge; +} + +void edge_del(edge_t *edge) { + if(edge->reverse) + edge->reverse->reverse = NULL; + + avl_del(edges, edge); + avl_del(edge->from->edges, edge); +} + +edge_t *edge_get(node_t *from, node_t *to) { + edge_t search = {0}; + + search.from = from; + search.to = to; + + return avl_get(from->edges, &search); +} + diff --git a/rt/edge.h b/rt/edge.h new file mode 100644 index 00000000..efaddb27 --- /dev/null +++ b/rt/edge.h @@ -0,0 +1,60 @@ +/* + edge.h -- edge management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __EDGE_H__ +#define __EDGE_H__ + +#include "rt/node.h" +#include "support/avl.h" + +typedef struct edge_status { + int visited:1; + int mst:1; +} edge_status_t; + +typedef struct edge { + struct node *from; + struct node *to; + struct sockaddr_storage address; + + int weight; + + struct edge *reverse; + + edge_status_t status; + node_options_t options; +} edge_t; + +extern avl_tree_t *edges; + +extern bool edge_init(void); +extern bool edge_exit(void); +extern struct edge *edge_new(void) __attribute__ ((__malloc__)); +extern void edge_free(struct edge *); +extern avl_tree_t *edge_tree_new(void) __attribute__ ((__malloc__)); +extern void edge_tree_free(avl_tree_t *); +extern void edge_add(struct edge *); +extern void edge_del(struct edge *); +extern struct edge *edge_get(struct node *, struct node *); + +#endif diff --git a/rt/graph.h b/rt/graph.h new file mode 100644 index 00000000..91f58fd0 --- /dev/null +++ b/rt/graph.h @@ -0,0 +1,30 @@ +/* + graph.h -- graph algorithms + + Copyright (C) 2001-2004 Guus Sliepen , + 2001-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __GRAPH_H__ +#define __GRAPH_H__ + +extern bool graph_changed; +extern void graph(void); + +#endif /* __GRAPH_H__ */ diff --git a/rt/node.c b/rt/node.c new file mode 100644 index 00000000..4178b15f --- /dev/null +++ b/rt/node.c @@ -0,0 +1,120 @@ +/* + node.c -- node management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "cfg/cfg.h" +#include "logger/logger.h" +#include "rt/node.h" +#include "support/avl.h" +#include "support/xalloc.h" +#include "tincd.h" + +avl_tree_t *nodes; + +node_t *myself; + +static int node_compare(const node_t *a, const node_t *b) { + return strcmp(a->name, b->name); +} + +bool node_validname(const char *name) { + for(; *name; name++) + if(!isalnum(*name) && *name != '_') + return false; + + return true; +} + +bool node_init(void) { + nodes = avl_tree_new((avl_compare_t)node_compare, (avl_action_t)node_free); + myself = node_new(); + + if(!cfg_get_string(tinc_cfg, "Name", NULL, &myself->name) || !myself->name) { + logger(LOG_ERR, _("rt: name for tinc daemon required!")); + node_exit(); + return false; + } + + if(!node_validname(myself->name)) { + logger(LOG_ERR, _("rt: invalid name for myself!")); + node_exit(); + return false; + } + + return true; +} + +bool node_exit(void) { + avl_tree_del(nodes); + return true; +} + +node_t *node_new(void) { + node_t *node; + + clear(new(node)); + node->subnets = subnet_tree_new(); + node->edges = edge_tree_new(); + node->queue = avl_tree_new(NULL, (avl_action_t)free); + + return node; +} + +void node_free(node_t *node) { + if(node->queue) + avl_tree_free(node->queue); + + if(node->subnets) + subnet_tree_free(node->subnets); + + if(node->edges) + edge_tree_free(node->edges); + + replace(node->name, NULL); + + free(node); +} + +void node_add(node_t *node) { + avl_add(nodes, node); +} + +void node_del(node_t *node) { + edge_t *edge; + subnet_t *subnet; + + avl_foreach(node->subnets, subnet, subnet_del(subnet)); + avl_foreach(node->edges, edge, edge_del(edge)); + + avl_del(nodes, node); +} + +node_t *node_get(char *name) { + node_t search = {0}; + + search.name = name; + + return avl_get(nodes, &search); +} + diff --git a/rt/node.h b/rt/node.h new file mode 100644 index 00000000..0ef62e2a --- /dev/null +++ b/rt/node.h @@ -0,0 +1,75 @@ +/* + node.h -- node management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __NODE_H__ +#define __NODE_H__ + +typedef int node_options_t; + +#define NODE_OPTIONS_INDIRECT 1 + +#include "rt/edge.h" +#include "rt/subnet.h" +#include "support/avl.h" +#include "tnl/tnl.h" + +typedef struct node_status { + int active:1; + int visited:1; + int reachable:1; + int indirect:1; +} node_status_t; + +typedef struct node { + char *name; + + avl_tree_t *queue; + + struct node *nexthop; + struct node *via; + + avl_tree_t *subnets; + avl_tree_t *edges; + + struct tnl *tnl; + + node_status_t status; + node_options_t options; + + struct sockaddr_storage address; + + avl_tree_t *cfg; +} node_t; + +extern avl_tree_t *nodes; +extern struct node *myself; + +extern bool node_init(void); +extern bool node_exit(void); +extern struct node *node_new(void) __attribute__ ((__malloc__)); +extern void node_free(struct node *); +extern void node_add(struct node *); +extern void node_del(struct node *); +extern struct node *node_get(char *); + +#endif diff --git a/rt/rt.c b/rt/rt.c new file mode 100644 index 00000000..105249a6 --- /dev/null +++ b/rt/rt.c @@ -0,0 +1,200 @@ +/* + rt.c -- routing + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "cfg/cfg.h" +#include "rt/edge.h" +#include "rt/node.h" +#include "rt/rt.h" +#include "rt/subnet.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" +#include "vnd/vnd.h" +#include "tincd.h" + +vnd_t *rt_vnd = NULL; +int rt_af = AF_UNSPEC; +int rt_macexpire = 600; +int rt_maxtimeout = 900; +rt_mode_t rt_mode = RT_MODE_ROUTER; +bool rt_priorityinheritance = false; +bool rt_hostnames = false; + +static bool rt_tnl_accept(tnl_t *t) { +} + +static bool rt_vnd_recv(vnd_t *vnd, const char *buf, int len) { + route(myself, buf, len); +} + +static bool rt_tnl_recv_packet(tnl_t *tnl, const char *buf, int len) { + route(tnl->data, buf, len); +} + +static bool rt_tnl_recv_meta(tnl_t *tnl, const char *buf, int len) { + //route(tnl->data, buf, len); +} + +static void rt_outgoing(char *wft) { +} + +static void route(node_t *node, char *buf, int len) { +} + +bool rt_init(void) { + char *bindtoaddress = NULL; + char *bindtointerface = NULL; + char *device = NULL; + char *iface = NULL; + char *port = NULL; + cfg_t *cfg; + subnet_t *subnet; + struct addrinfo hint, *ai, *aip; + int err; + int listeners; + char *connectto = NULL; + + cfg_choice_t mode_choice[] = { + {"Router", RT_MODE_ROUTER}, + {"Switch", RT_MODE_SWITCH}, + {"Hub", RT_MODE_HUB}, + }; + + cfg_choice_t af_choice[] = { + {"IPv4", AF_INET}, + {"IPv6", AF_INET6}, + {"Any", AF_UNSPEC}, + }; + + logger(LOG_INFO, _("rt: initialising")); + + if(!subnet_init() || !node_init() || !edge_init()) + return false; + + /* Read main configuration */ + + if(!cfg_get_choice(tinc_cfg, "AddressFamily", af_choice, AF_UNSPEC, &rt_af) + || !cfg_get_string(tinc_cfg, "BindToAddress", NULL, &bindtoaddress) + || !cfg_get_string(tinc_cfg, "BindToInterface", NULL, &bindtointerface) + || !cfg_get_string(tinc_cfg, "Device", "/dev/net/tun", &device) + || !cfg_get_bool(tinc_cfg, "Hostnames", false, &rt_hostnames) + || !cfg_get_string(tinc_cfg, "Interface", tinc_netname, &iface) + || !cfg_get_period(tinc_cfg, "MACExpire", 600, &rt_macexpire) + || !cfg_get_period(tinc_cfg, "MaxTimeout", 3600, &rt_maxtimeout) + || !cfg_get_choice(tinc_cfg, "Mode", mode_choice, RT_MODE_ROUTER, &rt_mode) + || !cfg_get_bool(tinc_cfg, "PriorityInheritance", false, &rt_priorityinheritance)) + return false; + + /* Read host configuration for myself */ + + if(!cfg_get_string(myself->cfg, "Port", "655", &port)) + return false; + + for(cfg = cfg_get(myself->cfg, "Subnet"); cfg; cfg = cfg_get_next(myself->cfg, cfg)) { + if(!cfg_subnet(cfg, &subnet)) + return false; + + subnet->owner = myself; + subnet_add(subnet); + } + + /* Open the virtual network device */ + + if(!cfg_get_string(tinc_cfg, "Device", "/dev/net/tun", &rt_vnd->device) + || !cfg_get_string(tinc_cfg, "Interface", tinc_netname, &rt_vnd->interface) + || !cfg_get_choice(tinc_cfg, "Mode", mode_choice, RT_MODE_ROUTER, rt_mode)) { + vnd_free(rt_vnd); + return false; + } + + rt_vnd->mode = (rt_mode == RT_MODE_ROUTER) ? VND_MODE_TUN : VND_MODE_TAP; + rt_vnd->recv = rt_vnd_recv; + + if(!vnd_open(rt_vnd)) { + vnd_free(rt_vnd); + return false; + } + + /* Create listening sockets */ + + hint.ai_family = rt_af; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + hint.ai_flags = AI_PASSIVE; + + err = getaddrinfo(bindtoaddress, port, &hint, &ai); + + if(err || !ai) { + logger(LOG_ERR, _("rt: system call '%s' failed: %s"), "getaddrinfo", gai_strerror(err)); + return false; + } + + listeners = 0; + + for(aip = ai; aip; aip = aip->ai_next) { + tnl_listen_t *listener; + + clear(new(listener)); + listener->local.address = *(struct sockaddr_storage *)aip->ai_addr; + listener->local.id = myself; + // listener->local.cred = ...; + + if(tnl_listen(listener)) + listeners++; + } + + freeaddrinfo(ai); + + if(!listeners) { + logger(LOG_ERR, _("rt: unable to create any listening socket!")); + return false; + } + + /* Setup outgoing connections */ + + for(cfg = cfg_get(tinc_cfg, "ConnectTo"); cfg; cfg = cfg_get_next(tinc_cfg, cfg)) { + if(!cfg_string(cfg, NULL, &connectto)) + return false; + + if(!node_validname(connectto)) { + logger(LOG_ERR, _("rt: invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line); + free(connectto); + continue; + } + + rt_outgoing(connectto); + } + + return true; +} + +bool rt_exit(void) { + edge_exit(); + node_exit(); + subnet_exit(); + + logger(LOG_INFO, _("rt: exitting")); +} + + diff --git a/rt/rt.h b/rt/rt.h new file mode 100644 index 00000000..8f988fc2 --- /dev/null +++ b/rt/rt.h @@ -0,0 +1,46 @@ +/* + route.h -- routing + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __RT_H__ +#define __RT_H__ + +#include "tnl/tnl.h" +#include "vnd/vnd.h" + +typedef enum rt_mode { + RT_MODE_ROUTER, + RT_MODE_SWITCH, + RT_MODE_HUB, +} rt_mode_t; + +extern int rt_af; +extern enum rt_mode rt_mode; +extern bool rt_hostnames; +extern bool rt_priorityinheritance; +extern int rt_macexpire; +extern int rt_maxtimeout; + +extern bool rt_init(void); +extern bool rt_exit(void); + +#endif diff --git a/rt/subnet.c b/rt/subnet.c new file mode 100644 index 00000000..ced16956 --- /dev/null +++ b/rt/subnet.c @@ -0,0 +1,441 @@ +/* + subnet.c -- subnet handling + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "cfg/cfg.h" +#include "logger/logger.h" +#include "rt/node.h" +#include "rt/subnet.h" +#include "support/avl.h" +#include "support/xalloc.h" + +avl_tree_t *subnets; +/* Subnet mask handling */ + +static int maskcmp(const void *va, const void *vb, int masklen, int len) { + int i, m, result; + const char *a = va; + const char *b = vb; + + for(m = masklen, i = 0; m >= 8; m -= 8, i++) { + result = a[i] - b[i]; + if(result) + return result; + } + + return m ? (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m)))) : 0; +} + +static void mask(void *va, int masklen, int len) { + int i; + char *a = va; + + i = masklen / 8; + masklen %= 8; + + if(masklen) + a[i++] &= (0x100 - (1 << masklen)); + + for(; i < len; i++) + a[i] = 0; +} + +static void maskcpy(void *va, const void *vb, int masklen, int len) { + int i, m; + char *a = va; + const char *b = vb; + + for(m = masklen, i = 0; m >= 8; m -= 8, i++) + a[i] = b[i]; + + if(m) { + a[i] = b[i] & (0x100 - (1 << m)); + i++; + } + + for(; i < len; i++) + a[i] = 0; +} + +static bool maskcheck(const void *va, int masklen, int len) { + int i; + const char *a = va; + + i = masklen / 8; + masklen %= 8; + + if(masklen && a[i++] & (0xff >> masklen)) + return false; + + for(; i < len; i++) + if(a[i] != 0) + return false; + + return true; +} + +/* Cache handling */ + +struct { + subnet_t key; + subnet_t *subnet; +} *cache; + +int cache_bits; +int cache_size; +uint32_t cache_mask; + +static void cache_flush(void) { + memset(cache, 0, sizeof *cache * cache_size); +} + +static void cache_init(void) { + cache_bits = 8; + cache_size = 1 << 8; + cache_mask = cache_size - 1; + + dim(cache, cache_size); + + cache_flush(); +} + +static void cache_exit(void) { + free(cache); +} + +static uint32_t subnet_hash(const subnet_t *subnet) { + uint32_t hash; + int i; + + hash = subnet->type; + + for(i = 0; i < sizeof subnet->net / sizeof(uint32_t); i++) + hash ^= ((uint32_t *)&subnet->net)[i]; + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash & cache_mask; +} + +static subnet_t *cache_get(subnet_t *subnet) { + uint32_t hash = subnet_hash(subnet); + + if(cache[hash].subnet && memcmp(&cache[hash].key, subnet, sizeof *subnet)) + return cache[hash].subnet; + else + return NULL; +} + +static void cache_add(subnet_t *key, subnet_t *subnet) { + uint32_t hash = subnet_hash(subnet); + + cache[hash].key = *key; + cache[hash].subnet = subnet; +} + +/* Subnet tree handling */ + +static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t)) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t)) + ?: (a->net.ipv4.prefixlength - b->net.ipv4.prefixlength) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t)) + ?: (a->net.ipv6.prefixlength - b->net.ipv6.prefixlength) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare(const subnet_t *a, const subnet_t *b) { + int result; + + result = a->type - b->type; + + if(result) + return result; + + switch (a->type) { + case SUBNET_TYPE_MAC: + return subnet_compare_mac(a, b); + case SUBNET_TYPE_IPV4: + return subnet_compare_ipv4(a, b); + case SUBNET_TYPE_IPV6: + return subnet_compare_ipv6(a, b); + default: + logger(LOG_ERR, _("rt: subnet_compare() was called with unknown subnet type %d, exitting!"), a->type); + exit(1); + } +} + +avl_tree_t *subnet_tree_new(void) { + return avl_tree_new((avl_compare_t)subnet_compare, NULL); +} + +void subnet_tree_free(avl_tree_t *subnets) { + avl_tree_free(subnets); +} + +subnet_t *subnet_new(void) { + subnet_t *subnet; + + return clear(new(subnet)); +} + +void subnet_free(subnet_t *subnet) { + free(subnet); +} + +void subnet_add(subnet_t *subnet) { + avl_add(subnets, subnet); + avl_add(subnet->owner->subnets, subnet); + cache_flush(); +} + +void subnet_del(subnet_t *subnet) { + avl_del(subnet->owner->subnets, subnet); + avl_del(subnets, subnet); + cache_flush(); +} + +bool subnet_init(void) { + cache_init(); + subnets = avl_tree_new((avl_compare_t)subnet_compare, (avl_action_t)subnet_free); + + return true; +} + +bool subnet_exit(void) { + avl_tree_del(subnets); + cache_exit(); + + return true; +} + +subnet_t *str2net(const char *subnetstr) { + int i, l; + subnet_t subnet = {0}; + uint16_t x[8]; + + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d", + &x[0], &x[1], &x[2], &x[3], &l) == 5) { + subnet.type = SUBNET_TYPE_IPV4; + subnet.net.ipv4.prefixlength = l; + + for(i = 0; i < 4; i++) + subnet.net.ipv4.address.x[i] = x[i]; + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], + &l) == 9) { + subnet.type = SUBNET_TYPE_IPV6; + subnet.net.ipv6.prefixlength = l; + + for(i = 0; i < 8; i++) + subnet.net.ipv6.address.x[i] = htons(x[i]); + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) { + subnet.type = SUBNET_TYPE_IPV4; + subnet.net.ipv4.prefixlength = 32; + + for(i = 0; i < 4; i++) + subnet.net.ipv4.address.x[i] = x[i]; + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) { + subnet.type = SUBNET_TYPE_IPV6; + subnet.net.ipv6.prefixlength = 128; + + for(i = 0; i < 8; i++) + subnet.net.ipv6.address.x[i] = htons(x[i]); + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) { + subnet.type = SUBNET_TYPE_MAC; + + for(i = 0; i < 6; i++) + subnet.net.mac.address.x[i] = x[i]; + + return copy(&subnet); + } + + return NULL; +} + +char *net2str(const subnet_t *subnet) { + char *netstr; + + switch (subnet->type) { + case SUBNET_TYPE_MAC: + asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx", + subnet->net.mac.address.x[0], + subnet->net.mac.address.x[1], + subnet->net.mac.address.x[2], + subnet->net.mac.address.x[3], + subnet->net.mac.address.x[4], + subnet->net.mac.address.x[5]); + break; + + case SUBNET_TYPE_IPV4: + asprintf(&netstr, "%hu.%hu.%hu.%hu/%d", + subnet->net.ipv4.address.x[0], + subnet->net.ipv4.address.x[1], + subnet->net.ipv4.address.x[2], + subnet->net.ipv4.address.x[3], + subnet->net.ipv4.prefixlength); + break; + + case SUBNET_TYPE_IPV6: + asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + ntohs(subnet->net.ipv6.address.x[0]), + ntohs(subnet->net.ipv6.address.x[1]), + ntohs(subnet->net.ipv6.address.x[2]), + ntohs(subnet->net.ipv6.address.x[3]), + ntohs(subnet->net.ipv6.address.x[4]), + ntohs(subnet->net.ipv6.address.x[5]), + ntohs(subnet->net.ipv6.address.x[6]), + ntohs(subnet->net.ipv6.address.x[7]), + subnet->net.ipv6.prefixlength); + break; + + default: + logger(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type); + exit(0); + } + + return netstr; +} + +bool cfg_subnet(cfg_t *cfg, subnet_t **result) { + subnet_t *subnet; + + subnet = str2net(cfg->value); + + if(!subnet) { + logger(LOG_ERR, _("rt: invalid subnet for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + return false; + } + + *result = subnet; + + return true; +} + +subnet_t *subnet_get(const subnet_t *subnet) { + return subnet->owner ? avl_get(subnet->owner->subnets, subnet) : avl_get(subnets, subnet); +} + +subnet_t *subnet_get_mac(const mac_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_MAC; + search.net.mac.address = *address; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + subnet = avl_get(subnets, &search); + + if(subnet) + cache_add(&search, subnet); + + return subnet; +} + +subnet_t *subnet_get_ipv4(const ipv4_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_IPV4; + search.net.ipv4.address = *address; + search.net.ipv4.prefixlength = 32; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + while(subnet = avl_get_closest_smaller(subnets, &search)) { + if(subnet->type != SUBNET_TYPE_IPV4) + return NULL; + + if(!maskcmp(address, &subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t))) { + cache_add(&search, subnet); + return subnet; + } + + search.net.ipv4.prefixlength = subnet->net.ipv4.prefixlength - 1; + maskcpy(&search.net.ipv4.address, &subnet->net.ipv4.address, search.net.ipv4.prefixlength, sizeof(ipv4_t)); + } + + return NULL; +} + +subnet_t *subnet_get_ipv6(const ipv6_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_IPV6; + search.net.ipv6.address = *address; + search.net.ipv6.prefixlength = 128; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + while(subnet = avl_get_closest_smaller(subnets, &search)) { + if(subnet->type != SUBNET_TYPE_IPV6) + return NULL; + + if(!maskcmp(address, &subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))) { + cache_add(&search, subnet); + return subnet; + } + + search.net.ipv6.prefixlength = subnet->net.ipv6.prefixlength - 1; + maskcpy(&search.net.ipv6.address, &subnet->net.ipv6.address, search.net.ipv6.prefixlength, sizeof(ipv6_t)); + } + + return NULL; +} diff --git a/rt/subnet.h b/rt/subnet.h new file mode 100644 index 00000000..5134ae26 --- /dev/null +++ b/rt/subnet.h @@ -0,0 +1,90 @@ +/* + subnet.h -- subnet handling + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __SUBNET_H__ +#define __SUBNET_H__ + +#include "rt/node.h" +#include "support/avl.h" + +typedef struct mac { + uint8_t x[6]; +} mac_t; + +typedef struct ipv4 { + uint8_t x[4]; +} ipv4_t; + +typedef struct ipv6 { + uint16_t x[8]; +} ipv6_t; + +typedef enum subnet_type { + SUBNET_TYPE_MAC, + SUBNET_TYPE_IPV4, + SUBNET_TYPE_IPV6, +} subnet_type_t; + +typedef struct subnet_mac { + mac_t address; +} subnet_mac_t; + +typedef struct subnet_ipv4 { + ipv4_t address; + int prefixlength; +} subnet_ipv4_t; + +typedef struct subnet_ipv6 { + ipv6_t address; + int prefixlength; +} subnet_ipv6_t; + +typedef struct subnet { + struct node *owner; + struct timeval expires; + + enum subnet_type type; + + union net { + struct subnet_mac mac; + struct subnet_ipv4 ipv4; + struct subnet_ipv6 ipv6; + } net; +} subnet_t; + +extern subnet_t *subnet_new(void) __attribute__ ((__malloc__)); +extern void subnet_free(struct subnet *); +extern bool subnet_init(void); +extern bool subnet_exit(void); +extern avl_tree_t *subnet_tree_new(void) __attribute__ ((__malloc__)); +extern void subnet_tree_free(avl_tree_t *); +extern void subnet_add(struct subnet *); +extern void subnet_del(struct subnet *); +extern char *net2str(const struct subnet *); +extern struct subnet *str2net(const char *); +extern struct subnet *subnet_get(const struct subnet *); +extern struct subnet *subnet_get_mac(const struct mac *); +extern struct subnet *subnet_get_ipv4(const struct ipv4 *); +extern struct subnet *subnet_get_ipv6(const struct ipv6 *); + +#endif diff --git a/support/Makefile.am b/support/Makefile.am new file mode 100644 index 00000000..0cadfed5 --- /dev/null +++ b/support/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libsupport.a + +noinst_HEADERS = avl.h ipv4.h ipv6.h sockaddr.h xalloc.h + +libsupport_a_SOURCES = avl.c xalloc.c + diff --git a/support/avl.c b/support/avl.c new file mode 100644 index 00000000..1550a622 --- /dev/null +++ b/support/avl.c @@ -0,0 +1,703 @@ +/* + avl.c -- AVL tree management + + Copyright (C) 1998 Michael H. Buselli + 2000-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + 2000-2004 Wessel Dankers + + 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. + + Original AVL tree library by Michael H. Buselli . + + Modified 2000-11-28 by Wessel Dankers to use counts + instead of depths, to add the ->next and ->prev and to generally obfuscate + the code. Mail me if you found a bug. + + Cleaned up and incorporated some of the ideas from the red-black tree + library for inclusion into tinc (http://www.tinc-vpn.org/) by + Guus Sliepen . + + $Id$ +*/ + +#include "system.h" + +#include "support/avl.h" +#include "support/xalloc.h" + +#ifdef AVL_COUNT +#define AVL_NODE_COUNT(n) ((n) ? (n)->count : 0) +#define AVL_L_COUNT(n) (AVL_NODE_COUNT((n)->left)) +#define AVL_R_COUNT(n) (AVL_NODE_COUNT((n)->right)) +#define AVL_CALC_COUNT(n) (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1) +#endif + +#ifdef AVL_DEPTH +#define AVL_NODE_DEPTH(n) ((n) ? (n)->depth : 0) +#define L_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->left)) +#define R_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->right)) +#define AVL_CALC_DEPTH(n) ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1) +#endif + +#ifndef AVL_DEPTH +static int lg(unsigned int u) __attribute__ ((__const__)); + +static int lg(unsigned int u) { + int r = 1; + + if(!u) + return 0; + + if(u & 0xffff0000) { + u >>= 16; + r += 16; + } + + if(u & 0x0000ff00) { + u >>= 8; + r += 8; + } + + if(u & 0x000000f0) { + u >>= 4; + r += 4; + } + + if(u & 0x0000000c) { + u >>= 2; + r += 2; + } + + if(u & 0x00000002) + r++; + + return r; +} +#endif + +/* Internal helper functions */ + +static int avl_check_balance(const avl_node_t *node) { +#ifdef AVL_DEPTH + int d; + + d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node); + + return d < -1 ? -1 : d > 1 ? 1 : 0; +#else +/* int d; + * d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node)); + * d = d<-1?-1:d>1?1:0; + */ + int pl, r; + + pl = lg(AVL_L_COUNT(node)); + r = AVL_R_COUNT(node); + + if(r >> pl + 1) + return 1; + + if(pl < 2 || r >> pl - 2) + return 0; + + return -1; +#endif +} + +static void avl_rebalance(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *child; + avl_node_t *gchild; + avl_node_t *parent; + avl_node_t **superparent; + + parent = node; + + while(node) { + parent = node->parent; + + superparent = + parent ? node == + parent->left ? &parent->left : &parent->right : &tree->root; + + switch (avl_check_balance(node)) { + case -1: + child = node->left; +#ifdef AVL_DEPTH + if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) { +#else + if(AVL_L_COUNT(child) >= AVL_R_COUNT(child)) { +#endif + node->left = child->right; + if(node->left) + node->left->parent = node; + + child->right = node; + node->parent = child; + *superparent = child; + child->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); +#endif + } else { + gchild = child->right; + node->left = gchild->right; + + if(node->left) + node->left->parent = node; + child->right = gchild->left; + + if(child->right) + child->right->parent = child; + gchild->right = node; + + if(gchild->right) + gchild->right->parent = gchild; + gchild->left = child; + + if(gchild->left) + gchild->left->parent = gchild; + *superparent = gchild; + + gchild->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); + gchild->count = AVL_CALC_COUNT(gchild); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); + gchild->depth = AVL_CALC_DEPTH(gchild); +#endif + } + break; + + case 1: + child = node->right; +#ifdef AVL_DEPTH + if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) { +#else + if(AVL_R_COUNT(child) >= AVL_L_COUNT(child)) { +#endif + node->right = child->left; + if(node->right) + node->right->parent = node; + child->left = node; + node->parent = child; + *superparent = child; + child->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); +#endif + } else { + gchild = child->left; + node->right = gchild->left; + + if(node->right) + node->right->parent = node; + child->left = gchild->right; + + if(child->left) + child->left->parent = child; + gchild->left = node; + + if(gchild->left) + gchild->left->parent = gchild; + gchild->right = child; + + if(gchild->right) + gchild->right->parent = gchild; + + *superparent = gchild; + gchild->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); + gchild->count = AVL_CALC_COUNT(gchild); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); + gchild->depth = AVL_CALC_DEPTH(gchild); +#endif + } + break; + + default: +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); +#endif + } + node = parent; + } +} + +static int avl_compare(const void *a, const void *b) { + return a - b; +} + +/* (De)constructors */ + +avl_tree_t *avl_tree_new(avl_compare_t compare, avl_action_t free) { + avl_tree_t *tree; + + clear(new(tree)); + tree->compare = compare ?: avl_compare; + tree->free = free; + + return tree; +} + +void avl_tree_free(avl_tree_t *tree) { + free(tree); +} + +avl_node_t *avl_node_new(void) { + avl_node_t *node; + + return clear(new(node)); +} + +void avl_node_free(avl_tree_t *tree, avl_node_t *node) { + if(node->data && tree->free) + tree->free(node->data); + + free(node); +} + +/* Searching */ + +void *avl_get(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + return node ? node->data : NULL; +} + +void *avl_get_closest(const avl_tree_t *tree, const void *data, int *result) { + avl_node_t *node; + + node = avl_get_closest_node(tree, data, result); + + return node ? node->data : NULL; +} + +void *avl_get_closest_smaller(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_closest_smaller_node(tree, data); + + return node ? node->data : NULL; +} + +void *avl_get_closest_greater(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_closest_greater_node(tree, data); + + return node ? node->data : NULL; +} + +avl_node_t *avl_get_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + return result ? NULL : node; +} + +avl_node_t *avl_get_closest_node(const avl_tree_t *tree, const void *data, int *result) { + avl_node_t *node; + int c; + + node = tree->root; + + if(!node) { + if(result) + *result = 0; + return NULL; + } + + for(;;) { + c = tree->compare(data, node->data); + + if(c < 0) { + if(node->left) + node = node->left; + else { + if(result) + *result = -1; + break; + } + } else if(c > 0) { + if(node->right) + node = node->right; + else { + if(result) + *result = 1; + break; + } + } else { + if(result) + *result = 0; + break; + } + } + + return node; +} + +avl_node_t *avl_get_closest_smaller_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + if(result < 0) + node = node->prev; + + return node; +} + +avl_node_t *avl_get_closest_greater_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + if(result > 0) + node = node->next; + + return node; +} + +/* Insertion and deletion */ + +avl_node_t *avl_add(avl_tree_t *tree, void *data) { + avl_node_t *node, *result; + + node = avl_node_new(); + node->data = data; + + result = avl_add_node(tree, node); + + if(!result) + free(node); + + return result; +} + +avl_node_t *avl_add_node(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *closest; + int result; + + if(!tree->root) + avl_add_top(tree, node); + else { + closest = avl_get_closest_node(tree, node->data, &result); + + switch (result) { + case -1: + avl_add_before(tree, closest, node); + break; + + case 1: + avl_add_after(tree, closest, node); + break; + + case 0: + return NULL; + } + } + +#ifdef AVL_COUNT + node->count = 1; +#endif +#ifdef AVL_DEPTH + node->depth = 1; +#endif + + return node; +} + +void avl_add_top(avl_tree_t *tree, avl_node_t *node) { + node->prev = node->next = node->parent = NULL; + tree->head = tree->tail = tree->root = node; +} + +void avl_add_before(avl_tree_t *tree, avl_node_t *before, avl_node_t *node) { + if(!before) { + if(tree->tail) + avl_add_after(tree, tree->tail, node); + else + avl_add_top(tree, node); + return; + } + + node->next = before; + node->parent = before; + node->prev = before->prev; + + if(before->left) { + avl_add_after(tree, before->prev, node); + return; + } + + if(before->prev) + before->prev->next = node; + else + tree->head = node; + + before->prev = node; + before->left = node; + + avl_rebalance(tree, before); +} + +void avl_add_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node) { + if(!after) { + if(tree->head) + avl_add_before(tree, tree->head, node); + else + avl_add_top(tree, node); + return; + } + + if(after->right) { + avl_add_before(tree, after->next, node); + return; + } + + node->prev = after; + node->parent = after; + node->next = after->next; + + if(after->next) + after->next->prev = node; + else + tree->tail = node; + + after->next = node; + after->right = node; + + avl_rebalance(tree, after); +} + +avl_node_t *avl_unlink(avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + if(node) + avl_unlink_node(tree, node); + + return node; +} + +void avl_unlink_node(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *parent; + avl_node_t **superparent; + avl_node_t *subst, *left, *right; + avl_node_t *balnode; + + if(node->prev) + node->prev->next = node->next; + else + tree->head = node->next; + if(node->next) + node->next->prev = node->prev; + else + tree->tail = node->prev; + + parent = node->parent; + + superparent = + parent ? node == + parent->left ? &parent->left : &parent->right : &tree->root; + + left = node->left; + right = node->right; + if(!left) { + *superparent = right; + + if(right) + right->parent = parent; + + balnode = parent; + } else if(!right) { + *superparent = left; + left->parent = parent; + balnode = parent; + } else { + subst = node->prev; + + if(subst == left) { + balnode = subst; + } else { + balnode = subst->parent; + balnode->right = subst->left; + + if(balnode->right) + balnode->right->parent = balnode; + + subst->left = left; + left->parent = subst; + } + + subst->right = right; + subst->parent = parent; + right->parent = subst; + *superparent = subst; + } + + avl_rebalance(tree, balnode); + + node->next = node->prev = node->parent = node->left = node->right = NULL; + +#ifdef AVL_COUNT + node->count = 0; +#endif +#ifdef AVL_DEPTH + node->depth = 0; +#endif +} + +void avl_del_node(avl_tree_t *tree, avl_node_t *node) { + avl_unlink_node(tree, node); + avl_node_free(tree, node); +} + +bool avl_del(avl_tree_t *tree, void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + if(node) + avl_del_node(tree, node); + + return node; +} + +/* Fast tree cleanup */ + +void avl_tree_del(avl_tree_t *tree) { + avl_node_t *node; + +#if 0 + for(node = tree->root; node; node = next) { + next = node->next; + avl_free_node(tree, node); + } +#endif + avl_foreach_node(tree, node, avl_node_free(tree, node)); + + avl_tree_free(tree); +} + +/* Tree walking */ + +#if 0 +void avl_foreach(avl_tree_t *tree, avl_action_t action) { + avl_node_t *node, *next; + + for(node = tree->head; node; node = next) { + next = node->next; + action(node->data); + } +} + +void avl_foreach_node(avl_tree_t *tree, avl_node_action_t action) { + avl_node_t *node, *next; + + for(node = tree->head; node; node = next) { + next = node->next; + action(node); + } +} +#endif + +/* Indexing */ + +#ifdef AVL_COUNT +avl_count_t avl_count(const avl_tree_t *tree) { + return AVL_NODE_COUNT(tree->root); +} + +void *avl_get_indexed(const avl_tree_t *tree, avl_count_t index) { + avl_node_t *node; + + node = avl_get_indexed_node(tree, index); + + return node ? node->data : NULL; +} + +avl_node_t *avl_get_indexed_node(const avl_tree_t *tree, avl_count_t index) { + avl_node_t *node; + avl_count_t c; + + node = tree->root; + + while(node) { + c = AVL_L_COUNT(node); + + if(index < c) { + node = node->left; + } else if(index > c) { + node = node->right; + index -= c + 1; + } else { + return node; + } + } + + return NULL; +} + +avl_count_t avl_index(const avl_node_t *node) { + avl_node_t *next; + avl_count_t index; + + index = AVL_L_COUNT(node); + + while((next = node->parent)) { + if(node == next->right) + index += AVL_L_COUNT(next) + 1; + node = next; + } + + return index; +} +#endif +#ifdef AVL_DEPTH +avl_depth_t avl_depth(const avl_tree_t *tree) { + return AVL_NODE_DEPTH(tree->root); +} +#endif diff --git a/support/avl.h b/support/avl.h new file mode 100644 index 00000000..5831bcc2 --- /dev/null +++ b/support/avl.h @@ -0,0 +1,154 @@ +/* + avl.h -- AVL tree management + + Copyright (C) 1998 Michael H. Buselli + 2000-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + 2000-2004 Wessel Dankers + + 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. + + Original AVL tree library by Michael H. Buselli . + + Modified 2000-11-28 by Wessel Dankers to use counts + instead of depths, to add the ->next and ->prev and to generally obfuscate + the code. Mail me if you found a bug. + + Cleaned up and incorporated some of the ideas from the red-black tree + library for inclusion into tinc (http://www.tinc-vpn.org/) by + Guus Sliepen . + + $Id$ +*/ + + +#ifndef __AVL_H__ +#define __AVL_H__ + +#ifndef AVL_DEPTH +#ifndef AVL_COUNT +#define AVL_DEPTH +#endif +#endif + +typedef uint32_t avl_count_t; +typedef uint16_t avl_depth_t; + +typedef struct avl_node { + struct avl_node *next; + struct avl_node *prev; + + struct avl_node *parent; + struct avl_node *left; + struct avl_node *right; + +#ifdef AVL_COUNT + avl_count_t count; +#endif + +#ifdef AVL_DEPTH + avl_depth_t depth; +#endif + + void *data; +} avl_node_t; + +typedef int (*avl_compare_t)(const void *, const void *); +typedef void (*avl_action_t)(void *); +typedef void (*avl_node_action_t)(struct avl_node *); + +typedef struct avl_tree { + struct avl_node *head; + struct avl_node *tail; + + struct avl_node *root; + + avl_compare_t compare; + avl_action_t free; +} avl_tree_t; + +/* (De)constructors */ + +extern struct avl_tree *avl_tree_new(avl_compare_t, avl_action_t); +extern void avl_tree_free(struct avl_tree *); + +extern struct avl_node *avl_node_new(void); +extern void avl_node_free(struct avl_tree *tree, struct avl_node *); + +/* Insertion and deletion */ + +extern struct avl_node *avl_add(struct avl_tree *, void *); +extern struct avl_node *avl_add_node(struct avl_tree *, struct avl_node *); + +extern void avl_add_top(struct avl_tree *, struct avl_node *); +extern void avl_add_before(struct avl_tree *, struct avl_node *, struct avl_node *); +extern void avl_add_after(struct avl_tree *, struct avl_node *, struct avl_node *); + +extern struct avl_node *avl_unlink(struct avl_tree *, const void *); +extern void avl_unlink_node(struct avl_tree *tree, struct avl_node *); +extern bool avl_del(struct avl_tree *, void *); +extern void avl_del_node(struct avl_tree *, struct avl_node *); + +/* Fast tree cleanup */ + +extern void avl_tree_del(struct avl_tree *); + +/* Searching */ + +extern void *avl_get(const struct avl_tree *, const void *); +extern void *avl_get_closest(const struct avl_tree *, const void *, int *); +extern void *avl_get_closest_smaller(const struct avl_tree *, const void *); +extern void *avl_get_closest_greater(const struct avl_tree *, const void *); + +extern struct avl_node *avl_get_node(const struct avl_tree *, const void *); +extern struct avl_node *avl_get_closest_node(const struct avl_tree *, const void *, int *); +extern struct avl_node *avl_get_closest_smaller_node(const struct avl_tree *, const void *); +extern struct avl_node *avl_get_closest_greater_node(const struct avl_tree *, const void *); + +/* Tree walking */ + +#define avl_foreach(tree, object, action) {avl_node_t *_node, *_next; \ + for(_node = (tree)->head; _node; _node = _next) { \ + _next = _node->next; \ + (object) = _node->data; \ + action; \ + } \ +} + +#define avl_foreach_node(tree, node, action) {avl_node_t *_next; \ + for((node) = (tree)->head; (node); (node) = _next) { \ + _next = (node)->next; \ + action; \ + } \ +} + +#if 0 +extern void avl_foreach(struct avl_tree *, avl_action_t); +extern void avl_foreach_node(struct avl_tree *, avl_node_action_t); +#endif + +/* Indexing */ + +#ifdef AVL_COUNT +extern avl_count_t avl_count(const struct avl_tree *); +extern avl_count_t avl_index(const struct avl_node *); +extern void *avl_get_indexed(const struct avl_tree *, avl_count_t); +extern struct avl_node *avl_get_indexed_node(const struct avl_tree *, avl_count_t); +#endif +#ifdef AVL_DEPTH +extern avl_depth_t avl_depth(const struct avl_tree *); +#endif + +#endif diff --git a/support/ipv4.h b/support/ipv4.h new file mode 100644 index 00000000..19a290bb --- /dev/null +++ b/support/ipv4.h @@ -0,0 +1,133 @@ +/* + ipv4.h -- missing IPv4 related definitions + + Copyright (C) 2003-2004 Ivo Timmermans + 2003-2004 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$ +*/ + +#ifndef __IPV4_H__ +#define __IPV4_H__ + +#ifndef AF_INET +#define AF_INET 2 +#endif + +#ifndef IPPROTO_ICMP +#define IPPROTO_ICMP 1 +#endif + +#ifndef ICMP_DEST_UNREACH +#define ICMP_DEST_UNREACH 3 +#endif + +#ifndef ICMP_NET_UNKNOWN +#define ICMP_NET_UNKNOWN 6 +#endif + +#ifndef ICMP_NET_UNREACH +#define ICMP_NET_UNREACH 0 +#endif + +#ifndef IP_MSS +#define IP_MSS 576 +#endif + +#ifndef HAVE_STRUCT_IP +struct ip { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ip_hl:4; + unsigned int ip_v:4; +#else + unsigned int ip_v:4; + unsigned int ip_hl:4; +#endif + uint8_t ip_tos; + uint16_t ip_len; + uint16_t ip_id; + uint16_t ip_off; +#define IP_RF 0x8000 +#define IP_DF 0x4000 +#define IP_MF 0x2000 +#define IP_OFFMASK 0x1fff + uint8_t ip_ttl; + uint8_t ip_p; + uint16_t ip_sum; + struct in_addr ip_src, ip_dst; +} __attribute__ ((__packed__)); +#endif + +#ifndef HAVE_STRUCT_ICMP +struct icmp { + uint8_t icmp_type; + uint8_t icmp_code; + uint16_t icmp_cksum; + union { + uint8_t ih_pptr; + struct in_addr ih_gwaddr; + struct ih_idseq { + uint16_t icd_id; + uint16_t icd_seq; + } ih_idseq; + uint32_t ih_void; + + + struct ih_pmtu { + uint16_t ipm_void; + uint16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv { + uint8_t irt_num_addrs; + uint8_t irt_wpa; + uint16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct { + uint32_t its_otime; + uint32_t its_rtime; + uint32_t its_ttime; + } id_ts; + struct { + struct ip idi_ip; + } id_ip; + uint32_t id_mask; + uint8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +} __attribute__ ((__packed__)); +#endif + +#endif diff --git a/support/ipv6.h b/support/ipv6.h new file mode 100644 index 00000000..5de7953f --- /dev/null +++ b/support/ipv6.h @@ -0,0 +1,128 @@ +/* + ipv6.h -- missing IPv6 related definitions + + Copyright (C) 2003-2004 Ivo Timmermans + 2003-2004 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$ +*/ + +#ifndef __IPV6_H__ +#define __IPV6_H__ + +#ifndef AF_INET6 +#define AF_INET6 10 +#endif + +#ifndef IPPROTO_ICMPV6 +#define IPPROTO_ICMPV6 58 +#endif + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + union { + uint8_t u6_addr8[16]; + uint16_t u6_addr16[8]; + uint32_t u6_addr32[4]; + } in6_u; +} __attribute__ ((__packed__)); +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#endif + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; +} __attribute__ ((__packed__)); +#endif + +#ifndef IN6_IS_ADDR_V4MAPPED +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((((__const uint32_t *) (a))[0] == 0) \ + && (((__const uint32_t *) (a))[1] == 0) \ + && (((__const uint32_t *) (a))[2] == htonl (0xffff))) +#endif + +#ifndef HAVE_STRUCT_IP6_HDR +struct ip6_hdr { + union { + struct ip6_hdrctl { + uint32_t ip6_un1_flow; + uint16_t ip6_un1_plen; + uint8_t ip6_un1_nxt; + uint8_t ip6_un1_hlim; + } ip6_un1; + uint8_t ip6_un2_vfc; + } ip6_ctlun; + struct in6_addr ip6_src; + struct in6_addr ip6_dst; +} __attribute__ ((__packed__)); +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim +#endif + +#ifndef HAVE_STRUCT_ICMP6_HDR +struct icmp6_hdr { + uint8_t icmp6_type; + uint8_t icmp6_code; + uint16_t icmp6_cksum; + union { + uint32_t icmp6_un_data32[1]; + uint16_t icmp6_un_data16[2]; + uint8_t icmp6_un_data8[4]; + } icmp6_dataun; +} __attribute__ ((__packed__)); +#define ICMP6_DST_UNREACH_NOROUTE 0 +#define ICMP6_DST_UNREACH 1 +#define ICMP6_DST_UNREACH_ADDR 3 +#define ND_NEIGHBOR_SOLICIT 135 +#define ND_NEIGHBOR_ADVERT 136 +#define icmp6_data32 icmp6_dataun.icmp6_un_data32 +#define icmp6_data16 icmp6_dataun.icmp6_un_data16 +#define icmp6_data8 icmp6_dataun.icmp6_un_data8 +#endif + +#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT +struct nd_neighbor_solicit { + struct icmp6_hdr nd_ns_hdr; + struct in6_addr nd_ns_target; +} __attribute__ ((__packed__)); +#define ND_OPT_SOURCE_LINKADDR 1 +#define ND_OPT_TARGET_LINKADDR 2 +#define nd_ns_type nd_ns_hdr.icmp6_type +#define nd_ns_code nd_ns_hdr.icmp6_code +#define nd_ns_cksum nd_ns_hdr.icmp6_cksum +#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] +#endif + +#ifndef HAVE_STRUCT_ND_OPT_HDR +struct nd_opt_hdr { + uint8_t nd_opt_type; + uint8_t nd_opt_len; +} __attribute__ ((__packed__)); +#endif + +#endif diff --git a/support/sockaddr.h b/support/sockaddr.h new file mode 100644 index 00000000..8cd1c5e2 --- /dev/null +++ b/support/sockaddr.h @@ -0,0 +1,51 @@ +/* + sockaddr.h -- sockaddr handling + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __SOCKADDR_H__ +#define __SOCKADDR_H__ + +#define AF_UNKNOWN 255 + +struct sockaddr_unknown { + uint16_t family; + uint16_t pad1; + uint32_t pad2; + char *address; + char *port; +}; + +#define sa(s) ((struct sockaddr *)(s)) +#ifdef SA_LEN +#define sa_len(s) SA_LEN((struct sockaddr *)(s)) +#else +#define sa_len(s) (((struct sockaddr *)(s))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +#endif + +#define sa_family(s) (((struct sockaddr *)(s))->sa_family) + +#define sa_unmap(s) ({if(((struct sockaddr *)(s))->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)(s))->sin6_addr)) { \ + ((struct sockaddr_in *)(s))->sin_addr.s_addr = ((struct sockaddr_in6 *)(s))->sin6_addr.s6_addr32[3]; \ + ((struct sockaddr *)(s))->sa_family = AF_INET; \ +} \ +s;}) + +#endif diff --git a/support/xalloc.c b/support/xalloc.c new file mode 100644 index 00000000..80699f9b --- /dev/null +++ b/support/xalloc.c @@ -0,0 +1,77 @@ +/* + xalloc.c -- safe memory allocation functions + + Copyright (C) 2003-2004 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 +#include +#include + +#include "xalloc.h" + +static void xalloc_fail(void) { + fprintf(stderr, "Memory exhausted\n"); + exit(1); +} + +void (*xalloc_fail_func)(void) = xalloc_fail; + +void *xmalloc(size_t n) { + void *p; + + p = malloc(n); + + if(!p) + xalloc_fail_func(); + + return p; +} + +void *xrealloc(void *p, size_t n) { + p = realloc(p, n); + + if(!p) + xalloc_fail_func(); + + return p; +} + +void *xcalloc(size_t n, size_t s) { + void *p; + + p = calloc(n, s); + + if(!p) + xalloc_fail_func(); + + return p; +} + +char *xstrdup(const char *s) { + char *p; + + p = strdup(s); + + if(!p) + xalloc_fail_func(); + + return p; +} + diff --git a/support/xalloc.h b/support/xalloc.h new file mode 100644 index 00000000..db2e5315 --- /dev/null +++ b/support/xalloc.h @@ -0,0 +1,37 @@ +/* + xalloc.h -- safe memory allocation functions + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __XALLOC_H__ +#define __XALLOC_H__ + +#define new(object) ({(object) = xmalloc(sizeof *(object));}) +#define dim(object, count) ({(object) = xmalloc(sizeof *(object) * (count));}) +#define redim(object, count) ({(object) = xrealloc((object), sizeof *(object) * (count));}) +#define copy(object) ({typeof(object) _copy; *(_copy = xmalloc(sizeof *(object))) = *(object); _copy;}) +#define clear(object) ({memset((object), 0, sizeof *(object));}) +#define replace(string, replacement) ({if(string) free(string); (string) = (replacement) ? xstrdup(replacement) : NULL;}) + +void *xmalloc(size_t n) __attribute__ ((__malloc__)); +void *xrealloc(void *p, size_t n) __attribute__ ((__malloc__)); +char *xstrdup(const char *s) __attribute__ ((__malloc__)); + +#endif diff --git a/system.h b/system.h new file mode 100644 index 00000000..104eaaa4 --- /dev/null +++ b/system.h @@ -0,0 +1,161 @@ +/* + system.h -- system headers + + Copyright (C) 1998-2004 Ivo Timmermans + 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id$ +*/ + +#ifndef __TINC_SYSTEM_H__ +#define __TINC_SYSTEM_H__ + +#include "config.h" + +/* Include standard headers */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STDBOOL_H +#include +#else +typedef int bool; +#define true 1 +#define false 0 +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* Include system specific headers */ + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_FILE_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_NETINET_IN_SYSTM_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_NETINET_IP_H +#include +#endif + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef HAVE_NETINET_IN6_H +#include +#endif + +#ifdef HAVE_NETINET_IP6_H +#include +#endif + +#ifdef HAVE_MINGW +#include +#include +#endif + +/* Include localisation support */ + +#if 0 + +#include "gettext.h" + +#ifndef HAVE_STRSIGNAL +# define strsignal(p) "" +#endif + +/* Other functions */ + +#include "dropin.h" + +#endif + +#define _(a) a + +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +#endif /* __TINC_SYSTEM_H__ */ diff --git a/tincd.c b/tincd.c new file mode 100644 index 00000000..5f99cea8 --- /dev/null +++ b/tincd.c @@ -0,0 +1,121 @@ +/* + tincd.c -- the main file for tincd + + Copyright (C) 2000-2004 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 "cfg/cfg.h" +#include "fd/event.h" +#include "fd/fd.h" +#include "logger/logger.h" +#include "support/avl.h" +#include "support/sockaddr.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" +#include "vnd/vnd.h" + +static bool vnd_recv(struct vnd *vnd, char *buf, int len) { + static int p = 0; + char b[4]; + logger(LOG_DEBUG, _("Read packet of %d bytes from vnd %p"), len, vnd); + memcpy(b, buf + 16, 4); + memcpy(buf + 16, buf + 20, 4); + memcpy(buf + 20, b, 4); + vnd->send(vnd, buf, len); + return true; +} + +static bool vnd_stop(event_t *event) { + static int i = 0; + + logger(LOG_DEBUG, "i = %d", i++); + + if(i > 5) { + fd_stop(); + return false; + } + + event_update(event, event->interval); + return true; +} + +int test(int argc, char **argv) { + vnd_t *vnd; + event_t *stop; + tnl_listen_t *listener; + + //vnd_init(); + if(fd_init() && tnl_init()) { + vnd = vnd_new(); + vnd_set(vnd, "/dev/tun", "test", VND_MODE_TUN, vnd_recv); + + stop = event_new(); + event_set(stop, (struct timeval){5, 0}, vnd_stop, NULL); + event_add(stop); + + clear(new(listener)); + listener->type = SOCK_STREAM; + listener->protocol = IPPROTO_TCP; + sa(&listener->local.address)->sa_family = AF_INET; + + if(tnl_listen(listener) && vnd_open(vnd)) { + fd_run(); + vnd_close(vnd); + listener->close(listener); + } + + vnd_free(vnd); + + tnl_exit(); + fd_exit(); + } + //vnd_exit(); +} + +avl_tree_t *tinc_cfg = NULL; +char *tinc_netname = NULL; + +int main(int argc, char **argv) { + tnl_listen_t *listener; + + logger_init("tinc", LOGGER_MODE_STDERR); + + tinc_cfg = cfg_tree_new(); + + if(!cfg_read_file(tinc_cfg, "tinc.conf")) + return 1; + + if(fd_init() && tnl_init()) { + clear(new(listener)); + listener->type = SOCK_STREAM; + listener->protocol = IPPROTO_TCP; + sa(&listener->local.address)->sa_family = AF_INET; + ((struct sockaddr_in *) &listener->local.address)->sin_port = htons(655); + if(tnl_listen(listener)) { + fd_run(); + listener->close(listener); + } + tnl_exit() && fd_exit(); + } + + return 0; +} + diff --git a/tnl/Makefile.am b/tnl/Makefile.am new file mode 100644 index 00000000..594838d5 --- /dev/null +++ b/tnl/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libtnl.a + +noinst_HEADERS = tnl.h + +libtnl_a_SOURCES = tnl.c + diff --git a/tnl/tnl.c b/tnl/tnl.c new file mode 100644 index 00000000..5a767f52 --- /dev/null +++ b/tnl/tnl.c @@ -0,0 +1,386 @@ +/* + tnl.c -- tunnels + + Copyright (C) 2003-2004 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 "support/avl.h" +#include "support/sockaddr.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" + +static avl_tree_t *tnls, *listeners; + +bool tnl_init(void) { + tnls = avl_tree_new(NULL, (avl_action_t)free); + listeners = avl_tree_new(NULL, (avl_action_t)free); + + return true; +} + +bool tnl_exit(void) { + avl_tree_del(listeners); + avl_tree_del(tnls); + + return true; +} + +#define tnl_add(t) avl_add(tnls, t) +#define tnl_del(t) avl_del(tnls, t) +#define tnl_listen_add(l) avl_add(listeners, l) +#define tnl_listen_del(l) avl_del(listeners, l) + +static bool tnl_send(tnl_t *tnl, const char *buf, int len) { + int result; + + while(len) { + result = gnutls_record_send(tnl->session, buf, len); + if(result <= 0) { + if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) + continue; + + if(result) + logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result)); + else + logger(LOG_INFO, _("tnl: connection closed by peer")); + + tnl->error(tnl, result); + tnl->close(tnl); + return !result; + } + + buf += result; + len -= result; + } + + return true; +} + +static bool tnl_recv(tnl_t *tnl) { + int result; + tnl_record_t *record = (tnl_record_t *)tnl->buf; + + result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread); + if(result <= 0) { + if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) + return true; + + if(result) + logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result)); + else + logger(LOG_INFO, _("tnl: connection closed by peer")); + + tnl->error(tnl, result); + tnl->close(tnl); + return !result; + } + + tnl->bufread += result; + + while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) { + switch(record->type) { + case TNL_RECORD_META: + tnl->recv_meta(tnl, record->data, record->len); + break; + + case TNL_RECORD_PACKET: + tnl->recv_packet(tnl, record->data, record->len); + break; + + default: + logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type")); + tnl->error(tnl, EINVAL); + tnl->close(tnl); + return false; + } + + tnl->bufread -= sizeof *record + record->len; + memmove(tnl->buf, record->data + record->len, tnl->bufread); + } +} + +static bool tnl_recv_handler(fd_t *fd) { + tnl_t *tnl = fd->data; + int result; + + result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread); + if(result < 0) { + if(gnutls_error_is_fatal(result)) { + logger(LOG_DEBUG, _("tnl: reception failed: %s\n"), gnutls_strerror(result)); + tnl->error(tnl, result); + tnl->close(tnl); + return false; + } + + return true; + } + + tnl->bufread += result; + return tnl_recv(tnl); +} + +static bool tnl_handshake_handler(fd_t *fd) { + tnl_t *tnl = fd->data; + int result; + + result = gnutls_handshake(tnl->session); + if(result < 0) { + if(gnutls_error_is_fatal(result)) { + logger(LOG_ERR, "tnl: handshake error: %s\n", gnutls_strerror(result)); + tnl->close(tnl); + return false; + } + + /* check other stuff? */ + return true; + } + + logger(LOG_DEBUG, _("tnl: handshake finished")); + + result = gnutls_certificate_verify_peers(tnl->session); + if(result < 0) { + logger(LOG_ERR, "tnl: certificate error: %s\n", gnutls_strerror(result)); + tnl->close(tnl); + return false; + } + + if(result) { + logger(LOG_ERR, "tnl: certificate not good, verification result %x", result); + tnl->close(tnl); + return false; + } + + tnl->status == TNL_STATUS_UP; + tnl->fd.handler = tnl_recv_handler; + tnl->accept(tnl); + return true; +} + +static bool tnl_send_meta(tnl_t *tnl, const char *buf, int len) { + tnl_record_t record = { + .type = TNL_RECORD_META, + .len = len, + }; + + return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len); +} + +static bool tnl_send_packet(tnl_t *tnl, const char *buf, int len) { + tnl_record_t record = { + .type = TNL_RECORD_PACKET, + .len = len, + }; + + return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len); +} + +static bool tnl_close(tnl_t *tnl) { + if(tnl->session) { + gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(tnl->session); + } + + fd_del(&tnl->fd); + close(tnl->fd.fd); + + tnl_del(tnl); + + return true; +} + +static bool tnl_accept_error(tnl_t *tnl, int errnum) { + logger(LOG_ERR, _("tnl: error %d on accepted tunnel")); + return true; +} + +static bool tnl_accept_handler(fd_t *fd) { + tnl_listen_t *listener = fd->data; + tnl_t *tnl; + struct sockaddr_storage ss; + socklen_t len = sizeof ss; + int sock; + + sock = accept(fd->fd, sa(&ss), &len); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno)); + return false; + } + + logger(LOG_DEBUG, _("tnl: accepted incoming connection")); + + sa_unmap(&ss); + + new(tnl); + tnl->local = listener->local; + tnl->remote.address = ss; + len = sizeof tnl->local.address; + getsockname(sock, sa(&tnl->local.address), &len); + sa_unmap(&tnl->local.address); + tnl->type = listener->type; + tnl->protocol = listener->protocol; + tnl->status = TNL_STATUS_CONNECTING; + tnl->error = tnl_accept_error; + tnl->close = tnl_close; + + tnl->fd.fd = sock; + tnl->fd.mode = FD_MODE_READ; + tnl->fd.handler = tnl_handshake_handler; + tnl->fd.data = tnl; + + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); + + tnl_add(tnl); + + gnutls_init(&tnl->session, GNUTLS_SERVER); + //gnutls_handshake_set_private_extensions(tnl->session, 1); + gnutls_set_default_priority(tnl->session); + gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred); + gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); + gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)sock); + gnutls_handshake(tnl->session); + + tnl->accept = listener->accept; + + fd_add(&tnl->fd); + + return true; +} + +static bool tnl_connect_handler(fd_t *fd) { + tnl_t *tnl = fd->data; + int result; + socklen_t len; + + len = sizeof result; + getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len); + if(result) { + logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result)); + tnl->error(tnl, result); + tnl->close(tnl); + return false; + } + + fd_del(&tnl->fd); + + fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK); + + tnl->status = TNL_STATUS_HANDSHAKE; + gnutls_init(&tnl->session, GNUTLS_CLIENT); + //gnutls_handshake_set_private_extensions(tnl->session, 1); + gnutls_set_default_priority(tnl->session); + gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred); + gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); + gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd); + gnutls_handshake(tnl->session); + + tnl->fd.mode = FD_MODE_READ; + tnl->fd.handler = tnl_handshake_handler; + fd_add(&tnl->fd); + + logger(LOG_DEBUG, _("tnl: connected")); + + return true; +} + +bool tnl_connect(tnl_t *tnl) { + int sock; + + sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno)); + return false; + } + +#if 0 + if(sa_nonzero(&tnl->local.address) && bind(sock, sa(&tnl->local.address), sa_len(&tnl->local.address)) == -1) { + logger(LOG_ERR, _("tnl: could not bind socket: %s"), strerror(errno)); + close(sock); + return false; + } +#endif + + if(connect(sock, sa(&tnl->remote.address), sa_len(&tnl->remote.address)) == -1) { + logger(LOG_ERR, _("tnl: could not connect: %s"), strerror(errno)); + close(sock); + return false; + } + + tnl->status = TNL_STATUS_CONNECTING; + + tnl->fd.fd = sock; + tnl->fd.mode = FD_MODE_WRITE; + tnl->fd.handler = tnl_connect_handler; + tnl->fd.data = tnl; + + tnl->send_packet = tnl_send_packet; + tnl->send_meta = tnl_send_meta; + tnl->close = tnl_close; + + tnl_add(tnl); + + + fd_add(&tnl->fd); + + return true; +} + +static bool tnl_listen_close(tnl_listen_t *listener) { + fd_del(&listener->fd); + close(listener->fd.fd); + tnl_listen_del(listener); + return true; +} + +bool tnl_listen(tnl_listen_t *listener) { + int sock; + + sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno)); + return false; + } + + if(bind(sock, sa(&listener->local.address), sa_len(&listener->local.address)) == -1) { + logger(LOG_ERR, _("tnl: could not bind listener socket: %s"), strerror(errno)); + return false; + } + + if(listen(sock, 10) == -1) { + logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno)); + return false; + } + + listener->fd.fd = sock; + listener->fd.mode = FD_MODE_READ; + listener->fd.handler = tnl_accept_handler; + listener->fd.data = listener; + listener->close = tnl_listen_close; + + tnl_listen_add(listener); + fd_add(&listener->fd); + + return true; +} diff --git a/tnl/tnl.h b/tnl/tnl.h new file mode 100644 index 00000000..9f1f8369 --- /dev/null +++ b/tnl/tnl.h @@ -0,0 +1,104 @@ +/* + tnl.h -- tunnels + + Copyright (C) 2003-2004 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$ +*/ + +#ifndef __TNL_H__ +#define __TNL_H__ + +#include + +#include "fd/fd.h" + +#define TNL_RECORD_PACKET 0 +#define TNL_RECORD_META 1 +#define TNL_RECORD_HELLO 2 +#define TNL_RECORD_BLA 3 + +typedef struct tnl_record { + uint16_t type; + uint16_t len; + char data[]; +} tnl_record_t; + +typedef enum tnl_status { + TNL_STATUS_DOWN, + TNL_STATUS_CONNECTING, + TNL_STATUS_HANDSHAKE, + TNL_STATUS_UP, +} tnl_status_t; + +typedef struct tnl_ep { + struct sockaddr_storage address; + struct tnl_ep_identity *id; + struct tnl_ep_credentials *cred; + struct tnl_ep_cryptoparm *parm; +} tnl_ep_t; + +typedef struct tnl { + struct tnl_ep local; + struct tnl_ep remote; + int type; + int protocol; + int mtu; + enum tnl_status status; + void *data; + + bool (*send_packet)(struct tnl *tnl, const char *buf, int len); + bool (*send_meta)(struct tnl *tnl, const char *buf, int len); + bool (*close)(struct tnl *tnl); + + bool (*recv_packet)(struct tnl *tnl, const char *buf, int len); + bool (*recv_meta)(struct tnl *tnl, const char *buf, int len); + bool (*accept)(struct tnl *tnl); + bool (*error)(struct tnl *tnl, int errnum); + + /* private */ + + struct fd fd; + gnutls_session session; + char buf[4096]; + int bufread; +} tnl_t; + +typedef struct tnl_listen { + struct tnl_ep local; + int type; + int protocol; + + bool (*accept)(struct tnl *tnl); + bool (*close)(struct tnl_listen *listener); + + struct fd fd; +} tnl_listen_t; + +extern bool tnl_init(void); +extern bool tnl_exit(void); +extern bool tnl_listen(struct tnl_listen *listener); +extern bool tnl_connect(struct tnl *tnl); + +extern bool tnl_credentials_sprint(const char *buf, int len, const struct tnl_ep_credentials *cred); +extern bool tnl_credentials_sscan(const char *buf, struct tnl_ep_credentials *cred); +extern bool tnl_cryptoparm_sprint(const char *buf, int len, const struct tnl_ep_cryptoparm *parm); +extern bool tnl_cryptoparm_sscan(const char *buf, struct tnl_ep_cryptoparm *parm); +extern bool tnl_credentials_fprint(FILE *stream, const struct tnl_ep_credentials *cred); +extern bool tnl_credentials_fscan(FILE *stream, struct tnl_ep_credentials *cred); + +#endif /* __TNL_H__ */ diff --git a/vnd/Makefile.am b/vnd/Makefile.am new file mode 100644 index 00000000..e5422981 --- /dev/null +++ b/vnd/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libvnd.a + +noinst_HEADERS = vnd.h + +libvnd_a_SOURCES = vnd.c + diff --git a/vnd/vnd.c b/vnd/vnd.c new file mode 100644 index 00000000..538dcb89 --- /dev/null +++ b/vnd/vnd.c @@ -0,0 +1,151 @@ +/* + vnd.c -- virtual network device management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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 "fd/fd.h" +#include "logger/logger.h" +#include "support/xalloc.h" + +#include "vnd/vnd.h" + +vnd_t *vnd_new(void) { + vnd_t *vnd; + + return clear(new(vnd)); +} + +void vnd_free(vnd_t *vnd) { + replace(vnd->device, NULL); + replace(vnd->interface, NULL); + replace(vnd->description, NULL); + free(vnd); +} + +void vnd_set(vnd_t *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv) { + replace(vnd->device, device); + replace(vnd->interface, interface); + vnd->mode = mode; + vnd->recv = recv; +} + +static bool vnd_send(vnd_t *vnd, char *buf, int len) { + int result; + + result = write(vnd->fd.fd, buf, len); + + if(result == len || result < 0 && (errno == EINTR || errno == EAGAIN)) { + logger(LOG_INFO, _("vnd: wrote packet of %d bytes to %s"), len, vnd->description); + return true; + } + + logger(LOG_INFO, _("vnd: error writing packet of %d bytes to %s: %s"), len, vnd->description, strerror(errno)); + + return false; +} + +static bool vnd_recv_handler(fd_t *fd) { + vnd_t *vnd = fd->data; + char buf[vnd->mtu]; + int len; + + vnd = fd->data; + + len = read(fd->fd, buf, sizeof(buf)); + + if(len > 0) { + logger(LOG_INFO, _("vnd: read packet of %d bytes from %s"), len, vnd->description); + return vnd->recv(vnd, buf, len); + } + + if(len < 0 && (errno == EINTR || errno == EAGAIN)) + return true; + + logger(LOG_ERR, _("vnd: error reading packet from %s: %s"), vnd->description, strerror(errno)); + + return false; +} + +bool vnd_open(vnd_t *vnd) { + struct ifreq ifr = {0}; + + if(!vnd->device) + vnd->device = xstrdup("/dev/net/tun"); + + vnd->fd.fd = open(vnd->device, O_RDWR | O_NONBLOCK); + + if(vnd->fd.fd < 0) { + logger(LOG_ERR, _("vnd: could not open %s: %s"), vnd->device, strerror(errno)); + return false; + } + + if(vnd->mode == VND_MODE_TUN) + ifr.ifr_flags = IFF_TUN; + else + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + if(vnd->interface) + strncpy(ifr.ifr_name, vnd->interface, IFNAMSIZ); + + if(!ioctl(vnd->fd.fd, TUNSETIFF, &ifr)) { + if(vnd->interface) + free(vnd->interface); + vnd->interface = xstrdup(ifr.ifr_name); + } else { + logger(LOG_ERR, _("vnd: %s is not a Linux tun/tap device"), vnd->device); + return false; + } + + if(!vnd->mtu) + vnd->mtu = 1514; + + vnd->send = vnd_send; + vnd->fd.mode = FD_MODE_READ; + vnd->fd.handler = vnd_recv_handler; + vnd->fd.data = vnd; + + if(vnd->description) + free(vnd->description); + + asprintf(&vnd->description, "Linux tun/tap device %s (interface %s)", vnd->device, vnd->interface); + + if(!fd_add(&vnd->fd)) + return false; + + logger(LOG_INFO, _("vnd: opened %s"), vnd->description); + + return true; +} + +bool vnd_close(vnd_t *vnd) { + fd_del(&vnd->fd); + + close(vnd->fd.fd); + + logger(LOG_INFO, _("vnd: closed %s"), vnd->description); + + return true; +} + diff --git a/vnd/vnd.h b/vnd/vnd.h new file mode 100644 index 00000000..46708739 --- /dev/null +++ b/vnd/vnd.h @@ -0,0 +1,59 @@ +/* + vnd.c -- virtual network device management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + 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$ +*/ + +#ifndef __VND_H__ +#define __VND_H__ + +typedef enum vnd_mode{ + VND_MODE_TUN, + VND_MODE_TAP, +} vnd_mode_t; + +struct vnd; + +typedef bool (*vnd_handler_t)(struct vnd *vnd, char *buf, int len); + +typedef struct vnd { + char *device; + char *interface; + enum vnd_mode mode; + int mtu; + + vnd_handler_t recv; + vnd_handler_t send; + + /* Private data */ + + struct fd fd; + char *description; +} vnd_t; + +extern bool vnd_init(void); +extern bool vnd_exit(void); +extern struct vnd *vnd_new(void); +extern void vnd_free(struct vnd *vnd); +extern void vnd_set(struct vnd *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv); +extern bool vnd_open(struct vnd *vnd); +extern bool vnd_close(struct vnd *vnd); + +#endif /* __VND_H__ */