From 942ee816b88f9c35b456abab1864e5e2b811e5c8 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sat, 27 Mar 2004 11:59:31 +0000 Subject: [PATCH] Lots of changes. It compiles, but doesn't do much yet. --- Makefile.am | 8 +- logger/logger.h | 2 + process.c | 565 +++++++++++++++++++++++++++++++++ rt/Makefile.am | 4 +- rt/README | 31 ++ rt/edge.h | 2 + rt/graph.c | 232 ++++++++++++++ rt/node.h | 2 +- rt/route.c | 754 ++++++++++++++++++++++++++++++++++++++++++++ rt/route.h | 31 ++ rt/rt.c | 28 +- rt/rt.h | 8 + support/Makefile.am | 4 +- support/avl.c | 28 -- support/avl.h | 5 - support/ethernet.h | 87 +++++ support/gettext.h | 79 +++++ support/list.c | 140 ++++++++ support/list.h | 89 ++++++ system.h | 6 +- tincd.c | 91 +++--- tnl/tnl.c | 39 +-- tnl/tnl.h | 12 +- vnd/vnd.c | 4 +- vnd/vnd.h | 2 +- 25 files changed, 2118 insertions(+), 135 deletions(-) create mode 100644 process.c create mode 100644 rt/README create mode 100644 rt/graph.c create mode 100644 rt/route.c create mode 100644 rt/route.h create mode 100644 support/ethernet.h create mode 100644 support/gettext.h create mode 100644 support/list.c create mode 100644 support/list.h diff --git a/Makefile.am b/Makefile.am index e64caa03..c9da143d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/logger/logger.h b/logger/logger.h index afcf1e5c..a65968ec 100644 --- a/logger/logger.h +++ b/logger/logger.h @@ -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 index 00000000..f04f7c01 --- /dev/null +++ b/process.c @@ -0,0 +1,565 @@ +/* + process.c -- process management functions + Copyright (C) 1999-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#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 +} diff --git a/rt/Makefile.am b/rt/Makefile.am index 9c5c4326..528c26c7 100644 --- a/rt/Makefile.am +++ b/rt/Makefile.am @@ -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 index 00000000..d0b6dc87 --- /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. + diff --git a/rt/edge.h b/rt/edge.h index efaddb27..775d8339 100644 --- 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 index 00000000..63e8fafe --- /dev/null +++ b/rt/graph.c @@ -0,0 +1,232 @@ +/* + graph.c -- graph algorithms + Copyright (C) 2001-2004 Guus Sliepen , + 2001-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +/* 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(); +} diff --git a/rt/node.h b/rt/node.h index 0ef62e2a..acf20bc1 100644 --- 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 index 00000000..47217638 --- /dev/null +++ b/rt/route.c @@ -0,0 +1,754 @@ +/* + route.c -- routing + Copyright (C) 2000-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#ifdef HAVE_NET_ETHERNET_H +#include +#endif +#ifdef HAVE_NET_IF_ARP_H +#include +#endif +#ifdef HAVE_NETINET_IP_ICMP_H +#include +#endif +#ifdef HAVE_NETINET_ICMP6_H +#include +#endif +#ifdef HAVE_NETINET_IF_ETHER_H +#include +#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 index 00000000..42c587fb --- /dev/null +++ b/rt/route.h @@ -0,0 +1,31 @@ +/* + route.h -- routing + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: 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 105249a6..25f22cf7 100644 --- 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 8f988fc2..dd8126d1 100644 --- a/rt/rt.h +++ b/rt/rt.h @@ -24,9 +24,12 @@ #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); diff --git a/support/Makefile.am b/support/Makefile.am index 0cadfed5..8f38b94b 100644 --- a/support/Makefile.am +++ b/support/Makefile.am @@ -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 diff --git a/support/avl.c b/support/avl.c index 1550a622..96500cb5 100644 --- a/support/avl.c +++ b/support/avl.c @@ -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 diff --git a/support/avl.h b/support/avl.h index 5831bcc2..ece7d1ad 100644 --- a/support/avl.h +++ b/support/avl.h @@ -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 index 00000000..e7ad508d --- /dev/null +++ b/support/ethernet.h @@ -0,0 +1,87 @@ +/* + ethernet.h -- missing Ethernet related definitions + Copyright (C) 2004 Ivo Timmermans + 2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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 index 00000000..a9074031 --- /dev/null +++ b/support/gettext.h @@ -0,0 +1,79 @@ +/* Convenience header for conditional use of GNU . + 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 +# include + +/* 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 a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#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 index 00000000..9e54ac08 --- /dev/null +++ b/support/list.c @@ -0,0 +1,140 @@ +/* + list.c -- linked lists + Copyright (C) 2000-2004 Ivo Timmermans + 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: 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 index 00000000..124d7fc4 --- /dev/null +++ b/support/list.h @@ -0,0 +1,89 @@ +/* + list.h -- linked lists + + Copyright (C) 2000-2004 Ivo Timmermans + 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: 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 diff --git a/system.h b/system.h index 104eaaa4..1e2f4aa4 100644 --- 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 7b8d02a6..2e401520 100644 --- a/tincd.c +++ b/tincd.c @@ -24,10 +24,21 @@ #include +/* Darwin (MacOS/X) needs the following definition... */ +#ifndef _P1003_1B_VISIBLE +#define _P1003_1B_VISIBLE +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#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(); diff --git a/tnl/tnl.c b/tnl/tnl.c index 113248f6..91706b2c 100644 --- a/tnl/tnl.c +++ b/tnl/tnl.c @@ -29,28 +29,7 @@ #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; diff --git a/tnl/tnl.h b/tnl/tnl.h index 14356be9..6b184080 100644 --- 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); diff --git a/vnd/vnd.c b/vnd/vnd.c index 802cdd6c..7eba993a 100644 --- 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); diff --git a/vnd/vnd.h b/vnd/vnd.h index 46708739..47f8c6a3 100644 --- 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; -- 2.20.1