Imported start of brand new codebase for tinc 2.0.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 22 Mar 2004 12:30:39 +0000 (12:30 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 22 Mar 2004 12:30:39 +0000 (12:30 +0000)
40 files changed:
Makefile.am [new file with mode: 0644]
cfg/Makefile.am [new file with mode: 0644]
cfg/cfg.c [new file with mode: 0644]
cfg/cfg.h [new file with mode: 0644]
configure.ac [new file with mode: 0644]
fd/Makefile.am [new file with mode: 0644]
fd/event.c [new file with mode: 0644]
fd/event.h [new file with mode: 0644]
fd/fd.c [new file with mode: 0644]
fd/fd.h [new file with mode: 0644]
logger/Makefile.am [new file with mode: 0644]
logger/log.h [new file with mode: 0644]
logger/logger.c [new file with mode: 0644]
logger/logger.h [new file with mode: 0644]
rt/Makefile.am [new file with mode: 0644]
rt/edge.c [new file with mode: 0644]
rt/edge.h [new file with mode: 0644]
rt/graph.h [new file with mode: 0644]
rt/node.c [new file with mode: 0644]
rt/node.h [new file with mode: 0644]
rt/rt.c [new file with mode: 0644]
rt/rt.h [new file with mode: 0644]
rt/subnet.c [new file with mode: 0644]
rt/subnet.h [new file with mode: 0644]
support/Makefile.am [new file with mode: 0644]
support/avl.c [new file with mode: 0644]
support/avl.h [new file with mode: 0644]
support/ipv4.h [new file with mode: 0644]
support/ipv6.h [new file with mode: 0644]
support/sockaddr.h [new file with mode: 0644]
support/xalloc.c [new file with mode: 0644]
support/xalloc.h [new file with mode: 0644]
system.h [new file with mode: 0644]
tincd.c [new file with mode: 0644]
tnl/Makefile.am [new file with mode: 0644]
tnl/tnl.c [new file with mode: 0644]
tnl/tnl.h [new file with mode: 0644]
vnd/Makefile.am [new file with mode: 0644]
vnd/vnd.c [new file with mode: 0644]
vnd/vnd.h [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..e64caa0
--- /dev/null
@@ -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 (file)
index 0000000..49d353a
--- /dev/null
@@ -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 (file)
index 0000000..f0c8dad
--- /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 <ivo@tinc-vpn.org>
+                  2000-2004 Guus Sliepen <guus@tinc-vpn.org>
+                 2000 Cris van Pelt <tribbel@arise.dhs.org>
+
+    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 (file)
index 0000000..bbc0139
--- /dev/null
+++ b/cfg/cfg.h
@@ -0,0 +1,64 @@
+/*
+    conf.h -- header for conf.c
+
+    Copyright (C) 1998-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+                  2000-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..c26b049
--- /dev/null
@@ -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 <sys/types.h>
+   #endif
+   #ifdef HAVE_NETDB_H
+   #include <netdb.h>
+   #endif
+   #ifdef HAVE_ARPA_INET_H
+   #include <arpa/inet.h>
+   #endif
+   #ifdef HAVE_SYS_SOCKET_H
+   #include <sys/socket.h>
+   #endif
+  ]
+)
+AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h],
+  [], [],
+  [#ifdef HAVE_SYS_TYPES_H
+   #include <sys/types.h>
+   #endif
+   #ifdef HAVE_NETDB_H
+   #include <netdb.h>
+   #endif
+   #ifdef HAVE_ARPA_INET_H
+   #include <arpa/inet.h>
+   #endif
+   #ifdef HAVE_SYS_SOCKET_H
+   #include <sys/socket.h>
+   #endif
+   #ifdef HAVE_NET_IF_H
+   #include <net/if.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_SYSTM_H
+   #include <netinet/in_systm.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_H
+   #include <netinet/in.h>
+   #endif
+   #ifdef HAVE_NETINET_IN6_H
+   #include <netinet/in6.h>
+   #endif
+   #ifdef HAVE_NET_ETHERNET_H
+   #include <net/ethernet.h>
+   #endif
+   #ifdef HAVE_NET_IF_ARP_H
+   #include <net/if_arp.h>
+   #endif
+  ]
+)
+AC_CHECK_HEADERS([netinet/tcp.h netinet/ip_icmp.h netinet/icmp6.h],
+  [], [],
+  [#ifdef HAVE_SYS_TYPES_H
+   #include <sys/types.h>
+   #endif
+   #ifdef HAVE_NETDB_H
+   #include <netdb.h>
+   #endif
+   #ifdef HAVE_ARPA_INET_H
+   #include <arpa/inet.h>
+   #endif
+   #ifdef HAVE_SYS_SOCKET_H
+   #include <sys/socket.h>
+   #endif
+   #ifdef HAVE_NET_IF_H
+   #include <net/if.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_SYSTM_H
+   #include <netinet/in_systm.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_H
+   #include <netinet/in.h>
+   #endif
+   #ifdef HAVE_NETINET_IP_H
+   #include <netinet/ip.h>
+   #endif
+   #ifdef HAVE_NETINET_IN6_H
+   #include <netinet/in6.h>
+   #endif
+   #ifdef HAVE_NETINET_IP6_H
+   #include <netinet/ip6.h>
+   #endif
+   #ifdef HAVE_NET_ETHERNET_H
+   #include <net/ethernet.h>
+   #endif
+   #ifdef HAVE_NET_IF_ARP_H
+   #include <net/if_arp.h>
+   #endif
+   #ifdef HAVE_NETINET_IF_ETHER_H
+   #include <netinet/if_ether.h>
+   #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 <sys/types.h>
+   #endif
+   #ifdef HAVE_NETDB_H
+   #include <netdb.h>
+   #endif
+   #ifdef HAVE_ARPA_INET_H
+   #include <arpa/inet.h>
+   #endif
+   #ifdef HAVE_SYS_SOCKET_H
+   #include <sys/socket.h>
+   #endif
+   #ifdef HAVE_NET_IF_H
+   #include <net/if.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_SYSTM_H
+   #include <netinet/in_systm.h>
+   #endif
+   #ifdef HAVE_NETINET_IN_H
+   #include <netinet/in.h>
+   #endif
+   #ifdef HAVE_NETINET_IP_H
+   #include <netinet/ip.h>
+   #endif
+   #ifdef HAVE_NETINET_TCP_H
+   #include <netinet/tcp.h>
+   #endif
+   #ifdef HAVE_NETINET_IN6_H
+   #include <netinet/in6.h>
+   #endif
+   #ifdef HAVE_NETINET_IP6_H
+   #include <netinet/ip6.h>
+   #endif
+   #ifdef HAVE_NET_ETHERNET_H
+   #include <net/ethernet.h>
+   #endif
+   #ifdef HAVE_NET_IF_ARP_H
+   #include <net/if_arp.h>
+   #endif
+   #ifdef HAVE_NETINET_IF_ETHER_H
+   #include <netinet/if_ether.h>
+   #endif
+   #ifdef HAVE_NETINET_IP_ICMP_H
+   #include <netinet/ip_icmp.h>
+   #endif
+   #ifdef HAVE_NETINET_ICMP6_H
+   #include <netinet/icmp6.h>
+   #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 (file)
index 0000000..511a1e6
--- /dev/null
@@ -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 (file)
index 0000000..56d7037
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    event.c -- event queue
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..e54b678
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    event.h -- event queue
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 (file)
index 0000000..966fe35
--- /dev/null
+++ b/fd/fd.c
@@ -0,0 +1,121 @@
+/*
+    fd.c -- I/O and event multiplexing
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 (file)
index 0000000..13714a4
--- /dev/null
+++ b/fd/fd.h
@@ -0,0 +1,51 @@
+/*
+    fd.h -- I/O and event multiplexing
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 (file)
index 0000000..6fe3047
--- /dev/null
@@ -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 (file)
index 0000000..e178d69
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+    log.h -- logging
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..9bbd8fe
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+    logger.c -- logging
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..afcf1e5
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    logger.h -- logging
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..9c5c432
--- /dev/null
@@ -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 (file)
index 0000000..bf78d1f
--- /dev/null
+++ b/rt/edge.c
@@ -0,0 +1,97 @@
+/*
+    edge.c -- edge management
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..efaddb2
--- /dev/null
+++ b/rt/edge.h
@@ -0,0 +1,60 @@
+/*
+    edge.h -- edge management
+    
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..91f58fd
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+    graph.h -- graph algorithms
+
+    Copyright (C) 2001-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2001-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..4178b15
--- /dev/null
+++ b/rt/node.c
@@ -0,0 +1,120 @@
+/*
+    node.c -- node management
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..0ef62e2
--- /dev/null
+++ b/rt/node.h
@@ -0,0 +1,75 @@
+/*
+    node.h -- node management
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..105249a
--- /dev/null
+++ b/rt/rt.c
@@ -0,0 +1,200 @@
+/*
+    rt.c -- routing
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..8f988fc
--- /dev/null
+++ b/rt/rt.h
@@ -0,0 +1,46 @@
+/*
+    route.h -- routing
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..ced1695
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+    subnet.c -- subnet handling
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..5134ae2
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+    subnet.h -- subnet handling
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 (file)
index 0000000..0cadfed
--- /dev/null
@@ -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 (file)
index 0000000..1550a62
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+    avl.c -- AVL tree management
+
+    Copyright (C) 1998 Michael H. Buselli
+                  2000-2004 Ivo Timmermans <ivo@tinc-vpn.org>,
+                  2000-2004 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2004 Wessel Dankers <wsl@tinc-vpn.org>
+
+    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 <cosine@cosine.org>.
+
+    Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> 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 <guus@tinc-vpn.org>.
+
+    $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 (file)
index 0000000..5831bcc
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+    avl.h -- AVL tree management
+
+    Copyright (C) 1998 Michael H. Buselli
+                  2000-2004 Ivo Timmermans <ivo@tinc-vpn.org>,
+                  2000-2004 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2004 Wessel Dankers <wsl@tinc-vpn.org>
+
+    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 <cosine@cosine.org>.
+
+    Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> 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 <guus@tinc-vpn.org>.
+
+    $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 (file)
index 0000000..19a290b
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+    ipv4.h -- missing IPv4 related definitions
+
+    Copyright (C) 2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+                  2003-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..5de7953
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    ipv6.h -- missing IPv6 related definitions
+
+    Copyright (C) 2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+                  2003-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..8cd1c5e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    sockaddr.h -- sockaddr handling
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 (file)
index 0000000..80699f9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+    xalloc.c -- safe memory allocation functions
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 (file)
index 0000000..db2e531
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+    xalloc.h -- safe memory allocation functions
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 (file)
index 0000000..104eaaa
--- /dev/null
+++ b/system.h
@@ -0,0 +1,161 @@
+/*
+    system.h -- system headers
+
+    Copyright (C) 1998-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+                       2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+typedef int bool;
+#define true 1
+#define false 0
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* Include system specific headers */
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+/* SunOS really wants sys/socket.h BEFORE net/if.h,
+   and FreeBSD wants these lines below the rest. */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+
+#ifdef HAVE_NETINET_IP6_H
+#include <netinet/ip6.h>
+#endif
+
+#ifdef HAVE_MINGW
+#include <windows.h>
+#include <winsock2.h>
+#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 (file)
index 0000000..5f99cea
--- /dev/null
+++ b/tincd.c
@@ -0,0 +1,121 @@
+/*
+    tincd.c -- the main file for tincd
+
+    Copyright (C) 2000-2004 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 (file)
index 0000000..594838d
--- /dev/null
@@ -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 (file)
index 0000000..5a767f5
--- /dev/null
+++ b/tnl/tnl.c
@@ -0,0 +1,386 @@
+/*
+    tnl.c -- tunnels
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 <gnutls/gnutls.h>
+
+#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 (file)
index 0000000..9f1f836
--- /dev/null
+++ b/tnl/tnl.h
@@ -0,0 +1,104 @@
+/*
+    tnl.h -- tunnels
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 <gnutls/gnutls.h>
+
+#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 (file)
index 0000000..e542298
--- /dev/null
@@ -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 (file)
index 0000000..538dcb8
--- /dev/null
+++ b/vnd/vnd.c
@@ -0,0 +1,151 @@
+/*
+    vnd.c -- virtual network device management
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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 <linux/if_tun.h>
+
+#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 (file)
index 0000000..4670873
--- /dev/null
+++ b/vnd/vnd.h
@@ -0,0 +1,59 @@
+/*
+    vnd.c -- virtual network device management
+
+    Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
+                  2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
+
+    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__ */