From: Guus Sliepen Date: Fri, 15 Dec 2006 14:05:26 +0000 (+0000) Subject: Move all source code to src/. X-Git-Url: https://git.librecmc.org/?p=oweals%2Ftinc.git;a=commitdiff_plain;h=ff03bb9b0a744530e1145fef656644987a10d62d Move all source code to src/. --- diff --git a/cfg/Makefile.am b/cfg/Makefile.am deleted file mode 100644 index 49d353a..0000000 --- a/cfg/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = libcfg.a - -noinst_HEADERS = cfg.h - -libcfg_a_SOURCES = cfg.c - diff --git a/cfg/cfg.c b/cfg/cfg.c deleted file mode 100644 index f0c8dad..0000000 --- a/cfg/cfg.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - cfg.c -- cfguration code - - Copyright (C) 1998 Robert van der Meulen - 1998-2004 Ivo Timmermans - 2000-2004 Guus Sliepen - 2000 Cris van Pelt - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "cfg/cfg.h" -#include "support/avl.h" -#include "support/xalloc.h" - -static int cfg_compare(const cfg_t *a, const cfg_t *b) { - return strcasecmp(a->variable, b->variable) ?: (a->line - b->line) ?: strcmp(a->file, b->file); -} - -avl_tree_t *cfg_tree_new(void) { - return avl_tree_new((avl_compare_t)cfg_compare, (avl_action_t)cfg_free); -} - -void cfg_tree_del(avl_tree_t *cfgs) { - avl_tree_del(cfgs); -} - -cfg_t *cfg_new(void) { - cfg_t *cfg; - - return clear(new(cfg)); -} - -void cfg_free(cfg_t *cfg) { - replace(cfg->variable, NULL); - replace(cfg->value, NULL); - replace(cfg->file, NULL); - free(cfg); -} - -void cfg_add(avl_tree_t *cfgs, cfg_t *cfg) { - avl_add(cfgs, cfg); -} - -cfg_t *cfg_get(const avl_tree_t *cfgs, char *variable) { - cfg_t search, *cfg; - - search.variable = variable; - search.file = ""; - search.line = 0; - - cfg = avl_get_closest_greater(cfgs, &search); - - if(!cfg || strcasecmp(cfg->variable, variable)) - return NULL; - - return cfg; -} - -cfg_t *cfg_get_next(const avl_tree_t *cfgs, const cfg_t *cfg) { - avl_node_t *avl; - cfg_t *next; - - avl = avl_get_node(cfgs, cfg); - - if(avl && avl->next) { - next = avl->next->data; - - if(!strcasecmp(next->variable, cfg->variable)) - return next; - } - - return NULL; -} - -bool cfg_bool(const cfg_t *cfg, const bool def, bool *result) { - if(!cfg) { - *result = def; - return true; - } - - if(!strcasecmp(cfg->value, "yes")) { - *result = true; - return true; - } else if(!strcasecmp(cfg->value, "no")) { - *result = false; - return true; - } - - logger(LOG_ERR, _("cfg: \"yes\" or \"no\" expected for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - - return false; -} - -bool cfg_int(const cfg_t *cfg, const int def, int *result) { - if(!cfg) { - *result = def; - return true; - } - - if(sscanf(cfg->value, "%d", result) == 1) - return true; - - logger(LOG_ERR, _("cfg: integer expected for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - - return false; -} - -bool cfg_string(const cfg_t *cfg, const char *def, char **result) { - if(!cfg) { - *result = def ? xstrdup(def) : NULL; - return true; - } - - *result = xstrdup(cfg->value); - - return true; -} - -bool cfg_choice(const cfg_t *cfg, const cfg_choice_t *choice, const int def, int *result) { - int i; - - if(!cfg) { - *result = def; - return true; - } - - for(i = 0; choice[i].key; i++) { - if(!strcasecmp(cfg->variable, choice[i].key)) { - *result = choice[i].value; - return true; - } - } - - logger(LOG_ERR, _("cfg: invalid choice for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - - return false; -} - -bool cfg_period(const cfg_t *cfg, const int def, int *result) { - char unit; - - if(!cfg) { - *result = def; - return true; - } - - if(sscanf(cfg->value, "%d%c", result, &unit) == 2) { - switch(unit) { - case 's': - break; - case 'm': - *result *= 60; - break; - case 'h': - *result *= 60 * 60; - break; - case 'd': - *result *= 60 * 60 * 24; - break; - case 'W': - *result *= 60 * 60 * 24 * 7; - break; - case 'M': - *result *= 60 * 60 * 24 * 30; - break; - case 'Y': - *result *= 60 * 60 * 24 * 365; - break; - default: - logger(LOG_ERR, _("cfg: invalid period for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - return false; - } - return true; - } - - if(sscanf(cfg->value, "%d", result) == 1) - return true; - - logger(LOG_ERR, _("cfg: period expected for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - - return false; -} - -static char *readline(FILE *fp, char **buf, size_t *buflen) { - char *newline = NULL; - char *p; - char *line; /* The array that contains everything that has been read so far */ - char *idx; /* Read into this pointer, which points to an offset within line */ - size_t size, newsize; /* The size of the current array pointed to by line */ - size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */ - - if(feof(fp)) - return NULL; - - if(buf && buflen) { - size = *buflen; - line = *buf; - } else { - dim(line, size = 100); - } - - maxlen = size; - idx = line; - *idx = 0; - - for(;;) { - errno = 0; - p = fgets(idx, maxlen, fp); - - if(!p) { - if(feof(fp)) - break; - - logger(LOG_ERR, _("cfg: error while reading: %s"), strerror(errno)); - free(line); - return NULL; - } - - newline = strchr(p, '\n'); - - if(!newline) { - idx = &line[size - 1]; - maxlen = size + 1; - redim(line, size *= 2); - } else { - *newline = '\0'; - break; - } - } - - if(buf && buflen) { - *buflen = size; - *buf = line; - } - - return line; -} - -bool cfg_read_file(avl_tree_t *cfgs, const char *fname) { - FILE *fp; - char *buffer, *line; - char *variable, *value; - int lineno = 0; - int len; - bool result = false; - bool ignore = false; - cfg_t *cfg; - size_t bufsize; - - fp = fopen(fname, "r"); - - if(!fp) { - logger(LOG_ERR, _("cfg: error opening %s: %s"), fname, strerror(errno)); - return false; - } - - dim(buffer, bufsize = 100); - - for(;;) { - line = readline(fp, &buffer, &bufsize); - - if(!line) - break; - - if(feof(fp)) { - result = true; - break; - } - - lineno++; - - if(!*line || *line == '#') - continue; - - if(ignore) { - if(!strncmp(line, "-----END", 8)) - ignore = false; - continue; - } - - if(!strncmp(line, "-----BEGIN", 10)) { - ignore = true; - continue; - } - - variable = value = line; - - len = strcspn(value, "\t ="); - value += len; - value += strspn(value, "\t "); - if(*value == '=') { - value++; - value += strspn(value, "\t "); - } - variable[len] = '\0'; - - if(!*value) { - logger(LOG_ERR, _("cfg: no value for variable %s on line %d while reading cfg file %s"), - variable, lineno, fname); - break; - } - - cfg = cfg_new(); - replace(cfg->variable, variable); - replace(cfg->value, value); - replace(cfg->file, fname); - cfg->line = lineno; - - cfg_add(cfgs, cfg); - } - - free(buffer); - fclose(fp); - - return result; -} diff --git a/cfg/cfg.h b/cfg/cfg.h deleted file mode 100644 index bbc0139..0000000 --- a/cfg/cfg.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - conf.h -- header for conf.c - - Copyright (C) 1998-2004 Ivo Timmermans - 2000-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __TINC_CONF_H__ -#define __TINC_CONF_H__ - -#include "support/avl.h" - -typedef struct cfg { - char *variable; - char *value; - char *file; - int line; -} cfg_t; - -typedef struct cfg_choice { - char *key; - int value; -} cfg_choice_t; - -extern avl_tree_t *cfgs; - -extern avl_tree_t *cfg_tree_new(void); -extern void cfg_tree_free(avl_tree_t *); -extern cfg_t *cfg_new(void) __attribute__ ((__malloc__)); -extern void cfg_free(cfg_t *); -extern void cfg_add(avl_tree_t *, cfg_t *); -extern void cfg_del(avl_tree_t *, cfg_t *); -extern cfg_t *cfg_get(const avl_tree_t *, char *); -extern cfg_t *cfg_get_next(const avl_tree_t *, const cfg_t *); -extern bool cfg_bool(const cfg_t *, const bool, bool *); -extern bool cfg_int(const cfg_t *, const int, int *); -extern bool cfg_string(const cfg_t *, const char *, char **); -extern bool cfg_choice(const cfg_t *, const cfg_choice_t *, const int, int *); -extern bool cfg_period(const cfg_t *, const int, int *); -#define cfg_get_bool(tree, var, def, result) cfg_bool(cfg_get(tree, var), def, result) -#define cfg_get_int(tree, var, def, result) cfg_int(cfg_get(tree, var), def, result) -#define cfg_get_string(tree, var, def, result) cfg_string(cfg_get(tree, var), def, result) -#define cfg_get_choice(tree, var, choice, def, result) cfg_choice(cfg_get(tree, var), choice, def, (int *)result) -#define cfg_get_period(tree, var, def, result) cfg_period(cfg_get(tree, var), def, (int *)result) - -extern bool cfg_read_file(avl_tree_t *, const char *); - -#endif diff --git a/logger/Makefile.am b/logger/Makefile.am deleted file mode 100644 index 6fe3047..0000000 --- a/logger/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = liblogger.a - -noinst_HEADERS = logger.h - -liblogger_a_SOURCES = logger.c - diff --git a/logger/log.h b/logger/log.h deleted file mode 100644 index e178d69..0000000 --- a/logger/log.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - log.h -- logging - - Copyright (C) 2003-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __LOGGER_H__ -#define __LOGGER_H__ - -typedef enum debug_t { - DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */ - DEBUG_ALWAYS = 0, - DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */ - DEBUG_ERROR = 2, /* Show error messages received from other hosts */ - DEBUG_STATUS = 2, /* Show status messages received from other hosts */ - DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */ - DEBUG_META = 4, /* Show contents of every request that is sent/received */ - DEBUG_TRAFFIC = 5, /* Show network traffic information */ - DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */ - DEBUG_SCARY_THINGS = 10 /* You have been warned */ -} debug_t; - -typedef enum logmode_t { - LOGMODE_NULL, - LOGMODE_STDERR, - LOGMODE_FILE, - LOGMODE_SYSLOG -} logmode_t; - -#ifdef HAVE_MINGW -#define LOG_EMERG EVENTLOG_ERROR_TYPE -#define LOG_ALERT EVENTLOG_ERROR_TYPE -#define LOG_CRIT EVENTLOG_ERROR_TYPE -#define LOG_ERR EVENTLOG_ERROR_TYPE -#define LOG_WARNING EVENTLOG_WARNING_TYPE -#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE -#define LOG_INFO EVENTLOG_INFORMATION_TYPE -#define LOG_DEBUG EVENTLOG_INFORMATION_TYPE -#else -#ifndef HAVE_SYSLOG_H -enum { - LOG_EMERG, - LOG_ALERT, - LOG_CRIT, - LOG_ERR, - LOG_WARNING, - LOG_NOTICE, - LOG_INFO, - LOG_DEBUG, -}; -#endif -#endif - -extern debug_t debug_level; -extern void openlogger(const char *, logmode_t); -extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); -extern void closelogger(void); - -#define ifdebug(l) if(debug_level >= DEBUG_##l) - -#endif /* __LOGGER_H__ */ diff --git a/logger/logger.c b/logger/logger.c deleted file mode 100644 index 9bbd8fe..0000000 --- a/logger/logger.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - logger.c -- logging - - Copyright (C) 2003-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "logger/logger.h" - -logger_level_t logger_level = LOGGER_LEVEL_NONE; - -static logger_mode_t logger_mode = LOGGER_MODE_STDERR; -static pid_t logger_pid; -char *logger_filename; -static FILE *logger_file = NULL; -#ifdef HAVE_MINGW -static HANDLE logger_handle = NULL; -#endif -static const char *logger_ident = NULL; - -bool logger_init(const char *ident, logger_mode_t mode) { - logger_ident = ident; - logger_mode = mode; - - switch(mode) { - case LOGGER_MODE_STDERR: - logger_pid = getpid(); - break; - case LOGGER_MODE_FILE: - logger_pid = getpid(); - logger_file = fopen(logger_filename, "a"); - if(!logger_file) - logger_mode = LOGGER_MODE_NULL; - break; - case LOGGER_MODE_SYSLOG: -#ifdef HAVE_MINGW - logger_handle = RegisterEventSource(NULL, logger_ident); - if(!logger_handle) - logger_mode = LOGGER_MODE_NULL; - break; -#else -#ifdef HAVE_SYSLOG_H - openlog(logger_ident, LOG_CONS | LOG_PID, LOG_DAEMON); - break; -#endif -#endif - case LOGGER_MODE_NULL: - break; - } - - return true; -} - -bool logger_exit(void) { - switch(logger_mode) { - case LOGGER_MODE_FILE: - fclose(logger_file); - break; - case LOGGER_MODE_SYSLOG: -#ifdef HAVE_MINGW - DeregisterEventSource(logger_handle); - break; -#else -#ifdef HAVE_SYSLOG_H - closelog(); - break; -#endif -#endif - case LOGGER_MODE_NULL: - case LOGGER_MODE_STDERR: - break; - break; - } - - return true; -} - -void logger(int priority, const char *format, ...) { - va_list ap; - - va_start(ap, format); - - switch(logger_mode) { - case LOGGER_MODE_STDERR: - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - fflush(stderr); - break; - case LOGGER_MODE_FILE: - fprintf(logger_file, "%ld %s[%ld]: ", time(NULL), logger_ident, (long)logger_pid); - vfprintf(logger_file, format, ap); - fprintf(logger_file, "\n"); - fflush(logger_file); - break; - case LOGGER_MODE_SYSLOG: -#ifdef HAVE_MINGW - { - char message[4096]; - char *messages[] = {message}; - vsnprintf(message, sizeof(message), format, ap); - ReportEvent(logger_handle, priority, 0, 0, NULL, 1, 0, messages, NULL); - } -#else -#ifdef HAVE_SYSLOG_H -#ifdef HAVE_VSYSLOG - vsyslog(priority, format, ap); -#else - { - char message[4096]; - vsnprintf(message, sizeof(message), format, ap); - syslog(priority, "%s", message); - } -#endif - break; -#endif -#endif - case LOGGER_MODE_NULL: - break; - } - - va_end(ap); -} - - diff --git a/logger/logger.h b/logger/logger.h deleted file mode 100644 index a65968e..0000000 --- a/logger/logger.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - logger.h -- logging - - Copyright (C) 2003-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __LOGGER_H__ -#define __LOGGER_H__ - -typedef enum logger_level { - LOGGER_LEVEL_NONE, - LOGGER_LEVEL_NOTICE, - LOGGER_LEVEL_WARNING, - LOGGER_LEVEL_ERROR, - LOGGER_LEVEL_DEBUG, -} logger_level_t; - -typedef enum logger_mode { - LOGGER_MODE_NULL, - LOGGER_MODE_STDERR, - LOGGER_MODE_FILE, - LOGGER_MODE_SYSLOG, -} logger_mode_t; - -extern bool logger_init(const char *, logger_mode_t); -extern bool logger_exit(void); -extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); - -extern enum logger_level logger_level; - -#endif diff --git a/process.c b/process.c deleted file mode 100644 index f04f7c0..0000000 --- a/process.c +++ /dev/null @@ -1,565 +0,0 @@ -/* - 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 deleted file mode 100644 index 528c26c..0000000 --- a/rt/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = librt.a - -noinst_HEADERS = edge.h graph.h node.h route.h rt.h subnet.h - -librt_a_SOURCES = edge.c graph.c node.c route.c rt.c subnet.c - diff --git a/rt/README b/rt/README deleted file mode 100644 index d0b6dc8..0000000 --- a/rt/README +++ /dev/null @@ -1,31 +0,0 @@ -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.c b/rt/edge.c deleted file mode 100644 index bf78d1f..0000000 --- a/rt/edge.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - edge.c -- edge management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "rt/edge.h" -#include "rt/node.h" -#include "support/avl.h" -#include "support/xalloc.h" - -avl_tree_t *edges; - -static int edge_compare(const edge_t *a, const edge_t *b) { - return strcmp(a->to->name, b->to->name); -} - -static int edge_weight_compare(const edge_t *a, const edge_t *b) { - return (a->weight - b->weight) ?: strcmp(a->from->name, b->from->name) ?: strcmp(a->to->name, b->to->name); -} - -bool edge_init(void) { - edges = avl_tree_new((avl_compare_t)edge_weight_compare, NULL); - - return true; -} - -bool edge_exit(void) { - avl_tree_free(edges); - - return true; -} - -avl_tree_t *edge_tree_new(void) { - return avl_tree_new((avl_compare_t)edge_compare, (avl_action_t)edge_free); -} - -void edge_tree_free(avl_tree_t *edge_tree) { - avl_tree_free(edge_tree); -} - -edge_t *edge_new(void) { - edge_t *edge; - - return clear(new(edge)); -} - -void edge_free(edge_t *edge) { - free(edge); -} - -void edge_add(edge_t *edge) { - avl_add(edge->from->edges, edge); - avl_add(edges, edge); - - edge->reverse = edge_get(edge->to, edge->from); - - if(edge->reverse) - edge->reverse->reverse = edge; -} - -void edge_del(edge_t *edge) { - if(edge->reverse) - edge->reverse->reverse = NULL; - - avl_del(edges, edge); - avl_del(edge->from->edges, edge); -} - -edge_t *edge_get(node_t *from, node_t *to) { - edge_t search = {0}; - - search.from = from; - search.to = to; - - return avl_get(from->edges, &search); -} - diff --git a/rt/edge.h b/rt/edge.h deleted file mode 100644 index 775d833..0000000 --- a/rt/edge.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - edge.h -- edge management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __EDGE_H__ -#define __EDGE_H__ - -#include "rt/node.h" -#include "support/avl.h" -#include "tnl/tnl.h" - -typedef struct edge_status { - int visited:1; - int mst:1; -} edge_status_t; - -typedef struct edge { - struct node *from; - struct node *to; - struct sockaddr_storage address; - - int weight; - - struct edge *reverse; - struct tnl *tnl; - - edge_status_t status; - node_options_t options; -} edge_t; - -extern avl_tree_t *edges; - -extern bool edge_init(void); -extern bool edge_exit(void); -extern struct edge *edge_new(void) __attribute__ ((__malloc__)); -extern void edge_free(struct edge *); -extern avl_tree_t *edge_tree_new(void) __attribute__ ((__malloc__)); -extern void edge_tree_free(avl_tree_t *); -extern void edge_add(struct edge *); -extern void edge_del(struct edge *); -extern struct edge *edge_get(struct node *, struct node *); - -#endif diff --git a/rt/graph.c b/rt/graph.c deleted file mode 100644 index 63e8faf..0000000 --- a/rt/graph.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - 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/graph.h b/rt/graph.h deleted file mode 100644 index 91f58fd..0000000 --- a/rt/graph.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - graph.h -- graph algorithms - - Copyright (C) 2001-2004 Guus Sliepen , - 2001-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __GRAPH_H__ -#define __GRAPH_H__ - -extern bool graph_changed; -extern void graph(void); - -#endif /* __GRAPH_H__ */ diff --git a/rt/node.c b/rt/node.c deleted file mode 100644 index 4eee529..0000000 --- a/rt/node.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - node.c -- node management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "cfg/cfg.h" -#include "logger/logger.h" -#include "rt/node.h" -#include "support/avl.h" -#include "support/xalloc.h" -#include "tincd.h" - -avl_tree_t *nodes; - -node_t *myself; - -static int node_compare(const node_t *a, const node_t *b) { - return strcmp(a->name, b->name); -} - -bool node_validname(const char *name) { - for(; *name; name++) - if(!isalnum(*name) && *name != '_') - return false; - - return true; -} - -bool node_init(void) { - char *cfgfilename; - - nodes = avl_tree_new((avl_compare_t)node_compare, (avl_action_t)node_free); - myself = node_new(); - - if(!cfg_get_string(tinc_cfg, "Name", NULL, &myself->name) || !myself->name) { - logger(LOG_ERR, _("rt: name for tinc daemon required!")); - node_exit(); - return false; - } - - if(!node_validname(myself->name)) { - logger(LOG_ERR, _("rt: invalid name for myself!")); - node_exit(); - return false; - } - - myself->cfg = cfg_tree_new(); - - asprintf(&cfgfilename, "%s/hosts/%s", tinc_confbase, myself->name); - - if(!cfg_read_file(myself->cfg, cfgfilename)) { - free(cfgfilename); - node_exit(); - return false; - } - - free(cfgfilename); - - return true; -} - -bool node_exit(void) { - avl_tree_del(nodes); - return true; -} - -node_t *node_new(void) { - node_t *node; - - clear(new(node)); - node->subnets = subnet_tree_new(); - node->edges = edge_tree_new(); - node->queue = avl_tree_new(NULL, (avl_action_t)free); - - return node; -} - -void node_free(node_t *node) { - if(node->queue) - avl_tree_free(node->queue); - - if(node->subnets) - subnet_tree_free(node->subnets); - - if(node->edges) - edge_tree_free(node->edges); - - replace(node->name, NULL); - - free(node); -} - -void node_add(node_t *node) { - avl_add(nodes, node); -} - -void node_del(node_t *node) { - edge_t *edge; - subnet_t *subnet; - - avl_foreach(node->subnets, subnet, subnet_del(subnet)); - avl_foreach(node->edges, edge, edge_del(edge)); - - avl_del(nodes, node); -} - -node_t *node_get(char *name) { - node_t search = {0}; - - search.name = name; - - return avl_get(nodes, &search); -} - diff --git a/rt/node.h b/rt/node.h deleted file mode 100644 index acf20bc..0000000 --- a/rt/node.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - node.h -- node management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __NODE_H__ -#define __NODE_H__ - -typedef int node_options_t; - -#define NODE_OPTION_INDIRECT 1 - -#include "rt/edge.h" -#include "rt/subnet.h" -#include "support/avl.h" -#include "tnl/tnl.h" - -typedef struct node_status { - int active:1; - int visited:1; - int reachable:1; - int indirect:1; -} node_status_t; - -typedef struct node { - char *name; - - avl_tree_t *queue; - - struct node *nexthop; - struct node *via; - - avl_tree_t *subnets; - avl_tree_t *edges; - - struct tnl *tnl; - - node_status_t status; - node_options_t options; - - struct sockaddr_storage address; - - avl_tree_t *cfg; -} node_t; - -extern avl_tree_t *nodes; -extern struct node *myself; - -extern bool node_init(void); -extern bool node_exit(void); -extern struct node *node_new(void) __attribute__ ((__malloc__)); -extern void node_free(struct node *); -extern void node_add(struct node *); -extern void node_del(struct node *); -extern struct node *node_get(char *); - -#endif diff --git a/rt/route.c b/rt/route.c deleted file mode 100644 index 4721763..0000000 --- a/rt/route.c +++ /dev/null @@ -1,754 +0,0 @@ -/* - 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 deleted file mode 100644 index 42c587f..0000000 --- a/rt/route.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - 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 deleted file mode 100644 index 43e33c6..0000000 --- a/rt/rt.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - rt.c -- routing - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "cfg/cfg.h" -#include "rt/edge.h" -#include "rt/node.h" -#include "rt/rt.h" -#include "rt/subnet.h" -#include "support/xalloc.h" -#include "tnl/tnl.h" -#include "vnd/vnd.h" -#include "tincd.h" - -vnd_t *rt_vnd = NULL; -int rt_af = AF_UNSPEC; -int rt_macexpire = 600; -int rt_maxtimeout = 900; -rt_mode_t rt_mode = RT_MODE_ROUTER; -bool rt_priorityinheritance = false; -bool rt_hostnames = false; -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 void *buf, int len) { - route(myself, 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 void *buf, int len) { -} - -static void rt_outgoing(char *name) { - tnl_t *tnl; - - clear(new(tnl)); - -} - -bool rt_init(void) { - char *bindtoaddress = NULL; - char *bindtointerface = NULL; - char *device = NULL; - char *iface = NULL; - char *port = NULL; - cfg_t *cfg; - subnet_t *subnet; - struct addrinfo hint, *ai, *aip; - int err; - int listeners; - char *connectto = NULL; - - cfg_choice_t mode_choice[] = { - {"Router", RT_MODE_ROUTER}, - {"Switch", RT_MODE_SWITCH}, - {"Hub", RT_MODE_HUB}, - }; - - cfg_choice_t af_choice[] = { - {"IPv4", AF_INET}, - {"IPv6", AF_INET6}, - {"Any", AF_UNSPEC}, - }; - - logger(LOG_INFO, _("rt: initialising")); - - if(!subnet_init() || !node_init() || !edge_init()) - return false; - - 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) - || !cfg_get_string(tinc_cfg, "BindToAddress", NULL, &bindtoaddress) - || !cfg_get_string(tinc_cfg, "BindToInterface", NULL, &bindtointerface) - || !cfg_get_string(tinc_cfg, "Device", "/dev/net/tun", &device) - || !cfg_get_bool(tinc_cfg, "Hostnames", false, &rt_hostnames) - || !cfg_get_string(tinc_cfg, "Interface", tinc_netname, &iface) - || !cfg_get_period(tinc_cfg, "MACExpire", 600, &rt_macexpire) - || !cfg_get_period(tinc_cfg, "MaxTimeout", 3600, &rt_maxtimeout) - || !cfg_get_choice(tinc_cfg, "Mode", mode_choice, RT_MODE_ROUTER, &rt_mode) - || !cfg_get_bool(tinc_cfg, "PriorityInheritance", false, &rt_priorityinheritance)) - return false; - - /* Read host configuration for myself */ - - if(!cfg_get_string(myself->cfg, "Port", "655", &port)) - return false; - - for(cfg = cfg_get(myself->cfg, "Subnet"); cfg; cfg = cfg_get_next(myself->cfg, cfg)) { - if(!cfg_subnet(cfg, &subnet)) - return false; - - subnet->owner = myself; - subnet_add(subnet); - } - - /* Open the virtual network device */ - - clear(new(rt_vnd)); - - replace(rt_vnd->device, device); - replace(rt_vnd->interface, iface); - - rt_vnd->mode = (rt_mode == RT_MODE_ROUTER) ? VND_MODE_TUN : VND_MODE_TAP; - rt_vnd->recv = rt_vnd_recv; - - if(!vnd_open(rt_vnd)) { - vnd_free(rt_vnd); - return false; - } - - /* Create listening sockets */ - - hint.ai_family = rt_af; - hint.ai_socktype = SOCK_STREAM; - hint.ai_protocol = IPPROTO_TCP; - hint.ai_flags = AI_PASSIVE; - - err = getaddrinfo(bindtoaddress, port, &hint, &ai); - - if(err || !ai) { - logger(LOG_ERR, _("rt: system call '%s' failed: %s"), "getaddrinfo", gai_strerror(err)); - return false; - } - - listeners = 0; - - for(aip = ai; aip; aip = aip->ai_next) { - tnl_listen_t *listener; - - clear(new(listener)); - listener->local.address = *(struct sockaddr_storage *)aip->ai_addr; - listener->local.id = myself->name; - listener->type = SOCK_STREAM; - listener->protocol = IPPROTO_TCP; - // listener->local.cred = ...; - listener->accept = rt_tnl_accept; - - if(tnl_listen(listener)) - listeners++; - } - - freeaddrinfo(ai); - - if(!listeners) { - logger(LOG_ERR, _("rt: unable to create any listening socket!")); - return false; - } - - /* Setup outgoing connections */ - - for(cfg = cfg_get(tinc_cfg, "ConnectTo"); cfg; cfg = cfg_get_next(tinc_cfg, cfg)) { - if(!cfg_string(cfg, NULL, &connectto)) - return false; - - if(!node_validname(connectto)) { - logger(LOG_ERR, _("rt: invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line); - free(connectto); - continue; - } - - rt_outgoing(connectto); - } - - return true; -} - -bool rt_exit(void) { - edge_exit(); - node_exit(); - subnet_exit(); - - logger(LOG_INFO, _("rt: exitting")); -} - - diff --git a/rt/rt.h b/rt/rt.h deleted file mode 100644 index dd8126d..0000000 --- a/rt/rt.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - route.h -- routing - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __RT_H__ -#define __RT_H__ - -#include "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, - RT_MODE_HUB, -} rt_mode_t; - -extern int rt_af; -extern enum rt_mode rt_mode; -extern bool rt_hostnames; -extern bool rt_priorityinheritance; -extern int rt_macexpire; -extern int rt_maxtimeout; -extern bool rt_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); - -#endif diff --git a/rt/subnet.c b/rt/subnet.c deleted file mode 100644 index ced1695..0000000 --- a/rt/subnet.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - subnet.c -- subnet handling - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include "cfg/cfg.h" -#include "logger/logger.h" -#include "rt/node.h" -#include "rt/subnet.h" -#include "support/avl.h" -#include "support/xalloc.h" - -avl_tree_t *subnets; -/* Subnet mask handling */ - -static int maskcmp(const void *va, const void *vb, int masklen, int len) { - int i, m, result; - const char *a = va; - const char *b = vb; - - for(m = masklen, i = 0; m >= 8; m -= 8, i++) { - result = a[i] - b[i]; - if(result) - return result; - } - - return m ? (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m)))) : 0; -} - -static void mask(void *va, int masklen, int len) { - int i; - char *a = va; - - i = masklen / 8; - masklen %= 8; - - if(masklen) - a[i++] &= (0x100 - (1 << masklen)); - - for(; i < len; i++) - a[i] = 0; -} - -static void maskcpy(void *va, const void *vb, int masklen, int len) { - int i, m; - char *a = va; - const char *b = vb; - - for(m = masklen, i = 0; m >= 8; m -= 8, i++) - a[i] = b[i]; - - if(m) { - a[i] = b[i] & (0x100 - (1 << m)); - i++; - } - - for(; i < len; i++) - a[i] = 0; -} - -static bool maskcheck(const void *va, int masklen, int len) { - int i; - const char *a = va; - - i = masklen / 8; - masklen %= 8; - - if(masklen && a[i++] & (0xff >> masklen)) - return false; - - for(; i < len; i++) - if(a[i] != 0) - return false; - - return true; -} - -/* Cache handling */ - -struct { - subnet_t key; - subnet_t *subnet; -} *cache; - -int cache_bits; -int cache_size; -uint32_t cache_mask; - -static void cache_flush(void) { - memset(cache, 0, sizeof *cache * cache_size); -} - -static void cache_init(void) { - cache_bits = 8; - cache_size = 1 << 8; - cache_mask = cache_size - 1; - - dim(cache, cache_size); - - cache_flush(); -} - -static void cache_exit(void) { - free(cache); -} - -static uint32_t subnet_hash(const subnet_t *subnet) { - uint32_t hash; - int i; - - hash = subnet->type; - - for(i = 0; i < sizeof subnet->net / sizeof(uint32_t); i++) - hash ^= ((uint32_t *)&subnet->net)[i]; - - hash ^= hash >> 16; - hash ^= hash >> 8; - - return hash & cache_mask; -} - -static subnet_t *cache_get(subnet_t *subnet) { - uint32_t hash = subnet_hash(subnet); - - if(cache[hash].subnet && memcmp(&cache[hash].key, subnet, sizeof *subnet)) - return cache[hash].subnet; - else - return NULL; -} - -static void cache_add(subnet_t *key, subnet_t *subnet) { - uint32_t hash = subnet_hash(subnet); - - cache[hash].key = *key; - cache[hash].subnet = subnet; -} - -/* Subnet tree handling */ - -static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) { - return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t)) - ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; -} - -static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) { - return memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t)) - ?: (a->net.ipv4.prefixlength - b->net.ipv4.prefixlength) - ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; -} - -static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) { - return memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t)) - ?: (a->net.ipv6.prefixlength - b->net.ipv6.prefixlength) - ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; -} - -static int subnet_compare(const subnet_t *a, const subnet_t *b) { - int result; - - result = a->type - b->type; - - if(result) - return result; - - switch (a->type) { - case SUBNET_TYPE_MAC: - return subnet_compare_mac(a, b); - case SUBNET_TYPE_IPV4: - return subnet_compare_ipv4(a, b); - case SUBNET_TYPE_IPV6: - return subnet_compare_ipv6(a, b); - default: - logger(LOG_ERR, _("rt: subnet_compare() was called with unknown subnet type %d, exitting!"), a->type); - exit(1); - } -} - -avl_tree_t *subnet_tree_new(void) { - return avl_tree_new((avl_compare_t)subnet_compare, NULL); -} - -void subnet_tree_free(avl_tree_t *subnets) { - avl_tree_free(subnets); -} - -subnet_t *subnet_new(void) { - subnet_t *subnet; - - return clear(new(subnet)); -} - -void subnet_free(subnet_t *subnet) { - free(subnet); -} - -void subnet_add(subnet_t *subnet) { - avl_add(subnets, subnet); - avl_add(subnet->owner->subnets, subnet); - cache_flush(); -} - -void subnet_del(subnet_t *subnet) { - avl_del(subnet->owner->subnets, subnet); - avl_del(subnets, subnet); - cache_flush(); -} - -bool subnet_init(void) { - cache_init(); - subnets = avl_tree_new((avl_compare_t)subnet_compare, (avl_action_t)subnet_free); - - return true; -} - -bool subnet_exit(void) { - avl_tree_del(subnets); - cache_exit(); - - return true; -} - -subnet_t *str2net(const char *subnetstr) { - int i, l; - subnet_t subnet = {0}; - uint16_t x[8]; - - if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d", - &x[0], &x[1], &x[2], &x[3], &l) == 5) { - subnet.type = SUBNET_TYPE_IPV4; - subnet.net.ipv4.prefixlength = l; - - for(i = 0; i < 4; i++) - subnet.net.ipv4.address.x[i] = x[i]; - - return copy(&subnet); - } - - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", - &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], - &l) == 9) { - subnet.type = SUBNET_TYPE_IPV6; - subnet.net.ipv6.prefixlength = l; - - for(i = 0; i < 8; i++) - subnet.net.ipv6.address.x[i] = htons(x[i]); - - return copy(&subnet); - } - - if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) { - subnet.type = SUBNET_TYPE_IPV4; - subnet.net.ipv4.prefixlength = 32; - - for(i = 0; i < 4; i++) - subnet.net.ipv4.address.x[i] = x[i]; - - return copy(&subnet); - } - - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", - &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) { - subnet.type = SUBNET_TYPE_IPV6; - subnet.net.ipv6.prefixlength = 128; - - for(i = 0; i < 8; i++) - subnet.net.ipv6.address.x[i] = htons(x[i]); - - return copy(&subnet); - } - - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx", - &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) { - subnet.type = SUBNET_TYPE_MAC; - - for(i = 0; i < 6; i++) - subnet.net.mac.address.x[i] = x[i]; - - return copy(&subnet); - } - - return NULL; -} - -char *net2str(const subnet_t *subnet) { - char *netstr; - - switch (subnet->type) { - case SUBNET_TYPE_MAC: - asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx", - subnet->net.mac.address.x[0], - subnet->net.mac.address.x[1], - subnet->net.mac.address.x[2], - subnet->net.mac.address.x[3], - subnet->net.mac.address.x[4], - subnet->net.mac.address.x[5]); - break; - - case SUBNET_TYPE_IPV4: - asprintf(&netstr, "%hu.%hu.%hu.%hu/%d", - subnet->net.ipv4.address.x[0], - subnet->net.ipv4.address.x[1], - subnet->net.ipv4.address.x[2], - subnet->net.ipv4.address.x[3], - subnet->net.ipv4.prefixlength); - break; - - case SUBNET_TYPE_IPV6: - asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", - ntohs(subnet->net.ipv6.address.x[0]), - ntohs(subnet->net.ipv6.address.x[1]), - ntohs(subnet->net.ipv6.address.x[2]), - ntohs(subnet->net.ipv6.address.x[3]), - ntohs(subnet->net.ipv6.address.x[4]), - ntohs(subnet->net.ipv6.address.x[5]), - ntohs(subnet->net.ipv6.address.x[6]), - ntohs(subnet->net.ipv6.address.x[7]), - subnet->net.ipv6.prefixlength); - break; - - default: - logger(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type); - exit(0); - } - - return netstr; -} - -bool cfg_subnet(cfg_t *cfg, subnet_t **result) { - subnet_t *subnet; - - subnet = str2net(cfg->value); - - if(!subnet) { - logger(LOG_ERR, _("rt: invalid subnet for configuration variable %s in %s line %d"), - cfg->variable, cfg->file, cfg->line); - return false; - } - - *result = subnet; - - return true; -} - -subnet_t *subnet_get(const subnet_t *subnet) { - return subnet->owner ? avl_get(subnet->owner->subnets, subnet) : avl_get(subnets, subnet); -} - -subnet_t *subnet_get_mac(const mac_t *address) { - subnet_t *subnet, search = {0}; - - search.type = SUBNET_TYPE_MAC; - search.net.mac.address = *address; - - subnet = cache_get(&search); - - if(subnet) - return subnet; - - subnet = avl_get(subnets, &search); - - if(subnet) - cache_add(&search, subnet); - - return subnet; -} - -subnet_t *subnet_get_ipv4(const ipv4_t *address) { - subnet_t *subnet, search = {0}; - - search.type = SUBNET_TYPE_IPV4; - search.net.ipv4.address = *address; - search.net.ipv4.prefixlength = 32; - - subnet = cache_get(&search); - - if(subnet) - return subnet; - - while(subnet = avl_get_closest_smaller(subnets, &search)) { - if(subnet->type != SUBNET_TYPE_IPV4) - return NULL; - - if(!maskcmp(address, &subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t))) { - cache_add(&search, subnet); - return subnet; - } - - search.net.ipv4.prefixlength = subnet->net.ipv4.prefixlength - 1; - maskcpy(&search.net.ipv4.address, &subnet->net.ipv4.address, search.net.ipv4.prefixlength, sizeof(ipv4_t)); - } - - return NULL; -} - -subnet_t *subnet_get_ipv6(const ipv6_t *address) { - subnet_t *subnet, search = {0}; - - search.type = SUBNET_TYPE_IPV6; - search.net.ipv6.address = *address; - search.net.ipv6.prefixlength = 128; - - subnet = cache_get(&search); - - if(subnet) - return subnet; - - while(subnet = avl_get_closest_smaller(subnets, &search)) { - if(subnet->type != SUBNET_TYPE_IPV6) - return NULL; - - if(!maskcmp(address, &subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))) { - cache_add(&search, subnet); - return subnet; - } - - search.net.ipv6.prefixlength = subnet->net.ipv6.prefixlength - 1; - maskcpy(&search.net.ipv6.address, &subnet->net.ipv6.address, search.net.ipv6.prefixlength, sizeof(ipv6_t)); - } - - return NULL; -} diff --git a/rt/subnet.h b/rt/subnet.h deleted file mode 100644 index 5134ae2..0000000 --- a/rt/subnet.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - subnet.h -- subnet handling - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __SUBNET_H__ -#define __SUBNET_H__ - -#include "rt/node.h" -#include "support/avl.h" - -typedef struct mac { - uint8_t x[6]; -} mac_t; - -typedef struct ipv4 { - uint8_t x[4]; -} ipv4_t; - -typedef struct ipv6 { - uint16_t x[8]; -} ipv6_t; - -typedef enum subnet_type { - SUBNET_TYPE_MAC, - SUBNET_TYPE_IPV4, - SUBNET_TYPE_IPV6, -} subnet_type_t; - -typedef struct subnet_mac { - mac_t address; -} subnet_mac_t; - -typedef struct subnet_ipv4 { - ipv4_t address; - int prefixlength; -} subnet_ipv4_t; - -typedef struct subnet_ipv6 { - ipv6_t address; - int prefixlength; -} subnet_ipv6_t; - -typedef struct subnet { - struct node *owner; - struct timeval expires; - - enum subnet_type type; - - union net { - struct subnet_mac mac; - struct subnet_ipv4 ipv4; - struct subnet_ipv6 ipv6; - } net; -} subnet_t; - -extern subnet_t *subnet_new(void) __attribute__ ((__malloc__)); -extern void subnet_free(struct subnet *); -extern bool subnet_init(void); -extern bool subnet_exit(void); -extern avl_tree_t *subnet_tree_new(void) __attribute__ ((__malloc__)); -extern void subnet_tree_free(avl_tree_t *); -extern void subnet_add(struct subnet *); -extern void subnet_del(struct subnet *); -extern char *net2str(const struct subnet *); -extern struct subnet *str2net(const char *); -extern struct subnet *subnet_get(const struct subnet *); -extern struct subnet *subnet_get_mac(const struct mac *); -extern struct subnet *subnet_get_ipv4(const struct ipv4 *); -extern struct subnet *subnet_get_ipv6(const struct ipv6 *); - -#endif diff --git a/src/avl.c b/src/avl.c new file mode 100644 index 0000000..96500cb --- /dev/null +++ b/src/avl.c @@ -0,0 +1,675 @@ +/* + avl.c -- AVL tree management + + Copyright (C) 1998 Michael H. Buselli + 2000-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + 2000-2004 Wessel Dankers + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Original AVL tree library by Michael H. Buselli . + + Modified 2000-11-28 by Wessel Dankers to use counts + instead of depths, to add the ->next and ->prev and to generally obfuscate + the code. Mail me if you found a bug. + + Cleaned up and incorporated some of the ideas from the red-black tree + library for inclusion into tinc (http://www.tinc-vpn.org/) by + Guus Sliepen . + + $Id$ +*/ + +#include "system.h" + +#include "support/avl.h" +#include "support/xalloc.h" + +#ifdef AVL_COUNT +#define AVL_NODE_COUNT(n) ((n) ? (n)->count : 0) +#define AVL_L_COUNT(n) (AVL_NODE_COUNT((n)->left)) +#define AVL_R_COUNT(n) (AVL_NODE_COUNT((n)->right)) +#define AVL_CALC_COUNT(n) (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1) +#endif + +#ifdef AVL_DEPTH +#define AVL_NODE_DEPTH(n) ((n) ? (n)->depth : 0) +#define L_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->left)) +#define R_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->right)) +#define AVL_CALC_DEPTH(n) ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1) +#endif + +#ifndef AVL_DEPTH +static int lg(unsigned int u) __attribute__ ((__const__)); + +static int lg(unsigned int u) { + int r = 1; + + if(!u) + return 0; + + if(u & 0xffff0000) { + u >>= 16; + r += 16; + } + + if(u & 0x0000ff00) { + u >>= 8; + r += 8; + } + + if(u & 0x000000f0) { + u >>= 4; + r += 4; + } + + if(u & 0x0000000c) { + u >>= 2; + r += 2; + } + + if(u & 0x00000002) + r++; + + return r; +} +#endif + +/* Internal helper functions */ + +static int avl_check_balance(const avl_node_t *node) { +#ifdef AVL_DEPTH + int d; + + d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node); + + return d < -1 ? -1 : d > 1 ? 1 : 0; +#else +/* int d; + * d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node)); + * d = d<-1?-1:d>1?1:0; + */ + int pl, r; + + pl = lg(AVL_L_COUNT(node)); + r = AVL_R_COUNT(node); + + if(r >> pl + 1) + return 1; + + if(pl < 2 || r >> pl - 2) + return 0; + + return -1; +#endif +} + +static void avl_rebalance(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *child; + avl_node_t *gchild; + avl_node_t *parent; + avl_node_t **superparent; + + parent = node; + + while(node) { + parent = node->parent; + + superparent = + parent ? node == + parent->left ? &parent->left : &parent->right : &tree->root; + + switch (avl_check_balance(node)) { + case -1: + child = node->left; +#ifdef AVL_DEPTH + if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) { +#else + if(AVL_L_COUNT(child) >= AVL_R_COUNT(child)) { +#endif + node->left = child->right; + if(node->left) + node->left->parent = node; + + child->right = node; + node->parent = child; + *superparent = child; + child->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); +#endif + } else { + gchild = child->right; + node->left = gchild->right; + + if(node->left) + node->left->parent = node; + child->right = gchild->left; + + if(child->right) + child->right->parent = child; + gchild->right = node; + + if(gchild->right) + gchild->right->parent = gchild; + gchild->left = child; + + if(gchild->left) + gchild->left->parent = gchild; + *superparent = gchild; + + gchild->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); + gchild->count = AVL_CALC_COUNT(gchild); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); + gchild->depth = AVL_CALC_DEPTH(gchild); +#endif + } + break; + + case 1: + child = node->right; +#ifdef AVL_DEPTH + if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) { +#else + if(AVL_R_COUNT(child) >= AVL_L_COUNT(child)) { +#endif + node->right = child->left; + if(node->right) + node->right->parent = node; + child->left = node; + node->parent = child; + *superparent = child; + child->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); +#endif + } else { + gchild = child->left; + node->right = gchild->left; + + if(node->right) + node->right->parent = node; + child->left = gchild->right; + + if(child->left) + child->left->parent = child; + gchild->left = node; + + if(gchild->left) + gchild->left->parent = gchild; + gchild->right = child; + + if(gchild->right) + gchild->right->parent = gchild; + + *superparent = gchild; + gchild->parent = parent; +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); + child->count = AVL_CALC_COUNT(child); + gchild->count = AVL_CALC_COUNT(gchild); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); + child->depth = AVL_CALC_DEPTH(child); + gchild->depth = AVL_CALC_DEPTH(gchild); +#endif + } + break; + + default: +#ifdef AVL_COUNT + node->count = AVL_CALC_COUNT(node); +#endif +#ifdef AVL_DEPTH + node->depth = AVL_CALC_DEPTH(node); +#endif + } + node = parent; + } +} + +static int avl_compare(const void *a, const void *b) { + return a - b; +} + +/* (De)constructors */ + +avl_tree_t *avl_tree_new(avl_compare_t compare, avl_action_t free) { + avl_tree_t *tree; + + clear(new(tree)); + tree->compare = compare ?: avl_compare; + tree->free = free; + + return tree; +} + +void avl_tree_free(avl_tree_t *tree) { + free(tree); +} + +avl_node_t *avl_node_new(void) { + avl_node_t *node; + + return clear(new(node)); +} + +void avl_node_free(avl_tree_t *tree, avl_node_t *node) { + if(node->data && tree->free) + tree->free(node->data); + + free(node); +} + +/* Searching */ + +void *avl_get(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + return node ? node->data : NULL; +} + +void *avl_get_closest(const avl_tree_t *tree, const void *data, int *result) { + avl_node_t *node; + + node = avl_get_closest_node(tree, data, result); + + return node ? node->data : NULL; +} + +void *avl_get_closest_smaller(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_closest_smaller_node(tree, data); + + return node ? node->data : NULL; +} + +void *avl_get_closest_greater(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_closest_greater_node(tree, data); + + return node ? node->data : NULL; +} + +avl_node_t *avl_get_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + return result ? NULL : node; +} + +avl_node_t *avl_get_closest_node(const avl_tree_t *tree, const void *data, int *result) { + avl_node_t *node; + int c; + + node = tree->root; + + if(!node) { + if(result) + *result = 0; + return NULL; + } + + for(;;) { + c = tree->compare(data, node->data); + + if(c < 0) { + if(node->left) + node = node->left; + else { + if(result) + *result = -1; + break; + } + } else if(c > 0) { + if(node->right) + node = node->right; + else { + if(result) + *result = 1; + break; + } + } else { + if(result) + *result = 0; + break; + } + } + + return node; +} + +avl_node_t *avl_get_closest_smaller_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + if(result < 0) + node = node->prev; + + return node; +} + +avl_node_t *avl_get_closest_greater_node(const avl_tree_t *tree, const void *data) { + avl_node_t *node; + int result; + + node = avl_get_closest_node(tree, data, &result); + + if(result > 0) + node = node->next; + + return node; +} + +/* Insertion and deletion */ + +avl_node_t *avl_add(avl_tree_t *tree, void *data) { + avl_node_t *node, *result; + + node = avl_node_new(); + node->data = data; + + result = avl_add_node(tree, node); + + if(!result) + free(node); + + return result; +} + +avl_node_t *avl_add_node(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *closest; + int result; + + if(!tree->root) + avl_add_top(tree, node); + else { + closest = avl_get_closest_node(tree, node->data, &result); + + switch (result) { + case -1: + avl_add_before(tree, closest, node); + break; + + case 1: + avl_add_after(tree, closest, node); + break; + + case 0: + return NULL; + } + } + +#ifdef AVL_COUNT + node->count = 1; +#endif +#ifdef AVL_DEPTH + node->depth = 1; +#endif + + return node; +} + +void avl_add_top(avl_tree_t *tree, avl_node_t *node) { + node->prev = node->next = node->parent = NULL; + tree->head = tree->tail = tree->root = node; +} + +void avl_add_before(avl_tree_t *tree, avl_node_t *before, avl_node_t *node) { + if(!before) { + if(tree->tail) + avl_add_after(tree, tree->tail, node); + else + avl_add_top(tree, node); + return; + } + + node->next = before; + node->parent = before; + node->prev = before->prev; + + if(before->left) { + avl_add_after(tree, before->prev, node); + return; + } + + if(before->prev) + before->prev->next = node; + else + tree->head = node; + + before->prev = node; + before->left = node; + + avl_rebalance(tree, before); +} + +void avl_add_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node) { + if(!after) { + if(tree->head) + avl_add_before(tree, tree->head, node); + else + avl_add_top(tree, node); + return; + } + + if(after->right) { + avl_add_before(tree, after->next, node); + return; + } + + node->prev = after; + node->parent = after; + node->next = after->next; + + if(after->next) + after->next->prev = node; + else + tree->tail = node; + + after->next = node; + after->right = node; + + avl_rebalance(tree, after); +} + +avl_node_t *avl_unlink(avl_tree_t *tree, const void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + if(node) + avl_unlink_node(tree, node); + + return node; +} + +void avl_unlink_node(avl_tree_t *tree, avl_node_t *node) { + avl_node_t *parent; + avl_node_t **superparent; + avl_node_t *subst, *left, *right; + avl_node_t *balnode; + + if(node->prev) + node->prev->next = node->next; + else + tree->head = node->next; + if(node->next) + node->next->prev = node->prev; + else + tree->tail = node->prev; + + parent = node->parent; + + superparent = + parent ? node == + parent->left ? &parent->left : &parent->right : &tree->root; + + left = node->left; + right = node->right; + if(!left) { + *superparent = right; + + if(right) + right->parent = parent; + + balnode = parent; + } else if(!right) { + *superparent = left; + left->parent = parent; + balnode = parent; + } else { + subst = node->prev; + + if(subst == left) { + balnode = subst; + } else { + balnode = subst->parent; + balnode->right = subst->left; + + if(balnode->right) + balnode->right->parent = balnode; + + subst->left = left; + left->parent = subst; + } + + subst->right = right; + subst->parent = parent; + right->parent = subst; + *superparent = subst; + } + + avl_rebalance(tree, balnode); + + node->next = node->prev = node->parent = node->left = node->right = NULL; + +#ifdef AVL_COUNT + node->count = 0; +#endif +#ifdef AVL_DEPTH + node->depth = 0; +#endif +} + +void avl_del_node(avl_tree_t *tree, avl_node_t *node) { + avl_unlink_node(tree, node); + avl_node_free(tree, node); +} + +bool avl_del(avl_tree_t *tree, void *data) { + avl_node_t *node; + + node = avl_get_node(tree, data); + + if(node) + avl_del_node(tree, node); + + return node; +} + +/* Fast tree cleanup */ + +void avl_tree_del(avl_tree_t *tree) { + avl_node_t *node; + + avl_foreach_node(tree, node, avl_node_free(tree, node)); + + avl_tree_free(tree); +} + +/* Indexing */ + +#ifdef AVL_COUNT +avl_count_t avl_count(const avl_tree_t *tree) { + return AVL_NODE_COUNT(tree->root); +} + +void *avl_get_indexed(const avl_tree_t *tree, avl_count_t index) { + avl_node_t *node; + + node = avl_get_indexed_node(tree, index); + + return node ? node->data : NULL; +} + +avl_node_t *avl_get_indexed_node(const avl_tree_t *tree, avl_count_t index) { + avl_node_t *node; + avl_count_t c; + + node = tree->root; + + while(node) { + c = AVL_L_COUNT(node); + + if(index < c) { + node = node->left; + } else if(index > c) { + node = node->right; + index -= c + 1; + } else { + return node; + } + } + + return NULL; +} + +avl_count_t avl_index(const avl_node_t *node) { + avl_node_t *next; + avl_count_t index; + + index = AVL_L_COUNT(node); + + while((next = node->parent)) { + if(node == next->right) + index += AVL_L_COUNT(next) + 1; + node = next; + } + + return index; +} +#endif +#ifdef AVL_DEPTH +avl_depth_t avl_depth(const avl_tree_t *tree) { + return AVL_NODE_DEPTH(tree->root); +} +#endif diff --git a/src/avl.h b/src/avl.h new file mode 100644 index 0000000..ece7d1a --- /dev/null +++ b/src/avl.h @@ -0,0 +1,149 @@ +/* + avl.h -- AVL tree management + + Copyright (C) 1998 Michael H. Buselli + 2000-2004 Ivo Timmermans , + 2000-2004 Guus Sliepen + 2000-2004 Wessel Dankers + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Original AVL tree library by Michael H. Buselli . + + Modified 2000-11-28 by Wessel Dankers to use counts + instead of depths, to add the ->next and ->prev and to generally obfuscate + the code. Mail me if you found a bug. + + Cleaned up and incorporated some of the ideas from the red-black tree + library for inclusion into tinc (http://www.tinc-vpn.org/) by + Guus Sliepen . + + $Id$ +*/ + + +#ifndef __AVL_H__ +#define __AVL_H__ + +#ifndef AVL_DEPTH +#ifndef AVL_COUNT +#define AVL_DEPTH +#endif +#endif + +typedef uint32_t avl_count_t; +typedef uint16_t avl_depth_t; + +typedef struct avl_node { + struct avl_node *next; + struct avl_node *prev; + + struct avl_node *parent; + struct avl_node *left; + struct avl_node *right; + +#ifdef AVL_COUNT + avl_count_t count; +#endif + +#ifdef AVL_DEPTH + avl_depth_t depth; +#endif + + void *data; +} avl_node_t; + +typedef int (*avl_compare_t)(const void *, const void *); +typedef void (*avl_action_t)(void *); +typedef void (*avl_node_action_t)(struct avl_node *); + +typedef struct avl_tree { + struct avl_node *head; + struct avl_node *tail; + + struct avl_node *root; + + avl_compare_t compare; + avl_action_t free; +} avl_tree_t; + +/* (De)constructors */ + +extern struct avl_tree *avl_tree_new(avl_compare_t, avl_action_t); +extern void avl_tree_free(struct avl_tree *); + +extern struct avl_node *avl_node_new(void); +extern void avl_node_free(struct avl_tree *tree, struct avl_node *); + +/* Insertion and deletion */ + +extern struct avl_node *avl_add(struct avl_tree *, void *); +extern struct avl_node *avl_add_node(struct avl_tree *, struct avl_node *); + +extern void avl_add_top(struct avl_tree *, struct avl_node *); +extern void avl_add_before(struct avl_tree *, struct avl_node *, struct avl_node *); +extern void avl_add_after(struct avl_tree *, struct avl_node *, struct avl_node *); + +extern struct avl_node *avl_unlink(struct avl_tree *, const void *); +extern void avl_unlink_node(struct avl_tree *tree, struct avl_node *); +extern bool avl_del(struct avl_tree *, void *); +extern void avl_del_node(struct avl_tree *, struct avl_node *); + +/* Fast tree cleanup */ + +extern void avl_tree_del(struct avl_tree *); + +/* Searching */ + +extern void *avl_get(const struct avl_tree *, const void *); +extern void *avl_get_closest(const struct avl_tree *, const void *, int *); +extern void *avl_get_closest_smaller(const struct avl_tree *, const void *); +extern void *avl_get_closest_greater(const struct avl_tree *, const void *); + +extern struct avl_node *avl_get_node(const struct avl_tree *, const void *); +extern struct avl_node *avl_get_closest_node(const struct avl_tree *, const void *, int *); +extern struct avl_node *avl_get_closest_smaller_node(const struct avl_tree *, const void *); +extern struct avl_node *avl_get_closest_greater_node(const struct avl_tree *, const void *); + +/* Tree walking */ + +#define avl_foreach(tree, object, action) {avl_node_t *_node, *_next; \ + for(_node = (tree)->head; _node; _node = _next) { \ + _next = _node->next; \ + (object) = _node->data; \ + action; \ + } \ +} + +#define avl_foreach_node(tree, node, action) {avl_node_t *_next; \ + for((node) = (tree)->head; (node); (node) = _next) { \ + _next = (node)->next; \ + action; \ + } \ +} + +/* Indexing */ + +#ifdef AVL_COUNT +extern avl_count_t avl_count(const struct avl_tree *); +extern avl_count_t avl_index(const struct avl_node *); +extern void *avl_get_indexed(const struct avl_tree *, avl_count_t); +extern struct avl_node *avl_get_indexed_node(const struct avl_tree *, avl_count_t); +#endif +#ifdef AVL_DEPTH +extern avl_depth_t avl_depth(const struct avl_tree *); +#endif + +#endif diff --git a/src/cfg.c b/src/cfg.c new file mode 100644 index 0000000..f0c8dad --- /dev/null +++ b/src/cfg.c @@ -0,0 +1,338 @@ +/* + cfg.c -- cfguration code + + Copyright (C) 1998 Robert van der Meulen + 1998-2004 Ivo Timmermans + 2000-2004 Guus Sliepen + 2000 Cris van Pelt + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "cfg/cfg.h" +#include "support/avl.h" +#include "support/xalloc.h" + +static int cfg_compare(const cfg_t *a, const cfg_t *b) { + return strcasecmp(a->variable, b->variable) ?: (a->line - b->line) ?: strcmp(a->file, b->file); +} + +avl_tree_t *cfg_tree_new(void) { + return avl_tree_new((avl_compare_t)cfg_compare, (avl_action_t)cfg_free); +} + +void cfg_tree_del(avl_tree_t *cfgs) { + avl_tree_del(cfgs); +} + +cfg_t *cfg_new(void) { + cfg_t *cfg; + + return clear(new(cfg)); +} + +void cfg_free(cfg_t *cfg) { + replace(cfg->variable, NULL); + replace(cfg->value, NULL); + replace(cfg->file, NULL); + free(cfg); +} + +void cfg_add(avl_tree_t *cfgs, cfg_t *cfg) { + avl_add(cfgs, cfg); +} + +cfg_t *cfg_get(const avl_tree_t *cfgs, char *variable) { + cfg_t search, *cfg; + + search.variable = variable; + search.file = ""; + search.line = 0; + + cfg = avl_get_closest_greater(cfgs, &search); + + if(!cfg || strcasecmp(cfg->variable, variable)) + return NULL; + + return cfg; +} + +cfg_t *cfg_get_next(const avl_tree_t *cfgs, const cfg_t *cfg) { + avl_node_t *avl; + cfg_t *next; + + avl = avl_get_node(cfgs, cfg); + + if(avl && avl->next) { + next = avl->next->data; + + if(!strcasecmp(next->variable, cfg->variable)) + return next; + } + + return NULL; +} + +bool cfg_bool(const cfg_t *cfg, const bool def, bool *result) { + if(!cfg) { + *result = def; + return true; + } + + if(!strcasecmp(cfg->value, "yes")) { + *result = true; + return true; + } else if(!strcasecmp(cfg->value, "no")) { + *result = false; + return true; + } + + logger(LOG_ERR, _("cfg: \"yes\" or \"no\" expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_int(const cfg_t *cfg, const int def, int *result) { + if(!cfg) { + *result = def; + return true; + } + + if(sscanf(cfg->value, "%d", result) == 1) + return true; + + logger(LOG_ERR, _("cfg: integer expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_string(const cfg_t *cfg, const char *def, char **result) { + if(!cfg) { + *result = def ? xstrdup(def) : NULL; + return true; + } + + *result = xstrdup(cfg->value); + + return true; +} + +bool cfg_choice(const cfg_t *cfg, const cfg_choice_t *choice, const int def, int *result) { + int i; + + if(!cfg) { + *result = def; + return true; + } + + for(i = 0; choice[i].key; i++) { + if(!strcasecmp(cfg->variable, choice[i].key)) { + *result = choice[i].value; + return true; + } + } + + logger(LOG_ERR, _("cfg: invalid choice for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +bool cfg_period(const cfg_t *cfg, const int def, int *result) { + char unit; + + if(!cfg) { + *result = def; + return true; + } + + if(sscanf(cfg->value, "%d%c", result, &unit) == 2) { + switch(unit) { + case 's': + break; + case 'm': + *result *= 60; + break; + case 'h': + *result *= 60 * 60; + break; + case 'd': + *result *= 60 * 60 * 24; + break; + case 'W': + *result *= 60 * 60 * 24 * 7; + break; + case 'M': + *result *= 60 * 60 * 24 * 30; + break; + case 'Y': + *result *= 60 * 60 * 24 * 365; + break; + default: + logger(LOG_ERR, _("cfg: invalid period for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + return false; + } + return true; + } + + if(sscanf(cfg->value, "%d", result) == 1) + return true; + + logger(LOG_ERR, _("cfg: period expected for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + + return false; +} + +static char *readline(FILE *fp, char **buf, size_t *buflen) { + char *newline = NULL; + char *p; + char *line; /* The array that contains everything that has been read so far */ + char *idx; /* Read into this pointer, which points to an offset within line */ + size_t size, newsize; /* The size of the current array pointed to by line */ + size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */ + + if(feof(fp)) + return NULL; + + if(buf && buflen) { + size = *buflen; + line = *buf; + } else { + dim(line, size = 100); + } + + maxlen = size; + idx = line; + *idx = 0; + + for(;;) { + errno = 0; + p = fgets(idx, maxlen, fp); + + if(!p) { + if(feof(fp)) + break; + + logger(LOG_ERR, _("cfg: error while reading: %s"), strerror(errno)); + free(line); + return NULL; + } + + newline = strchr(p, '\n'); + + if(!newline) { + idx = &line[size - 1]; + maxlen = size + 1; + redim(line, size *= 2); + } else { + *newline = '\0'; + break; + } + } + + if(buf && buflen) { + *buflen = size; + *buf = line; + } + + return line; +} + +bool cfg_read_file(avl_tree_t *cfgs, const char *fname) { + FILE *fp; + char *buffer, *line; + char *variable, *value; + int lineno = 0; + int len; + bool result = false; + bool ignore = false; + cfg_t *cfg; + size_t bufsize; + + fp = fopen(fname, "r"); + + if(!fp) { + logger(LOG_ERR, _("cfg: error opening %s: %s"), fname, strerror(errno)); + return false; + } + + dim(buffer, bufsize = 100); + + for(;;) { + line = readline(fp, &buffer, &bufsize); + + if(!line) + break; + + if(feof(fp)) { + result = true; + break; + } + + lineno++; + + if(!*line || *line == '#') + continue; + + if(ignore) { + if(!strncmp(line, "-----END", 8)) + ignore = false; + continue; + } + + if(!strncmp(line, "-----BEGIN", 10)) { + ignore = true; + continue; + } + + variable = value = line; + + len = strcspn(value, "\t ="); + value += len; + value += strspn(value, "\t "); + if(*value == '=') { + value++; + value += strspn(value, "\t "); + } + variable[len] = '\0'; + + if(!*value) { + logger(LOG_ERR, _("cfg: no value for variable %s on line %d while reading cfg file %s"), + variable, lineno, fname); + break; + } + + cfg = cfg_new(); + replace(cfg->variable, variable); + replace(cfg->value, value); + replace(cfg->file, fname); + cfg->line = lineno; + + cfg_add(cfgs, cfg); + } + + free(buffer); + fclose(fp); + + return result; +} diff --git a/src/cfg.h b/src/cfg.h new file mode 100644 index 0000000..bbc0139 --- /dev/null +++ b/src/cfg.h @@ -0,0 +1,64 @@ +/* + conf.h -- header for conf.c + + Copyright (C) 1998-2004 Ivo Timmermans + 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CONF_H__ +#define __TINC_CONF_H__ + +#include "support/avl.h" + +typedef struct cfg { + char *variable; + char *value; + char *file; + int line; +} cfg_t; + +typedef struct cfg_choice { + char *key; + int value; +} cfg_choice_t; + +extern avl_tree_t *cfgs; + +extern avl_tree_t *cfg_tree_new(void); +extern void cfg_tree_free(avl_tree_t *); +extern cfg_t *cfg_new(void) __attribute__ ((__malloc__)); +extern void cfg_free(cfg_t *); +extern void cfg_add(avl_tree_t *, cfg_t *); +extern void cfg_del(avl_tree_t *, cfg_t *); +extern cfg_t *cfg_get(const avl_tree_t *, char *); +extern cfg_t *cfg_get_next(const avl_tree_t *, const cfg_t *); +extern bool cfg_bool(const cfg_t *, const bool, bool *); +extern bool cfg_int(const cfg_t *, const int, int *); +extern bool cfg_string(const cfg_t *, const char *, char **); +extern bool cfg_choice(const cfg_t *, const cfg_choice_t *, const int, int *); +extern bool cfg_period(const cfg_t *, const int, int *); +#define cfg_get_bool(tree, var, def, result) cfg_bool(cfg_get(tree, var), def, result) +#define cfg_get_int(tree, var, def, result) cfg_int(cfg_get(tree, var), def, result) +#define cfg_get_string(tree, var, def, result) cfg_string(cfg_get(tree, var), def, result) +#define cfg_get_choice(tree, var, choice, def, result) cfg_choice(cfg_get(tree, var), choice, def, (int *)result) +#define cfg_get_period(tree, var, def, result) cfg_period(cfg_get(tree, var), def, (int *)result) + +extern bool cfg_read_file(avl_tree_t *, const char *); + +#endif diff --git a/src/edge.c b/src/edge.c new file mode 100644 index 0000000..bf78d1f --- /dev/null +++ b/src/edge.c @@ -0,0 +1,97 @@ +/* + edge.c -- edge management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "rt/edge.h" +#include "rt/node.h" +#include "support/avl.h" +#include "support/xalloc.h" + +avl_tree_t *edges; + +static int edge_compare(const edge_t *a, const edge_t *b) { + return strcmp(a->to->name, b->to->name); +} + +static int edge_weight_compare(const edge_t *a, const edge_t *b) { + return (a->weight - b->weight) ?: strcmp(a->from->name, b->from->name) ?: strcmp(a->to->name, b->to->name); +} + +bool edge_init(void) { + edges = avl_tree_new((avl_compare_t)edge_weight_compare, NULL); + + return true; +} + +bool edge_exit(void) { + avl_tree_free(edges); + + return true; +} + +avl_tree_t *edge_tree_new(void) { + return avl_tree_new((avl_compare_t)edge_compare, (avl_action_t)edge_free); +} + +void edge_tree_free(avl_tree_t *edge_tree) { + avl_tree_free(edge_tree); +} + +edge_t *edge_new(void) { + edge_t *edge; + + return clear(new(edge)); +} + +void edge_free(edge_t *edge) { + free(edge); +} + +void edge_add(edge_t *edge) { + avl_add(edge->from->edges, edge); + avl_add(edges, edge); + + edge->reverse = edge_get(edge->to, edge->from); + + if(edge->reverse) + edge->reverse->reverse = edge; +} + +void edge_del(edge_t *edge) { + if(edge->reverse) + edge->reverse->reverse = NULL; + + avl_del(edges, edge); + avl_del(edge->from->edges, edge); +} + +edge_t *edge_get(node_t *from, node_t *to) { + edge_t search = {0}; + + search.from = from; + search.to = to; + + return avl_get(from->edges, &search); +} + diff --git a/src/edge.h b/src/edge.h new file mode 100644 index 0000000..775d833 --- /dev/null +++ b/src/edge.h @@ -0,0 +1,62 @@ +/* + edge.h -- edge management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __EDGE_H__ +#define __EDGE_H__ + +#include "rt/node.h" +#include "support/avl.h" +#include "tnl/tnl.h" + +typedef struct edge_status { + int visited:1; + int mst:1; +} edge_status_t; + +typedef struct edge { + struct node *from; + struct node *to; + struct sockaddr_storage address; + + int weight; + + struct edge *reverse; + struct tnl *tnl; + + edge_status_t status; + node_options_t options; +} edge_t; + +extern avl_tree_t *edges; + +extern bool edge_init(void); +extern bool edge_exit(void); +extern struct edge *edge_new(void) __attribute__ ((__malloc__)); +extern void edge_free(struct edge *); +extern avl_tree_t *edge_tree_new(void) __attribute__ ((__malloc__)); +extern void edge_tree_free(avl_tree_t *); +extern void edge_add(struct edge *); +extern void edge_del(struct edge *); +extern struct edge *edge_get(struct node *, struct node *); + +#endif diff --git a/src/ethernet.h b/src/ethernet.h new file mode 100644 index 0000000..e7ad508 --- /dev/null +++ b/src/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/src/gettext.h b/src/gettext.h new file mode 100644 index 0000000..a907403 --- /dev/null +++ b/src/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/src/graph.c b/src/graph.c new file mode 100644 index 0000000..63e8faf --- /dev/null +++ b/src/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/src/graph.h b/src/graph.h new file mode 100644 index 0000000..91f58fd --- /dev/null +++ b/src/graph.h @@ -0,0 +1,30 @@ +/* + graph.h -- graph algorithms + + Copyright (C) 2001-2004 Guus Sliepen , + 2001-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __GRAPH_H__ +#define __GRAPH_H__ + +extern bool graph_changed; +extern void graph(void); + +#endif /* __GRAPH_H__ */ diff --git a/src/ipv4.h b/src/ipv4.h new file mode 100644 index 0000000..19a290b --- /dev/null +++ b/src/ipv4.h @@ -0,0 +1,133 @@ +/* + ipv4.h -- missing IPv4 related definitions + + Copyright (C) 2003-2004 Ivo Timmermans + 2003-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __IPV4_H__ +#define __IPV4_H__ + +#ifndef AF_INET +#define AF_INET 2 +#endif + +#ifndef IPPROTO_ICMP +#define IPPROTO_ICMP 1 +#endif + +#ifndef ICMP_DEST_UNREACH +#define ICMP_DEST_UNREACH 3 +#endif + +#ifndef ICMP_NET_UNKNOWN +#define ICMP_NET_UNKNOWN 6 +#endif + +#ifndef ICMP_NET_UNREACH +#define ICMP_NET_UNREACH 0 +#endif + +#ifndef IP_MSS +#define IP_MSS 576 +#endif + +#ifndef HAVE_STRUCT_IP +struct ip { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ip_hl:4; + unsigned int ip_v:4; +#else + unsigned int ip_v:4; + unsigned int ip_hl:4; +#endif + uint8_t ip_tos; + uint16_t ip_len; + uint16_t ip_id; + uint16_t ip_off; +#define IP_RF 0x8000 +#define IP_DF 0x4000 +#define IP_MF 0x2000 +#define IP_OFFMASK 0x1fff + uint8_t ip_ttl; + uint8_t ip_p; + uint16_t ip_sum; + struct in_addr ip_src, ip_dst; +} __attribute__ ((__packed__)); +#endif + +#ifndef HAVE_STRUCT_ICMP +struct icmp { + uint8_t icmp_type; + uint8_t icmp_code; + uint16_t icmp_cksum; + union { + uint8_t ih_pptr; + struct in_addr ih_gwaddr; + struct ih_idseq { + uint16_t icd_id; + uint16_t icd_seq; + } ih_idseq; + uint32_t ih_void; + + + struct ih_pmtu { + uint16_t ipm_void; + uint16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv { + uint8_t irt_num_addrs; + uint8_t irt_wpa; + uint16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct { + uint32_t its_otime; + uint32_t its_rtime; + uint32_t its_ttime; + } id_ts; + struct { + struct ip idi_ip; + } id_ip; + uint32_t id_mask; + uint8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +} __attribute__ ((__packed__)); +#endif + +#endif diff --git a/src/ipv6.h b/src/ipv6.h new file mode 100644 index 0000000..5de7953 --- /dev/null +++ b/src/ipv6.h @@ -0,0 +1,128 @@ +/* + ipv6.h -- missing IPv6 related definitions + + Copyright (C) 2003-2004 Ivo Timmermans + 2003-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __IPV6_H__ +#define __IPV6_H__ + +#ifndef AF_INET6 +#define AF_INET6 10 +#endif + +#ifndef IPPROTO_ICMPV6 +#define IPPROTO_ICMPV6 58 +#endif + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + union { + uint8_t u6_addr8[16]; + uint16_t u6_addr16[8]; + uint32_t u6_addr32[4]; + } in6_u; +} __attribute__ ((__packed__)); +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#endif + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; +} __attribute__ ((__packed__)); +#endif + +#ifndef IN6_IS_ADDR_V4MAPPED +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((((__const uint32_t *) (a))[0] == 0) \ + && (((__const uint32_t *) (a))[1] == 0) \ + && (((__const uint32_t *) (a))[2] == htonl (0xffff))) +#endif + +#ifndef HAVE_STRUCT_IP6_HDR +struct ip6_hdr { + union { + struct ip6_hdrctl { + uint32_t ip6_un1_flow; + uint16_t ip6_un1_plen; + uint8_t ip6_un1_nxt; + uint8_t ip6_un1_hlim; + } ip6_un1; + uint8_t ip6_un2_vfc; + } ip6_ctlun; + struct in6_addr ip6_src; + struct in6_addr ip6_dst; +} __attribute__ ((__packed__)); +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim +#endif + +#ifndef HAVE_STRUCT_ICMP6_HDR +struct icmp6_hdr { + uint8_t icmp6_type; + uint8_t icmp6_code; + uint16_t icmp6_cksum; + union { + uint32_t icmp6_un_data32[1]; + uint16_t icmp6_un_data16[2]; + uint8_t icmp6_un_data8[4]; + } icmp6_dataun; +} __attribute__ ((__packed__)); +#define ICMP6_DST_UNREACH_NOROUTE 0 +#define ICMP6_DST_UNREACH 1 +#define ICMP6_DST_UNREACH_ADDR 3 +#define ND_NEIGHBOR_SOLICIT 135 +#define ND_NEIGHBOR_ADVERT 136 +#define icmp6_data32 icmp6_dataun.icmp6_un_data32 +#define icmp6_data16 icmp6_dataun.icmp6_un_data16 +#define icmp6_data8 icmp6_dataun.icmp6_un_data8 +#endif + +#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT +struct nd_neighbor_solicit { + struct icmp6_hdr nd_ns_hdr; + struct in6_addr nd_ns_target; +} __attribute__ ((__packed__)); +#define ND_OPT_SOURCE_LINKADDR 1 +#define ND_OPT_TARGET_LINKADDR 2 +#define nd_ns_type nd_ns_hdr.icmp6_type +#define nd_ns_code nd_ns_hdr.icmp6_code +#define nd_ns_cksum nd_ns_hdr.icmp6_cksum +#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] +#endif + +#ifndef HAVE_STRUCT_ND_OPT_HDR +struct nd_opt_hdr { + uint8_t nd_opt_type; + uint8_t nd_opt_len; +} __attribute__ ((__packed__)); +#endif + +#endif diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..9e54ac0 --- /dev/null +++ b/src/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/src/list.h b/src/list.h new file mode 100644 index 0000000..124d7fc --- /dev/null +++ b/src/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/src/log.h b/src/log.h new file mode 100644 index 0000000..e178d69 --- /dev/null +++ b/src/log.h @@ -0,0 +1,77 @@ +/* + log.h -- logging + + Copyright (C) 2003-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +typedef enum debug_t { + DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */ + DEBUG_ALWAYS = 0, + DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */ + DEBUG_ERROR = 2, /* Show error messages received from other hosts */ + DEBUG_STATUS = 2, /* Show status messages received from other hosts */ + DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */ + DEBUG_META = 4, /* Show contents of every request that is sent/received */ + DEBUG_TRAFFIC = 5, /* Show network traffic information */ + DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */ + DEBUG_SCARY_THINGS = 10 /* You have been warned */ +} debug_t; + +typedef enum logmode_t { + LOGMODE_NULL, + LOGMODE_STDERR, + LOGMODE_FILE, + LOGMODE_SYSLOG +} logmode_t; + +#ifdef HAVE_MINGW +#define LOG_EMERG EVENTLOG_ERROR_TYPE +#define LOG_ALERT EVENTLOG_ERROR_TYPE +#define LOG_CRIT EVENTLOG_ERROR_TYPE +#define LOG_ERR EVENTLOG_ERROR_TYPE +#define LOG_WARNING EVENTLOG_WARNING_TYPE +#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE +#define LOG_INFO EVENTLOG_INFORMATION_TYPE +#define LOG_DEBUG EVENTLOG_INFORMATION_TYPE +#else +#ifndef HAVE_SYSLOG_H +enum { + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG, +}; +#endif +#endif + +extern debug_t debug_level; +extern void openlogger(const char *, logmode_t); +extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); +extern void closelogger(void); + +#define ifdebug(l) if(debug_level >= DEBUG_##l) + +#endif /* __LOGGER_H__ */ diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..9bbd8fe --- /dev/null +++ b/src/logger.c @@ -0,0 +1,141 @@ +/* + logger.c -- logging + + Copyright (C) 2003-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "logger/logger.h" + +logger_level_t logger_level = LOGGER_LEVEL_NONE; + +static logger_mode_t logger_mode = LOGGER_MODE_STDERR; +static pid_t logger_pid; +char *logger_filename; +static FILE *logger_file = NULL; +#ifdef HAVE_MINGW +static HANDLE logger_handle = NULL; +#endif +static const char *logger_ident = NULL; + +bool logger_init(const char *ident, logger_mode_t mode) { + logger_ident = ident; + logger_mode = mode; + + switch(mode) { + case LOGGER_MODE_STDERR: + logger_pid = getpid(); + break; + case LOGGER_MODE_FILE: + logger_pid = getpid(); + logger_file = fopen(logger_filename, "a"); + if(!logger_file) + logger_mode = LOGGER_MODE_NULL; + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + logger_handle = RegisterEventSource(NULL, logger_ident); + if(!logger_handle) + logger_mode = LOGGER_MODE_NULL; + break; +#else +#ifdef HAVE_SYSLOG_H + openlog(logger_ident, LOG_CONS | LOG_PID, LOG_DAEMON); + break; +#endif +#endif + case LOGGER_MODE_NULL: + break; + } + + return true; +} + +bool logger_exit(void) { + switch(logger_mode) { + case LOGGER_MODE_FILE: + fclose(logger_file); + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + DeregisterEventSource(logger_handle); + break; +#else +#ifdef HAVE_SYSLOG_H + closelog(); + break; +#endif +#endif + case LOGGER_MODE_NULL: + case LOGGER_MODE_STDERR: + break; + break; + } + + return true; +} + +void logger(int priority, const char *format, ...) { + va_list ap; + + va_start(ap, format); + + switch(logger_mode) { + case LOGGER_MODE_STDERR: + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + fflush(stderr); + break; + case LOGGER_MODE_FILE: + fprintf(logger_file, "%ld %s[%ld]: ", time(NULL), logger_ident, (long)logger_pid); + vfprintf(logger_file, format, ap); + fprintf(logger_file, "\n"); + fflush(logger_file); + break; + case LOGGER_MODE_SYSLOG: +#ifdef HAVE_MINGW + { + char message[4096]; + char *messages[] = {message}; + vsnprintf(message, sizeof(message), format, ap); + ReportEvent(logger_handle, priority, 0, 0, NULL, 1, 0, messages, NULL); + } +#else +#ifdef HAVE_SYSLOG_H +#ifdef HAVE_VSYSLOG + vsyslog(priority, format, ap); +#else + { + char message[4096]; + vsnprintf(message, sizeof(message), format, ap); + syslog(priority, "%s", message); + } +#endif + break; +#endif +#endif + case LOGGER_MODE_NULL: + break; + } + + va_end(ap); +} + + diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..a65968e --- /dev/null +++ b/src/logger.h @@ -0,0 +1,47 @@ +/* + logger.h -- logging + + Copyright (C) 2003-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +typedef enum logger_level { + LOGGER_LEVEL_NONE, + LOGGER_LEVEL_NOTICE, + LOGGER_LEVEL_WARNING, + LOGGER_LEVEL_ERROR, + LOGGER_LEVEL_DEBUG, +} logger_level_t; + +typedef enum logger_mode { + LOGGER_MODE_NULL, + LOGGER_MODE_STDERR, + LOGGER_MODE_FILE, + LOGGER_MODE_SYSLOG, +} logger_mode_t; + +extern bool logger_init(const char *, logger_mode_t); +extern bool logger_exit(void); +extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3))); + +extern enum logger_level logger_level; + +#endif diff --git a/src/node.c b/src/node.c new file mode 100644 index 0000000..4eee529 --- /dev/null +++ b/src/node.c @@ -0,0 +1,134 @@ +/* + node.c -- node management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "cfg/cfg.h" +#include "logger/logger.h" +#include "rt/node.h" +#include "support/avl.h" +#include "support/xalloc.h" +#include "tincd.h" + +avl_tree_t *nodes; + +node_t *myself; + +static int node_compare(const node_t *a, const node_t *b) { + return strcmp(a->name, b->name); +} + +bool node_validname(const char *name) { + for(; *name; name++) + if(!isalnum(*name) && *name != '_') + return false; + + return true; +} + +bool node_init(void) { + char *cfgfilename; + + nodes = avl_tree_new((avl_compare_t)node_compare, (avl_action_t)node_free); + myself = node_new(); + + if(!cfg_get_string(tinc_cfg, "Name", NULL, &myself->name) || !myself->name) { + logger(LOG_ERR, _("rt: name for tinc daemon required!")); + node_exit(); + return false; + } + + if(!node_validname(myself->name)) { + logger(LOG_ERR, _("rt: invalid name for myself!")); + node_exit(); + return false; + } + + myself->cfg = cfg_tree_new(); + + asprintf(&cfgfilename, "%s/hosts/%s", tinc_confbase, myself->name); + + if(!cfg_read_file(myself->cfg, cfgfilename)) { + free(cfgfilename); + node_exit(); + return false; + } + + free(cfgfilename); + + return true; +} + +bool node_exit(void) { + avl_tree_del(nodes); + return true; +} + +node_t *node_new(void) { + node_t *node; + + clear(new(node)); + node->subnets = subnet_tree_new(); + node->edges = edge_tree_new(); + node->queue = avl_tree_new(NULL, (avl_action_t)free); + + return node; +} + +void node_free(node_t *node) { + if(node->queue) + avl_tree_free(node->queue); + + if(node->subnets) + subnet_tree_free(node->subnets); + + if(node->edges) + edge_tree_free(node->edges); + + replace(node->name, NULL); + + free(node); +} + +void node_add(node_t *node) { + avl_add(nodes, node); +} + +void node_del(node_t *node) { + edge_t *edge; + subnet_t *subnet; + + avl_foreach(node->subnets, subnet, subnet_del(subnet)); + avl_foreach(node->edges, edge, edge_del(edge)); + + avl_del(nodes, node); +} + +node_t *node_get(char *name) { + node_t search = {0}; + + search.name = name; + + return avl_get(nodes, &search); +} + diff --git a/src/node.h b/src/node.h new file mode 100644 index 0000000..acf20bc --- /dev/null +++ b/src/node.h @@ -0,0 +1,75 @@ +/* + node.h -- node management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __NODE_H__ +#define __NODE_H__ + +typedef int node_options_t; + +#define NODE_OPTION_INDIRECT 1 + +#include "rt/edge.h" +#include "rt/subnet.h" +#include "support/avl.h" +#include "tnl/tnl.h" + +typedef struct node_status { + int active:1; + int visited:1; + int reachable:1; + int indirect:1; +} node_status_t; + +typedef struct node { + char *name; + + avl_tree_t *queue; + + struct node *nexthop; + struct node *via; + + avl_tree_t *subnets; + avl_tree_t *edges; + + struct tnl *tnl; + + node_status_t status; + node_options_t options; + + struct sockaddr_storage address; + + avl_tree_t *cfg; +} node_t; + +extern avl_tree_t *nodes; +extern struct node *myself; + +extern bool node_init(void); +extern bool node_exit(void); +extern struct node *node_new(void) __attribute__ ((__malloc__)); +extern void node_free(struct node *); +extern void node_add(struct node *); +extern void node_del(struct node *); +extern struct node *node_get(char *); + +#endif diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..f04f7c0 --- /dev/null +++ b/src/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/src/route.c b/src/route.c new file mode 100644 index 0000000..4721763 --- /dev/null +++ b/src/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/src/route.h b/src/route.h new file mode 100644 index 0000000..42c587f --- /dev/null +++ b/src/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/src/rt.c b/src/rt.c new file mode 100644 index 0000000..43e33c6 --- /dev/null +++ b/src/rt.c @@ -0,0 +1,210 @@ +/* + rt.c -- routing + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "cfg/cfg.h" +#include "rt/edge.h" +#include "rt/node.h" +#include "rt/rt.h" +#include "rt/subnet.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" +#include "vnd/vnd.h" +#include "tincd.h" + +vnd_t *rt_vnd = NULL; +int rt_af = AF_UNSPEC; +int rt_macexpire = 600; +int rt_maxtimeout = 900; +rt_mode_t rt_mode = RT_MODE_ROUTER; +bool rt_priorityinheritance = false; +bool rt_hostnames = false; +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 void *buf, int len) { + route(myself, 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 void *buf, int len) { +} + +static void rt_outgoing(char *name) { + tnl_t *tnl; + + clear(new(tnl)); + +} + +bool rt_init(void) { + char *bindtoaddress = NULL; + char *bindtointerface = NULL; + char *device = NULL; + char *iface = NULL; + char *port = NULL; + cfg_t *cfg; + subnet_t *subnet; + struct addrinfo hint, *ai, *aip; + int err; + int listeners; + char *connectto = NULL; + + cfg_choice_t mode_choice[] = { + {"Router", RT_MODE_ROUTER}, + {"Switch", RT_MODE_SWITCH}, + {"Hub", RT_MODE_HUB}, + }; + + cfg_choice_t af_choice[] = { + {"IPv4", AF_INET}, + {"IPv6", AF_INET6}, + {"Any", AF_UNSPEC}, + }; + + logger(LOG_INFO, _("rt: initialising")); + + if(!subnet_init() || !node_init() || !edge_init()) + return false; + + 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) + || !cfg_get_string(tinc_cfg, "BindToAddress", NULL, &bindtoaddress) + || !cfg_get_string(tinc_cfg, "BindToInterface", NULL, &bindtointerface) + || !cfg_get_string(tinc_cfg, "Device", "/dev/net/tun", &device) + || !cfg_get_bool(tinc_cfg, "Hostnames", false, &rt_hostnames) + || !cfg_get_string(tinc_cfg, "Interface", tinc_netname, &iface) + || !cfg_get_period(tinc_cfg, "MACExpire", 600, &rt_macexpire) + || !cfg_get_period(tinc_cfg, "MaxTimeout", 3600, &rt_maxtimeout) + || !cfg_get_choice(tinc_cfg, "Mode", mode_choice, RT_MODE_ROUTER, &rt_mode) + || !cfg_get_bool(tinc_cfg, "PriorityInheritance", false, &rt_priorityinheritance)) + return false; + + /* Read host configuration for myself */ + + if(!cfg_get_string(myself->cfg, "Port", "655", &port)) + return false; + + for(cfg = cfg_get(myself->cfg, "Subnet"); cfg; cfg = cfg_get_next(myself->cfg, cfg)) { + if(!cfg_subnet(cfg, &subnet)) + return false; + + subnet->owner = myself; + subnet_add(subnet); + } + + /* Open the virtual network device */ + + clear(new(rt_vnd)); + + replace(rt_vnd->device, device); + replace(rt_vnd->interface, iface); + + rt_vnd->mode = (rt_mode == RT_MODE_ROUTER) ? VND_MODE_TUN : VND_MODE_TAP; + rt_vnd->recv = rt_vnd_recv; + + if(!vnd_open(rt_vnd)) { + vnd_free(rt_vnd); + return false; + } + + /* Create listening sockets */ + + hint.ai_family = rt_af; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + hint.ai_flags = AI_PASSIVE; + + err = getaddrinfo(bindtoaddress, port, &hint, &ai); + + if(err || !ai) { + logger(LOG_ERR, _("rt: system call '%s' failed: %s"), "getaddrinfo", gai_strerror(err)); + return false; + } + + listeners = 0; + + for(aip = ai; aip; aip = aip->ai_next) { + tnl_listen_t *listener; + + clear(new(listener)); + listener->local.address = *(struct sockaddr_storage *)aip->ai_addr; + listener->local.id = myself->name; + listener->type = SOCK_STREAM; + listener->protocol = IPPROTO_TCP; + // listener->local.cred = ...; + listener->accept = rt_tnl_accept; + + if(tnl_listen(listener)) + listeners++; + } + + freeaddrinfo(ai); + + if(!listeners) { + logger(LOG_ERR, _("rt: unable to create any listening socket!")); + return false; + } + + /* Setup outgoing connections */ + + for(cfg = cfg_get(tinc_cfg, "ConnectTo"); cfg; cfg = cfg_get_next(tinc_cfg, cfg)) { + if(!cfg_string(cfg, NULL, &connectto)) + return false; + + if(!node_validname(connectto)) { + logger(LOG_ERR, _("rt: invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line); + free(connectto); + continue; + } + + rt_outgoing(connectto); + } + + return true; +} + +bool rt_exit(void) { + edge_exit(); + node_exit(); + subnet_exit(); + + logger(LOG_INFO, _("rt: exitting")); +} + + diff --git a/src/rt.h b/src/rt.h new file mode 100644 index 0000000..dd8126d --- /dev/null +++ b/src/rt.h @@ -0,0 +1,54 @@ +/* + route.h -- routing + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __RT_H__ +#define __RT_H__ + +#include "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, + RT_MODE_HUB, +} rt_mode_t; + +extern int rt_af; +extern enum rt_mode rt_mode; +extern bool rt_hostnames; +extern bool rt_priorityinheritance; +extern int rt_macexpire; +extern int rt_maxtimeout; +extern bool rt_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); + +#endif diff --git a/src/sockaddr.h b/src/sockaddr.h new file mode 100644 index 0000000..8cd1c5e --- /dev/null +++ b/src/sockaddr.h @@ -0,0 +1,51 @@ +/* + sockaddr.h -- sockaddr handling + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __SOCKADDR_H__ +#define __SOCKADDR_H__ + +#define AF_UNKNOWN 255 + +struct sockaddr_unknown { + uint16_t family; + uint16_t pad1; + uint32_t pad2; + char *address; + char *port; +}; + +#define sa(s) ((struct sockaddr *)(s)) +#ifdef SA_LEN +#define sa_len(s) SA_LEN((struct sockaddr *)(s)) +#else +#define sa_len(s) (((struct sockaddr *)(s))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +#endif + +#define sa_family(s) (((struct sockaddr *)(s))->sa_family) + +#define sa_unmap(s) ({if(((struct sockaddr *)(s))->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)(s))->sin6_addr)) { \ + ((struct sockaddr_in *)(s))->sin_addr.s_addr = ((struct sockaddr_in6 *)(s))->sin6_addr.s6_addr32[3]; \ + ((struct sockaddr *)(s))->sa_family = AF_INET; \ +} \ +s;}) + +#endif diff --git a/src/subnet.c b/src/subnet.c new file mode 100644 index 0000000..ced1695 --- /dev/null +++ b/src/subnet.c @@ -0,0 +1,441 @@ +/* + subnet.c -- subnet handling + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include "cfg/cfg.h" +#include "logger/logger.h" +#include "rt/node.h" +#include "rt/subnet.h" +#include "support/avl.h" +#include "support/xalloc.h" + +avl_tree_t *subnets; +/* Subnet mask handling */ + +static int maskcmp(const void *va, const void *vb, int masklen, int len) { + int i, m, result; + const char *a = va; + const char *b = vb; + + for(m = masklen, i = 0; m >= 8; m -= 8, i++) { + result = a[i] - b[i]; + if(result) + return result; + } + + return m ? (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m)))) : 0; +} + +static void mask(void *va, int masklen, int len) { + int i; + char *a = va; + + i = masklen / 8; + masklen %= 8; + + if(masklen) + a[i++] &= (0x100 - (1 << masklen)); + + for(; i < len; i++) + a[i] = 0; +} + +static void maskcpy(void *va, const void *vb, int masklen, int len) { + int i, m; + char *a = va; + const char *b = vb; + + for(m = masklen, i = 0; m >= 8; m -= 8, i++) + a[i] = b[i]; + + if(m) { + a[i] = b[i] & (0x100 - (1 << m)); + i++; + } + + for(; i < len; i++) + a[i] = 0; +} + +static bool maskcheck(const void *va, int masklen, int len) { + int i; + const char *a = va; + + i = masklen / 8; + masklen %= 8; + + if(masklen && a[i++] & (0xff >> masklen)) + return false; + + for(; i < len; i++) + if(a[i] != 0) + return false; + + return true; +} + +/* Cache handling */ + +struct { + subnet_t key; + subnet_t *subnet; +} *cache; + +int cache_bits; +int cache_size; +uint32_t cache_mask; + +static void cache_flush(void) { + memset(cache, 0, sizeof *cache * cache_size); +} + +static void cache_init(void) { + cache_bits = 8; + cache_size = 1 << 8; + cache_mask = cache_size - 1; + + dim(cache, cache_size); + + cache_flush(); +} + +static void cache_exit(void) { + free(cache); +} + +static uint32_t subnet_hash(const subnet_t *subnet) { + uint32_t hash; + int i; + + hash = subnet->type; + + for(i = 0; i < sizeof subnet->net / sizeof(uint32_t); i++) + hash ^= ((uint32_t *)&subnet->net)[i]; + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash & cache_mask; +} + +static subnet_t *cache_get(subnet_t *subnet) { + uint32_t hash = subnet_hash(subnet); + + if(cache[hash].subnet && memcmp(&cache[hash].key, subnet, sizeof *subnet)) + return cache[hash].subnet; + else + return NULL; +} + +static void cache_add(subnet_t *key, subnet_t *subnet) { + uint32_t hash = subnet_hash(subnet); + + cache[hash].key = *key; + cache[hash].subnet = subnet; +} + +/* Subnet tree handling */ + +static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t)) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t)) + ?: (a->net.ipv4.prefixlength - b->net.ipv4.prefixlength) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) { + return memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t)) + ?: (a->net.ipv6.prefixlength - b->net.ipv6.prefixlength) + ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0; +} + +static int subnet_compare(const subnet_t *a, const subnet_t *b) { + int result; + + result = a->type - b->type; + + if(result) + return result; + + switch (a->type) { + case SUBNET_TYPE_MAC: + return subnet_compare_mac(a, b); + case SUBNET_TYPE_IPV4: + return subnet_compare_ipv4(a, b); + case SUBNET_TYPE_IPV6: + return subnet_compare_ipv6(a, b); + default: + logger(LOG_ERR, _("rt: subnet_compare() was called with unknown subnet type %d, exitting!"), a->type); + exit(1); + } +} + +avl_tree_t *subnet_tree_new(void) { + return avl_tree_new((avl_compare_t)subnet_compare, NULL); +} + +void subnet_tree_free(avl_tree_t *subnets) { + avl_tree_free(subnets); +} + +subnet_t *subnet_new(void) { + subnet_t *subnet; + + return clear(new(subnet)); +} + +void subnet_free(subnet_t *subnet) { + free(subnet); +} + +void subnet_add(subnet_t *subnet) { + avl_add(subnets, subnet); + avl_add(subnet->owner->subnets, subnet); + cache_flush(); +} + +void subnet_del(subnet_t *subnet) { + avl_del(subnet->owner->subnets, subnet); + avl_del(subnets, subnet); + cache_flush(); +} + +bool subnet_init(void) { + cache_init(); + subnets = avl_tree_new((avl_compare_t)subnet_compare, (avl_action_t)subnet_free); + + return true; +} + +bool subnet_exit(void) { + avl_tree_del(subnets); + cache_exit(); + + return true; +} + +subnet_t *str2net(const char *subnetstr) { + int i, l; + subnet_t subnet = {0}; + uint16_t x[8]; + + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d", + &x[0], &x[1], &x[2], &x[3], &l) == 5) { + subnet.type = SUBNET_TYPE_IPV4; + subnet.net.ipv4.prefixlength = l; + + for(i = 0; i < 4; i++) + subnet.net.ipv4.address.x[i] = x[i]; + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], + &l) == 9) { + subnet.type = SUBNET_TYPE_IPV6; + subnet.net.ipv6.prefixlength = l; + + for(i = 0; i < 8; i++) + subnet.net.ipv6.address.x[i] = htons(x[i]); + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) { + subnet.type = SUBNET_TYPE_IPV4; + subnet.net.ipv4.prefixlength = 32; + + for(i = 0; i < 4; i++) + subnet.net.ipv4.address.x[i] = x[i]; + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) { + subnet.type = SUBNET_TYPE_IPV6; + subnet.net.ipv6.prefixlength = 128; + + for(i = 0; i < 8; i++) + subnet.net.ipv6.address.x[i] = htons(x[i]); + + return copy(&subnet); + } + + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) { + subnet.type = SUBNET_TYPE_MAC; + + for(i = 0; i < 6; i++) + subnet.net.mac.address.x[i] = x[i]; + + return copy(&subnet); + } + + return NULL; +} + +char *net2str(const subnet_t *subnet) { + char *netstr; + + switch (subnet->type) { + case SUBNET_TYPE_MAC: + asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx", + subnet->net.mac.address.x[0], + subnet->net.mac.address.x[1], + subnet->net.mac.address.x[2], + subnet->net.mac.address.x[3], + subnet->net.mac.address.x[4], + subnet->net.mac.address.x[5]); + break; + + case SUBNET_TYPE_IPV4: + asprintf(&netstr, "%hu.%hu.%hu.%hu/%d", + subnet->net.ipv4.address.x[0], + subnet->net.ipv4.address.x[1], + subnet->net.ipv4.address.x[2], + subnet->net.ipv4.address.x[3], + subnet->net.ipv4.prefixlength); + break; + + case SUBNET_TYPE_IPV6: + asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + ntohs(subnet->net.ipv6.address.x[0]), + ntohs(subnet->net.ipv6.address.x[1]), + ntohs(subnet->net.ipv6.address.x[2]), + ntohs(subnet->net.ipv6.address.x[3]), + ntohs(subnet->net.ipv6.address.x[4]), + ntohs(subnet->net.ipv6.address.x[5]), + ntohs(subnet->net.ipv6.address.x[6]), + ntohs(subnet->net.ipv6.address.x[7]), + subnet->net.ipv6.prefixlength); + break; + + default: + logger(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type); + exit(0); + } + + return netstr; +} + +bool cfg_subnet(cfg_t *cfg, subnet_t **result) { + subnet_t *subnet; + + subnet = str2net(cfg->value); + + if(!subnet) { + logger(LOG_ERR, _("rt: invalid subnet for configuration variable %s in %s line %d"), + cfg->variable, cfg->file, cfg->line); + return false; + } + + *result = subnet; + + return true; +} + +subnet_t *subnet_get(const subnet_t *subnet) { + return subnet->owner ? avl_get(subnet->owner->subnets, subnet) : avl_get(subnets, subnet); +} + +subnet_t *subnet_get_mac(const mac_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_MAC; + search.net.mac.address = *address; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + subnet = avl_get(subnets, &search); + + if(subnet) + cache_add(&search, subnet); + + return subnet; +} + +subnet_t *subnet_get_ipv4(const ipv4_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_IPV4; + search.net.ipv4.address = *address; + search.net.ipv4.prefixlength = 32; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + while(subnet = avl_get_closest_smaller(subnets, &search)) { + if(subnet->type != SUBNET_TYPE_IPV4) + return NULL; + + if(!maskcmp(address, &subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t))) { + cache_add(&search, subnet); + return subnet; + } + + search.net.ipv4.prefixlength = subnet->net.ipv4.prefixlength - 1; + maskcpy(&search.net.ipv4.address, &subnet->net.ipv4.address, search.net.ipv4.prefixlength, sizeof(ipv4_t)); + } + + return NULL; +} + +subnet_t *subnet_get_ipv6(const ipv6_t *address) { + subnet_t *subnet, search = {0}; + + search.type = SUBNET_TYPE_IPV6; + search.net.ipv6.address = *address; + search.net.ipv6.prefixlength = 128; + + subnet = cache_get(&search); + + if(subnet) + return subnet; + + while(subnet = avl_get_closest_smaller(subnets, &search)) { + if(subnet->type != SUBNET_TYPE_IPV6) + return NULL; + + if(!maskcmp(address, &subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))) { + cache_add(&search, subnet); + return subnet; + } + + search.net.ipv6.prefixlength = subnet->net.ipv6.prefixlength - 1; + maskcpy(&search.net.ipv6.address, &subnet->net.ipv6.address, search.net.ipv6.prefixlength, sizeof(ipv6_t)); + } + + return NULL; +} diff --git a/src/subnet.h b/src/subnet.h new file mode 100644 index 0000000..5134ae2 --- /dev/null +++ b/src/subnet.h @@ -0,0 +1,90 @@ +/* + subnet.h -- subnet handling + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __SUBNET_H__ +#define __SUBNET_H__ + +#include "rt/node.h" +#include "support/avl.h" + +typedef struct mac { + uint8_t x[6]; +} mac_t; + +typedef struct ipv4 { + uint8_t x[4]; +} ipv4_t; + +typedef struct ipv6 { + uint16_t x[8]; +} ipv6_t; + +typedef enum subnet_type { + SUBNET_TYPE_MAC, + SUBNET_TYPE_IPV4, + SUBNET_TYPE_IPV6, +} subnet_type_t; + +typedef struct subnet_mac { + mac_t address; +} subnet_mac_t; + +typedef struct subnet_ipv4 { + ipv4_t address; + int prefixlength; +} subnet_ipv4_t; + +typedef struct subnet_ipv6 { + ipv6_t address; + int prefixlength; +} subnet_ipv6_t; + +typedef struct subnet { + struct node *owner; + struct timeval expires; + + enum subnet_type type; + + union net { + struct subnet_mac mac; + struct subnet_ipv4 ipv4; + struct subnet_ipv6 ipv6; + } net; +} subnet_t; + +extern subnet_t *subnet_new(void) __attribute__ ((__malloc__)); +extern void subnet_free(struct subnet *); +extern bool subnet_init(void); +extern bool subnet_exit(void); +extern avl_tree_t *subnet_tree_new(void) __attribute__ ((__malloc__)); +extern void subnet_tree_free(avl_tree_t *); +extern void subnet_add(struct subnet *); +extern void subnet_del(struct subnet *); +extern char *net2str(const struct subnet *); +extern struct subnet *str2net(const char *); +extern struct subnet *subnet_get(const struct subnet *); +extern struct subnet *subnet_get_mac(const struct mac *); +extern struct subnet *subnet_get_ipv4(const struct ipv4 *); +extern struct subnet *subnet_get_ipv6(const struct ipv6 *); + +#endif diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..1e2f4aa --- /dev/null +++ b/src/system.h @@ -0,0 +1,159 @@ +/* + system.h -- system headers + + Copyright (C) 1998-2004 Ivo Timmermans + 2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id$ +*/ + +#ifndef __TINC_SYSTEM_H__ +#define __TINC_SYSTEM_H__ + +#include "config.h" + +/* Include standard headers */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STDBOOL_H +#include +#else +typedef int bool; +#define true 1 +#define false 0 +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* Include system specific headers */ + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_FILE_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_NETINET_IN_SYSTM_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_NETINET_IP_H +#include +#endif + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef HAVE_NETINET_IN6_H +#include +#endif + +#ifdef HAVE_NETINET_IP6_H +#include +#endif + +#ifdef HAVE_MINGW +#include +#include +#endif + +/* Include localisation support */ + +#include "support/gettext.h" + +#if 0 + +#ifndef HAVE_STRSIGNAL +# define strsignal(p) "" +#endif + +/* Other functions */ + +#include "dropin.h" + +#endif + +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +#endif /* __TINC_SYSTEM_H__ */ diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..65810c2 --- /dev/null +++ b/src/test.c @@ -0,0 +1,198 @@ +/* + test.c -- tunnel test + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: tnl.c 1379 2004-03-27 11:59:31Z guus $ +*/ + +#include "system.h" + +#include +#include + +#include "logger/logger.h" +#include "support/avl.h" +#include "support/sockaddr.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" + +static const int addressfamily = AF_UNSPEC; +static const int socktype = SOCK_STREAM; +static const int protocol = IPPROTO_TCP; + +bool server_recv_meta(struct tnl *tnl, const void *buf, int len) { + const char *in = buf; + char out[len]; + + for(int i = 0; i < len; i++) { + if(isupper(in[i])) + out[i] = tolower(in[i]); + else if(islower(in[i])) + out[i] = toupper(in[i]); + else + out[i] = in[i]; + } + + tnl->send_meta(tnl, out, len); + + return true; +} + +bool server_accept(struct tnl *tnl) { + logger(LOG_INFO, _("Got connection from %s"), tnl->remote.id); + tnl->recv_meta = server_recv_meta; + return true; +} + +void server(char *port) { + struct addrinfo *ai, hint = {0}; + int err; + tnl_listen_t *listen = clear(new(listen)); + + hint.ai_family = addressfamily; + hint.ai_socktype = socktype; + hint.ai_protocol = protocol; + hint.ai_flags = AI_PASSIVE; + + err = getaddrinfo(NULL, port, &hint, &ai); + + if(err || !ai) { + logger(LOG_WARNING, _("Error looking up port %s: %s"), port, gai_strerror(err)); + return; + } + + if(sizeof listen->local.address < ai->ai_addrlen) { + logger(LOG_ERR, "%d < %d!", sizeof listen->local.address, ai->ai_addrlen); + return; + } + + memcpy(&listen->local.address, ai->ai_addr, ai->ai_addrlen); + listen->local.id = xstrdup("CommonA"); + listen->type = socktype; + listen->protocol = protocol; + listen->accept = server_accept; + + logger(LOG_DEBUG, "Nu ga ik iets doen hoor"); + if(!tnl_ep_set_x509_credentials(&listen->local, "server_key", "server_cert", "trust", NULL)) { + logger(LOG_ERR, "Couldn't set X.509 credentials!"); + return; + } + + if(!tnl_listen(listen)) { + logger(LOG_ERR, _("Could not listen!")); + return; + } +} + +bool client_stdin_handler(fd_t *fd) { + tnl_t *tnl = fd->data; + char buf[1024]; + int len; + + len = read(fd->fd, buf, sizeof buf); + + if(len <= 0) { + gnutls_bye(tnl->session, GNUTLS_SHUT_WR); + fd_del(fd); + return false; + } + + tnl->send_meta(tnl, buf, len); + + return true; +} + +bool client_recv_meta(struct tnl *tnl, const void *buf, int len) { + write(1, buf, len); + return true; +} + +bool client_error(tnl_t *tnl, int err) { + exit(err); +} + +bool client_accept(tnl_t *tnl) { + fd_t *fd; + + logger(LOG_INFO, _("Connected to %s"), tnl->remote.id); + tnl->recv_meta = client_recv_meta; + + clear(new(fd)); + fd->fd = 0; + fd->read = client_stdin_handler; + fd->data = tnl; + fd_add(fd); + + return true; +} + +void client(char *host, char *port) { + struct addrinfo *ai, hint = {0}; + int err; + static tnl_t *tnl; + + hint.ai_family = addressfamily; + hint.ai_socktype = socktype; + + err = getaddrinfo(host, port, &hint, &ai); + + if(err || !ai) { + logger(LOG_WARNING, _("Error looking up %s port %s: %s"), host, port, gai_strerror(err)); + return; + } + + clear(new(tnl)); + memcpy(&tnl->remote.address, ai->ai_addr, ai->ai_addrlen); + tnl->local.id = xstrdup("CommonB"); + tnl->remote.id = xstrdup("CommonA"); + tnl->type = socktype; + tnl->protocol = protocol; + tnl->accept = client_accept; + tnl->error = client_error; + + if(!tnl_ep_set_x509_credentials(&tnl->local, "client_key", "client_cert", "trust", NULL)) { + logger(LOG_ERR, "Couldn't set credentials!"); + return; + } + + if(!tnl_connect(tnl)) { + logger(LOG_ERR, _("Could not connect to server!")); + return; + } +} + +int main(int argc, char **argv) { + gnutls_global_init(); + gnutls_global_init_extra(); + + fd_init(); + logger_init(argv[0], LOGGER_MODE_NULL); + + if(argc > 2) + client(argv[1], argv[2]); + else if(argc > 1) + server(argv[1]); + else { + logger(LOG_ERR, "Usage: %s [host] port\n", argv[0]); + return 1; + } + + fd_run(); + + return 0; +} diff --git a/src/tincd.c b/src/tincd.c new file mode 100644 index 0000000..2e40152 --- /dev/null +++ b/src/tincd.c @@ -0,0 +1,342 @@ +/* + tincd.c -- the main file for tincd + + Copyright (C) 2000-2004 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include + +/* 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" +#include "tnl/tnl.h" +#include "vnd/vnd.h" + +static bool show_help = false; +static bool show_version = false; +static int kill_tincd = 0; +static bool bypass_security = false; +static bool do_mlock = false; +static bool do_detach = true; +static int debug_level = 1; + +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; +avl_tree_t *tinc_cfg; + +static struct option const long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"kill", optional_argument, NULL, 'k'}, + {"net", required_argument, NULL, 'n'}, + {"help", no_argument, NULL, 1}, + {"version", no_argument, NULL, 2}, + {"no-detach", no_argument, NULL, 'D'}, + {"debug", optional_argument, NULL, 'd'}, + {"bypass-security", no_argument, NULL, 3}, + {"mlock", no_argument, NULL, 'L'}, + {"logfile", optional_argument, NULL, 4}, + {"pidfile", required_argument, NULL, 5}, + {NULL, 0, NULL, 0} +}; + +#ifdef HAVE_MINGW +static struct WSAData wsa_state; +#endif + +static void usage(bool status) { + if(status) + fprintf(stderr, _("Try `%s --help\' for more information.\n"), tinc_argv[0]); + else { + printf(_("Usage: %s [option]...\n\n"), tinc_argv[0]); + printf(_(" -c, --config=DIR Read configuration options from DIR.\n" + " -D, --no-detach Don't fork and detach.\n" + " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" + " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" + " -n, --net=NETNAME Connect to net NETNAME.\n" + " -L, --mlock Lock tinc into main memory.\n" + " --logfile[=FILENAME] Write log entries to a logfile.\n" + " --pidfile=FILENAME Write PID to FILENAME.\n" + " --help Display this help and exit.\n" + " --version Output version information and exit.\n\n")); + printf(_("Report bugs to tinc@tinc-vpn.org.\n")); + } +} + +static bool parse_options(int argc, char **argv) { + int result; + int option_index = 0; + + while((result = getopt_long(argc, argv, "c:DLd::k::n:", long_options, &option_index)) != EOF) { + switch (result) { + case 0: + break; + + case 'c': /* --config */ + tinc_confbase = xstrdup(optarg); + break; + + case 'D': /* --no-detach */ + do_detach = false; + break; + + case 'L': /* --mlock */ + do_mlock = true; + break; + + case 'd': /* --debug */ + if(optarg) + debug_level = atoi(optarg); + else + debug_level++; + break; + + case 'k': /* --kill */ +#ifndef HAVE_MINGW + if(optarg) { + if(!strcasecmp(optarg, "HUP")) + kill_tincd = SIGHUP; + else if(!strcasecmp(optarg, "TERM")) + kill_tincd = SIGTERM; + else if(!strcasecmp(optarg, "KILL")) + kill_tincd = SIGKILL; + else if(!strcasecmp(optarg, "USR1")) + kill_tincd = SIGUSR1; + else if(!strcasecmp(optarg, "USR2")) + kill_tincd = SIGUSR2; + else if(!strcasecmp(optarg, "WINCH")) + kill_tincd = SIGWINCH; + else if(!strcasecmp(optarg, "INT")) + kill_tincd = SIGINT; + else if(!strcasecmp(optarg, "ALRM")) + kill_tincd = SIGALRM; + else { + kill_tincd = atoi(optarg); + + if(!kill_tincd) { + fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), + optarg); + usage(true); + return false; + } + } + } else + kill_tincd = SIGTERM; +#else + kill_tincd = 1; +#endif + break; + + case 'n': /* --net */ + tinc_netname = xstrdup(optarg); + break; + + case 1: /* --help */ + show_help = true; + break; + + case 2: /* --version */ + show_version = true; + break; + + case 3: /* --bypass-security */ + bypass_security = true; + break; + + case 4: /* --logfile */ + tinc_use_logfile = true; + if(optarg) + tinc_logfilename = xstrdup(optarg); + break; + + case 5: /* --pidfile */ + tinc_pidfilename = xstrdup(optarg); + break; + + case '?': + usage(true); + return false; + + default: + break; + } + } + + return true; +} + +static void make_names(void) +{ +#ifdef HAVE_MINGW + HKEY key; + char installdir[1024] = ""; + long len = sizeof(installdir); +#endif + + if(tinc_netname) + asprintf(&tinc_identname, "tinc.%s", tinc_netname); + else + 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(!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(&tinc_confbase, "%s", installdir); + } + } + RegCloseKey(key); + if(*installdir) + return; + } +#endif + + if(!tinc_pidfilename) + asprintf(&tinc_pidfilename, LOCALSTATEDIR "/run/%s.pid", tinc_identname); + + if(!tinc_logfilename) + asprintf(&tinc_logfilename, LOCALSTATEDIR "/log/%s.log", tinc_identname); + + if(!tinc_confbase) { + if(tinc_netname) + asprintf(&tinc_confbase, CONFDIR "/tinc/%s", tinc_netname); + else + asprintf(&tinc_confbase, CONFDIR "/tinc"); + } + + asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase); +} + +int main(int argc, char **argv) { + tinc_argc = argc; + tinc_argv = argv; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + if(!parse_options(argc, argv)) + return 1; + + make_names(); + + if(show_version) { + 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" + "and you are welcome to redistribute it under certain conditions;\n" + "see the file COPYING for details.\n")); + + return 0; + } + + if(show_help) { + usage(false); + return 0; + } + + if(kill_tincd) + return !kill_other(kill_tincd); + + logger_init("tinc", tinc_use_logfile ? LOGGER_MODE_FILE : LOGGER_MODE_STDERR); + + /* Lock all pages into memory if requested */ + + if(do_mlock) +#ifdef HAVE_MLOCKALL + if(mlockall(MCL_CURRENT | MCL_FUTURE)) { + logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall", + strerror(errno)); +#else + { + logger(LOG_ERR, _("mlockall() not supported on this platform!")); +#endif + return -1; + } + + tinc_cfg = cfg_tree_new(); + + asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase); + + if(!cfg_read_file(tinc_cfg, tinc_cfgfilename)) + return 1; + +#ifdef HAVE_MINGW + if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) { + logger(LOG_ERR, _("System call `%s' failed: %s"), "WSAStartup", winerror(GetLastError())); + return 1; + } +#endif + + if(do_detach && !detach()) + return 1; + + 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(); + fd_exit(); +end: + logger(LOG_NOTICE, _("Terminating")); + +#ifndef HAVE_MINGW + remove_pid(tinc_pidfilename); +#endif + + logger_exit(); + + return 0; +} diff --git a/src/tincd.h b/src/tincd.h new file mode 100644 index 0000000..b2e6620 --- /dev/null +++ b/src/tincd.h @@ -0,0 +1,43 @@ +/* + tincd.h -- tinc specific global variables and functions + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id +*/ + +#ifndef __TINCD_H__ +#define __TINCD_H__ + +#include "support/avl.h" + +extern char *tinc_confbase; +extern char *tinc_netname; +extern char *tinc_identname; +extern char *tinc_pidfilename; +extern char *tinc_logfilename; +extern char *tinc_cfgfilename; + +extern bool tinc_use_logfile; + +extern int tinc_argc; +extern char **tinc_argv; +extern avl_tree_t *tinc_cfg; + +extern bool remove_pid(const char *pidfilename); + +#endif diff --git a/src/tnl.c b/src/tnl.c new file mode 100644 index 0000000..75f82f7 --- /dev/null +++ b/src/tnl.c @@ -0,0 +1,521 @@ +/* + tnl.c -- tunnels + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include + +#include "logger/logger.h" +#include "support/avl.h" +#include "support/sockaddr.h" +#include "support/xalloc.h" +#include "tnl/tnl.h" + +static bool tnl_send(tnl_t *tnl, const void *buf, int len) { + int result; + + while(len) { + result = gnutls_record_send(tnl->session, buf, len); + if(result <= 0) { + if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) + continue; + + if(result) + logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result)); + else + logger(LOG_INFO, _("tnl: connection closed by peer")); + + if(tnl->error) + tnl->error(tnl, result); + tnl->close(tnl); + return !result; + } + + buf += result; + len -= result; + } + + return true; +} + +static bool tnl_recv(tnl_t *tnl) { + tnl_record_t *record = (tnl_record_t *)tnl->buf; + +#if 0 + int result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread); + if(result <= 0) { + if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) + return true; + + if(result) + logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result)); + else + logger(LOG_INFO, _("tnl: connection closed by peer")); + + if(tnl->error) + tnl->error(tnl, result); + tnl->close(tnl); + return !result; + } + + tnl->bufread += result; +#endif + + while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) { + switch(record->type) { + case TNL_RECORD_META: + if(tnl->recv_meta) + tnl->recv_meta(tnl, record->data, record->len); + break; + + case TNL_RECORD_PACKET: + if(tnl->recv_packet) + tnl->recv_packet(tnl, record->data, record->len); + break; + + default: + logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type")); + if(tnl->error) + tnl->error(tnl, EINVAL); + tnl->close(tnl); + return false; + } + + tnl->bufread -= sizeof *record + record->len; + memmove(tnl->buf, record->data + record->len, tnl->bufread); + } + + return true; +} + +static bool tnl_recv_handler(fd_t *fd) { + if(!fd) + abort(); + + tnl_t *tnl = fd->data; + int result; + + result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread); + if(result <= 0) { + if(!result) { + logger(LOG_DEBUG, _("tnl: connection closed by peer %s (%s)"), tnl->remote.id, tnl->remote.hostname); + if(tnl->error) + tnl->error(tnl, 0); + tnl->close(tnl); + return false; + } + + if(gnutls_error_is_fatal(result)) { + logger(LOG_DEBUG, _("tnl: reception failed: %s"), gnutls_strerror(result)); + if(tnl->error) + tnl->error(tnl, result); + tnl->close(tnl); + return false; + } + + return true; + } + + tnl->bufread += result; + return tnl_recv(tnl); +} + +bool tnl_ep_set_x509_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *certificate, const char *trust, const char *crl) { + int err; + + if(tnl_ep->cred.certificate) { + gnutls_certificate_free_credentials(tnl_ep->cred.certificate); + tnl_ep->cred.certificate = NULL; + } + + if((err = gnutls_certificate_allocate_credentials(&tnl_ep->cred.certificate)) < 0) { + logger(LOG_ERR, _("Failed to allocate certificate credentials: %s"), gnutls_strerror(err)); + return false; + } + + if((err = gnutls_certificate_set_x509_key_file(tnl_ep->cred.certificate, certificate, privkey, GNUTLS_X509_FMT_PEM)) < 0) { + logger(LOG_ERR, _("Failed to load X.509 key and/or certificate: %s"), gnutls_strerror(err)); + return false; + } + + tnl_ep->cred.type = GNUTLS_CRD_CERTIFICATE; + + if(trust && (err = gnutls_certificate_set_x509_trust_file(tnl_ep->cred.certificate, trust, GNUTLS_X509_FMT_PEM)) < 0) { + logger(LOG_ERR, _("Failed to set X.509 trust file: %s"), gnutls_strerror(err)); + return false; + } + + if(crl && (err = gnutls_certificate_set_x509_crl_file(tnl_ep->cred.certificate, crl, GNUTLS_X509_FMT_PEM)) < 0) { + logger(LOG_ERR, _("Failed to set X.509 CRL file: %s"), gnutls_strerror(err)); + return false; + } + + //gnutls_certificate_set_verify_flags(tnl_ep->cred.certificate, GNUTLS_VERIFY_DISABLE_CA_SIGN); + + return true; +} + +bool tnl_ep_set_openpgp_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *pubkey, const char *keyring, const char *trustdb) { + int err; + + if(tnl_ep->cred.certificate) { + gnutls_certificate_free_credentials(tnl_ep->cred.certificate); + tnl_ep->cred.certificate = NULL; + } + + if((err = gnutls_certificate_allocate_credentials(&tnl_ep->cred.certificate)) < 0) { + logger(LOG_ERR, _("Failed to allocate certificate credentials: %s"), gnutls_strerror(err)); + return false; + } + + if((err = gnutls_certificate_set_openpgp_key_file(tnl_ep->cred.certificate, pubkey, privkey)) < 0) { + logger(LOG_ERR, _("Failed to load public and/or private OpenPGP key: %s"), gnutls_strerror(err)); + return false; + } + + tnl_ep->cred.type = GNUTLS_CRD_CERTIFICATE; + + if(keyring && (err = gnutls_certificate_set_openpgp_keyring_file(tnl_ep->cred.certificate, keyring)) < 0) { + logger(LOG_ERR, _("Failed to set OpenPGP keyring file: %s"), gnutls_strerror(err)); + return false; + } + + if(trustdb && (err = gnutls_certificate_set_openpgp_trustdb(tnl_ep->cred.certificate, trustdb)) < 0) { + logger(LOG_ERR, _("Failed to set OpenPGP trustdb file: %s"), gnutls_strerror(err)); + return false; + } + + //gnutls_certificate_set_verify_flags(tnl_ep->cred.certificate, GNUTLS_VERIFY_DISABLE_CA_SIGN); + + return true; +} + +static bool tnl_authenticate_x509(tnl_t *tnl) { + gnutls_x509_crt cert; + const gnutls_datum *certs; + int ncerts = 0, result; + char name[1024]; + int len; + + certs = gnutls_certificate_get_peers(tnl->session, &ncerts); + + if (!certs || !ncerts) { + logger(LOG_ERR, _("tnl: no certificates from %s"), tnl->remote.hostname); + return false; + } + + gnutls_x509_crt_init(&cert); + result = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER); + + if(result) { + logger(LOG_ERR, _("tnl: error importing certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(result)); + gnutls_x509_crt_deinit(cert); + return false; + } + + len = sizeof name; + result = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &len); + gnutls_x509_crt_deinit(cert); + + if(result) { + logger(LOG_ERR, _("tnl: could not extract common name from certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(result)); + return false; + } + + if(len > sizeof name) { + logger(LOG_ERR, _("tnl: common name from certificate from %s too long"), tnl->remote.hostname); + return false; + } + + if(tnl->remote.id && strcmp(tnl->remote.id, name)) { + logger(LOG_ERR, _("tnl: peer %s is %s instead of %s"), tnl->remote.hostname, name, tnl->remote.id); + return false; + } + + replace(tnl->remote.id, xstrdup(name)); + + result = gnutls_certificate_verify_peers(tnl->session); + + if(result < 0) { + logger(LOG_ERR, "tnl: error verifying certificate from %s (%s): %s", tnl->remote.id, tnl->remote.hostname, gnutls_strerror(result)); + return false; + } + + if(result) { + logger(LOG_ERR, "tnl: certificate from %s (%s) not good, verification result %x", tnl->remote.id, tnl->remote.hostname, result); + return false; + } + + return true; +} + +static bool tnl_authenticate(tnl_t *tnl) { + switch(tnl->local.cred.type) { + case GNUTLS_CRD_CERTIFICATE: + switch(gnutls_certificate_type_get(tnl->session)) { + case GNUTLS_CRT_X509: + return tnl_authenticate_x509(tnl); + case GNUTLS_CRT_OPENPGP: + //return tnl_authenticate_openpgp(tnl); + default: + logger(LOG_ERR, "tnl: unknown certificate type for session with %s (%s)", tnl->remote.id, tnl->remote.hostname); + return false; + } + + case GNUTLS_CRD_ANON: + logger(LOG_ERR, "tnl: anonymous authentication not yet supported"); + return false; + + case GNUTLS_CRD_SRP: + logger(LOG_ERR, "tnl: SRP authentication not yet supported"); + return false; + + default: + logger(LOG_ERR, "tnl: unknown authentication type for session with %s (%s)", tnl->remote.id, tnl->remote.hostname); + return false; + } +} + +static bool tnl_handshake_handler(fd_t *fd) { + //char id[1024]; + tnl_t *tnl = fd->data; + int result; + + result = gnutls_handshake(tnl->session); + if(result < 0) { + if(gnutls_error_is_fatal(result)) { + logger(LOG_ERR, "tnl: handshake error: %s", gnutls_strerror(result)); + tnl->close(tnl); + return false; + } + + /* check other stuff? */ + return true; + } + + logger(LOG_DEBUG, _("tnl: handshake finished")); + + if(!tnl_authenticate(tnl)) + return false; + + tnl->status = TNL_STATUS_UP; + tnl->fd.read = tnl_recv_handler; + if(tnl->accept) + tnl->accept(tnl); + + return true; +} + +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, &record, sizeof record) && tnl_send(tnl, buf, 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, &record, sizeof record) && tnl_send(tnl, buf, len); +} + +static bool tnl_close(tnl_t *tnl) { + if(tnl->session) { + gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(tnl->session); + } + + fd_del(&tnl->fd); + close(tnl->fd.fd); + + return true; +} + +static bool tnl_accept_handler(fd_t *fd) { + tnl_listen_t *listener = fd->data; + tnl_t *tnl; + struct sockaddr_storage ss; + socklen_t len = sizeof ss; + int sock; + + sock = accept(fd->fd, sa(&ss), &len); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno)); + return false; + } + + sa_unmap(&ss); + + logger(LOG_DEBUG, _("tnl: accepted incoming connection")); + + clear(new(tnl)); + tnl->local = listener->local; + tnl->remote.address = ss; + len = sizeof tnl->local.address; + getsockname(sock, sa(&tnl->local.address), &len); + sa_unmap(&tnl->local.address); + tnl->type = listener->type; + tnl->protocol = listener->protocol; + tnl->send_packet = tnl_send_packet; + tnl->send_meta = tnl_send_meta; + tnl->close = tnl_close; + + tnl->fd.fd = sock; + tnl->fd.read = tnl_handshake_handler; + tnl->fd.data = tnl; + + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); + + tnl->status = TNL_STATUS_HANDSHAKE; + gnutls_init(&tnl->session, GNUTLS_SERVER); + //gnutls_handshake_set_private_extensions(tnl->session, 1); + gnutls_set_default_priority(tnl->session); + gnutls_credentials_set(tnl->session, tnl->local.cred.type, tnl->local.cred.certificate); + gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); + gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)sock); + + tnl->accept = listener->accept; + + fd_add(&tnl->fd); + + tnl_handshake_handler(&tnl->fd); + + return true; +} + +static bool tnl_connect_handler(fd_t *fd) { + tnl_t *tnl = fd->data; + int result; + socklen_t len; + + len = sizeof result; + getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len); + if(result) { + logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result)); + if(tnl->error) + tnl->error(tnl, result); + tnl->close(tnl); + return false; + } + + logger(LOG_DEBUG, _("tnl: connected")); + + fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK); + + tnl->status = TNL_STATUS_HANDSHAKE; + gnutls_init(&tnl->session, GNUTLS_CLIENT); + //gnutls_handshake_set_private_extensions(tnl->session, 1); + gnutls_set_default_priority(tnl->session); + gnutls_credentials_set(tnl->session, tnl->local.cred.type, tnl->local.cred.certificate); + gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); + gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd); + + tnl->fd.write = NULL; + tnl->fd.read = tnl_handshake_handler; + fd_mod(&tnl->fd); + + tnl_handshake_handler(&tnl->fd); + + return true; +} + +bool tnl_connect(tnl_t *tnl) { + int sock; + + sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno)); + return false; + } + +#if 0 + if(sa_nonzero(&tnl->local.address) && bind(sock, sa(&tnl->local.address), sa_len(&tnl->local.address)) == -1) { + logger(LOG_ERR, _("tnl: could not bind socket: %s"), strerror(errno)); + close(sock); + return false; + } +#endif + + if(connect(sock, sa(&tnl->remote.address), sa_len(&tnl->remote.address)) == -1) { + logger(LOG_ERR, _("tnl: could not connect: %s"), strerror(errno)); + close(sock); + return false; + } + + tnl->status = TNL_STATUS_CONNECTING; + + tnl->fd.fd = sock; + tnl->fd.write = tnl_connect_handler; + tnl->fd.data = tnl; + + tnl->send_packet = tnl_send_packet; + tnl->send_meta = tnl_send_meta; + tnl->close = tnl_close; + + fd_add(&tnl->fd); + + return true; +} + +static bool tnl_listen_close(tnl_listen_t *listener) { + fd_del(&listener->fd); + close(listener->fd.fd); + return true; +} + +bool tnl_listen(tnl_listen_t *listener) { + int sock; + + sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol); + + if(sock == -1) { + logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno)); + return false; + } + + if(bind(sock, sa(&listener->local.address), sa_len(&listener->local.address)) == -1) { + logger(LOG_ERR, _("tnl: could not bind listener socket: %s"), strerror(errno)); + return false; + } + + if(listen(sock, 10) == -1) { + logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno)); + return false; + } + + listener->fd.fd = sock; + listener->fd.read = tnl_accept_handler; + listener->fd.data = listener; + listener->close = tnl_listen_close; + + fd_add(&listener->fd); + + return true; +} diff --git a/src/tnl.h b/src/tnl.h new file mode 100644 index 0000000..77a8b2a --- /dev/null +++ b/src/tnl.h @@ -0,0 +1,116 @@ +/* + tnl.h -- tunnels + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TNL_H__ +#define __TNL_H__ + +#include +#include + +#include "fd/fd.h" + +#define TNL_PROTOCOL 0 + +#define TNL_RECORD_PACKET 0 +#define TNL_RECORD_META 1 +#define TNL_RECORD_HELLO 2 +#define TNL_RECORD_BLA 3 + +typedef struct tnl_record { + uint16_t type; + uint16_t len; + char data[]; +} tnl_record_t; + +typedef enum tnl_status { + TNL_STATUS_DOWN, + TNL_STATUS_CONNECTING, + TNL_STATUS_HANDSHAKE, + TNL_STATUS_UP, +} tnl_status_t; + +typedef struct tnl_ep_credentials { + gnutls_credentials_type type; + union { + gnutls_anon_client_credentials anon_client; + gnutls_anon_server_credentials anon_server; + gnutls_srp_client_credentials srp_client; + gnutls_srp_server_credentials srp_server; + gnutls_certificate_credentials certificate; + }; +} tnl_ep_credentials_t; + +typedef struct tnl_ep_cryptoparm { +} tnl_ep_cryptoparm_t; + +typedef struct tnl_ep { + struct sockaddr_storage address; + char *id; + char *hostname; + struct tnl_ep_credentials cred; + struct tnl_ep_cryptoparm parm; +} tnl_ep_t; + +typedef struct tnl { + struct tnl_ep local; + struct tnl_ep remote; + int type; + int protocol; + int mtu; + enum tnl_status status; + void *data; + + bool (*send_packet)(struct tnl *tnl, const 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 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); + + /* private */ + + gnutls_session session; + struct fd fd; + char buf[4096]; + int bufread; +} tnl_t; + +typedef struct tnl_listen { + struct tnl_ep local; + int type; + int protocol; + + bool (*accept)(struct tnl *tnl); + bool (*close)(struct tnl_listen *listener); + + struct fd fd; +} tnl_listen_t; + +extern bool tnl_listen(struct tnl_listen *listener); +extern bool tnl_connect(struct tnl *tnl); + +extern bool tnl_ep_set_x509_credentials(tnl_ep_t *tnl_ep, const char *key, const char *certificate, const char *trust, const char *crl); +extern bool tnl_ep_set_openpgp_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *pubkey, const char *keyring, const char *trustdb); + +#endif /* __TNL_H__ */ diff --git a/src/vnd.c b/src/vnd.c new file mode 100644 index 0000000..7eba993 --- /dev/null +++ b/src/vnd.c @@ -0,0 +1,150 @@ +/* + vnd.c -- virtual network device management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include + +#include "fd/fd.h" +#include "logger/logger.h" +#include "support/xalloc.h" + +#include "vnd/vnd.h" + +vnd_t *vnd_new(void) { + vnd_t *vnd; + + return clear(new(vnd)); +} + +void vnd_free(vnd_t *vnd) { + replace(vnd->device, NULL); + replace(vnd->interface, NULL); + replace(vnd->description, NULL); + free(vnd); +} + +void vnd_set(vnd_t *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv) { + replace(vnd->device, device); + replace(vnd->interface, interface); + vnd->mode = mode; + vnd->recv = recv; +} + +static bool vnd_send(vnd_t *vnd, const void *buf, int len) { + int result; + + result = write(vnd->fd.fd, buf, len); + + if(result == len || result < 0 && (errno == EINTR || errno == EAGAIN)) { + logger(LOG_INFO, _("vnd: wrote packet of %d bytes to %s"), len, vnd->description); + return true; + } + + logger(LOG_INFO, _("vnd: error writing packet of %d bytes to %s: %s"), len, vnd->description, strerror(errno)); + + return false; +} + +static bool vnd_recv_handler(fd_t *fd) { + vnd_t *vnd = fd->data; + char buf[vnd->mtu]; + int len; + + vnd = fd->data; + + len = read(fd->fd, buf, sizeof buf); + + if(len > 0) { + logger(LOG_INFO, _("vnd: read packet of %d bytes from %s"), len, vnd->description); + return vnd->recv(vnd, buf, len); + } + + if(len < 0 && (errno == EINTR || errno == EAGAIN)) + return true; + + logger(LOG_ERR, _("vnd: error reading packet from %s: %s"), vnd->description, strerror(errno)); + + return false; +} + +bool vnd_open(vnd_t *vnd) { + struct ifreq ifr = {0}; + + if(!vnd->device) + vnd->device = xstrdup("/dev/net/tun"); + + vnd->fd.fd = open(vnd->device, O_RDWR | O_NONBLOCK); + + if(vnd->fd.fd < 0) { + logger(LOG_ERR, _("vnd: could not open %s: %s"), vnd->device, strerror(errno)); + return false; + } + + if(vnd->mode == VND_MODE_TUN) + ifr.ifr_flags = IFF_TUN; + else + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + if(vnd->interface) + strncpy(ifr.ifr_name, vnd->interface, IFNAMSIZ); + + if(!ioctl(vnd->fd.fd, TUNSETIFF, &ifr)) { + if(vnd->interface) + free(vnd->interface); + vnd->interface = xstrdup(ifr.ifr_name); + } else { + logger(LOG_ERR, _("vnd: %s is not a Linux tun/tap device"), vnd->device); + return false; + } + + if(!vnd->mtu) + vnd->mtu = 1514; + + vnd->send = vnd_send; + vnd->fd.read = vnd_recv_handler; + vnd->fd.data = vnd; + + if(vnd->description) + free(vnd->description); + + asprintf(&vnd->description, "Linux tun/tap device %s (interface %s)", vnd->device, vnd->interface); + + if(!fd_add(&vnd->fd)) + return false; + + logger(LOG_INFO, _("vnd: opened %s"), vnd->description); + + return true; +} + +bool vnd_close(vnd_t *vnd) { + fd_del(&vnd->fd); + + close(vnd->fd.fd); + + logger(LOG_INFO, _("vnd: closed %s"), vnd->description); + + return true; +} + diff --git a/src/vnd.h b/src/vnd.h new file mode 100644 index 0000000..47f8c6a --- /dev/null +++ b/src/vnd.h @@ -0,0 +1,59 @@ +/* + vnd.c -- virtual network device management + + Copyright (C) 2003-2004 Guus Sliepen , + 2003-2004 Ivo Timmermans + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __VND_H__ +#define __VND_H__ + +typedef enum vnd_mode{ + VND_MODE_TUN, + VND_MODE_TAP, +} vnd_mode_t; + +struct vnd; + +typedef bool (*vnd_handler_t)(struct vnd *vnd, const void *buf, int len); + +typedef struct vnd { + char *device; + char *interface; + enum vnd_mode mode; + int mtu; + + vnd_handler_t recv; + vnd_handler_t send; + + /* Private data */ + + struct fd fd; + char *description; +} vnd_t; + +extern bool vnd_init(void); +extern bool vnd_exit(void); +extern struct vnd *vnd_new(void); +extern void vnd_free(struct vnd *vnd); +extern void vnd_set(struct vnd *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv); +extern bool vnd_open(struct vnd *vnd); +extern bool vnd_close(struct vnd *vnd); + +#endif /* __VND_H__ */ diff --git a/src/xalloc.c b/src/xalloc.c new file mode 100644 index 0000000..80699f9 --- /dev/null +++ b/src/xalloc.c @@ -0,0 +1,77 @@ +/* + xalloc.c -- safe memory allocation functions + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include +#include +#include + +#include "xalloc.h" + +static void xalloc_fail(void) { + fprintf(stderr, "Memory exhausted\n"); + exit(1); +} + +void (*xalloc_fail_func)(void) = xalloc_fail; + +void *xmalloc(size_t n) { + void *p; + + p = malloc(n); + + if(!p) + xalloc_fail_func(); + + return p; +} + +void *xrealloc(void *p, size_t n) { + p = realloc(p, n); + + if(!p) + xalloc_fail_func(); + + return p; +} + +void *xcalloc(size_t n, size_t s) { + void *p; + + p = calloc(n, s); + + if(!p) + xalloc_fail_func(); + + return p; +} + +char *xstrdup(const char *s) { + char *p; + + p = strdup(s); + + if(!p) + xalloc_fail_func(); + + return p; +} + diff --git a/src/xalloc.h b/src/xalloc.h new file mode 100644 index 0000000..db2e531 --- /dev/null +++ b/src/xalloc.h @@ -0,0 +1,37 @@ +/* + xalloc.h -- safe memory allocation functions + + Copyright (C) 2003-2004 Guus Sliepen , + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __XALLOC_H__ +#define __XALLOC_H__ + +#define new(object) ({(object) = xmalloc(sizeof *(object));}) +#define dim(object, count) ({(object) = xmalloc(sizeof *(object) * (count));}) +#define redim(object, count) ({(object) = xrealloc((object), sizeof *(object) * (count));}) +#define copy(object) ({typeof(object) _copy; *(_copy = xmalloc(sizeof *(object))) = *(object); _copy;}) +#define clear(object) ({memset((object), 0, sizeof *(object));}) +#define replace(string, replacement) ({if(string) free(string); (string) = (replacement) ? xstrdup(replacement) : NULL;}) + +void *xmalloc(size_t n) __attribute__ ((__malloc__)); +void *xrealloc(void *p, size_t n) __attribute__ ((__malloc__)); +char *xstrdup(const char *s) __attribute__ ((__malloc__)); + +#endif diff --git a/support/Makefile.am b/support/Makefile.am deleted file mode 100644 index 8f38b94..0000000 --- a/support/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = libsupport.a - -noinst_HEADERS = avl.h ethernet.h ipv4.h ipv6.h list.h sockaddr.h xalloc.h - -libsupport_a_SOURCES = avl.c list.c xalloc.c - diff --git a/support/avl.c b/support/avl.c deleted file mode 100644 index 96500cb..0000000 --- a/support/avl.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - avl.c -- AVL tree management - - Copyright (C) 1998 Michael H. Buselli - 2000-2004 Ivo Timmermans , - 2000-2004 Guus Sliepen - 2000-2004 Wessel Dankers - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Original AVL tree library by Michael H. Buselli . - - Modified 2000-11-28 by Wessel Dankers to use counts - instead of depths, to add the ->next and ->prev and to generally obfuscate - the code. Mail me if you found a bug. - - Cleaned up and incorporated some of the ideas from the red-black tree - library for inclusion into tinc (http://www.tinc-vpn.org/) by - Guus Sliepen . - - $Id$ -*/ - -#include "system.h" - -#include "support/avl.h" -#include "support/xalloc.h" - -#ifdef AVL_COUNT -#define AVL_NODE_COUNT(n) ((n) ? (n)->count : 0) -#define AVL_L_COUNT(n) (AVL_NODE_COUNT((n)->left)) -#define AVL_R_COUNT(n) (AVL_NODE_COUNT((n)->right)) -#define AVL_CALC_COUNT(n) (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1) -#endif - -#ifdef AVL_DEPTH -#define AVL_NODE_DEPTH(n) ((n) ? (n)->depth : 0) -#define L_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->left)) -#define R_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->right)) -#define AVL_CALC_DEPTH(n) ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1) -#endif - -#ifndef AVL_DEPTH -static int lg(unsigned int u) __attribute__ ((__const__)); - -static int lg(unsigned int u) { - int r = 1; - - if(!u) - return 0; - - if(u & 0xffff0000) { - u >>= 16; - r += 16; - } - - if(u & 0x0000ff00) { - u >>= 8; - r += 8; - } - - if(u & 0x000000f0) { - u >>= 4; - r += 4; - } - - if(u & 0x0000000c) { - u >>= 2; - r += 2; - } - - if(u & 0x00000002) - r++; - - return r; -} -#endif - -/* Internal helper functions */ - -static int avl_check_balance(const avl_node_t *node) { -#ifdef AVL_DEPTH - int d; - - d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node); - - return d < -1 ? -1 : d > 1 ? 1 : 0; -#else -/* int d; - * d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node)); - * d = d<-1?-1:d>1?1:0; - */ - int pl, r; - - pl = lg(AVL_L_COUNT(node)); - r = AVL_R_COUNT(node); - - if(r >> pl + 1) - return 1; - - if(pl < 2 || r >> pl - 2) - return 0; - - return -1; -#endif -} - -static void avl_rebalance(avl_tree_t *tree, avl_node_t *node) { - avl_node_t *child; - avl_node_t *gchild; - avl_node_t *parent; - avl_node_t **superparent; - - parent = node; - - while(node) { - parent = node->parent; - - superparent = - parent ? node == - parent->left ? &parent->left : &parent->right : &tree->root; - - switch (avl_check_balance(node)) { - case -1: - child = node->left; -#ifdef AVL_DEPTH - if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) { -#else - if(AVL_L_COUNT(child) >= AVL_R_COUNT(child)) { -#endif - node->left = child->right; - if(node->left) - node->left->parent = node; - - child->right = node; - node->parent = child; - *superparent = child; - child->parent = parent; -#ifdef AVL_COUNT - node->count = AVL_CALC_COUNT(node); - child->count = AVL_CALC_COUNT(child); -#endif -#ifdef AVL_DEPTH - node->depth = AVL_CALC_DEPTH(node); - child->depth = AVL_CALC_DEPTH(child); -#endif - } else { - gchild = child->right; - node->left = gchild->right; - - if(node->left) - node->left->parent = node; - child->right = gchild->left; - - if(child->right) - child->right->parent = child; - gchild->right = node; - - if(gchild->right) - gchild->right->parent = gchild; - gchild->left = child; - - if(gchild->left) - gchild->left->parent = gchild; - *superparent = gchild; - - gchild->parent = parent; -#ifdef AVL_COUNT - node->count = AVL_CALC_COUNT(node); - child->count = AVL_CALC_COUNT(child); - gchild->count = AVL_CALC_COUNT(gchild); -#endif -#ifdef AVL_DEPTH - node->depth = AVL_CALC_DEPTH(node); - child->depth = AVL_CALC_DEPTH(child); - gchild->depth = AVL_CALC_DEPTH(gchild); -#endif - } - break; - - case 1: - child = node->right; -#ifdef AVL_DEPTH - if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) { -#else - if(AVL_R_COUNT(child) >= AVL_L_COUNT(child)) { -#endif - node->right = child->left; - if(node->right) - node->right->parent = node; - child->left = node; - node->parent = child; - *superparent = child; - child->parent = parent; -#ifdef AVL_COUNT - node->count = AVL_CALC_COUNT(node); - child->count = AVL_CALC_COUNT(child); -#endif -#ifdef AVL_DEPTH - node->depth = AVL_CALC_DEPTH(node); - child->depth = AVL_CALC_DEPTH(child); -#endif - } else { - gchild = child->left; - node->right = gchild->left; - - if(node->right) - node->right->parent = node; - child->left = gchild->right; - - if(child->left) - child->left->parent = child; - gchild->left = node; - - if(gchild->left) - gchild->left->parent = gchild; - gchild->right = child; - - if(gchild->right) - gchild->right->parent = gchild; - - *superparent = gchild; - gchild->parent = parent; -#ifdef AVL_COUNT - node->count = AVL_CALC_COUNT(node); - child->count = AVL_CALC_COUNT(child); - gchild->count = AVL_CALC_COUNT(gchild); -#endif -#ifdef AVL_DEPTH - node->depth = AVL_CALC_DEPTH(node); - child->depth = AVL_CALC_DEPTH(child); - gchild->depth = AVL_CALC_DEPTH(gchild); -#endif - } - break; - - default: -#ifdef AVL_COUNT - node->count = AVL_CALC_COUNT(node); -#endif -#ifdef AVL_DEPTH - node->depth = AVL_CALC_DEPTH(node); -#endif - } - node = parent; - } -} - -static int avl_compare(const void *a, const void *b) { - return a - b; -} - -/* (De)constructors */ - -avl_tree_t *avl_tree_new(avl_compare_t compare, avl_action_t free) { - avl_tree_t *tree; - - clear(new(tree)); - tree->compare = compare ?: avl_compare; - tree->free = free; - - return tree; -} - -void avl_tree_free(avl_tree_t *tree) { - free(tree); -} - -avl_node_t *avl_node_new(void) { - avl_node_t *node; - - return clear(new(node)); -} - -void avl_node_free(avl_tree_t *tree, avl_node_t *node) { - if(node->data && tree->free) - tree->free(node->data); - - free(node); -} - -/* Searching */ - -void *avl_get(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - - node = avl_get_node(tree, data); - - return node ? node->data : NULL; -} - -void *avl_get_closest(const avl_tree_t *tree, const void *data, int *result) { - avl_node_t *node; - - node = avl_get_closest_node(tree, data, result); - - return node ? node->data : NULL; -} - -void *avl_get_closest_smaller(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - - node = avl_get_closest_smaller_node(tree, data); - - return node ? node->data : NULL; -} - -void *avl_get_closest_greater(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - - node = avl_get_closest_greater_node(tree, data); - - return node ? node->data : NULL; -} - -avl_node_t *avl_get_node(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - int result; - - node = avl_get_closest_node(tree, data, &result); - - return result ? NULL : node; -} - -avl_node_t *avl_get_closest_node(const avl_tree_t *tree, const void *data, int *result) { - avl_node_t *node; - int c; - - node = tree->root; - - if(!node) { - if(result) - *result = 0; - return NULL; - } - - for(;;) { - c = tree->compare(data, node->data); - - if(c < 0) { - if(node->left) - node = node->left; - else { - if(result) - *result = -1; - break; - } - } else if(c > 0) { - if(node->right) - node = node->right; - else { - if(result) - *result = 1; - break; - } - } else { - if(result) - *result = 0; - break; - } - } - - return node; -} - -avl_node_t *avl_get_closest_smaller_node(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - int result; - - node = avl_get_closest_node(tree, data, &result); - - if(result < 0) - node = node->prev; - - return node; -} - -avl_node_t *avl_get_closest_greater_node(const avl_tree_t *tree, const void *data) { - avl_node_t *node; - int result; - - node = avl_get_closest_node(tree, data, &result); - - if(result > 0) - node = node->next; - - return node; -} - -/* Insertion and deletion */ - -avl_node_t *avl_add(avl_tree_t *tree, void *data) { - avl_node_t *node, *result; - - node = avl_node_new(); - node->data = data; - - result = avl_add_node(tree, node); - - if(!result) - free(node); - - return result; -} - -avl_node_t *avl_add_node(avl_tree_t *tree, avl_node_t *node) { - avl_node_t *closest; - int result; - - if(!tree->root) - avl_add_top(tree, node); - else { - closest = avl_get_closest_node(tree, node->data, &result); - - switch (result) { - case -1: - avl_add_before(tree, closest, node); - break; - - case 1: - avl_add_after(tree, closest, node); - break; - - case 0: - return NULL; - } - } - -#ifdef AVL_COUNT - node->count = 1; -#endif -#ifdef AVL_DEPTH - node->depth = 1; -#endif - - return node; -} - -void avl_add_top(avl_tree_t *tree, avl_node_t *node) { - node->prev = node->next = node->parent = NULL; - tree->head = tree->tail = tree->root = node; -} - -void avl_add_before(avl_tree_t *tree, avl_node_t *before, avl_node_t *node) { - if(!before) { - if(tree->tail) - avl_add_after(tree, tree->tail, node); - else - avl_add_top(tree, node); - return; - } - - node->next = before; - node->parent = before; - node->prev = before->prev; - - if(before->left) { - avl_add_after(tree, before->prev, node); - return; - } - - if(before->prev) - before->prev->next = node; - else - tree->head = node; - - before->prev = node; - before->left = node; - - avl_rebalance(tree, before); -} - -void avl_add_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node) { - if(!after) { - if(tree->head) - avl_add_before(tree, tree->head, node); - else - avl_add_top(tree, node); - return; - } - - if(after->right) { - avl_add_before(tree, after->next, node); - return; - } - - node->prev = after; - node->parent = after; - node->next = after->next; - - if(after->next) - after->next->prev = node; - else - tree->tail = node; - - after->next = node; - after->right = node; - - avl_rebalance(tree, after); -} - -avl_node_t *avl_unlink(avl_tree_t *tree, const void *data) { - avl_node_t *node; - - node = avl_get_node(tree, data); - - if(node) - avl_unlink_node(tree, node); - - return node; -} - -void avl_unlink_node(avl_tree_t *tree, avl_node_t *node) { - avl_node_t *parent; - avl_node_t **superparent; - avl_node_t *subst, *left, *right; - avl_node_t *balnode; - - if(node->prev) - node->prev->next = node->next; - else - tree->head = node->next; - if(node->next) - node->next->prev = node->prev; - else - tree->tail = node->prev; - - parent = node->parent; - - superparent = - parent ? node == - parent->left ? &parent->left : &parent->right : &tree->root; - - left = node->left; - right = node->right; - if(!left) { - *superparent = right; - - if(right) - right->parent = parent; - - balnode = parent; - } else if(!right) { - *superparent = left; - left->parent = parent; - balnode = parent; - } else { - subst = node->prev; - - if(subst == left) { - balnode = subst; - } else { - balnode = subst->parent; - balnode->right = subst->left; - - if(balnode->right) - balnode->right->parent = balnode; - - subst->left = left; - left->parent = subst; - } - - subst->right = right; - subst->parent = parent; - right->parent = subst; - *superparent = subst; - } - - avl_rebalance(tree, balnode); - - node->next = node->prev = node->parent = node->left = node->right = NULL; - -#ifdef AVL_COUNT - node->count = 0; -#endif -#ifdef AVL_DEPTH - node->depth = 0; -#endif -} - -void avl_del_node(avl_tree_t *tree, avl_node_t *node) { - avl_unlink_node(tree, node); - avl_node_free(tree, node); -} - -bool avl_del(avl_tree_t *tree, void *data) { - avl_node_t *node; - - node = avl_get_node(tree, data); - - if(node) - avl_del_node(tree, node); - - return node; -} - -/* Fast tree cleanup */ - -void avl_tree_del(avl_tree_t *tree) { - avl_node_t *node; - - avl_foreach_node(tree, node, avl_node_free(tree, node)); - - avl_tree_free(tree); -} - -/* Indexing */ - -#ifdef AVL_COUNT -avl_count_t avl_count(const avl_tree_t *tree) { - return AVL_NODE_COUNT(tree->root); -} - -void *avl_get_indexed(const avl_tree_t *tree, avl_count_t index) { - avl_node_t *node; - - node = avl_get_indexed_node(tree, index); - - return node ? node->data : NULL; -} - -avl_node_t *avl_get_indexed_node(const avl_tree_t *tree, avl_count_t index) { - avl_node_t *node; - avl_count_t c; - - node = tree->root; - - while(node) { - c = AVL_L_COUNT(node); - - if(index < c) { - node = node->left; - } else if(index > c) { - node = node->right; - index -= c + 1; - } else { - return node; - } - } - - return NULL; -} - -avl_count_t avl_index(const avl_node_t *node) { - avl_node_t *next; - avl_count_t index; - - index = AVL_L_COUNT(node); - - while((next = node->parent)) { - if(node == next->right) - index += AVL_L_COUNT(next) + 1; - node = next; - } - - return index; -} -#endif -#ifdef AVL_DEPTH -avl_depth_t avl_depth(const avl_tree_t *tree) { - return AVL_NODE_DEPTH(tree->root); -} -#endif diff --git a/support/avl.h b/support/avl.h deleted file mode 100644 index ece7d1a..0000000 --- a/support/avl.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - avl.h -- AVL tree management - - Copyright (C) 1998 Michael H. Buselli - 2000-2004 Ivo Timmermans , - 2000-2004 Guus Sliepen - 2000-2004 Wessel Dankers - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Original AVL tree library by Michael H. Buselli . - - Modified 2000-11-28 by Wessel Dankers to use counts - instead of depths, to add the ->next and ->prev and to generally obfuscate - the code. Mail me if you found a bug. - - Cleaned up and incorporated some of the ideas from the red-black tree - library for inclusion into tinc (http://www.tinc-vpn.org/) by - Guus Sliepen . - - $Id$ -*/ - - -#ifndef __AVL_H__ -#define __AVL_H__ - -#ifndef AVL_DEPTH -#ifndef AVL_COUNT -#define AVL_DEPTH -#endif -#endif - -typedef uint32_t avl_count_t; -typedef uint16_t avl_depth_t; - -typedef struct avl_node { - struct avl_node *next; - struct avl_node *prev; - - struct avl_node *parent; - struct avl_node *left; - struct avl_node *right; - -#ifdef AVL_COUNT - avl_count_t count; -#endif - -#ifdef AVL_DEPTH - avl_depth_t depth; -#endif - - void *data; -} avl_node_t; - -typedef int (*avl_compare_t)(const void *, const void *); -typedef void (*avl_action_t)(void *); -typedef void (*avl_node_action_t)(struct avl_node *); - -typedef struct avl_tree { - struct avl_node *head; - struct avl_node *tail; - - struct avl_node *root; - - avl_compare_t compare; - avl_action_t free; -} avl_tree_t; - -/* (De)constructors */ - -extern struct avl_tree *avl_tree_new(avl_compare_t, avl_action_t); -extern void avl_tree_free(struct avl_tree *); - -extern struct avl_node *avl_node_new(void); -extern void avl_node_free(struct avl_tree *tree, struct avl_node *); - -/* Insertion and deletion */ - -extern struct avl_node *avl_add(struct avl_tree *, void *); -extern struct avl_node *avl_add_node(struct avl_tree *, struct avl_node *); - -extern void avl_add_top(struct avl_tree *, struct avl_node *); -extern void avl_add_before(struct avl_tree *, struct avl_node *, struct avl_node *); -extern void avl_add_after(struct avl_tree *, struct avl_node *, struct avl_node *); - -extern struct avl_node *avl_unlink(struct avl_tree *, const void *); -extern void avl_unlink_node(struct avl_tree *tree, struct avl_node *); -extern bool avl_del(struct avl_tree *, void *); -extern void avl_del_node(struct avl_tree *, struct avl_node *); - -/* Fast tree cleanup */ - -extern void avl_tree_del(struct avl_tree *); - -/* Searching */ - -extern void *avl_get(const struct avl_tree *, const void *); -extern void *avl_get_closest(const struct avl_tree *, const void *, int *); -extern void *avl_get_closest_smaller(const struct avl_tree *, const void *); -extern void *avl_get_closest_greater(const struct avl_tree *, const void *); - -extern struct avl_node *avl_get_node(const struct avl_tree *, const void *); -extern struct avl_node *avl_get_closest_node(const struct avl_tree *, const void *, int *); -extern struct avl_node *avl_get_closest_smaller_node(const struct avl_tree *, const void *); -extern struct avl_node *avl_get_closest_greater_node(const struct avl_tree *, const void *); - -/* Tree walking */ - -#define avl_foreach(tree, object, action) {avl_node_t *_node, *_next; \ - for(_node = (tree)->head; _node; _node = _next) { \ - _next = _node->next; \ - (object) = _node->data; \ - action; \ - } \ -} - -#define avl_foreach_node(tree, node, action) {avl_node_t *_next; \ - for((node) = (tree)->head; (node); (node) = _next) { \ - _next = (node)->next; \ - action; \ - } \ -} - -/* Indexing */ - -#ifdef AVL_COUNT -extern avl_count_t avl_count(const struct avl_tree *); -extern avl_count_t avl_index(const struct avl_node *); -extern void *avl_get_indexed(const struct avl_tree *, avl_count_t); -extern struct avl_node *avl_get_indexed_node(const struct avl_tree *, avl_count_t); -#endif -#ifdef AVL_DEPTH -extern avl_depth_t avl_depth(const struct avl_tree *); -#endif - -#endif diff --git a/support/ethernet.h b/support/ethernet.h deleted file mode 100644 index e7ad508..0000000 --- a/support/ethernet.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - 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 deleted file mode 100644 index a907403..0000000 --- a/support/gettext.h +++ /dev/null @@ -1,79 +0,0 @@ -/* 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/ipv4.h b/support/ipv4.h deleted file mode 100644 index 19a290b..0000000 --- a/support/ipv4.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - ipv4.h -- missing IPv4 related definitions - - Copyright (C) 2003-2004 Ivo Timmermans - 2003-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __IPV4_H__ -#define __IPV4_H__ - -#ifndef AF_INET -#define AF_INET 2 -#endif - -#ifndef IPPROTO_ICMP -#define IPPROTO_ICMP 1 -#endif - -#ifndef ICMP_DEST_UNREACH -#define ICMP_DEST_UNREACH 3 -#endif - -#ifndef ICMP_NET_UNKNOWN -#define ICMP_NET_UNKNOWN 6 -#endif - -#ifndef ICMP_NET_UNREACH -#define ICMP_NET_UNREACH 0 -#endif - -#ifndef IP_MSS -#define IP_MSS 576 -#endif - -#ifndef HAVE_STRUCT_IP -struct ip { -#if __BYTE_ORDER == __LITTLE_ENDIAN - unsigned int ip_hl:4; - unsigned int ip_v:4; -#else - unsigned int ip_v:4; - unsigned int ip_hl:4; -#endif - uint8_t ip_tos; - uint16_t ip_len; - uint16_t ip_id; - uint16_t ip_off; -#define IP_RF 0x8000 -#define IP_DF 0x4000 -#define IP_MF 0x2000 -#define IP_OFFMASK 0x1fff - uint8_t ip_ttl; - uint8_t ip_p; - uint16_t ip_sum; - struct in_addr ip_src, ip_dst; -} __attribute__ ((__packed__)); -#endif - -#ifndef HAVE_STRUCT_ICMP -struct icmp { - uint8_t icmp_type; - uint8_t icmp_code; - uint16_t icmp_cksum; - union { - uint8_t ih_pptr; - struct in_addr ih_gwaddr; - struct ih_idseq { - uint16_t icd_id; - uint16_t icd_seq; - } ih_idseq; - uint32_t ih_void; - - - struct ih_pmtu { - uint16_t ipm_void; - uint16_t ipm_nextmtu; - } ih_pmtu; - - struct ih_rtradv { - uint8_t irt_num_addrs; - uint8_t irt_wpa; - uint16_t irt_lifetime; - } ih_rtradv; - } icmp_hun; -#define icmp_pptr icmp_hun.ih_pptr -#define icmp_gwaddr icmp_hun.ih_gwaddr -#define icmp_id icmp_hun.ih_idseq.icd_id -#define icmp_seq icmp_hun.ih_idseq.icd_seq -#define icmp_void icmp_hun.ih_void -#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void -#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu -#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs -#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa -#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime - union { - struct { - uint32_t its_otime; - uint32_t its_rtime; - uint32_t its_ttime; - } id_ts; - struct { - struct ip idi_ip; - } id_ip; - uint32_t id_mask; - uint8_t id_data[1]; - } icmp_dun; -#define icmp_otime icmp_dun.id_ts.its_otime -#define icmp_rtime icmp_dun.id_ts.its_rtime -#define icmp_ttime icmp_dun.id_ts.its_ttime -#define icmp_ip icmp_dun.id_ip.idi_ip -#define icmp_radv icmp_dun.id_radv -#define icmp_mask icmp_dun.id_mask -#define icmp_data icmp_dun.id_data -} __attribute__ ((__packed__)); -#endif - -#endif diff --git a/support/ipv6.h b/support/ipv6.h deleted file mode 100644 index 5de7953..0000000 --- a/support/ipv6.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - ipv6.h -- missing IPv6 related definitions - - Copyright (C) 2003-2004 Ivo Timmermans - 2003-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __IPV6_H__ -#define __IPV6_H__ - -#ifndef AF_INET6 -#define AF_INET6 10 -#endif - -#ifndef IPPROTO_ICMPV6 -#define IPPROTO_ICMPV6 58 -#endif - -#ifndef HAVE_STRUCT_IN6_ADDR -struct in6_addr { - union { - uint8_t u6_addr8[16]; - uint16_t u6_addr16[8]; - uint32_t u6_addr32[4]; - } in6_u; -} __attribute__ ((__packed__)); -#define s6_addr in6_u.u6_addr8 -#define s6_addr16 in6_u.u6_addr16 -#define s6_addr32 in6_u.u6_addr32 -#endif - -#ifndef HAVE_STRUCT_SOCKADDR_IN6 -struct sockaddr_in6 { - uint16_t sin6_family; - uint16_t sin6_port; - uint32_t sin6_flowinfo; - struct in6_addr sin6_addr; - uint32_t sin6_scope_id; -} __attribute__ ((__packed__)); -#endif - -#ifndef IN6_IS_ADDR_V4MAPPED -#define IN6_IS_ADDR_V4MAPPED(a) \ - ((((__const uint32_t *) (a))[0] == 0) \ - && (((__const uint32_t *) (a))[1] == 0) \ - && (((__const uint32_t *) (a))[2] == htonl (0xffff))) -#endif - -#ifndef HAVE_STRUCT_IP6_HDR -struct ip6_hdr { - union { - struct ip6_hdrctl { - uint32_t ip6_un1_flow; - uint16_t ip6_un1_plen; - uint8_t ip6_un1_nxt; - uint8_t ip6_un1_hlim; - } ip6_un1; - uint8_t ip6_un2_vfc; - } ip6_ctlun; - struct in6_addr ip6_src; - struct in6_addr ip6_dst; -} __attribute__ ((__packed__)); -#define ip6_vfc ip6_ctlun.ip6_un2_vfc -#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow -#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen -#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt -#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim -#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim -#endif - -#ifndef HAVE_STRUCT_ICMP6_HDR -struct icmp6_hdr { - uint8_t icmp6_type; - uint8_t icmp6_code; - uint16_t icmp6_cksum; - union { - uint32_t icmp6_un_data32[1]; - uint16_t icmp6_un_data16[2]; - uint8_t icmp6_un_data8[4]; - } icmp6_dataun; -} __attribute__ ((__packed__)); -#define ICMP6_DST_UNREACH_NOROUTE 0 -#define ICMP6_DST_UNREACH 1 -#define ICMP6_DST_UNREACH_ADDR 3 -#define ND_NEIGHBOR_SOLICIT 135 -#define ND_NEIGHBOR_ADVERT 136 -#define icmp6_data32 icmp6_dataun.icmp6_un_data32 -#define icmp6_data16 icmp6_dataun.icmp6_un_data16 -#define icmp6_data8 icmp6_dataun.icmp6_un_data8 -#endif - -#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT -struct nd_neighbor_solicit { - struct icmp6_hdr nd_ns_hdr; - struct in6_addr nd_ns_target; -} __attribute__ ((__packed__)); -#define ND_OPT_SOURCE_LINKADDR 1 -#define ND_OPT_TARGET_LINKADDR 2 -#define nd_ns_type nd_ns_hdr.icmp6_type -#define nd_ns_code nd_ns_hdr.icmp6_code -#define nd_ns_cksum nd_ns_hdr.icmp6_cksum -#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] -#endif - -#ifndef HAVE_STRUCT_ND_OPT_HDR -struct nd_opt_hdr { - uint8_t nd_opt_type; - uint8_t nd_opt_len; -} __attribute__ ((__packed__)); -#endif - -#endif diff --git a/support/list.c b/support/list.c deleted file mode 100644 index 9e54ac0..0000000 --- a/support/list.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - 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 deleted file mode 100644 index 124d7fc..0000000 --- a/support/list.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - 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/support/sockaddr.h b/support/sockaddr.h deleted file mode 100644 index 8cd1c5e..0000000 --- a/support/sockaddr.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - sockaddr.h -- sockaddr handling - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __SOCKADDR_H__ -#define __SOCKADDR_H__ - -#define AF_UNKNOWN 255 - -struct sockaddr_unknown { - uint16_t family; - uint16_t pad1; - uint32_t pad2; - char *address; - char *port; -}; - -#define sa(s) ((struct sockaddr *)(s)) -#ifdef SA_LEN -#define sa_len(s) SA_LEN((struct sockaddr *)(s)) -#else -#define sa_len(s) (((struct sockaddr *)(s))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) -#endif - -#define sa_family(s) (((struct sockaddr *)(s))->sa_family) - -#define sa_unmap(s) ({if(((struct sockaddr *)(s))->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)(s))->sin6_addr)) { \ - ((struct sockaddr_in *)(s))->sin_addr.s_addr = ((struct sockaddr_in6 *)(s))->sin6_addr.s6_addr32[3]; \ - ((struct sockaddr *)(s))->sa_family = AF_INET; \ -} \ -s;}) - -#endif diff --git a/support/xalloc.c b/support/xalloc.c deleted file mode 100644 index 80699f9..0000000 --- a/support/xalloc.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - xalloc.c -- safe memory allocation functions - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include -#include -#include - -#include "xalloc.h" - -static void xalloc_fail(void) { - fprintf(stderr, "Memory exhausted\n"); - exit(1); -} - -void (*xalloc_fail_func)(void) = xalloc_fail; - -void *xmalloc(size_t n) { - void *p; - - p = malloc(n); - - if(!p) - xalloc_fail_func(); - - return p; -} - -void *xrealloc(void *p, size_t n) { - p = realloc(p, n); - - if(!p) - xalloc_fail_func(); - - return p; -} - -void *xcalloc(size_t n, size_t s) { - void *p; - - p = calloc(n, s); - - if(!p) - xalloc_fail_func(); - - return p; -} - -char *xstrdup(const char *s) { - char *p; - - p = strdup(s); - - if(!p) - xalloc_fail_func(); - - return p; -} - diff --git a/support/xalloc.h b/support/xalloc.h deleted file mode 100644 index db2e531..0000000 --- a/support/xalloc.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - xalloc.h -- safe memory allocation functions - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __XALLOC_H__ -#define __XALLOC_H__ - -#define new(object) ({(object) = xmalloc(sizeof *(object));}) -#define dim(object, count) ({(object) = xmalloc(sizeof *(object) * (count));}) -#define redim(object, count) ({(object) = xrealloc((object), sizeof *(object) * (count));}) -#define copy(object) ({typeof(object) _copy; *(_copy = xmalloc(sizeof *(object))) = *(object); _copy;}) -#define clear(object) ({memset((object), 0, sizeof *(object));}) -#define replace(string, replacement) ({if(string) free(string); (string) = (replacement) ? xstrdup(replacement) : NULL;}) - -void *xmalloc(size_t n) __attribute__ ((__malloc__)); -void *xrealloc(void *p, size_t n) __attribute__ ((__malloc__)); -char *xstrdup(const char *s) __attribute__ ((__malloc__)); - -#endif diff --git a/system.h b/system.h deleted file mode 100644 index 1e2f4aa..0000000 --- a/system.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - system.h -- system headers - - Copyright (C) 1998-2004 Ivo Timmermans - 2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - $Id$ -*/ - -#ifndef __TINC_SYSTEM_H__ -#define __TINC_SYSTEM_H__ - -#include "config.h" - -/* Include standard headers */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_STDBOOL_H -#include -#else -typedef int bool; -#define true 1 -#define false 0 -#endif - -#ifdef HAVE_TERMIOS_H -#include -#endif - -#ifdef HAVE_INTTYPES_H -#include -#endif - -/* Include system specific headers */ - -#ifdef HAVE_SYSLOG_H -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#ifdef HAVE_SYS_FILE_H -#include -#endif - -#ifdef HAVE_SYS_WAIT_H -#include -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#ifdef HAVE_SYS_PARAM_H -#include -#endif - -/* SunOS really wants sys/socket.h BEFORE net/if.h, - and FreeBSD wants these lines below the rest. */ - -#ifdef HAVE_NETDB_H -#include -#endif - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - -#ifdef HAVE_NET_IF_H -#include -#endif - -#ifdef HAVE_NETINET_IN_SYSTM_H -#include -#endif - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_ARPA_INET_H -#include -#endif - -#ifdef HAVE_NETINET_IP_H -#include -#endif - -#ifdef HAVE_NETINET_TCP_H -#include -#endif - -#ifdef HAVE_NETINET_IN6_H -#include -#endif - -#ifdef HAVE_NETINET_IP6_H -#include -#endif - -#ifdef HAVE_MINGW -#include -#include -#endif - -/* Include localisation support */ - -#include "support/gettext.h" - -#if 0 - -#ifndef HAVE_STRSIGNAL -# define strsignal(p) "" -#endif - -/* Other functions */ - -#include "dropin.h" - -#endif - -#ifndef HAVE_SOCKLEN_T -typedef int socklen_t; -#endif - -#endif /* __TINC_SYSTEM_H__ */ diff --git a/tincd.c b/tincd.c deleted file mode 100644 index 2e40152..0000000 --- a/tincd.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - tincd.c -- the main file for tincd - - Copyright (C) 2000-2004 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include - -/* 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" -#include "tnl/tnl.h" -#include "vnd/vnd.h" - -static bool show_help = false; -static bool show_version = false; -static int kill_tincd = 0; -static bool bypass_security = false; -static bool do_mlock = false; -static bool do_detach = true; -static int debug_level = 1; - -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; -avl_tree_t *tinc_cfg; - -static struct option const long_options[] = { - {"config", required_argument, NULL, 'c'}, - {"kill", optional_argument, NULL, 'k'}, - {"net", required_argument, NULL, 'n'}, - {"help", no_argument, NULL, 1}, - {"version", no_argument, NULL, 2}, - {"no-detach", no_argument, NULL, 'D'}, - {"debug", optional_argument, NULL, 'd'}, - {"bypass-security", no_argument, NULL, 3}, - {"mlock", no_argument, NULL, 'L'}, - {"logfile", optional_argument, NULL, 4}, - {"pidfile", required_argument, NULL, 5}, - {NULL, 0, NULL, 0} -}; - -#ifdef HAVE_MINGW -static struct WSAData wsa_state; -#endif - -static void usage(bool status) { - if(status) - fprintf(stderr, _("Try `%s --help\' for more information.\n"), tinc_argv[0]); - else { - printf(_("Usage: %s [option]...\n\n"), tinc_argv[0]); - printf(_(" -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID to FILENAME.\n" - " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n")); - printf(_("Report bugs to tinc@tinc-vpn.org.\n")); - } -} - -static bool parse_options(int argc, char **argv) { - int result; - int option_index = 0; - - while((result = getopt_long(argc, argv, "c:DLd::k::n:", long_options, &option_index)) != EOF) { - switch (result) { - case 0: - break; - - case 'c': /* --config */ - tinc_confbase = xstrdup(optarg); - break; - - case 'D': /* --no-detach */ - do_detach = false; - break; - - case 'L': /* --mlock */ - do_mlock = true; - break; - - case 'd': /* --debug */ - if(optarg) - debug_level = atoi(optarg); - else - debug_level++; - break; - - case 'k': /* --kill */ -#ifndef HAVE_MINGW - if(optarg) { - if(!strcasecmp(optarg, "HUP")) - kill_tincd = SIGHUP; - else if(!strcasecmp(optarg, "TERM")) - kill_tincd = SIGTERM; - else if(!strcasecmp(optarg, "KILL")) - kill_tincd = SIGKILL; - else if(!strcasecmp(optarg, "USR1")) - kill_tincd = SIGUSR1; - else if(!strcasecmp(optarg, "USR2")) - kill_tincd = SIGUSR2; - else if(!strcasecmp(optarg, "WINCH")) - kill_tincd = SIGWINCH; - else if(!strcasecmp(optarg, "INT")) - kill_tincd = SIGINT; - else if(!strcasecmp(optarg, "ALRM")) - kill_tincd = SIGALRM; - else { - kill_tincd = atoi(optarg); - - if(!kill_tincd) { - fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), - optarg); - usage(true); - return false; - } - } - } else - kill_tincd = SIGTERM; -#else - kill_tincd = 1; -#endif - break; - - case 'n': /* --net */ - tinc_netname = xstrdup(optarg); - break; - - case 1: /* --help */ - show_help = true; - break; - - case 2: /* --version */ - show_version = true; - break; - - case 3: /* --bypass-security */ - bypass_security = true; - break; - - case 4: /* --logfile */ - tinc_use_logfile = true; - if(optarg) - tinc_logfilename = xstrdup(optarg); - break; - - case 5: /* --pidfile */ - tinc_pidfilename = xstrdup(optarg); - break; - - case '?': - usage(true); - return false; - - default: - break; - } - } - - return true; -} - -static void make_names(void) -{ -#ifdef HAVE_MINGW - HKEY key; - char installdir[1024] = ""; - long len = sizeof(installdir); -#endif - - if(tinc_netname) - asprintf(&tinc_identname, "tinc.%s", tinc_netname); - else - 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(!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(&tinc_confbase, "%s", installdir); - } - } - RegCloseKey(key); - if(*installdir) - return; - } -#endif - - if(!tinc_pidfilename) - asprintf(&tinc_pidfilename, LOCALSTATEDIR "/run/%s.pid", tinc_identname); - - if(!tinc_logfilename) - asprintf(&tinc_logfilename, LOCALSTATEDIR "/log/%s.log", tinc_identname); - - if(!tinc_confbase) { - if(tinc_netname) - asprintf(&tinc_confbase, CONFDIR "/tinc/%s", tinc_netname); - else - asprintf(&tinc_confbase, CONFDIR "/tinc"); - } - - asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase); -} - -int main(int argc, char **argv) { - tinc_argc = argc; - tinc_argv = argv; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - if(!parse_options(argc, argv)) - return 1; - - make_names(); - - if(show_version) { - 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" - "and you are welcome to redistribute it under certain conditions;\n" - "see the file COPYING for details.\n")); - - return 0; - } - - if(show_help) { - usage(false); - return 0; - } - - if(kill_tincd) - return !kill_other(kill_tincd); - - logger_init("tinc", tinc_use_logfile ? LOGGER_MODE_FILE : LOGGER_MODE_STDERR); - - /* Lock all pages into memory if requested */ - - if(do_mlock) -#ifdef HAVE_MLOCKALL - if(mlockall(MCL_CURRENT | MCL_FUTURE)) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall", - strerror(errno)); -#else - { - logger(LOG_ERR, _("mlockall() not supported on this platform!")); -#endif - return -1; - } - - tinc_cfg = cfg_tree_new(); - - asprintf(&tinc_cfgfilename, "%s/tinc.conf", tinc_confbase); - - if(!cfg_read_file(tinc_cfg, tinc_cfgfilename)) - return 1; - -#ifdef HAVE_MINGW - if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) { - logger(LOG_ERR, _("System call `%s' failed: %s"), "WSAStartup", winerror(GetLastError())); - return 1; - } -#endif - - if(do_detach && !detach()) - return 1; - - 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(); - fd_exit(); -end: - logger(LOG_NOTICE, _("Terminating")); - -#ifndef HAVE_MINGW - remove_pid(tinc_pidfilename); -#endif - - logger_exit(); - - return 0; -} diff --git a/tincd.h b/tincd.h deleted file mode 100644 index b2e6620..0000000 --- a/tincd.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - tincd.h -- tinc specific global variables and functions - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id -*/ - -#ifndef __TINCD_H__ -#define __TINCD_H__ - -#include "support/avl.h" - -extern char *tinc_confbase; -extern char *tinc_netname; -extern char *tinc_identname; -extern char *tinc_pidfilename; -extern char *tinc_logfilename; -extern char *tinc_cfgfilename; - -extern bool tinc_use_logfile; - -extern int tinc_argc; -extern char **tinc_argv; -extern avl_tree_t *tinc_cfg; - -extern bool remove_pid(const char *pidfilename); - -#endif diff --git a/tnl/Makefile.am b/tnl/Makefile.am deleted file mode 100644 index 594838d..0000000 --- a/tnl/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = libtnl.a - -noinst_HEADERS = tnl.h - -libtnl_a_SOURCES = tnl.c - diff --git a/tnl/test.c b/tnl/test.c deleted file mode 100644 index 65810c2..0000000 --- a/tnl/test.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - test.c -- tunnel test - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id: tnl.c 1379 2004-03-27 11:59:31Z guus $ -*/ - -#include "system.h" - -#include -#include - -#include "logger/logger.h" -#include "support/avl.h" -#include "support/sockaddr.h" -#include "support/xalloc.h" -#include "tnl/tnl.h" - -static const int addressfamily = AF_UNSPEC; -static const int socktype = SOCK_STREAM; -static const int protocol = IPPROTO_TCP; - -bool server_recv_meta(struct tnl *tnl, const void *buf, int len) { - const char *in = buf; - char out[len]; - - for(int i = 0; i < len; i++) { - if(isupper(in[i])) - out[i] = tolower(in[i]); - else if(islower(in[i])) - out[i] = toupper(in[i]); - else - out[i] = in[i]; - } - - tnl->send_meta(tnl, out, len); - - return true; -} - -bool server_accept(struct tnl *tnl) { - logger(LOG_INFO, _("Got connection from %s"), tnl->remote.id); - tnl->recv_meta = server_recv_meta; - return true; -} - -void server(char *port) { - struct addrinfo *ai, hint = {0}; - int err; - tnl_listen_t *listen = clear(new(listen)); - - hint.ai_family = addressfamily; - hint.ai_socktype = socktype; - hint.ai_protocol = protocol; - hint.ai_flags = AI_PASSIVE; - - err = getaddrinfo(NULL, port, &hint, &ai); - - if(err || !ai) { - logger(LOG_WARNING, _("Error looking up port %s: %s"), port, gai_strerror(err)); - return; - } - - if(sizeof listen->local.address < ai->ai_addrlen) { - logger(LOG_ERR, "%d < %d!", sizeof listen->local.address, ai->ai_addrlen); - return; - } - - memcpy(&listen->local.address, ai->ai_addr, ai->ai_addrlen); - listen->local.id = xstrdup("CommonA"); - listen->type = socktype; - listen->protocol = protocol; - listen->accept = server_accept; - - logger(LOG_DEBUG, "Nu ga ik iets doen hoor"); - if(!tnl_ep_set_x509_credentials(&listen->local, "server_key", "server_cert", "trust", NULL)) { - logger(LOG_ERR, "Couldn't set X.509 credentials!"); - return; - } - - if(!tnl_listen(listen)) { - logger(LOG_ERR, _("Could not listen!")); - return; - } -} - -bool client_stdin_handler(fd_t *fd) { - tnl_t *tnl = fd->data; - char buf[1024]; - int len; - - len = read(fd->fd, buf, sizeof buf); - - if(len <= 0) { - gnutls_bye(tnl->session, GNUTLS_SHUT_WR); - fd_del(fd); - return false; - } - - tnl->send_meta(tnl, buf, len); - - return true; -} - -bool client_recv_meta(struct tnl *tnl, const void *buf, int len) { - write(1, buf, len); - return true; -} - -bool client_error(tnl_t *tnl, int err) { - exit(err); -} - -bool client_accept(tnl_t *tnl) { - fd_t *fd; - - logger(LOG_INFO, _("Connected to %s"), tnl->remote.id); - tnl->recv_meta = client_recv_meta; - - clear(new(fd)); - fd->fd = 0; - fd->read = client_stdin_handler; - fd->data = tnl; - fd_add(fd); - - return true; -} - -void client(char *host, char *port) { - struct addrinfo *ai, hint = {0}; - int err; - static tnl_t *tnl; - - hint.ai_family = addressfamily; - hint.ai_socktype = socktype; - - err = getaddrinfo(host, port, &hint, &ai); - - if(err || !ai) { - logger(LOG_WARNING, _("Error looking up %s port %s: %s"), host, port, gai_strerror(err)); - return; - } - - clear(new(tnl)); - memcpy(&tnl->remote.address, ai->ai_addr, ai->ai_addrlen); - tnl->local.id = xstrdup("CommonB"); - tnl->remote.id = xstrdup("CommonA"); - tnl->type = socktype; - tnl->protocol = protocol; - tnl->accept = client_accept; - tnl->error = client_error; - - if(!tnl_ep_set_x509_credentials(&tnl->local, "client_key", "client_cert", "trust", NULL)) { - logger(LOG_ERR, "Couldn't set credentials!"); - return; - } - - if(!tnl_connect(tnl)) { - logger(LOG_ERR, _("Could not connect to server!")); - return; - } -} - -int main(int argc, char **argv) { - gnutls_global_init(); - gnutls_global_init_extra(); - - fd_init(); - logger_init(argv[0], LOGGER_MODE_NULL); - - if(argc > 2) - client(argv[1], argv[2]); - else if(argc > 1) - server(argv[1]); - else { - logger(LOG_ERR, "Usage: %s [host] port\n", argv[0]); - return 1; - } - - fd_run(); - - return 0; -} diff --git a/tnl/tnl.c b/tnl/tnl.c deleted file mode 100644 index 75f82f7..0000000 --- a/tnl/tnl.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - tnl.c -- tunnels - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include -#include - -#include "logger/logger.h" -#include "support/avl.h" -#include "support/sockaddr.h" -#include "support/xalloc.h" -#include "tnl/tnl.h" - -static bool tnl_send(tnl_t *tnl, const void *buf, int len) { - int result; - - while(len) { - result = gnutls_record_send(tnl->session, buf, len); - if(result <= 0) { - if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) - continue; - - if(result) - logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result)); - else - logger(LOG_INFO, _("tnl: connection closed by peer")); - - if(tnl->error) - tnl->error(tnl, result); - tnl->close(tnl); - return !result; - } - - buf += result; - len -= result; - } - - return true; -} - -static bool tnl_recv(tnl_t *tnl) { - tnl_record_t *record = (tnl_record_t *)tnl->buf; - -#if 0 - int result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread); - if(result <= 0) { - if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) - return true; - - if(result) - logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result)); - else - logger(LOG_INFO, _("tnl: connection closed by peer")); - - if(tnl->error) - tnl->error(tnl, result); - tnl->close(tnl); - return !result; - } - - tnl->bufread += result; -#endif - - while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) { - switch(record->type) { - case TNL_RECORD_META: - if(tnl->recv_meta) - tnl->recv_meta(tnl, record->data, record->len); - break; - - case TNL_RECORD_PACKET: - if(tnl->recv_packet) - tnl->recv_packet(tnl, record->data, record->len); - break; - - default: - logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type")); - if(tnl->error) - tnl->error(tnl, EINVAL); - tnl->close(tnl); - return false; - } - - tnl->bufread -= sizeof *record + record->len; - memmove(tnl->buf, record->data + record->len, tnl->bufread); - } - - return true; -} - -static bool tnl_recv_handler(fd_t *fd) { - if(!fd) - abort(); - - tnl_t *tnl = fd->data; - int result; - - result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread); - if(result <= 0) { - if(!result) { - logger(LOG_DEBUG, _("tnl: connection closed by peer %s (%s)"), tnl->remote.id, tnl->remote.hostname); - if(tnl->error) - tnl->error(tnl, 0); - tnl->close(tnl); - return false; - } - - if(gnutls_error_is_fatal(result)) { - logger(LOG_DEBUG, _("tnl: reception failed: %s"), gnutls_strerror(result)); - if(tnl->error) - tnl->error(tnl, result); - tnl->close(tnl); - return false; - } - - return true; - } - - tnl->bufread += result; - return tnl_recv(tnl); -} - -bool tnl_ep_set_x509_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *certificate, const char *trust, const char *crl) { - int err; - - if(tnl_ep->cred.certificate) { - gnutls_certificate_free_credentials(tnl_ep->cred.certificate); - tnl_ep->cred.certificate = NULL; - } - - if((err = gnutls_certificate_allocate_credentials(&tnl_ep->cred.certificate)) < 0) { - logger(LOG_ERR, _("Failed to allocate certificate credentials: %s"), gnutls_strerror(err)); - return false; - } - - if((err = gnutls_certificate_set_x509_key_file(tnl_ep->cred.certificate, certificate, privkey, GNUTLS_X509_FMT_PEM)) < 0) { - logger(LOG_ERR, _("Failed to load X.509 key and/or certificate: %s"), gnutls_strerror(err)); - return false; - } - - tnl_ep->cred.type = GNUTLS_CRD_CERTIFICATE; - - if(trust && (err = gnutls_certificate_set_x509_trust_file(tnl_ep->cred.certificate, trust, GNUTLS_X509_FMT_PEM)) < 0) { - logger(LOG_ERR, _("Failed to set X.509 trust file: %s"), gnutls_strerror(err)); - return false; - } - - if(crl && (err = gnutls_certificate_set_x509_crl_file(tnl_ep->cred.certificate, crl, GNUTLS_X509_FMT_PEM)) < 0) { - logger(LOG_ERR, _("Failed to set X.509 CRL file: %s"), gnutls_strerror(err)); - return false; - } - - //gnutls_certificate_set_verify_flags(tnl_ep->cred.certificate, GNUTLS_VERIFY_DISABLE_CA_SIGN); - - return true; -} - -bool tnl_ep_set_openpgp_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *pubkey, const char *keyring, const char *trustdb) { - int err; - - if(tnl_ep->cred.certificate) { - gnutls_certificate_free_credentials(tnl_ep->cred.certificate); - tnl_ep->cred.certificate = NULL; - } - - if((err = gnutls_certificate_allocate_credentials(&tnl_ep->cred.certificate)) < 0) { - logger(LOG_ERR, _("Failed to allocate certificate credentials: %s"), gnutls_strerror(err)); - return false; - } - - if((err = gnutls_certificate_set_openpgp_key_file(tnl_ep->cred.certificate, pubkey, privkey)) < 0) { - logger(LOG_ERR, _("Failed to load public and/or private OpenPGP key: %s"), gnutls_strerror(err)); - return false; - } - - tnl_ep->cred.type = GNUTLS_CRD_CERTIFICATE; - - if(keyring && (err = gnutls_certificate_set_openpgp_keyring_file(tnl_ep->cred.certificate, keyring)) < 0) { - logger(LOG_ERR, _("Failed to set OpenPGP keyring file: %s"), gnutls_strerror(err)); - return false; - } - - if(trustdb && (err = gnutls_certificate_set_openpgp_trustdb(tnl_ep->cred.certificate, trustdb)) < 0) { - logger(LOG_ERR, _("Failed to set OpenPGP trustdb file: %s"), gnutls_strerror(err)); - return false; - } - - //gnutls_certificate_set_verify_flags(tnl_ep->cred.certificate, GNUTLS_VERIFY_DISABLE_CA_SIGN); - - return true; -} - -static bool tnl_authenticate_x509(tnl_t *tnl) { - gnutls_x509_crt cert; - const gnutls_datum *certs; - int ncerts = 0, result; - char name[1024]; - int len; - - certs = gnutls_certificate_get_peers(tnl->session, &ncerts); - - if (!certs || !ncerts) { - logger(LOG_ERR, _("tnl: no certificates from %s"), tnl->remote.hostname); - return false; - } - - gnutls_x509_crt_init(&cert); - result = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER); - - if(result) { - logger(LOG_ERR, _("tnl: error importing certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(result)); - gnutls_x509_crt_deinit(cert); - return false; - } - - len = sizeof name; - result = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &len); - gnutls_x509_crt_deinit(cert); - - if(result) { - logger(LOG_ERR, _("tnl: could not extract common name from certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(result)); - return false; - } - - if(len > sizeof name) { - logger(LOG_ERR, _("tnl: common name from certificate from %s too long"), tnl->remote.hostname); - return false; - } - - if(tnl->remote.id && strcmp(tnl->remote.id, name)) { - logger(LOG_ERR, _("tnl: peer %s is %s instead of %s"), tnl->remote.hostname, name, tnl->remote.id); - return false; - } - - replace(tnl->remote.id, xstrdup(name)); - - result = gnutls_certificate_verify_peers(tnl->session); - - if(result < 0) { - logger(LOG_ERR, "tnl: error verifying certificate from %s (%s): %s", tnl->remote.id, tnl->remote.hostname, gnutls_strerror(result)); - return false; - } - - if(result) { - logger(LOG_ERR, "tnl: certificate from %s (%s) not good, verification result %x", tnl->remote.id, tnl->remote.hostname, result); - return false; - } - - return true; -} - -static bool tnl_authenticate(tnl_t *tnl) { - switch(tnl->local.cred.type) { - case GNUTLS_CRD_CERTIFICATE: - switch(gnutls_certificate_type_get(tnl->session)) { - case GNUTLS_CRT_X509: - return tnl_authenticate_x509(tnl); - case GNUTLS_CRT_OPENPGP: - //return tnl_authenticate_openpgp(tnl); - default: - logger(LOG_ERR, "tnl: unknown certificate type for session with %s (%s)", tnl->remote.id, tnl->remote.hostname); - return false; - } - - case GNUTLS_CRD_ANON: - logger(LOG_ERR, "tnl: anonymous authentication not yet supported"); - return false; - - case GNUTLS_CRD_SRP: - logger(LOG_ERR, "tnl: SRP authentication not yet supported"); - return false; - - default: - logger(LOG_ERR, "tnl: unknown authentication type for session with %s (%s)", tnl->remote.id, tnl->remote.hostname); - return false; - } -} - -static bool tnl_handshake_handler(fd_t *fd) { - //char id[1024]; - tnl_t *tnl = fd->data; - int result; - - result = gnutls_handshake(tnl->session); - if(result < 0) { - if(gnutls_error_is_fatal(result)) { - logger(LOG_ERR, "tnl: handshake error: %s", gnutls_strerror(result)); - tnl->close(tnl); - return false; - } - - /* check other stuff? */ - return true; - } - - logger(LOG_DEBUG, _("tnl: handshake finished")); - - if(!tnl_authenticate(tnl)) - return false; - - tnl->status = TNL_STATUS_UP; - tnl->fd.read = tnl_recv_handler; - if(tnl->accept) - tnl->accept(tnl); - - return true; -} - -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, &record, sizeof record) && tnl_send(tnl, buf, 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, &record, sizeof record) && tnl_send(tnl, buf, len); -} - -static bool tnl_close(tnl_t *tnl) { - if(tnl->session) { - gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR); - gnutls_deinit(tnl->session); - } - - fd_del(&tnl->fd); - close(tnl->fd.fd); - - return true; -} - -static bool tnl_accept_handler(fd_t *fd) { - tnl_listen_t *listener = fd->data; - tnl_t *tnl; - struct sockaddr_storage ss; - socklen_t len = sizeof ss; - int sock; - - sock = accept(fd->fd, sa(&ss), &len); - - if(sock == -1) { - logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno)); - return false; - } - - sa_unmap(&ss); - - logger(LOG_DEBUG, _("tnl: accepted incoming connection")); - - clear(new(tnl)); - tnl->local = listener->local; - tnl->remote.address = ss; - len = sizeof tnl->local.address; - getsockname(sock, sa(&tnl->local.address), &len); - sa_unmap(&tnl->local.address); - tnl->type = listener->type; - tnl->protocol = listener->protocol; - tnl->send_packet = tnl_send_packet; - tnl->send_meta = tnl_send_meta; - tnl->close = tnl_close; - - tnl->fd.fd = sock; - tnl->fd.read = tnl_handshake_handler; - tnl->fd.data = tnl; - - fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); - - tnl->status = TNL_STATUS_HANDSHAKE; - gnutls_init(&tnl->session, GNUTLS_SERVER); - //gnutls_handshake_set_private_extensions(tnl->session, 1); - gnutls_set_default_priority(tnl->session); - gnutls_credentials_set(tnl->session, tnl->local.cred.type, tnl->local.cred.certificate); - gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); - gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)sock); - - tnl->accept = listener->accept; - - fd_add(&tnl->fd); - - tnl_handshake_handler(&tnl->fd); - - return true; -} - -static bool tnl_connect_handler(fd_t *fd) { - tnl_t *tnl = fd->data; - int result; - socklen_t len; - - len = sizeof result; - getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len); - if(result) { - logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result)); - if(tnl->error) - tnl->error(tnl, result); - tnl->close(tnl); - return false; - } - - logger(LOG_DEBUG, _("tnl: connected")); - - fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK); - - tnl->status = TNL_STATUS_HANDSHAKE; - gnutls_init(&tnl->session, GNUTLS_CLIENT); - //gnutls_handshake_set_private_extensions(tnl->session, 1); - gnutls_set_default_priority(tnl->session); - gnutls_credentials_set(tnl->session, tnl->local.cred.type, tnl->local.cred.certificate); - gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST); - gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd); - - tnl->fd.write = NULL; - tnl->fd.read = tnl_handshake_handler; - fd_mod(&tnl->fd); - - tnl_handshake_handler(&tnl->fd); - - return true; -} - -bool tnl_connect(tnl_t *tnl) { - int sock; - - sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol); - - if(sock == -1) { - logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno)); - return false; - } - -#if 0 - if(sa_nonzero(&tnl->local.address) && bind(sock, sa(&tnl->local.address), sa_len(&tnl->local.address)) == -1) { - logger(LOG_ERR, _("tnl: could not bind socket: %s"), strerror(errno)); - close(sock); - return false; - } -#endif - - if(connect(sock, sa(&tnl->remote.address), sa_len(&tnl->remote.address)) == -1) { - logger(LOG_ERR, _("tnl: could not connect: %s"), strerror(errno)); - close(sock); - return false; - } - - tnl->status = TNL_STATUS_CONNECTING; - - tnl->fd.fd = sock; - tnl->fd.write = tnl_connect_handler; - tnl->fd.data = tnl; - - tnl->send_packet = tnl_send_packet; - tnl->send_meta = tnl_send_meta; - tnl->close = tnl_close; - - fd_add(&tnl->fd); - - return true; -} - -static bool tnl_listen_close(tnl_listen_t *listener) { - fd_del(&listener->fd); - close(listener->fd.fd); - return true; -} - -bool tnl_listen(tnl_listen_t *listener) { - int sock; - - sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol); - - if(sock == -1) { - logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno)); - return false; - } - - if(bind(sock, sa(&listener->local.address), sa_len(&listener->local.address)) == -1) { - logger(LOG_ERR, _("tnl: could not bind listener socket: %s"), strerror(errno)); - return false; - } - - if(listen(sock, 10) == -1) { - logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno)); - return false; - } - - listener->fd.fd = sock; - listener->fd.read = tnl_accept_handler; - listener->fd.data = listener; - listener->close = tnl_listen_close; - - fd_add(&listener->fd); - - return true; -} diff --git a/tnl/tnl.h b/tnl/tnl.h deleted file mode 100644 index 77a8b2a..0000000 --- a/tnl/tnl.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - tnl.h -- tunnels - - Copyright (C) 2003-2004 Guus Sliepen , - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __TNL_H__ -#define __TNL_H__ - -#include -#include - -#include "fd/fd.h" - -#define TNL_PROTOCOL 0 - -#define TNL_RECORD_PACKET 0 -#define TNL_RECORD_META 1 -#define TNL_RECORD_HELLO 2 -#define TNL_RECORD_BLA 3 - -typedef struct tnl_record { - uint16_t type; - uint16_t len; - char data[]; -} tnl_record_t; - -typedef enum tnl_status { - TNL_STATUS_DOWN, - TNL_STATUS_CONNECTING, - TNL_STATUS_HANDSHAKE, - TNL_STATUS_UP, -} tnl_status_t; - -typedef struct tnl_ep_credentials { - gnutls_credentials_type type; - union { - gnutls_anon_client_credentials anon_client; - gnutls_anon_server_credentials anon_server; - gnutls_srp_client_credentials srp_client; - gnutls_srp_server_credentials srp_server; - gnutls_certificate_credentials certificate; - }; -} tnl_ep_credentials_t; - -typedef struct tnl_ep_cryptoparm { -} tnl_ep_cryptoparm_t; - -typedef struct tnl_ep { - struct sockaddr_storage address; - char *id; - char *hostname; - struct tnl_ep_credentials cred; - struct tnl_ep_cryptoparm parm; -} tnl_ep_t; - -typedef struct tnl { - struct tnl_ep local; - struct tnl_ep remote; - int type; - int protocol; - int mtu; - enum tnl_status status; - void *data; - - bool (*send_packet)(struct tnl *tnl, const 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 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); - - /* private */ - - gnutls_session session; - struct fd fd; - char buf[4096]; - int bufread; -} tnl_t; - -typedef struct tnl_listen { - struct tnl_ep local; - int type; - int protocol; - - bool (*accept)(struct tnl *tnl); - bool (*close)(struct tnl_listen *listener); - - struct fd fd; -} tnl_listen_t; - -extern bool tnl_listen(struct tnl_listen *listener); -extern bool tnl_connect(struct tnl *tnl); - -extern bool tnl_ep_set_x509_credentials(tnl_ep_t *tnl_ep, const char *key, const char *certificate, const char *trust, const char *crl); -extern bool tnl_ep_set_openpgp_credentials(tnl_ep_t *tnl_ep, const char *privkey, const char *pubkey, const char *keyring, const char *trustdb); - -#endif /* __TNL_H__ */ diff --git a/vnd/Makefile.am b/vnd/Makefile.am deleted file mode 100644 index e542298..0000000 --- a/vnd/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LIBRARIES = libvnd.a - -noinst_HEADERS = vnd.h - -libvnd_a_SOURCES = vnd.c - diff --git a/vnd/vnd.c b/vnd/vnd.c deleted file mode 100644 index 7eba993..0000000 --- a/vnd/vnd.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - vnd.c -- virtual network device management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#include "system.h" - -#include - -#include "fd/fd.h" -#include "logger/logger.h" -#include "support/xalloc.h" - -#include "vnd/vnd.h" - -vnd_t *vnd_new(void) { - vnd_t *vnd; - - return clear(new(vnd)); -} - -void vnd_free(vnd_t *vnd) { - replace(vnd->device, NULL); - replace(vnd->interface, NULL); - replace(vnd->description, NULL); - free(vnd); -} - -void vnd_set(vnd_t *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv) { - replace(vnd->device, device); - replace(vnd->interface, interface); - vnd->mode = mode; - vnd->recv = recv; -} - -static bool vnd_send(vnd_t *vnd, const void *buf, int len) { - int result; - - result = write(vnd->fd.fd, buf, len); - - if(result == len || result < 0 && (errno == EINTR || errno == EAGAIN)) { - logger(LOG_INFO, _("vnd: wrote packet of %d bytes to %s"), len, vnd->description); - return true; - } - - logger(LOG_INFO, _("vnd: error writing packet of %d bytes to %s: %s"), len, vnd->description, strerror(errno)); - - return false; -} - -static bool vnd_recv_handler(fd_t *fd) { - vnd_t *vnd = fd->data; - char buf[vnd->mtu]; - int len; - - vnd = fd->data; - - len = read(fd->fd, buf, sizeof buf); - - if(len > 0) { - logger(LOG_INFO, _("vnd: read packet of %d bytes from %s"), len, vnd->description); - return vnd->recv(vnd, buf, len); - } - - if(len < 0 && (errno == EINTR || errno == EAGAIN)) - return true; - - logger(LOG_ERR, _("vnd: error reading packet from %s: %s"), vnd->description, strerror(errno)); - - return false; -} - -bool vnd_open(vnd_t *vnd) { - struct ifreq ifr = {0}; - - if(!vnd->device) - vnd->device = xstrdup("/dev/net/tun"); - - vnd->fd.fd = open(vnd->device, O_RDWR | O_NONBLOCK); - - if(vnd->fd.fd < 0) { - logger(LOG_ERR, _("vnd: could not open %s: %s"), vnd->device, strerror(errno)); - return false; - } - - if(vnd->mode == VND_MODE_TUN) - ifr.ifr_flags = IFF_TUN; - else - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - - if(vnd->interface) - strncpy(ifr.ifr_name, vnd->interface, IFNAMSIZ); - - if(!ioctl(vnd->fd.fd, TUNSETIFF, &ifr)) { - if(vnd->interface) - free(vnd->interface); - vnd->interface = xstrdup(ifr.ifr_name); - } else { - logger(LOG_ERR, _("vnd: %s is not a Linux tun/tap device"), vnd->device); - return false; - } - - if(!vnd->mtu) - vnd->mtu = 1514; - - vnd->send = vnd_send; - vnd->fd.read = vnd_recv_handler; - vnd->fd.data = vnd; - - if(vnd->description) - free(vnd->description); - - asprintf(&vnd->description, "Linux tun/tap device %s (interface %s)", vnd->device, vnd->interface); - - if(!fd_add(&vnd->fd)) - return false; - - logger(LOG_INFO, _("vnd: opened %s"), vnd->description); - - return true; -} - -bool vnd_close(vnd_t *vnd) { - fd_del(&vnd->fd); - - close(vnd->fd.fd); - - logger(LOG_INFO, _("vnd: closed %s"), vnd->description); - - return true; -} - diff --git a/vnd/vnd.h b/vnd/vnd.h deleted file mode 100644 index 47f8c6a..0000000 --- a/vnd/vnd.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - vnd.c -- virtual network device management - - Copyright (C) 2003-2004 Guus Sliepen , - 2003-2004 Ivo Timmermans - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ -*/ - -#ifndef __VND_H__ -#define __VND_H__ - -typedef enum vnd_mode{ - VND_MODE_TUN, - VND_MODE_TAP, -} vnd_mode_t; - -struct vnd; - -typedef bool (*vnd_handler_t)(struct vnd *vnd, const void *buf, int len); - -typedef struct vnd { - char *device; - char *interface; - enum vnd_mode mode; - int mtu; - - vnd_handler_t recv; - vnd_handler_t send; - - /* Private data */ - - struct fd fd; - char *description; -} vnd_t; - -extern bool vnd_init(void); -extern bool vnd_exit(void); -extern struct vnd *vnd_new(void); -extern void vnd_free(struct vnd *vnd); -extern void vnd_set(struct vnd *vnd, char *device, char *interface, vnd_mode_t mode, vnd_handler_t recv); -extern bool vnd_open(struct vnd *vnd); -extern bool vnd_close(struct vnd *vnd); - -#endif /* __VND_H__ */