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
extern bool logger_exit(void);
extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
+extern enum logger_level logger_level;
+
#endif
--- /dev/null
+/*
+ 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
+}
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
--- /dev/null
+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.
+
#include "rt/node.h"
#include "support/avl.h"
+#include "tnl/tnl.h"
typedef struct edge_status {
int visited:1;
int weight;
struct edge *reverse;
+ struct tnl *tnl;
edge_status_t status;
node_options_t options;
--- /dev/null
+/*
+ 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();
+}
typedef int node_options_t;
-#define NODE_OPTIONS_INDIRECT 1
+#define NODE_OPTION_INDIRECT 1
#include "rt/edge.h"
#include "rt/subnet.h"
--- /dev/null
+/*
+ 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;
+ }
+}
--- /dev/null
+/*
+ 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
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) {
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)
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++;
#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,
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);
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
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
} \
}
-#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
--- /dev/null
+/*
+ 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
--- /dev/null
+/* 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 */
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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
/* Include localisation support */
-#if 0
+#include "support/gettext.h"
-#include "gettext.h"
+#if 0
#ifndef HAVE_STRSIGNAL
# define strsignal(p) ""
#endif
-#define _(a) a
-
#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
#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"
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'},
break;
case 'c': /* --config */
- confbase = xstrdup(optarg);
+ tinc_confbase = xstrdup(optarg);
break;
case 'D': /* --no-detach */
break;
case 'n': /* --net */
- netname = xstrdup(optarg);
+ tinc_netname = xstrdup(optarg);
break;
case 1: /* --help */
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 '?':
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);
}
#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) {
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"
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 */
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
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();
#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) {
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) {
fd_del(&tnl->fd);
close(tnl->fd.fd);
- tnl_del(tnl);
-
return true;
}
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);
tnl->send_meta = tnl_send_meta;
tnl->close = tnl_close;
- tnl_add(tnl);
-
fd_add(&tnl->fd);
return true;
static bool tnl_listen_close(tnl_listen_t *listener) {
fd_del(&listener->fd);
close(listener->fd.fd);
- tnl_listen_del(listener);
return true;
}
listener->fd.data = listener;
listener->close = tnl_listen_close;
- tnl_listen_add(listener);
fd_add(&listener->fd);
return true;
#include "fd/fd.h"
+#define TNL_PROTOCOL 0
+
#define TNL_RECORD_PACKET 0
#define TNL_RECORD_META 1
#define TNL_RECORD_HELLO 2
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);
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);
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);
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);
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;