Lots of changes. It compiles, but doesn't do much yet.
authorGuus Sliepen <guus@tinc-vpn.org>
Sat, 27 Mar 2004 11:59:31 +0000 (11:59 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Sat, 27 Mar 2004 11:59:31 +0000 (11:59 +0000)
25 files changed:
Makefile.am
logger/logger.h
process.c [new file with mode: 0644]
rt/Makefile.am
rt/README [new file with mode: 0644]
rt/edge.h
rt/graph.c [new file with mode: 0644]
rt/node.h
rt/route.c [new file with mode: 0644]
rt/route.h [new file with mode: 0644]
rt/rt.c
rt/rt.h
support/Makefile.am
support/avl.c
support/avl.h
support/ethernet.h [new file with mode: 0644]
support/gettext.h [new file with mode: 0644]
support/list.c [new file with mode: 0644]
support/list.h [new file with mode: 0644]
system.h
tincd.c
tnl/tnl.c
tnl/tnl.h
vnd/vnd.c
vnd/vnd.h

index e64caa0..c9da143 100644 (file)
@@ -2,16 +2,18 @@ AUTOMAKE_OPTIONS = gnu
 
 EXTRA_DIST = config.rpath mkinstalldirs system.h depcomp
 
-SUBDIRS = cfg fd logger support tnl vnd
+SUBDIRS = cfg fd logger rt support tnl vnd
 
 sbin_PROGRAMS = tincd
 
-tincd_SOURCES = tincd.c
+tincd_SOURCES = process.c tincd.c
 
-tincd_LDADD = cfg/libcfg.a fd/libfd.a logger/liblogger.a support/libsupport.a tnl/libtnl.a vnd/libvnd.a -lgnutls
+tincd_LDADD = cfg/libcfg.a fd/libfd.a logger/liblogger.a rt/librt.a support/libsupport.a tnl/libtnl.a vnd/libvnd.a -lgnutls
 
 ACLOCAL_AMFLAGS = -I m4 
 
+AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
+
 ChangeLog:
        svn log > ChangeLog
 
index afcf1e5..a65968e 100644 (file)
@@ -42,4 +42,6 @@ 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)));
 
+extern enum logger_level logger_level;
+
 #endif
diff --git a/process.c b/process.c
new file mode 100644 (file)
index 0000000..f04f7c0
--- /dev/null
+++ b/process.c
@@ -0,0 +1,565 @@
+/*
+    process.c -- process management functions
+    Copyright (C) 1999-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$
+*/
+
+#include "system.h"
+
+#include "tincd.h"
+#include "logger/logger.h"
+
+static sigset_t emptysigset;
+
+/* Some functions the less gifted operating systems might lack... */
+
+#ifdef HAVE_MINGW
+static SC_HANDLE manager = NULL;
+static SC_HANDLE service = NULL;
+static SERVICE_STATUS status = {0};
+static SERVICE_STATUS_HANDLE statushandle = 0;
+
+bool install_service(void) {
+       char command[4096] = "\"";
+       char **argp;
+       bool space;
+       SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
+
+       manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+       if(!manager) {
+               logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
+               return false;
+       }
+
+       if(!strchr(program_name, '\\')) {
+               GetCurrentDirectory(sizeof(command) - 1, command + 1);
+               strncat(command, "\\", sizeof(command));
+       }
+
+       strncat(command, program_name, sizeof(command));
+
+       strncat(command, "\"", sizeof(command));
+
+       for(argp = g_argv + 1; *argp; argp++) {
+               space = strchr(*argp, ' ');
+               strncat(command, " ", sizeof(command));
+               
+               if(space)
+                       strncat(command, "\"", sizeof(command));
+               
+               strncat(command, *argp, sizeof(command));
+
+               if(space)
+                       strncat(command, "\"", sizeof(command));
+       }
+
+       service = CreateService(manager, identname, identname,
+                       SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+                       command, "NDIS", NULL, NULL, NULL, NULL);
+       
+       if(!service) {
+               logger(LOG_ERR, _("Could not create %s service: %s"), identname, winerror(GetLastError()));
+               return false;
+       }
+
+       ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
+
+       logger(LOG_INFO, _("%s service installed"), identname);
+
+       if(!StartService(service, 0, NULL))
+               logger(LOG_WARNING, _("Could not start %s service: %s"), identname, winerror(GetLastError()));
+       else
+               logger(LOG_INFO, _("%s service started"), identname);
+
+       return true;
+}
+
+bool remove_service(void) {
+       manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+       if(!manager) {
+               logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
+               return false;
+       }
+
+       service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
+
+       if(!service) {
+               logger(LOG_ERR, _("Could not open %s service: %s"), identname, winerror(GetLastError()));
+               return false;
+       }
+
+       if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
+               logger(LOG_ERR, _("Could not stop %s service: %s"), identname, winerror(GetLastError()));
+       else
+               logger(LOG_INFO, _("%s service stopped"), identname);
+
+       if(!DeleteService(service)) {
+               logger(LOG_ERR, _("Could not remove %s service: %s"), identname, winerror(GetLastError()));
+               return false;
+       }
+
+       logger(LOG_INFO, _("%s service removed"), identname);
+
+       return true;
+}
+
+DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
+       switch(request) {
+               case SERVICE_CONTROL_STOP:
+                       logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_STOP");
+                       break;
+               case SERVICE_CONTROL_SHUTDOWN:
+                       logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_SHUTDOWN");
+                       break;
+               default:
+                       logger(LOG_WARNING, _("Got unexpected request %d"), request);
+                       return ERROR_CALL_NOT_IMPLEMENTED;
+       }
+
+       if(running) {
+               running = false;
+               status.dwWaitHint = 30000; 
+               status.dwCurrentState = SERVICE_STOP_PENDING; 
+               SetServiceStatus(statushandle, &status);
+               return NO_ERROR;
+       } else {
+               status.dwWaitHint = 0; 
+               status.dwCurrentState = SERVICE_STOPPED; 
+               SetServiceStatus(statushandle, &status);
+               exit(1);
+       }
+
+}
+
+VOID WINAPI run_service(DWORD argc, LPTSTR* argv)
+{
+       int err = 1;
+       extern int main2(int argc, char **argv);
+
+
+       status.dwServiceType = SERVICE_WIN32; 
+       status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+       status.dwWin32ExitCode = 0; 
+       status.dwServiceSpecificExitCode = 0; 
+       status.dwCheckPoint = 0; 
+
+       statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
+
+       if (!statushandle) {
+               logger(LOG_ERR, _("System call `%s' failed: %s"), "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
+               err = 1;
+       } else {
+               status.dwWaitHint = 30000; 
+               status.dwCurrentState = SERVICE_START_PENDING; 
+               SetServiceStatus(statushandle, &status);
+
+               status.dwWaitHint = 0; 
+               status.dwCurrentState = SERVICE_RUNNING;
+               SetServiceStatus(statushandle, &status);
+
+               err = main2(argc, argv);
+
+               status.dwWaitHint = 0;
+               status.dwCurrentState = SERVICE_STOPPED; 
+               //status.dwWin32ExitCode = err; 
+               SetServiceStatus(statushandle, &status);
+       }
+
+       return;
+}
+
+bool init_service(void) {
+       SERVICE_TABLE_ENTRY services[] = {
+               {identname, run_service},
+               {NULL, NULL}
+       };
+
+       if(!StartServiceCtrlDispatcher(services)) {
+               if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
+                       return false;
+               }
+               else
+                       logger(LOG_ERR, _("System call `%s' failed: %s"), "StartServiceCtrlDispatcher", winerror(GetLastError()));
+       }
+
+       return true;
+}
+#endif
+
+#ifndef HAVE_MINGW
+/*
+  check for an existing tinc for this net, and write pid to pidfile
+*/
+
+static pid_t read_pid(const char *pidfilename) {
+       FILE *pidfile;
+       long int pid = 0;
+
+       pidfile = fopen(pidfilename, "r");
+       if(!pidfile)
+               return 0;
+
+       fscanf(pidfile, "%ld", &pid);
+       fclose(pidfile);
+       
+       return pid;
+}
+
+static pid_t check_pid(const char *pidfilename) {
+       pid_t pid;
+       
+       pid = read_pid(pidfilename);
+
+       if (!pid || pid == getpid())
+               return 0;
+
+       if(kill(pid, 0) && errno == ESRCH)
+               return 0;
+
+       return pid;
+}
+
+static pid_t write_pid(const char *pidfilename) {
+       FILE *pidfile;
+       int pidfd;
+       pid_t pid;
+
+       pidfd = open(pidfilename, O_RDWR | O_CREAT);
+
+       if(pidfd == -1)
+               return 0;
+
+#ifdef HAVE_FLOCK
+       if(flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
+               close(pidfd);
+               return 0;
+       }
+#endif
+
+       pidfile = fdopen(pidfd, "r+");
+
+       if(!pidfile) {
+               close(pidfd);
+               return 0;
+       }
+
+       pid = getpid();
+       fprintf(pidfile, "%ld\n", (long)pid);
+       fflush(pidfile);
+
+#ifdef HAVE_FLOCK
+       flock(pidfd, LOCK_UN);
+#endif
+
+       close(pidfd);
+
+       return pid;
+}
+
+bool remove_pid(const char *pidfilename) {
+       return unlink(pidfilename) == 0;
+}
+
+static bool write_pidfile(void)
+{
+       pid_t pid;
+
+       pid = check_pid(tinc_pidfilename);
+
+       if(pid) {
+               if(tinc_netname)
+                       fprintf(stderr, _("A tincd is already running for net `%s' with pid %ld.\n"),
+                                       tinc_netname, (long)pid);
+               else
+                       fprintf(stderr, _("A tincd is already running with pid %ld.\n"), (long)pid);
+               return false;
+       }
+
+       /* if it's locked, write-protected, or whatever */
+       if(!write_pid(tinc_pidfilename)) {
+               fprintf(stderr, _("Could write pid file %s: %s\n"), tinc_pidfilename, strerror(errno));
+               return false;
+       }
+
+       return true;
+}
+#endif
+
+/*
+  kill older tincd for this net
+*/
+bool kill_other(int signal)
+{
+#ifndef HAVE_MINGW
+       pid_t pid;
+
+       pid = read_pid(tinc_pidfilename);
+
+       if(!pid) {
+               if(tinc_netname)
+                       fprintf(stderr, _("No other tincd is running for net `%s'.\n"),
+                                       tinc_netname);
+               else
+                       fprintf(stderr, _("No other tincd is running.\n"));
+               return false;
+       }
+
+       errno = 0;                                      /* No error, sometimes errno is only changed on error */
+
+       /* ESRCH is returned when no process with that pid is found */
+       if(kill(pid, signal) && errno == ESRCH) {
+               if(tinc_netname)
+                       fprintf(stderr, _("The tincd for net `%s' is no longer running. "),
+                                       tinc_netname);
+               else
+                       fprintf(stderr, _("The tincd is no longer running. "));
+
+               fprintf(stderr, _("Removing stale lock file.\n"));
+               remove_pid(tinc_pidfilename);
+       }
+
+       return true;
+#else
+       return remove_service();
+#endif
+}
+
+/*
+  Detach from current terminal, write pidfile, kill parent
+*/
+bool detach(void) {
+#ifndef HAVE_MINGW
+       if(!write_pidfile())
+               return false;
+
+       /* If we succeeded in doing that, detach */
+
+       logger_exit();
+
+       if(daemon(0, 0)) {
+               fprintf(stderr, _("Couldn't detach from terminal: %s"),
+                               strerror(errno));
+               return false;
+       }
+
+       /* Now UPDATE the pid in the pidfile, because we changed it... */
+
+       if(!write_pid(tinc_pidfilename)) {
+               fprintf(stderr, _("Could not write pid file %s: %s\n"), tinc_pidfilename, strerror(errno));
+               return false;
+       }
+#else
+       if(!statushandle)
+               exit(install_service());
+#endif
+
+       logger_init(tinc_identname, tinc_use_logfile ? LOGGER_MODE_FILE : LOGGER_MODE_SYSLOG);
+
+       return true;
+}
+
+bool execute_script(const char *name, char **envp)
+{
+#ifdef HAVE_SYSTEM
+       int status, len;
+       struct stat s;
+       char *scriptname;
+
+#ifndef HAVE_MINGW
+       len = asprintf(&scriptname, "\"%s/%s\"", tinc_confbase, name);
+#else
+       len = asprintf(&scriptname, "\"%s/%s.bat\"", tinc_confbase, name);
+#endif
+       if(len < 0)
+               return false;
+
+       scriptname[len - 1] = '\0';
+
+       /* First check if there is a script */
+
+       if(stat(scriptname + 1, &s))
+               return true;
+
+       logger(LOG_INFO, _("Executing script %s"), name);
+
+#ifdef HAVE_PUTENV
+       /* Set environment */
+       
+       while(*envp)
+               putenv(*envp++);
+#endif
+
+       scriptname[len - 1] = '\"';
+       status = system(scriptname);
+
+       free(scriptname);
+
+       /* Unset environment? */
+
+#ifdef WEXITSTATUS
+       if(status != -1) {
+               if(WIFEXITED(status)) { /* Child exited by itself */
+                       if(WEXITSTATUS(status)) {
+                               logger(LOG_ERR, _("Script %s exited with non-zero status %d"),
+                                          name, WEXITSTATUS(status));
+                               return false;
+                       }
+               } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
+                       logger(LOG_ERR, _("Script %s was killed by signal %d (%s)"),
+                                  name, WTERMSIG(status), strsignal(WTERMSIG(status)));
+                       return false;
+               } else {                        /* Something strange happened */
+                       logger(LOG_ERR, _("Script %s terminated abnormally"), name);
+                       return false;
+               }
+       } else {
+               logger(LOG_ERR, _("System call `%s' failed: %s"), "system", strerror(errno));
+               return false;
+       }
+#endif
+#endif
+       return true;
+}
+
+
+/*
+  Signal handlers.
+*/
+
+#ifndef HAVE_MINGW
+static RETSIGTYPE sigterm_handler(int a) {
+       logger(LOG_NOTICE, _("Got %s signal"), "TERM");
+       exit(1);
+}
+
+static RETSIGTYPE sigquit_handler(int a) {
+       logger(LOG_NOTICE, _("Got %s signal"), "QUIT");
+       exit(1);
+}
+
+static RETSIGTYPE fatal_signal_square(int a) {
+       logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
+                  strsignal(a));
+       exit(1);
+}
+
+static RETSIGTYPE fatal_signal_handler(int a) {
+       struct sigaction act;
+       logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
+       logger(LOG_NOTICE, _("Not restarting."));
+       exit(1);
+}
+
+static RETSIGTYPE sighup_handler(int a) {
+       logger(LOG_NOTICE, _("Got %s signal"), "HUP");
+//     sighup = true;
+}
+
+static RETSIGTYPE sigint_handler(int a) {
+       static logger_level_t saved_logger_level = -1;
+
+       logger(LOG_NOTICE, _("Got %s signal"), "INT");
+
+       if(saved_logger_level != -1) {
+               logger(LOG_NOTICE, _("Reverting to old debug level (%d)"),
+                       saved_logger_level);
+               logger_level = saved_logger_level;
+               saved_logger_level = -1;
+       } else {
+               logger(LOG_NOTICE,
+                       _("Temporarily setting debug level to 5.  Kill me with SIGINT again to go back to level %d."),
+                       logger_level);
+               saved_logger_level = logger_level;
+               logger_level = 5;
+       }
+}
+
+static RETSIGTYPE sigalrm_handler(int a) {
+       logger(LOG_NOTICE, _("Got %s signal"), "ALRM");
+       //sigalrm = true;
+}
+
+static RETSIGTYPE sigusr1_handler(int a) {
+}
+
+static RETSIGTYPE sigusr2_handler(int a) {
+}
+
+static RETSIGTYPE sigwinch_handler(int a) {
+       //do_purge = true;
+}
+
+static RETSIGTYPE unexpected_signal_handler(int a) {
+       logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
+}
+
+static RETSIGTYPE ignore_signal_handler(int a) {
+       logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
+}
+
+static struct {
+       int signal;
+       void (*handler)(int);
+} sighandlers[] = {
+       {SIGHUP, sighup_handler},
+       {SIGTERM, sigterm_handler},
+       {SIGQUIT, sigquit_handler},
+       {SIGSEGV, SIG_DFL},
+       {SIGBUS, fatal_signal_handler},
+       {SIGILL, fatal_signal_handler},
+       {SIGPIPE, ignore_signal_handler},
+       {SIGINT, sigint_handler},
+       {SIGUSR1, sigusr1_handler},
+       {SIGUSR2, sigusr2_handler},
+       {SIGCHLD, ignore_signal_handler},
+       {SIGALRM, sigalrm_handler},
+       {SIGWINCH, sigwinch_handler},
+       {0, NULL}
+};
+#endif
+
+void setup_signals(void)
+{
+#ifndef HAVE_MINGW
+       int i;
+       struct sigaction act;
+
+       sigemptyset(&emptysigset);
+       act.sa_handler = NULL;
+       act.sa_mask = emptysigset;
+       act.sa_flags = 0;
+
+       /* Set a default signal handler for every signal, errors will be
+          ignored. */
+       for(i = 0; i < NSIG; i++) {
+               act.sa_handler = unexpected_signal_handler;
+               sigaction(i, &act, NULL);
+       }
+
+       /* Then, for each known signal that we want to catch, assign a
+          handler to the signal, with error checking this time. */
+       for(i = 0; sighandlers[i].signal; i++) {
+               act.sa_handler = sighandlers[i].handler;
+               if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
+                       fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
+                                       sighandlers[i].signal, strsignal(sighandlers[i].signal),
+                                       strerror(errno));
+       }
+#endif
+}
index 9c5c432..528c26c 100644 (file)
@@ -1,6 +1,6 @@
 noinst_LIBRARIES = librt.a
 
-noinst_HEADERS = edge.h graph.h node.h rt.h subnet.h
+noinst_HEADERS = edge.h graph.h node.h route.h rt.h subnet.h
 
-librt_a_SOURCES = edge.c graph.c node.c rt.c subnet.c
+librt_a_SOURCES = edge.c graph.c node.c route.c rt.c subnet.c
 
diff --git a/rt/README b/rt/README
new file mode 100644 (file)
index 0000000..d0b6dc8
--- /dev/null
+++ b/rt/README
@@ -0,0 +1,31 @@
+Routing subsystem
+=================
+
+The routing part of tinc reads/writes packets to/from the virtual network
+device, sets up tunnels on demand and routes packets to/from the virtual
+network device and the tunnels.  It can also exchange information about other
+daemons using the tunnels.
+
+The following datatypes are used in this subsystem:
+
+struct node
+-----------
+
+This represents a tinc daemon. It keeps track of the best way to reach this
+daemon (if it is reachable at all), which subnets are associated with this
+daemon, and the connections it has with other daemons.
+
+struct subnet
+-------------
+
+This represents a subnet, can have an expiry time and is associated with a node.
+
+struct edge
+-----------
+
+This represents a connection from one tinc daemon to another. The edges have a
+specific direction, and normally if there really is a connection between tinc
+daemon A and B, there will be two edges, A -> B and B -> A.  This seems
+redundant, but is necessary to allow disparities between information learned
+from A and information learned from B.
+
index efaddb2..775d833 100644 (file)
--- a/rt/edge.h
+++ b/rt/edge.h
@@ -26,6 +26,7 @@
 
 #include "rt/node.h"
 #include "support/avl.h"
+#include "tnl/tnl.h"
 
 typedef struct edge_status {
        int visited:1;
@@ -40,6 +41,7 @@ typedef struct edge {
        int weight;
 
        struct edge *reverse;
+       struct tnl *tnl;
 
        edge_status_t status;
        node_options_t options;
diff --git a/rt/graph.c b/rt/graph.c
new file mode 100644 (file)
index 0000000..63e8faf
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+    graph.c -- 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$
+*/
+
+/* We need to generate two trees from the graph:
+
+   1. A minimum spanning tree for broadcasts,
+   2. A single-source shortest path tree for unicasts.
+
+   Actually, the first one alone would suffice but would make unicast packets
+   take longer routes than necessary.
+
+   For the MST algorithm we can choose from Prim's or Kruskal's. I personally
+   favour Kruskal's, because we make an extra AVL tree of edges sorted on
+   weights (metric). That tree only has to be updated when an edge is added or
+   removed, and during the MST algorithm we just have go linearly through that
+   tree, adding safe edges until #edges = #nodes - 1. The implementation here
+   however is not so fast, because I tried to avoid having to make a forest and
+   merge trees.
+
+   For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
+   simple breadth-first search is presented here.
+
+   The SSSP algorithm will also be used to determine whether nodes are directly,
+   indirectly or not reachable from the source. It will also set the correct
+   destination address and port of a node if possible.
+*/
+
+#include "system.h"
+
+#include "rt/edge.h"
+#include "rt/node.h"
+#include "support/avl.h"
+#include "support/list.h"
+
+/* Implementation of Kruskal's algorithm.
+   Running time: O(EN)
+   Please note that sorting on weight is already done by add_edge().
+*/
+
+void mst_kruskal(void) {
+       avl_node_t *avl, *next;
+       edge_t *edge;
+       node_t *node;
+       int safe_edges = 0;
+       bool skipped;
+
+       /* Do we have something to do at all? */
+
+       if(!edges->head)
+               return;
+
+       logger(LOG_DEBUG, "Running Kruskal's algorithm:");
+
+       /* Clear MST status on edges */
+
+       avl_foreach(edges, edge, edge->status.mst = false);
+
+       /* Clear visited status on nodes */
+
+       avl_foreach(nodes, node, node->status.visited = false);
+
+       /* Starting point */
+
+       ((edge_t *) edges->head->data)->from->status.visited = true;
+
+       /* Add safe edges */
+
+       for(skipped = false, avl = edges->head; avl; avl = next) {
+               next = avl->next;
+               edge = avl->data;
+
+               if(!edge->reverse || edge->from->status.visited == edge->to->status.visited) {
+                       skipped = true;
+                       continue;
+               }
+
+               edge->from->status.visited = true;
+               edge->to->status.visited = true;
+               edge->status.mst = true;
+               edge->reverse->status.mst = true;
+
+               if(skipped) {
+                       skipped = false;
+                       next = edges->head;
+                       continue;
+               }
+       }
+}
+
+/* Implementation of a simple breadth-first search algorithm.
+   Running time: O(E)
+*/
+
+void sssp_bfs(void) {
+       list_t *todo;
+       list_node_t *todonode;
+       edge_t *edge;
+       node_t *node;
+       bool indirect;
+       char *name;
+       char *address, *port;
+       int i;
+
+       todo = list_new(NULL);
+
+       /* Clear visited status on nodes */
+
+       avl_foreach(nodes, node, {
+               node->status.visited = false;
+               node->status.indirect = true;
+       });
+
+       /* Begin with myself */
+
+       myself->status.visited = true;
+       myself->status.indirect = false;
+       myself->nexthop = myself;
+       myself->via = myself;
+
+       list_add_head(todo, myself);
+
+       /* Loop while todo list is filled */
+
+       while(todo->head) {
+               list_foreach_node(todo, todonode, {
+                       node = todonode->data;
+
+                       avl_foreach(node->edges, edge, {
+                               if(!edge->reverse)
+                                       continue;
+
+                               /* Situation:
+
+                                            /
+                                           /
+                                  ----->(node)---edge-->(edge->to)
+                                           \
+                                            \
+
+                                  node->address is set to the ->address of the edge left of node.
+                                  We are currently examining the edge right of node:
+
+                                  - If edge->reverse->address != node->address, then edge->to is probably
+                                    not reachable for the nodes left of node. We do as if the indirectdata
+                                    flag is set on edge.
+                                  - If edge provides for better reachability of edge->to, update
+                                    edge->to and (re)add it to the todo_tree to (re)examine the reachability
+                                    of nodes behind it.
+                                */
+
+                               indirect = node->status.indirect || edge->options & NODE_OPTION_INDIRECT
+                                       || ((node != myself) && sockaddrcmp(&node->address, &edge->reverse->address));
+
+                               if(edge->to->status.visited && (!edge->to->status.indirect || indirect))
+                                       continue;
+
+                               edge->to->status.visited = true;
+                               edge->to->status.indirect = indirect;
+                               edge->to->nexthop = (node->nexthop == myself) ? edge->to : node->nexthop;
+                               edge->to->via = indirect ? node->via : edge->to;
+                               edge->to->options = edge->options;
+
+                               list_add_head(todo, edge->to);
+                       });
+
+                       list_del_node(todo, todonode);
+               });
+       }
+
+       list_free(todo);
+
+       /* Check reachability status. */
+
+       avl_foreach(nodes, node, {
+               if(node->status.visited != node->status.reachable) {
+                       node->status.reachable = !node->status.reachable;
+
+                       if(node->status.reachable)
+                               logger(LOG_DEBUG, _("Node %s became reachable"), node->name);
+                       else
+                               logger(LOG_DEBUG, _("Node %s became unreachable"), node->name);
+
+#if 0
+                       asprintf(&envp[0], "NETNAME=%s", netname ? : "");
+                       asprintf(&envp[1], "DEVICE=%s", device ? : "");
+                       asprintf(&envp[2], "INTERFACE=%s", iface ? : "");
+                       asprintf(&envp[3], "NODE=%s", n->name);
+                       sockaddr2str(&n->address, &address, &port);
+                       asprintf(&envp[4], "REMOTEADDRESS=%s", address);
+                       asprintf(&envp[5], "REMOTEPORT=%s", port);
+                       envp[6] = NULL;
+
+                       asprintf(&name,
+                                        n->status.reachable ? "hosts/%s-up" : "hosts/%s-down",
+                                        n->name);
+                       execute_script(name, envp);
+
+                       free(name);
+                       free(address);
+                       free(port);
+
+                       for(i = 0; i < 7; i++)
+                               free(envp[i]);
+#endif
+               }
+       });
+}
+
+void graph(void)
+{
+       mst_kruskal();
+       sssp_bfs();
+}
index 0ef62e2..acf20bc 100644 (file)
--- a/rt/node.h
+++ b/rt/node.h
@@ -26,7 +26,7 @@
 
 typedef int node_options_t;
 
-#define NODE_OPTIONS_INDIRECT 1
+#define NODE_OPTION_INDIRECT 1
 
 #include "rt/edge.h"
 #include "rt/subnet.h"
diff --git a/rt/route.c b/rt/route.c
new file mode 100644 (file)
index 0000000..4721763
--- /dev/null
@@ -0,0 +1,754 @@
+/*
+    route.c -- routing
+    Copyright (C) 2000-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$
+*/
+
+#include "system.h"
+
+#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_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif
+#ifdef HAVE_NETINET_ICMP6_H
+#include <netinet/icmp6.h>
+#endif
+#ifdef HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif
+
+#include "logger/logger.h"
+#include "rt/rt.h"
+#include "rt/subnet.h"
+#include "support/avl.h"
+#include "support/ethernet.h"
+#include "support/ipv4.h"
+#include "support/ipv6.h"
+
+static mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
+
+/* Sizes of various headers */
+
+static const size_t ether_size = sizeof(struct ether_header);
+static const size_t arp_size = sizeof(struct ether_arp);
+static const size_t ip_size = sizeof(struct ip);
+static const size_t icmp_size = sizeof(struct icmp) - sizeof(struct ip);
+static const size_t ip6_size = sizeof(struct ip6_hdr);
+static const size_t icmp6_size = sizeof(struct icmp6_hdr);
+static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
+static const size_t opt_size = sizeof(struct nd_opt_hdr);
+
+static struct timeval expires(int seconds) {
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+       tv.tv_sec += seconds;
+
+       return tv;
+}
+
+/* RFC 1071 */
+
+static __inline__ uint16_t inet_checksum(const void *data, int len, uint16_t prevsum) {
+       const uint16_t *p = data;
+       uint32_t checksum = prevsum ^ 0xFFFF;
+
+       while(len >= 2) {
+               checksum += *p++;
+               len -= 2;
+       }
+       
+       if(len)
+               checksum += *(uint8_t *)p;
+
+       while(checksum >> 16)
+               checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+       return ~checksum;
+}
+
+static __inline__ bool ratelimit(int frequency) {
+       static time_t lasttime = 0;
+       static int count = 0;
+       time_t now = time(NULL);
+       
+       if(lasttime == now) {
+               if(++count > frequency)
+                       return true;
+       } else {
+               lasttime = now;
+               count = 0;
+       }
+
+       return false;
+}
+
+static __inline__ bool checklength(node_t *source, int len, int minlen) {
+       if(len < minlen) {
+               logger(LOG_WARNING, _("Got too short packet from %s"), source->name);
+               return false;
+       } else
+               return true;
+}
+       
+static __inline__ void learn_mac(mac_t *address) {
+       subnet_t *subnet;
+       avl_node_t *node;
+
+       subnet = subnet_get_mac(address);
+
+       /* If we don't know this MAC address yet, store it */
+
+       if(!subnet) {
+               logger(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
+                                  address->x[0], address->x[1], address->x[2], address->x[3],
+                                  address->x[4], address->x[5]);
+
+               subnet = subnet_new();
+               subnet->type = SUBNET_TYPE_MAC;
+               subnet->expires = expires(rt_macexpire);
+               subnet->net.mac.address = *address;
+               subnet->owner = myself;
+               subnet_add(subnet);
+
+               /* And tell all other tinc daemons it's our MAC */
+
+#if 0
+               for(node = connection_tree->head; node; node = node->next) {
+                       c = node->data;
+                       if(c->status.active)
+                               send_add_subnet(c, subnet);
+               }
+#endif
+       }
+
+       if(timerisset(&subnet->expires))
+               subnet->expires = expires(rt_macexpire);
+}
+
+void age_subnets(void) {
+       subnet_t *s;
+
+#if 0
+       for(node = myself->subnet_tree->head; node; node = next) {
+               next = node->next;
+               s = node->data;
+               if(s->expires && s->expires < now) {
+                       {
+                               char netstr[MAXNETSTR];
+                               if(net2str(netstr, sizeof netstr, s))
+                                       logger(LOG_INFO, _("Subnet %s expired"), netstr);
+                       }
+
+                       for(node2 = connection_tree->head; node2; node2 = node2->next) {
+                               c = node2->data;
+                               if(c->status.active)
+                                       send_del_subnet(c, s);
+                       }
+
+                       subnet_del(myself, s);
+               }
+       }
+#endif
+}
+
+static void send_packet(node_t *dest, const uint8_t *packet, int len) {
+       if(dest == myself) {
+               rt_vnd->send(rt_vnd, packet, len);
+       } else if (dest->tnl) {
+               dest->tnl->send_packet(dest->tnl, packet, len);
+       } else {
+               logger(LOG_ERR, _("No tunnel for packet destination %s!"), dest->name);
+       }
+}
+
+static void broadcast_packet(node_t *source, const uint8_t *packet, int len) {
+       tnl_t *tnl;
+       edge_t *edge;
+
+       if(source != myself)
+               send_packet(myself, packet, len);
+       
+       avl_foreach(rt_tnls, tnl, {
+               edge = tnl->data;
+               if(edge && edge->status.mst && edge->to != source)
+                       send_packet(edge->to, packet, len);
+       });
+}
+
+static __inline__ void route_mac(node_t *source, const uint8_t *packet, int len) {
+       subnet_t *subnet;
+
+       /* Learn source address */
+
+       if(source == myself)
+               learn_mac((mac_t *)(packet + 6));
+
+       /* Lookup destination address */
+
+       subnet = subnet_get_mac((mac_t *)(packet));
+
+       if(!subnet) {
+               broadcast_packet(source, packet, len);
+               return;
+       }
+
+       if(subnet->owner == source) {
+               logger(LOG_WARNING, _("Packet looping back to %s!"), source->name);
+               return;
+       }
+
+       send_packet(subnet->owner, packet, len);
+}
+
+/* RFC 792 */
+
+static void route_ipv4_unreachable(node_t *source, const uint8_t *packet, int len, uint8_t type, uint8_t code) {
+       uint8_t reply[ether_size + IP_MSS];
+
+       struct ip ip = {0};
+       struct icmp icmp = {0};
+       
+       struct in_addr ip_src;
+       struct in_addr ip_dst;
+       uint32_t oldlen;
+
+       if(ratelimit(3))
+               return;
+       
+       /* Copy headers from packet into properly aligned structs on the stack */
+
+       memcpy(&ip, packet + ether_size, ip_size);
+
+       /* Remember original source and destination */
+       
+       ip_src = ip.ip_src;
+       ip_dst = ip.ip_dst;
+
+       oldlen = len - ether_size;
+
+       if(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+               icmp.icmp_nextmtu = htons(len - ether_size);
+
+       if(oldlen >= IP_MSS - ip_size - icmp_size)
+               oldlen = IP_MSS - ip_size - icmp_size;
+       
+       /* Copy first part of original contents to ICMP message */
+       
+       memmove(reply + ether_size + ip_size + icmp_size, packet + ether_size, oldlen);
+
+       /* Fill in IPv4 header */
+       
+       ip.ip_v = 4;
+       ip.ip_hl = ip_size / 4;
+       ip.ip_tos = 0;
+       ip.ip_len = htons(ip_size + icmp_size + oldlen);
+       ip.ip_id = 0;
+       ip.ip_off = 0;
+       ip.ip_ttl = 255;
+       ip.ip_p = IPPROTO_ICMP;
+       ip.ip_sum = 0;
+       ip.ip_src = ip_dst;
+       ip.ip_dst = ip_src;
+
+       ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
+       
+       /* Fill in ICMP header */
+       
+       icmp.icmp_type = type;
+       icmp.icmp_code = code;
+       icmp.icmp_cksum = 0;
+       
+       icmp.icmp_cksum = inet_checksum(&icmp, icmp_size, ~0);
+       icmp.icmp_cksum = inet_checksum(packet + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
+
+       /* Copy structs on stack back to packet */
+
+       memcpy(reply + ether_size, &ip, ip_size);
+       memcpy(reply + ether_size + ip_size, &icmp, icmp_size);
+       
+       send_packet(source, reply, ether_size + ip_size + icmp_size + oldlen);
+}
+
+/* RFC 791 */
+
+static __inline__ void fragment_ipv4_packet(node_t *dest, const uint8_t *packet, int len) {
+       struct ip ip;
+       char fragment[dest->tnl->mtu];
+       int fraglen, maxlen, todo;
+       const uint8_t *offset;
+       uint16_t ip_off, origf;
+       
+       memcpy(&ip, packet + ether_size, ip_size);
+
+       if(ip.ip_hl != ip_size / 4)
+               return;
+       
+       todo = ntohs(ip.ip_len) - ip_size;
+
+       if(ether_size + ip_size + todo != len) {
+               logger(LOG_WARNING, _("Length of packet (%d) doesn't match length in IPv4 header (%d)"), len, ether_size + ip_size + todo);
+               return;
+       }
+
+       logger(LOG_INFO, _("Fragmenting packet of %d bytes to %s"), len, dest->name);
+
+       offset = packet + ether_size + ip_size;
+       maxlen = (dest->tnl->mtu - ether_size - ip_size) & ~0x7;
+       ip_off = ntohs(ip.ip_off);
+       origf = ip_off & ~IP_OFFMASK;
+       ip_off &= IP_OFFMASK;
+       
+       while(todo) {
+               fraglen = todo > maxlen ? maxlen : todo;
+               memcpy(fragment + ether_size + ip_size, offset, fraglen);
+               todo -= fraglen;
+               offset += fraglen;
+
+               ip.ip_len = htons(ip_size + fraglen);
+               ip.ip_off = htons(ip_off | origf | (todo ? IP_MF : 0));
+               ip.ip_sum = 0;
+               ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
+               memcpy(fragment, packet, ether_size);
+               memcpy(fragment + ether_size, &ip, ip_size);
+
+               send_packet(dest, fragment, ether_size + ip_size + fraglen);
+
+               ip_off += fraglen / 8;
+       }       
+}
+
+static __inline__ void route_ipv4_unicast(node_t *source, const uint8_t *packet, int len) {
+       subnet_t *subnet;
+       node_t *via;
+
+       subnet = subnet_get_ipv4((ipv4_t *)(packet + 30));
+
+       if(!subnet) {
+               logger(LOG_WARNING, _("Cannot route packet from %s: unknown IPv4 destination address %d.%d.%d.%d"),
+                               source->name,
+                               packet[30],
+                               packet[31],
+                               packet[32],
+                               packet[33]);
+
+               route_ipv4_unreachable(source, packet, len, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
+               return;
+       }
+       
+       if(subnet->owner == source) {
+               logger(LOG_WARNING, _("Packet looping back to %s!"), source->name);
+               return;
+       }
+
+       if(!subnet->owner->status.reachable)
+               route_ipv4_unreachable(source, packet, len, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
+
+       via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
+       
+       if(len > via->tnl->mtu && via != myself) {
+               logger(LOG_INFO, _("Packet for %s length %d larger than MTU %d"), subnet->owner->name, len, via->tnl->mtu);
+               if(packet[20] & 0x40) {
+                       len = via->tnl->mtu;
+                       route_ipv4_unreachable(source, packet, len, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
+               } else {
+                       fragment_ipv4_packet(via, packet, len);
+               }
+
+               return;
+       }
+
+       send_packet(subnet->owner, packet, len);
+}
+
+static __inline__ void route_ipv4(node_t *source, const uint8_t *packet, int len) {
+       if(!checklength(source, len, ether_size + ip_size))
+               return;
+
+       route_ipv4_unicast(source, packet, len);
+}
+
+/* RFC 2463 */
+
+static void route_ipv6_unreachable(node_t *source, const uint8_t *packet, int len, uint8_t type, uint8_t code) {
+       uint8_t reply[ether_size + IP_MSS];
+       struct ip6_hdr ip6;
+       struct icmp6_hdr icmp6 = {0};
+       uint16_t checksum;      
+
+       struct {
+               struct in6_addr ip6_src;        /* source address */
+               struct in6_addr ip6_dst;        /* destination address */
+               uint32_t length;
+               uint32_t next;
+       } pseudo;
+
+       if(ratelimit(3))
+               return;
+       
+       /* Copy headers from packet to structs on the stack */
+
+       memcpy(&ip6, packet + ether_size, ip6_size);
+
+       /* Remember original source and destination */
+       
+       pseudo.ip6_src = ip6.ip6_dst;
+       pseudo.ip6_dst = ip6.ip6_src;
+
+       pseudo.length = len - ether_size;
+
+       if(type == ICMP6_PACKET_TOO_BIG)
+               icmp6.icmp6_mtu = htonl(pseudo.length);
+       
+       if(pseudo.length >= IP_MSS - ip6_size - icmp6_size)
+               pseudo.length = IP_MSS - ip6_size - icmp6_size;
+       
+       /* Copy first part of original contents to ICMP message */
+       
+       memcpy(reply + ether_size + ip6_size + icmp6_size, packet + ether_size, pseudo.length);
+
+       /* Fill in IPv6 header */
+       
+       ip6.ip6_flow = htonl(0x60000000UL);
+       ip6.ip6_plen = htons(icmp6_size + pseudo.length);
+       ip6.ip6_nxt = IPPROTO_ICMPV6;
+       ip6.ip6_hlim = 255;
+       ip6.ip6_src = pseudo.ip6_src;
+       ip6.ip6_dst = pseudo.ip6_dst;
+
+       /* Fill in ICMP header */
+       
+       icmp6.icmp6_type = type;
+       icmp6.icmp6_code = code;
+       icmp6.icmp6_cksum = 0;
+
+       /* Create pseudo header */
+               
+       pseudo.length = htonl(icmp6_size + pseudo.length);
+       pseudo.next = htonl(IPPROTO_ICMPV6);
+
+       /* Generate checksum */
+       
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&icmp6, icmp6_size, checksum);
+       checksum = inet_checksum(reply + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
+
+       icmp6.icmp6_cksum = checksum;
+
+       /* Copy structs on stack back to packet */
+
+       memcpy(reply + ether_size, &ip6, ip6_size);
+       memcpy(reply + ether_size + ip6_size, &icmp6, icmp6_size);
+       
+       send_packet(source, reply, ether_size + ip6_size + ntohl(pseudo.length));
+}
+
+static __inline__ void route_ipv6_unicast(node_t *source, const uint8_t *packet, int len) {
+       subnet_t *subnet;
+       node_t *via;
+
+       subnet = subnet_get_ipv6((ipv6_t *)(packet + 38));
+
+       if(!subnet) {
+               logger(LOG_WARNING, _("Cannot route packet from %s: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+                               source->name,
+                               ntohs(*(uint16_t *)(packet + 38)),
+                               ntohs(*(uint16_t *)(packet + 40)),
+                               ntohs(*(uint16_t *)(packet + 42)),
+                               ntohs(*(uint16_t *)(packet + 44)),
+                               ntohs(*(uint16_t *)(packet + 46)),
+                               ntohs(*(uint16_t *)(packet + 48)),
+                               ntohs(*(uint16_t *)(packet + 50)),
+                               ntohs(*(uint16_t *)(packet + 52)));
+
+               route_ipv6_unreachable(source, packet, len, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
+               return;
+       }
+
+       if(subnet->owner == source) {
+               logger(LOG_WARNING, _("Packet looping back to %s!"), source->name);
+               return;
+       }
+
+       if(!subnet->owner->status.reachable)
+               route_ipv6_unreachable(source, packet, len, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
+
+       via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
+       
+       if(len > via->tnl->mtu && via != myself) {
+               logger(LOG_INFO, _("Packet for %s length %d larger than MTU %d"), subnet->owner->name, len, via->tnl->mtu);
+               len = via->tnl->mtu;
+               route_ipv6_unreachable(source, packet, len, ICMP6_PACKET_TOO_BIG, 0);
+               return;
+       }
+
+       send_packet(subnet->owner, packet, len);
+}
+
+/* RFC 2461 */
+
+static void route_neighborsol(node_t *source, const uint8_t *packet, int len) {
+       uint8_t reply[len];
+       struct ip6_hdr ip6;
+       struct nd_neighbor_solicit ns;
+       struct nd_opt_hdr opt;
+       subnet_t *subnet;
+       uint16_t checksum;
+
+       struct {
+               struct in6_addr ip6_src;        /* source address */
+               struct in6_addr ip6_dst;        /* destination address */
+               uint32_t length;
+               uint32_t next;
+       } pseudo;
+
+       if(!checklength(source, len, ether_size + ip6_size + ns_size + opt_size + ETH_ALEN))
+               return;
+       
+       if(source != myself) {
+               logger(LOG_WARNING, _("Got neighbor solicitation request from %s while in router mode!"), source->name);
+               return;
+       }
+
+       /* Copy headers from packet to structs on the stack */
+
+       memcpy(&ip6, packet + ether_size, ip6_size);
+       memcpy(&ns, packet + ether_size + ip6_size, ns_size);
+       memcpy(&opt, packet + ether_size + ip6_size + ns_size, opt_size);
+
+       /* First, snatch the source address from the neighbor solicitation packet */
+
+       if(rt_overwrite_mac)
+               memcpy(mymac.x, packet + ETH_ALEN, ETH_ALEN);
+
+       /* Check if this is a valid neighbor solicitation request */
+
+       if(ns.nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
+          opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR) {
+               logger(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
+               return;
+       }
+
+       /* Create pseudo header */
+
+       pseudo.ip6_src = ip6.ip6_src;
+       pseudo.ip6_dst = ip6.ip6_dst;
+       pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
+       pseudo.next = htonl(IPPROTO_ICMPV6);
+
+       /* Generate checksum */
+
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&ns, ns_size, checksum);
+       checksum = inet_checksum(&opt, opt_size, checksum);
+       checksum = inet_checksum(packet + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
+
+       if(checksum) {
+               logger(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
+               return;
+       }
+
+       /* Check if the IPv6 address exists on the VPN */
+
+       subnet = subnet_get_ipv6((ipv6_t *) &ns.nd_ns_target);
+
+       if(!subnet) {
+               logger(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[0]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[1]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[2]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[3]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[4]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[5]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[6]),
+                                  ntohs(((uint16_t *) &ns.nd_ns_target)[7]));
+
+               return;
+       }
+
+       /* Check if it is for our own subnet */
+
+       if(subnet->owner == myself)
+               return;                                 /* silently ignore */
+
+       /* Create neighbor advertation reply */
+
+       memcpy(reply, packet + ETH_ALEN, ETH_ALEN);     /* copy destination address */
+       memcpy(reply + ETH_ALEN, packet + ETH_ALEN, ETH_ALEN);  /* copy destination address */
+       reply[ETH_ALEN * 2 - 1] ^= 0xFF;        /* mangle source address so it looks like it's not from us */
+
+       ip6.ip6_dst = ip6.ip6_src;                      /* swap destination and source protocoll address */
+       ip6.ip6_src = ns.nd_ns_target;
+
+       memcpy(reply + ether_size + ip6_size + ns_size + opt_size, reply + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
+
+       ns.nd_ns_cksum = 0;
+       ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
+       ns.nd_ns_reserved = htonl(0x40000000UL);        /* Set solicited flag */
+       opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
+
+       /* Create pseudo header */
+
+       pseudo.ip6_src = ip6.ip6_src;
+       pseudo.ip6_dst = ip6.ip6_dst;
+       pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
+       pseudo.next = htonl(IPPROTO_ICMPV6);
+
+       /* Generate checksum */
+
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&ns, ns_size, checksum);
+       checksum = inet_checksum(&opt, opt_size, checksum);
+       checksum = inet_checksum(packet + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
+
+       ns.nd_ns_hdr.icmp6_cksum = checksum;
+
+       /* Copy structs on stack back to packet */
+
+       memcpy(reply + ether_size, &ip6, ip6_size);
+       memcpy(reply + ether_size + ip6_size, &ns, ns_size);
+       memcpy(reply + ether_size + ip6_size + ns_size, &opt, opt_size);
+
+       send_packet(source, reply, len);
+}
+
+static __inline__ void route_ipv6(node_t *source, const uint8_t *packet, int len) {
+       if(!checklength(source, len, ether_size + ip6_size))
+               return;
+
+       if(packet[20] == IPPROTO_ICMPV6 && checklength(source, len, ether_size + ip6_size + icmp6_size) && packet[54] == ND_NEIGHBOR_SOLICIT) {
+               route_neighborsol(source, packet, len);
+               return;
+       }
+
+       route_ipv6_unicast(source, packet, len);
+}
+
+/* RFC 826 */
+
+static void route_arp(node_t *source, const uint8_t *packet, int len) {
+       uint8_t reply[len];
+       struct ether_arp arp;
+       subnet_t *subnet;
+       struct in_addr addr;
+
+       if(!checklength(source, len, ether_size + arp_size))
+               return;
+
+       if(source != myself) {
+               logger(LOG_WARNING, _("Got ARP request from %s while in router mode!"), source->name);
+               return;
+       }
+
+       /* First, snatch the source address from the ARP packet */
+
+       if(rt_overwrite_mac)
+               memcpy(mymac.x, packet + ETH_ALEN, ETH_ALEN);
+
+       /* Copy headers from packet to structs on the stack */
+
+       memcpy(&arp, packet + ether_size, arp_size);
+
+       /* Check if this is a valid ARP request */
+
+       if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
+          arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) {
+               logger(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
+               return;
+       }
+
+       /* Check if the IPv4 address exists on the VPN */
+
+       subnet = subnet_get_ipv4((ipv4_t *) &arp.arp_tpa);
+
+       if(!subnet) {
+               logger(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
+                                  arp.arp_tpa[0], arp.arp_tpa[1], arp.arp_tpa[2],
+                                  arp.arp_tpa[3]);
+               return;
+       }
+
+       /* Check if it is for our own subnet */
+
+       if(subnet->owner == myself)
+               return;                                 /* silently ignore */
+
+       memcpy(reply, packet + ETH_ALEN, ETH_ALEN);     /* copy destination address */
+       memcpy(reply + ETH_ALEN, packet + ETH_ALEN, ETH_ALEN);  /* copy destination address */
+       reply[ETH_ALEN * 2 - 1] ^= 0xFF;        /* mangle source address so it looks like it's not from us */
+
+       memcpy(&addr, arp.arp_tpa, sizeof(addr));       /* save protocol addr */
+       memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */
+       memcpy(arp.arp_spa, &addr, sizeof(addr));       /* ... */
+
+       memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN);     /* set target hard/proto addr */
+       memcpy(arp.arp_sha, reply + ETH_ALEN, ETH_ALEN);        /* add fake source hard addr */
+       arp.arp_op = htons(ARPOP_REPLY);
+
+       /* Copy structs on stack back to packet */
+
+       memcpy(reply + ether_size, &arp, arp_size);
+
+       send_packet(source, reply, len);
+}
+
+void route(node_t *source, const uint8_t *packet, int len) {
+       if(!checklength(source, len, ether_size))
+               return;
+
+       switch (rt_mode) {
+               case RT_MODE_ROUTER:
+                       {
+                               uint16_t type;
+
+                               type = ntohs(*((uint16_t *)(packet + 12)));
+                               switch (type) {
+                                       case ETH_P_ARP:
+                                               route_arp(source, packet, len);
+                                               break;
+
+                                       case ETH_P_IP:
+                                               route_ipv4(source, packet, len);
+                                               break;
+
+                                       case ETH_P_IPV6:
+                                               route_ipv6(source, packet, len);
+                                               break;
+
+                                       default:
+                                               logger(LOG_WARNING, _("Cannot route packet from %s: unknown type %hx"), source->name, type);
+                                               break;
+                               }
+                       }
+                       break;
+
+               case RT_MODE_SWITCH:
+                       route_mac(source, packet, len);
+                       break;
+
+               case RT_MODE_HUB:
+                       broadcast_packet(source, packet, len);
+                       break;
+       }
+}
diff --git a/rt/route.h b/rt/route.h
new file mode 100644 (file)
index 0000000..42c587f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+    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: rt.h 1375 2004-03-22 12:30:39Z guus $
+*/
+
+#ifndef __ROUTE_H__
+#define __ROUTE_H__
+
+#include "rt/node.h"
+
+extern void route(node_t *, const uint8_t *, int);
+
+#endif
diff --git a/rt/rt.c b/rt/rt.c
index 105249a..25f22cf 100644 (file)
--- a/rt/rt.c
+++ b/rt/rt.c
@@ -40,26 +40,32 @@ int rt_maxtimeout = 900;
 rt_mode_t rt_mode = RT_MODE_ROUTER;
 bool rt_priorityinheritance = false;
 bool rt_hostnames = false;
+bool rt_overwrite_mac = false;
+
+avl_tree_t *rt_tnls;
+avl_tree_t *rt_listeners;
 
 static bool rt_tnl_accept(tnl_t *t) {
+       
 }
 
-static bool rt_vnd_recv(vnd_t *vnd, const char *buf, int len) {
+static bool rt_vnd_recv(vnd_t *vnd, const void *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_packet(tnl_t *tnl, const void *buf, int len) {
+       edge_t *edge = tnl->data;
+       route(edge->to, buf, len);
 }
 
-static bool rt_tnl_recv_meta(tnl_t *tnl, const char *buf, int len) {
-       //route(tnl->data, buf, len);
+static bool rt_tnl_recv_meta(tnl_t *tnl, const void *buf, int len) {
 }
 
-static void rt_outgoing(char *wft) {
-}
+static void rt_outgoing(char *name) {
+       tnl_t *tnl;
 
-static void route(node_t *node, char *buf, int len) {
+       clear(new(tnl));
+       
 }
 
 bool rt_init(void) {
@@ -92,6 +98,9 @@ bool rt_init(void) {
        if(!subnet_init() || !node_init() || !edge_init())
                return false;
 
+       rt_tnls = avl_tree_new(NULL, NULL);
+       rt_listeners = avl_tree_new(NULL, NULL);
+
        /* Read main configuration */
 
        if(!cfg_get_choice(tinc_cfg, "AddressFamily", af_choice, AF_UNSPEC, &rt_af)
@@ -157,8 +166,9 @@ bool rt_init(void) {
                
                clear(new(listener));
                listener->local.address = *(struct sockaddr_storage *)aip->ai_addr;
-               listener->local.id = myself;
+               listener->local.id = myself->name;
                // listener->local.cred = ...;
+               listener->accept = rt_tnl_accept;
 
                if(tnl_listen(listener))
                        listeners++;
diff --git a/rt/rt.h b/rt/rt.h
index 8f988fc..dd8126d 100644 (file)
--- a/rt/rt.h
+++ b/rt/rt.h
 #ifndef __RT_H__
 #define __RT_H__
 
+#include "rt/node.h"
 #include "tnl/tnl.h"
 #include "vnd/vnd.h"
 
+#define RT_PROTOCOL 0
+
 typedef enum rt_mode {
        RT_MODE_ROUTER,
        RT_MODE_SWITCH,
@@ -39,6 +42,11 @@ extern bool rt_hostnames;
 extern bool rt_priorityinheritance;
 extern int rt_macexpire;
 extern int rt_maxtimeout;
+extern bool rt_overwrite_mac;
+
+extern node_t *myself;
+extern vnd_t *rt_vnd;
+extern avl_tree_t *rt_tnls;
 
 extern bool rt_init(void);
 extern bool rt_exit(void);
index 0cadfed..8f38b94 100644 (file)
@@ -1,6 +1,6 @@
 noinst_LIBRARIES = libsupport.a
 
-noinst_HEADERS = avl.h ipv4.h ipv6.h sockaddr.h xalloc.h
+noinst_HEADERS = avl.h ethernet.h ipv4.h ipv6.h list.h sockaddr.h xalloc.h
 
-libsupport_a_SOURCES = avl.c xalloc.c
+libsupport_a_SOURCES = avl.c list.c xalloc.c
 
index 1550a62..96500cb 100644 (file)
@@ -611,39 +611,11 @@ bool avl_del(avl_tree_t *tree, void *data) {
 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
index 5831bcc..ece7d1a 100644 (file)
@@ -134,11 +134,6 @@ extern struct avl_node *avl_get_closest_greater_node(const struct avl_tree *, co
        } \
 }
 
-#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
diff --git a/support/ethernet.h b/support/ethernet.h
new file mode 100644 (file)
index 0000000..e7ad508
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+    ethernet.h -- missing Ethernet related definitions
+    Copyright (C) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    $Id$
+*/
+
+#ifndef __ETHERNET_H__
+#define __ETHERNET_H__
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#ifndef ARPHRD_ETHER
+#define ARPHRD_ETHER 1
+#endif
+
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
+
+#ifndef ETH_P_ARP
+#define ETH_P_ARP 0x0806
+#endif
+
+#ifndef ETH_P_IPV6
+#define ETH_P_IPV6 0x86DD
+#endif
+
+#ifndef HAVE_STRUCT_ETHER_HEADER
+struct ether_header {
+       uint8_t ether_dhost[ETH_ALEN];
+       uint8_t ether_shost[ETH_ALEN];
+       uint16_t ether_type;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef HAVE_STRUCT_ARPHDR
+struct arphdr {
+       uint16_t ar_hrd;
+       uint16_t ar_pro;
+       uint8_t ar_hln;
+       uint8_t ar_pln; 
+       uint16_t ar_op; 
+} __attribute__ ((__packed__));
+
+#define ARPOP_REQUEST 1 
+#define ARPOP_REPLY 2 
+#define ARPOP_RREQUEST 3 
+#define ARPOP_RREPLY 4 
+#define ARPOP_InREQUEST 8 
+#define ARPOP_InREPLY 9 
+#define ARPOP_NAK 10 
+#endif
+
+#ifndef HAVE_STRUCT_ETHER_ARP
+struct  ether_arp {
+       struct  arphdr ea_hdr;
+       uint8_t arp_sha[ETH_ALEN];
+       uint8_t arp_spa[4];
+       uint8_t arp_tha[ETH_ALEN];
+       uint8_t arp_tpa[4];
+} __attribute__ ((__packed__));
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op ea_hdr.ar_op
+#endif
+
+#endif
diff --git a/support/gettext.h b/support/gettext.h
new file mode 100644 (file)
index 0000000..a907403
--- /dev/null
@@ -0,0 +1,79 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library 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.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+# include <libintl.h>
+# include <locale.h>
+
+/* Shorthand notation */
+
+# define _(Text) gettext (Text)
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+   chokes if dcgettext is defined as a macro.  So include it now, to make
+   later inclusions of <locale.h> a NOP.  We don't include <libintl.h>
+   as well because people using "gettext.h" will not include <libintl.h>,
+   and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+   is OK.  */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+   The casts to 'const char *' serve the purpose of producing warnings
+   for invalid uses of the value returned from these functions.
+   On pre-ANSI systems without 'const', the config.h file is supposed to
+   contain "#define const".  */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+# define _(Text) Text
+# define setlocale(Category, Locale) ((const char *) (Locale))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+   extraction of messages, but does not call gettext().  The run-time
+   translation is done at a different place in the code.
+   The argument, String, should be a literal string.  Concatenated strings
+   and other string expressions won't work.
+   The macro's expansion is not parenthesized, so that it is suitable as
+   initializer for static 'char[]' or 'const char[]' variables.  */
+#define gettext_noop(String) String
+
+#define N_(Text) Text
+
+#endif /* _LIBGETTEXT_H */
diff --git a/support/list.c b/support/list.c
new file mode 100644 (file)
index 0000000..9e54ac0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    list.c -- linked lists
+    Copyright (C) 2000-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: list.c 1374 2004-03-21 14:21:22Z guus $
+*/
+
+#include "system.h"
+
+#include "support/list.h"
+#include "support/xalloc.h"
+
+list_t *list_new(list_action_t free) {
+       list_t *list;
+
+       clear(new(list));
+       list->free = free;
+
+       return list;
+}
+
+void list_free(list_t *list) {
+       free(list);
+}
+
+list_node_t *list_node_new(void) {
+       list_node_t *node;
+
+       return clear(new(node));
+}
+
+void list_node_free(list_t *list, list_node_t *node) {
+       if(node->data && list->free)
+               list->free(node->data);
+
+       free(node);
+}
+
+list_node_t *list_add_head(list_t *list, void *data) {
+       list_node_t *node;
+
+       node = list_node_new();
+
+       node->data = data;
+       node->prev = NULL;
+       node->next = list->head;
+       list->head = node;
+
+       if(node->next)
+               node->next->prev = node;
+       else
+               list->tail = node;
+
+       list->count++;
+
+       return node;
+}
+
+list_node_t *list_add_tail(list_t *list, void *data) {
+       list_node_t *node;
+
+       node = list_node_new();
+
+       node->data = data;
+       node->next = NULL;
+       node->prev = list->tail;
+       list->tail = node;
+
+       if(node->prev)
+               node->prev->next = node;
+       else
+               list->head = node;
+
+       list->count++;
+
+       return node;
+}
+
+void list_unlink_node(list_t *list, list_node_t *node) {
+       if(node->prev)
+               node->prev->next = node->next;
+       else
+               list->head = node->next;
+
+       if(node->next)
+               node->next->prev = node->prev;
+       else
+               list->tail = node->prev;
+
+       list->count--;
+}
+
+void list_del_node(list_t *list, list_node_t *node) {
+       list_unlink_node(list, node);
+       list_node_free(list, node);
+}
+
+void list_del_head(list_t *list) {
+       list_del_node(list, list->head);
+}
+
+void list_del_tail(list_t *list) {
+       list_del_node(list, list->tail);
+}
+
+void *list_get_head(const list_t *list) {
+       if(list->head)
+               return list->head->data;
+       else
+               return NULL;
+}
+
+void *list_get_tail(const list_t *list) {
+       if(list->tail)
+               return list->tail->data;
+       else
+               return NULL;
+}
+
+void list_del(list_t *list) {
+       list_node_t *node;
+
+       list_foreach_node(list, node, list_node_free(list, node));
+       list_free(list);
+}
diff --git a/support/list.h b/support/list.h
new file mode 100644 (file)
index 0000000..124d7fc
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+    list.h -- linked lists
+
+    Copyright (C) 2000-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: list.h 1374 2004-03-21 14:21:22Z guus $
+*/
+
+#ifndef __LIST_H__
+#define __LIST_H__
+
+typedef struct list_node {
+       struct list_node *prev;
+       struct list_node *next;
+
+       void *data;
+} list_node_t;
+
+typedef void (*list_action_t)(const void *);
+typedef void (*list_node_action_t)(const list_node_t *);
+
+typedef struct list {
+       struct list_node *head;
+       struct list_node *tail;
+       int count;
+
+       list_action_t free;
+} list_t;
+
+/* (De)constructors */
+
+extern struct list *list_new(list_action_t) __attribute__ ((__malloc__));
+extern void list_free(struct list *);
+extern struct list_node *list_node_new(void);
+extern void list_node_free(struct list *, struct list_node *);
+
+/* Insertion and deletion */
+
+extern struct list_node *list_add_head(struct list *, void *);
+extern struct list_node *list_add_tail(struct list *, void *);
+
+extern void list_unlink_node(struct list *, struct list_node *);
+extern void list_node_del(struct list *, struct list_node *);
+
+extern void list_del_head(struct list *);
+extern void list_del_tail(struct list *);
+
+/* Head/tail lookup */
+
+extern void *list_get_head(const struct list *);
+extern void *list_get_tail(const struct list *);
+
+/* Fast list deletion */
+
+extern void list_del(struct list *);
+
+/* Traversing */
+
+#define list_foreach(list, object, action) {list_node_t *_node, *_next; \
+        for(_node = (list)->head; _node; _node = _next) { \
+                _next = _node->next; \
+                (object) = _node->data; \
+                action; \
+        } \
+}
+
+#define list_foreach_node(list, node, action) {list_node_t *_next; \
+        for((node) = (list)->head; (node); (node) = _next) { \
+                _next = (node)->next; \
+                action; \
+        } \
+}
+
+#endif
index 104eaaa..1e2f4aa 100644 (file)
--- a/system.h
+++ b/system.h
@@ -138,9 +138,9 @@ typedef int bool;
 
 /* Include localisation support */
 
-#if 0
+#include "support/gettext.h"
 
-#include "gettext.h"
+#if 0
 
 #ifndef HAVE_STRSIGNAL
 # define strsignal(p) ""
@@ -152,8 +152,6 @@ typedef int bool;
 
 #endif
 
-#define _(a) a
-
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
diff --git a/tincd.c b/tincd.c
index 7b8d02a..2e40152 100644 (file)
--- a/tincd.c
+++ b/tincd.c
 
 #include <getopt.h>
 
+/* Darwin (MacOS/X) needs the following definition... */
+#ifndef _P1003_1B_VISIBLE
+#define _P1003_1B_VISIBLE
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include "tincd.h"
 #include "cfg/cfg.h"
 #include "fd/event.h"
 #include "fd/fd.h"
 #include "logger/logger.h"
+#include "rt/rt.h"
 #include "support/avl.h"
 #include "support/sockaddr.h"
 #include "support/xalloc.h"
@@ -39,19 +50,21 @@ static bool show_version = false;
 static int kill_tincd = 0;
 static bool bypass_security = false;
 static bool do_mlock = false;
-static bool use_logfile = false;
 static bool do_detach = true;
 static int debug_level = 1;
 
-static char *confbase = NULL;  
-static char *identname = NULL; 
-static char *pidfilename = NULL;
-static char *logfilename = NULL;
-static char *cfgfilename = NULL;
+char *tinc_confbase = NULL;    
+char *tinc_netname = NULL;     
+char *tinc_identname = NULL;   
+char *tinc_pidfilename = NULL;
+char *tinc_logfilename = NULL;
+char *tinc_cfgfilename = NULL;
+
+bool tinc_use_logfile = false;
 
 int tinc_argc;
 char **tinc_argv;
-cfg_tree_t tinc_cfg;
+avl_tree_t *tinc_cfg;
 
 static struct option const long_options[] = {
        {"config", required_argument, NULL, 'c'},
@@ -101,7 +114,7 @@ static bool parse_options(int argc, char **argv) {
                                break;
 
                        case 'c': /* --config */
-                               confbase = xstrdup(optarg);
+                               tinc_confbase = xstrdup(optarg);
                                break;
 
                        case 'D': /* --no-detach */
@@ -156,7 +169,7 @@ static bool parse_options(int argc, char **argv) {
                                break;
 
                        case 'n': /* --net */
-                               netname = xstrdup(optarg);
+                               tinc_netname = xstrdup(optarg);
                                break;
 
                        case 1: /* --help */
@@ -172,13 +185,13 @@ static bool parse_options(int argc, char **argv) {
                                break;
 
                        case 4: /* --logfile */
-                               use_logfile = true;
+                               tinc_use_logfile = true;
                                if(optarg)
-                                       logfilename = xstrdup(optarg);
+                                       tinc_logfilename = xstrdup(optarg);
                                break;
 
                        case 5: /* --pidfile */
-                               pidfilename = xstrdup(optarg);
+                               tinc_pidfilename = xstrdup(optarg);
                                break;
 
                        case '?':
@@ -201,21 +214,21 @@ static void make_names(void)
        long len = sizeof(installdir);
 #endif
 
-       if(netname)
-               asprintf(&identname, "tinc.%s", netname);
+       if(tinc_netname)
+               asprintf(&tinc_identname, "tinc.%s", tinc_netname);
        else
-               identname = xstrdup("tinc");
+               tinc_identname = xstrdup("tinc");
 
 #ifdef HAVE_MINGW
        if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
                if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
-                       if(!logfilename)
-                               asprintf(&logfilename, "%s/log/%s.log", identname);
-                       if(!confbase) {
-                               if(netname)
-                                       asprintf(&confbase, "%s/%s", installdir, netname);
+                       if(!tinc_logfilename)
+                               asprintf(&tinc_logfilename, "%s/log/%s.log", tinc_identname);
+                       if(!tinc_confbase) {
+                               if(tinc_netname)
+                                       asprintf(&tinc_confbase, "%s/%s", installdir, tinc_netname);
                                else
-                                       asprintf(&confbase, "%s", installdir);
+                                       asprintf(&tinc_confbase, "%s", installdir);
                        }
                }
                RegCloseKey(key);
@@ -224,20 +237,20 @@ static void make_names(void)
        }
 #endif
 
-       if(!pidfilename)
-               asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
+       if(!tinc_pidfilename)
+               asprintf(&tinc_pidfilename, LOCALSTATEDIR "/run/%s.pid", tinc_identname);
 
-       if(!logfilename)
-               asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
+       if(!tinc_logfilename)
+               asprintf(&tinc_logfilename, LOCALSTATEDIR "/log/%s.log", tinc_identname);
 
-       if(!confbase) {
-               if(netname)
-                       asprintf(&confbase, CONFDIR "/tinc/%s", netname);
+       if(!tinc_confbase) {
+               if(tinc_netname)
+                       asprintf(&tinc_confbase, CONFDIR "/tinc/%s", tinc_netname);
                else
-                       asprintf(&confbase, CONFDIR "/tinc");
+                       asprintf(&tinc_confbase, CONFDIR "/tinc");
        }
 
-       asprintf(&cfgfilename, "%s/tinc.conf", confbase);
+       asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase);
 }
 
 int main(int argc, char **argv) {
@@ -254,8 +267,8 @@ int main(int argc, char **argv) {
        make_names();
 
        if(show_version) {
-               printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE,
-                          VERSION, __DATE__, __TIME__, PROT_CURRENT);
+               printf(_("%s version %s (built %s %s, protocol %d/%d)\n"), PACKAGE,
+                          VERSION, __DATE__, __TIME__, TNL_PROTOCOL, RT_PROTOCOL);
                printf(_("Copyright (C) 1998-2004 Ivo Timmermans, Guus Sliepen and others.\n"
                                "See the AUTHORS file for a complete list.\n\n"
                                "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
@@ -273,7 +286,7 @@ int main(int argc, char **argv) {
        if(kill_tincd)
                return !kill_other(kill_tincd);
 
-       openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
+       logger_init("tinc", tinc_use_logfile ? LOGGER_MODE_FILE : LOGGER_MODE_STDERR);
 
        /* Lock all pages into memory if requested */
 
@@ -291,9 +304,9 @@ int main(int argc, char **argv) {
 
        tinc_cfg = cfg_tree_new();
 
-       asprintf(cfgfilename, "%s/tinc.conf", confbase);
+       asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase);
        
-       if(!cfg_read_file(tinc_cfg, cfgfilename))
+       if(!cfg_read_file(tinc_cfg, tinc_cfgfilename))
                return 1;
 
 #ifdef HAVE_MINGW
@@ -306,19 +319,21 @@ int main(int argc, char **argv) {
        if(do_detach && !detach())
                return 1;
 
-       if(!fd_init() || !tnl_init() || !rt_init())
+       logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
+                       VERSION, __DATE__, __TIME__, logger_level);
+
+       if(!fd_init() || !rt_init())
                return 1;
 
        fd_run();
 
        rt_exit();
-       tnl_exit();
        fd_exit();
 end:
        logger(LOG_NOTICE, _("Terminating"));
 
 #ifndef HAVE_MINGW
-       remove_pid(pidfilename);
+       remove_pid(tinc_pidfilename);
 #endif
 
        logger_exit();
index 113248f..91706b2 100644 (file)
--- a/tnl/tnl.c
+++ b/tnl/tnl.c
 #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) {
+static bool tnl_send(tnl_t *tnl, const void *buf, int len) {
        int result;
 
        while(len) {
@@ -224,22 +203,22 @@ static bool tnl_handshake_handler(fd_t *fd) {
        return true;
 }
 
-static bool tnl_send_meta(tnl_t *tnl, const char *buf, int len) {
+static bool tnl_send_meta(tnl_t *tnl, const void *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);
+       return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
 }
 
-static bool tnl_send_packet(tnl_t *tnl, const char *buf, int len) {
+static bool tnl_send_packet(tnl_t *tnl, const void *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);
+       return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
 }
 
 static bool tnl_close(tnl_t *tnl) {
@@ -251,8 +230,6 @@ static bool tnl_close(tnl_t *tnl) {
        fd_del(&tnl->fd);
        close(tnl->fd.fd);
        
-       tnl_del(tnl);
-
        return true;
 }
 
@@ -297,8 +274,6 @@ static bool tnl_accept_handler(fd_t *fd) {
 
        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);
@@ -382,8 +357,6 @@ bool tnl_connect(tnl_t *tnl) {
        tnl->send_meta = tnl_send_meta;
        tnl->close = tnl_close;
        
-       tnl_add(tnl);
-
        fd_add(&tnl->fd);
 
        return true;
@@ -392,7 +365,6 @@ bool tnl_connect(tnl_t *tnl) {
 static bool tnl_listen_close(tnl_listen_t *listener) {
        fd_del(&listener->fd);
        close(listener->fd.fd);
-       tnl_listen_del(listener);
        return true;
 }
 
@@ -421,7 +393,6 @@ bool tnl_listen(tnl_listen_t *listener) {
        listener->fd.data = listener;
        listener->close = tnl_listen_close;
 
-       tnl_listen_add(listener);
        fd_add(&listener->fd);
 
        return true;
index 14356be..6b18408 100644 (file)
--- a/tnl/tnl.h
+++ b/tnl/tnl.h
@@ -27,6 +27,8 @@
 
 #include "fd/fd.h"
 
+#define TNL_PROTOCOL 0
+
 #define TNL_RECORD_PACKET 0
 #define TNL_RECORD_META 1
 #define TNL_RECORD_HELLO 2
@@ -62,12 +64,12 @@ typedef struct tnl {
        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 (*send_packet)(struct tnl *tnl, const void *buf, int len);
+       bool (*send_meta)(struct tnl *tnl, const void *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 (*recv_packet)(struct tnl *tnl, const void *buf, int len);
+       bool (*recv_meta)(struct tnl *tnl, const void *buf, int len);
        bool (*accept)(struct tnl *tnl);
        bool (*error)(struct tnl *tnl, int errnum);
 
@@ -90,8 +92,6 @@ typedef struct tnl_listen {
        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);
 
index 802cdd6..7eba993 100644 (file)
--- a/vnd/vnd.c
+++ b/vnd/vnd.c
@@ -51,7 +51,7 @@ void vnd_set(vnd_t *vnd, char *device, char *interface, vnd_mode_t mode, vnd_han
        vnd->recv = recv;
 }
 
-static bool vnd_send(vnd_t *vnd, char *buf, int len) {
+static bool vnd_send(vnd_t *vnd, const void *buf, int len) {
        int result;
 
        result = write(vnd->fd.fd, buf, len);
@@ -73,7 +73,7 @@ static bool vnd_recv_handler(fd_t *fd) {
 
        vnd = fd->data;
 
-       len = read(fd->fd, buf, sizeof(buf));
+       len = read(fd->fd, buf, sizeof buf);
 
        if(len > 0) {
                logger(LOG_INFO, _("vnd: read packet of %d bytes from %s"), len, vnd->description);
index 4670873..47f8c6a 100644 (file)
--- a/vnd/vnd.h
+++ b/vnd/vnd.h
@@ -31,7 +31,7 @@ typedef enum vnd_mode{
 
 struct vnd;
 
-typedef bool (*vnd_handler_t)(struct vnd *vnd, char *buf, int len);
+typedef bool (*vnd_handler_t)(struct vnd *vnd, const void *buf, int len);
 
 typedef struct vnd {
        char *device;