Import Upstream version 1.0pre7

This commit is contained in:
Guus Sliepen 2019-08-26 13:44:36 +02:00
commit ed8d36a434
154 changed files with 53905 additions and 0 deletions

31
src/Makefile.am Normal file
View file

@ -0,0 +1,31 @@
## Produce this file with automake to get Makefile.in
# $Id: Makefile.am,v 1.4.4.21 2002/03/27 15:47:06 guus Exp $
sbin_PROGRAMS = tincd
EXTRA_DIST = linux/device.c freebsd/device.c openbsd/device.c solaris/device.c
tincd_SOURCES = conf.c connection.c device.c edge.c event.c graph.c meta.c net.c net_packet.c net_setup.c \
net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c subnet.c tincd.c
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -I$(top_srcdir)/intl
noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h
LIBS = @LIBS@ @INTLLIBS@
tincd_LDADD = \
$(top_builddir)/lib/libvpn.a
localedir = $(datadir)/locale
CFLAGS = @CFLAGS@ -DPKGLIBDIR=$(pkglibdir) -DCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
dist-hook:
rm -f `find . -type l`
lint: $(tincd_SOURCES)
lclint -nullassign -nullret +trytorecover +posixlib -skipansiheaders -skipposixheaders +gnuextensions -I/usr/include -I/usr/lib/gcc-lib/i386-linux/2.95.2/include -I. -I/home/zarq/p/tinc/cvs/cabal/src -I.. -I.. -I/home/zarq/p/tinc/cvs/cabal/lib -I/home/zarq/p/tinc/cvs/cabal/intl -D_POSIX_SOURCE -D__ELF__ -Dunix -D__i386__ -Dlinux -DHAVE_CONFIG_H -DPKGLIBDIR=/usr/local/lib/tinc -DCONFDIR=\"/usr/local/etc\" -DLOCALEDIR=\"/usr/local/share/locale\" $^

402
src/Makefile.in Normal file
View file

@ -0,0 +1,402 @@
# Makefile.in generated automatically by automake 1.5 from Makefile.am.
# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
# Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
# $Id: Makefile.am,v 1.4.4.21 2002/03/27 15:47:06 guus Exp $
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_HEADER = $(INSTALL_DATA)
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
host_alias = @host_alias@
host_triplet = @host@
AMTAR = @AMTAR@
AWK = @AWK@
BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
CATALOGS = @CATALOGS@
CATOBJEXT = @CATOBJEXT@
CC = @CC@
CPP = @CPP@
DATADIRNAME = @DATADIRNAME@
DEPDIR = @DEPDIR@
EXEEXT = @EXEEXT@
GENCAT = @GENCAT@
GLIBC21 = @GLIBC21@
GMOFILES = @GMOFILES@
GMSGFMT = @GMSGFMT@
HAVE_TUNTAP = @HAVE_TUNTAP@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
INSTOBJEXT = @INSTOBJEXT@
INTLBISON = @INTLBISON@
INTLLIBS = @INTLLIBS@
INTLOBJS = @INTLOBJS@
INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
LIBICONV = @LIBICONV@
LINUX_IF_TUN_H = @LINUX_IF_TUN_H@
LN_S = @LN_S@
MKINSTALLDIRS = @MKINSTALLDIRS@
MSGFMT = @MSGFMT@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PERL = @PERL@
POFILES = @POFILES@
POSUB = @POSUB@
RANLIB = @RANLIB@
USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
USE_NLS = @USE_NLS@
VERSION = @VERSION@
am__include = @am__include@
am__quote = @am__quote@
install_sh = @install_sh@
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -I$(top_srcdir)/intl
sbin_PROGRAMS = tincd
EXTRA_DIST = linux/device.c freebsd/device.c openbsd/device.c solaris/device.c
tincd_SOURCES = conf.c connection.c device.c edge.c event.c graph.c meta.c net.c net_packet.c net_setup.c \
net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c subnet.c tincd.c
noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h
LIBS = @LIBS@ @INTLLIBS@
tincd_LDADD = \
$(top_builddir)/lib/libvpn.a
localedir = $(datadir)/locale
CFLAGS = @CFLAGS@ -DPKGLIBDIR=$(pkglibdir) -DCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
subdir = src
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
sbin_PROGRAMS = tincd$(EXEEXT)
PROGRAMS = $(sbin_PROGRAMS)
am_tincd_OBJECTS = conf.$(OBJEXT) connection.$(OBJEXT) device.$(OBJEXT) \
edge.$(OBJEXT) event.$(OBJEXT) graph.$(OBJEXT) meta.$(OBJEXT) \
net.$(OBJEXT) net_packet.$(OBJEXT) net_setup.$(OBJEXT) \
net_socket.$(OBJEXT) netutl.$(OBJEXT) node.$(OBJEXT) \
process.$(OBJEXT) protocol.$(OBJEXT) protocol_auth.$(OBJEXT) \
protocol_edge.$(OBJEXT) protocol_misc.$(OBJEXT) \
protocol_key.$(OBJEXT) protocol_subnet.$(OBJEXT) \
route.$(OBJEXT) subnet.$(OBJEXT) tincd.$(OBJEXT)
tincd_OBJECTS = $(am_tincd_OBJECTS)
tincd_DEPENDENCIES = $(top_builddir)/lib/libvpn.a
tincd_LDFLAGS =
DEFS = @DEFS@
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
depcomp = $(SHELL) $(top_srcdir)/depcomp
@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/conf.Po $(DEPDIR)/connection.Po \
@AMDEP_TRUE@ $(DEPDIR)/device.Po $(DEPDIR)/edge.Po \
@AMDEP_TRUE@ $(DEPDIR)/event.Po $(DEPDIR)/graph.Po \
@AMDEP_TRUE@ $(DEPDIR)/meta.Po $(DEPDIR)/net.Po \
@AMDEP_TRUE@ $(DEPDIR)/net_packet.Po $(DEPDIR)/net_setup.Po \
@AMDEP_TRUE@ $(DEPDIR)/net_socket.Po $(DEPDIR)/netutl.Po \
@AMDEP_TRUE@ $(DEPDIR)/node.Po $(DEPDIR)/process.Po \
@AMDEP_TRUE@ $(DEPDIR)/protocol.Po $(DEPDIR)/protocol_auth.Po \
@AMDEP_TRUE@ $(DEPDIR)/protocol_edge.Po \
@AMDEP_TRUE@ $(DEPDIR)/protocol_key.Po \
@AMDEP_TRUE@ $(DEPDIR)/protocol_misc.Po \
@AMDEP_TRUE@ $(DEPDIR)/protocol_subnet.Po $(DEPDIR)/route.Po \
@AMDEP_TRUE@ $(DEPDIR)/subnet.Po $(DEPDIR)/tincd.Po
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
DIST_SOURCES = $(tincd_SOURCES)
HEADERS = $(noinst_HEADERS)
DIST_COMMON = $(noinst_HEADERS) Makefile.am Makefile.in
SOURCES = $(tincd_SOURCES)
all: all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && \
$(AUTOMAKE) --gnu src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) && \
CONFIG_HEADERS= CONFIG_LINKS= \
CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status
install-sbinPROGRAMS: $(sbin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(sbindir)
@list='$(sbin_PROGRAMS)'; for p in $$list; do \
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
if test -f $$p \
; then \
f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/$$f"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/$$f; \
else :; fi; \
done
uninstall-sbinPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(sbin_PROGRAMS)'; for p in $$list; do \
f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
echo " rm -f $(DESTDIR)$(sbindir)/$$f"; \
rm -f $(DESTDIR)$(sbindir)/$$f; \
done
clean-sbinPROGRAMS:
-test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES)
@rm -f tincd$(EXEEXT)
$(LINK) $(tincd_LDFLAGS) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT) core *.core
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/conf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/connection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/edge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/event.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/graph.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/meta.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/net.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/net_packet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/net_setup.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/net_socket.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/netutl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/node.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/process.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol_auth.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol_edge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol_key.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol_misc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protocol_subnet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/route.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/subnet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tincd.Po@am__quote@
distclean-depend:
-rm -rf $(DEPDIR)
.c.o:
@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
$(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$<
.c.obj:
@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
$(COMPILE) -c `cygpath -w $<`
CCDEPMODE = @CCDEPMODE@
uninstall-info-am:
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
mkid -fID $$unique $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| etags $(ETAGS_ARGS) $$tags $$unique $(LISP)
GTAGS:
here=`CDPATH=: && cd $(top_builddir) && pwd` \
&& cd $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) $$here
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
top_distdir = ..
distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
distdir: $(DISTFILES)
$(mkinstalldirs) $(distdir)/freebsd $(distdir)/linux $(distdir)/openbsd $(distdir)/solaris
@for file in $(DISTFILES); do \
if test -f $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
$(mkinstalldirs) "$(distdir)/$$dir"; \
fi; \
if test -d $$d/$$file; then \
cp -pR $$d/$$file $(distdir) \
|| exit 1; \
else \
test -f $(distdir)/$$file \
|| cp -p $$d/$$file $(distdir)/$$file \
|| exit 1; \
fi; \
done
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="${top_distdir}" distdir="$(distdir)" \
dist-hook
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(HEADERS)
installdirs:
$(mkinstalldirs) $(DESTDIR)$(sbindir)
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]*
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-am
distclean-am: clean-am distclean-compile distclean-depend \
distclean-generic distclean-tags
dvi: dvi-am
dvi-am:
info: info-am
info-am:
install-data-am:
install-exec-am: install-sbinPROGRAMS
install-info: install-info-am
install-man:
installcheck-am:
maintainer-clean: maintainer-clean-am
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
uninstall-am: uninstall-info-am uninstall-sbinPROGRAMS
.PHONY: GTAGS all all-am check check-am clean clean-generic \
clean-sbinPROGRAMS distclean distclean-compile distclean-depend \
distclean-generic distclean-tags distdir dvi dvi-am info \
info-am install install-am install-data install-data-am \
install-exec install-exec-am install-info install-info-am \
install-man install-sbinPROGRAMS install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic tags uninstall uninstall-am \
uninstall-info-am uninstall-sbinPROGRAMS
dist-hook:
rm -f `find . -type l`
lint: $(tincd_SOURCES)
lclint -nullassign -nullret +trytorecover +posixlib -skipansiheaders -skipposixheaders +gnuextensions -I/usr/include -I/usr/lib/gcc-lib/i386-linux/2.95.2/include -I. -I/home/zarq/p/tinc/cvs/cabal/src -I.. -I.. -I/home/zarq/p/tinc/cvs/cabal/lib -I/home/zarq/p/tinc/cvs/cabal/intl -D_POSIX_SOURCE -D__ELF__ -Dunix -D__i386__ -Dlinux -DHAVE_CONFIG_H -DPKGLIBDIR=/usr/local/lib/tinc -DCONFDIR=\"/usr/local/etc\" -DLOCALEDIR=\"/usr/local/share/locale\" $^
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

623
src/conf.c Normal file
View file

@ -0,0 +1,623 @@
/*
conf.c -- configuration code
Copyright (C) 1998 Robert van der Meulen
1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
2000 Cris van Pelt <tribbel@arise.dhs.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: conf.c,v 1.9.4.55 2002/04/09 11:42:48 guus Exp $
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <xalloc.h>
#include <utils.h> /* for cp */
#include <avl_tree.h>
#include "conf.h"
#include "netutl.h" /* for str2address */
#include "system.h"
avl_tree_t *config_tree;
int debug_lvl = 0;
int pingtimeout = 0; /* seconds before timeout */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
int config_compare(config_t *a, config_t *b)
{
int result;
result = strcasecmp(a->variable, b->variable);
if(result)
return result;
result = a->line - b->line;
if(result)
return result;
else
return strcmp(a->file, b->file);
}
void init_configuration(avl_tree_t **config_tree)
{
cp
*config_tree = avl_alloc_tree((avl_compare_t)config_compare, (avl_action_t)free_config);
cp
}
void exit_configuration(avl_tree_t **config_tree)
{
cp
avl_delete_tree(*config_tree);
*config_tree = NULL;
cp
}
config_t *new_config(void)
{
config_t *cfg;
cp
cfg = (config_t *)xmalloc_and_zero(sizeof(*cfg));
return cfg;
}
void free_config(config_t *cfg)
{
cp
if(cfg->variable)
free(cfg->variable);
if(cfg->value)
free(cfg->value);
if(cfg->file)
free(cfg->file);
free(cfg);
cp
}
void config_add(avl_tree_t *config_tree, config_t *cfg)
{
cp
avl_insert(config_tree, cfg);
cp
}
config_t *lookup_config(avl_tree_t *config_tree, char *variable)
{
config_t cfg, *found;
cp
cfg.variable = variable;
cfg.file = "";
cfg.line = 0;
found = avl_search_closest_greater(config_tree, &cfg);
if(!found)
return NULL;
if(strcasecmp(found->variable, variable))
return NULL;
return found;
}
config_t *lookup_config_next(avl_tree_t *config_tree, config_t *cfg)
{
avl_node_t *node;
config_t *found;
cp
node = avl_search_node(config_tree, cfg);
if(node)
{
if(node->next)
{
found = (config_t *)node->next->data;
if(!strcasecmp(found->variable, cfg->variable))
return found;
}
}
return NULL;
}
int get_config_bool(config_t *cfg, int *result)
{
cp
if(!cfg)
return 0;
if(!strcasecmp(cfg->value, "yes"))
{
*result = 1;
return 1;
}
else if(!strcasecmp(cfg->value, "no"))
{
*result = 0;
return 1;
}
syslog(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return 0;
}
int get_config_int(config_t *cfg, int *result)
{
cp
if(!cfg)
return 0;
if(sscanf(cfg->value, "%d", result) == 1)
return 1;
syslog(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return 0;
}
int get_config_string(config_t *cfg, char **result)
{
cp
if(!cfg)
return 0;
*result = xstrdup(cfg->value);
return 1;
}
int get_config_address(config_t *cfg, struct addrinfo **result)
{
struct addrinfo *ai;
cp
if(!cfg)
return 0;
ai = str2addrinfo(cfg->value, NULL, 0);
if(ai)
{
*result = ai;
return 1;
}
syslog(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return 0;
}
int get_config_port(config_t *cfg, port_t *result)
{
cp
if(!cfg)
return 0;
if(sscanf(cfg->value, "%hu", result) == 1)
{
*result = htons(*result);
return 1;
}
syslog(LOG_ERR, _("Port number expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return 0;
}
int get_config_subnet(config_t *cfg, subnet_t **result)
{
subnet_t *subnet;
cp
if(!cfg)
return 0;
subnet = str2net(cfg->value);
if(!subnet)
{
syslog(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
return 0;
}
/* Teach newbies what subnets are... */
if(((subnet->type == SUBNET_IPV4) && maskcheck((char *)&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
|| ((subnet->type == SUBNET_IPV6) && maskcheck((char *)&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))))
{
syslog(LOG_ERR, _("Network address and prefix length do not match for configuration variable %s in %s line %d"),
cfg->variable, cfg->file, cfg->line);
free(subnet);
return 0;
}
*result = subnet;
return 1;
}
/*
Read exactly one line and strip the trailing newline if any. If the
file was on EOF, return NULL. Otherwise, return all the data in a
dynamically allocated buffer.
If line is non-NULL, it will be used as an initial buffer, to avoid
unnecessary mallocing each time this function is called. If buf is
given, and buf needs to be expanded, the var pointed to by buflen
will be increased.
*/
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 != NULL) && (buflen != NULL))
{
size = *buflen;
line = *buf;
}
else
{
size = 100;
line = xmalloc(size);
}
maxlen = size;
idx = line;
*idx = 0;
for(;;)
{
errno = 0;
p = fgets(idx, maxlen, fp);
if(p == NULL) /* EOF or error */
{
if(feof(fp))
break;
/* otherwise: error; let the calling function print an error
message if applicable */
free(line);
return NULL;
}
newline = strchr(p, '\n');
if(newline == NULL)
/* We haven't yet read everything to the end of the line */
{
newsize = size << 1;
line = xrealloc(line, newsize);
idx = &line[size - 1];
maxlen = newsize - size + 1;
size = newsize;
}
else
{
*newline = '\0'; /* kill newline */
break; /* yay */
}
}
if((buf != NULL) && (buflen != NULL))
{
*buflen = size;
*buf = line;
}
return line;
}
/*
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
int read_config_file(avl_tree_t *config_tree, const char *fname)
{
int err = -2; /* Parse error */
FILE *fp;
char *buffer, *line;
char *variable, *value;
int lineno = 0, ignore = 0;
config_t *cfg;
size_t bufsize;
cp
if((fp = fopen (fname, "r")) == NULL)
{
syslog(LOG_ERR, _("Cannot open config file %s: %s"), fname, strerror(errno));
return -3;
}
bufsize = 100;
buffer = xmalloc(bufsize);
for(;;)
{
if((line = readline(fp, &buffer, &bufsize)) == NULL)
{
err = -1;
break;
}
if(feof(fp))
{
err = 0;
break;
}
lineno++;
if((variable = strtok(line, "\t =")) == NULL)
continue; /* no tokens on this line */
if(variable[0] == '#')
continue; /* comment: ignore */
if(!strcmp(variable, "-----BEGIN"))
ignore = 1;
if(!ignore)
{
if(((value = strtok(NULL, "\t\n\r =")) == NULL) || value[0] == '#')
{
syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
variable, lineno, fname);
break;
}
cfg = new_config();
cfg->variable = xstrdup(variable);
cfg->value = xstrdup(value);
cfg->file = xstrdup(fname);
cfg->line = lineno;
config_add(config_tree, cfg);
}
if(!strcmp(variable, "-----END"))
ignore = 0;
}
free(buffer);
fclose (fp);
cp
return err;
}
int read_server_config()
{
char *fname;
int x;
cp
asprintf(&fname, "%s/tinc.conf", confbase);
x = read_config_file(config_tree, fname);
if(x == -1) /* System error: complain */
{
syslog(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
}
free(fname);
cp
return x;
}
int isadir(const char* f)
{
struct stat s;
if(stat(f, &s) < 0)
return 0;
else
return S_ISDIR(s.st_mode);
}
int is_safe_path(const char *file)
{
char *p;
const char *f;
char x;
struct stat s;
char l[MAXBUFSIZE];
if(*file != '/')
{
syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
return 0;
}
p = strrchr(file, '/');
if(p == file) /* It's in the root */
p++;
x = *p;
*p = '\0';
f = file;
check1:
if(lstat(f, &s) < 0)
{
syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
return 0;
}
if(s.st_uid != geteuid())
{
syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
f, s.st_uid, geteuid());
return 0;
}
if(S_ISLNK(s.st_mode))
{
syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
f);
if(readlink(f, l, MAXBUFSIZE) < 0)
{
syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
return 0;
}
f = l;
goto check1;
}
*p = x;
f = file;
check2:
if(lstat(f, &s) < 0 && errno != ENOENT)
{
syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
return 0;
}
if(errno == ENOENT)
return 1;
if(s.st_uid != geteuid())
{
syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
f, s.st_uid, geteuid());
return 0;
}
if(S_ISLNK(s.st_mode))
{
syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
f);
if(readlink(f, l, MAXBUFSIZE) < 0)
{
syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
return 0;
}
f = l;
goto check2;
}
if(s.st_mode & 0007)
{
/* Accessible by others */
syslog(LOG_ERR, _("`%s' has unsecure permissions"),
f);
return 0;
}
return 1;
}
FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
{
FILE *r;
char *directory;
char *fn;
/* Check stdin and stdout */
if(!isatty(0) || !isatty(1))
{
/* Argh, they are running us from a script or something. Write
the files to the current directory and let them burn in hell
for ever. */
fn = xstrdup(filename);
}
else
{
/* Ask for a file and/or directory name. */
fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
what, filename);
fflush(stdout);
if((fn = readline(stdin, NULL, NULL)) == NULL)
{
fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
return NULL;
}
if(strlen(fn) == 0)
/* User just pressed enter. */
fn = xstrdup(filename);
}
if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
{
/* The directory is a relative path or a filename. */
char *p;
directory = get_current_dir_name();
asprintf(&p, "%s/%s", directory, fn);
free(fn);
free(directory);
fn = p;
}
umask(0077); /* Disallow everything for group and other */
/* Open it first to keep the inode busy */
if((r = fopen(fn, mode)) == NULL)
{
fprintf(stderr, _("Error opening file `%s': %s\n"),
fn, strerror(errno));
free(fn);
return NULL;
}
/* Then check the file for nasty attacks */
if(!is_safe_path(fn)) /* Do not permit any directories that are
readable or writeable by other users. */
{
fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
"I will not create or overwrite this file.\n"),
fn);
fclose(r);
free(fn);
return NULL;
}
free(fn);
return r;
}

70
src/conf.h Normal file
View file

@ -0,0 +1,70 @@
/*
conf.h -- header for conf.c
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: conf.h,v 1.6.4.32 2002/02/18 16:25:16 guus Exp $
*/
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <avl_tree.h>
#include "net.h"
#include "subnet.h"
typedef struct config_t {
char *variable;
char *value;
char *file;
int line;
} config_t;
extern avl_tree_t *config_tree;
extern int debug_lvl;
extern int pingtimeout;
extern int maxtimeout;
extern int bypass_security;
extern char *confbase;
extern char *netname;
extern void init_configuration(avl_tree_t **);
extern void exit_configuration(avl_tree_t **);
extern config_t *new_config(void);
extern void free_config(config_t *);
extern void config_add(avl_tree_t *, config_t *);
extern config_t *lookup_config(avl_tree_t *, char *);
extern config_t *lookup_config_next(avl_tree_t *, config_t *);
extern int get_config_bool(config_t *, int *);
extern int get_config_int(config_t *, int *);
extern int get_config_port(config_t *, port_t *);
extern int get_config_string(config_t *, char **);
extern int get_config_address(config_t *, struct addrinfo **);
struct subnet_t; /* Needed for next line. */
extern int get_config_subnet(config_t *, struct subnet_t **);
extern int read_config_file(avl_tree_t *, const char *);
extern int read_server_config(void);
extern FILE *ask_and_safe_open(const char*, const char*, const char *);
extern int is_safe_path(const char *);
#endif /* __TINC_CONF_H__ */

137
src/connection.c Normal file
View file

@ -0,0 +1,137 @@
/*
connection.c -- connection list management
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: connection.c,v 1.1.2.29 2002/03/22 13:31:18 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <sys/time.h>
#include <avl_tree.h>
#include <list.h>
#include "net.h" /* Don't ask. */
#include "netutl.h"
#include "config.h"
#include "conf.h"
#include <utils.h>
#include "subnet.h"
#include "xalloc.h"
#include "system.h"
avl_tree_t *connection_tree; /* Meta connections */
int connection_compare(connection_t *a, connection_t *b)
{
return a - b;
}
void init_connections(void)
{
cp
connection_tree = avl_alloc_tree((avl_compare_t)connection_compare, NULL);
cp
}
void exit_connections(void)
{
cp
avl_delete_tree(connection_tree);
cp
}
connection_t *new_connection(void)
{
connection_t *c;
cp
c = (connection_t *)xmalloc_and_zero(sizeof(connection_t));
if(!c)
return NULL;
gettimeofday(&c->start, NULL);
cp
return c;
}
void free_connection(connection_t *c)
{
cp
if(c->hostname)
free(c->hostname);
if(c->inkey)
free(c->inkey);
if(c->outkey)
free(c->outkey);
if(c->mychallenge)
free(c->mychallenge);
if(c->hischallenge)
free(c->hischallenge);
free(c);
cp
}
void connection_add(connection_t *c)
{
cp
avl_insert(connection_tree, c);
cp
}
void connection_del(connection_t *c)
{
cp
avl_delete(connection_tree, c);
cp
}
void dump_connections(void)
{
avl_node_t *node;
connection_t *c;
cp
syslog(LOG_DEBUG, _("Connections:"));
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
syslog(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x"),
c->name, c->hostname, c->options, c->socket, c->status);
}
syslog(LOG_DEBUG, _("End of connections."));
cp
}
int read_connection_config(connection_t *c)
{
char *fname;
int x;
cp
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
x = read_config_file(c->config_tree, fname);
free(fname);
cp
return x;
}

121
src/connection.h Normal file
View file

@ -0,0 +1,121 @@
/*
connection.h -- header for connection.c
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: connection.h,v 1.1.2.26 2002/02/20 19:25:09 guus Exp $
*/
#ifndef __TINC_CONNECTION_H__
#define __TINC_CONNECTION_H__
#include <sys/time.h>
#include <avl_tree.h>
#include <list.h>
#ifdef HAVE_OPENSSL_EVP_H
# include <openssl/evp.h>
#else
# include <evp.h>
#endif
#ifdef HAVE_OPENSSL_RSA_H
# include <openssl/rsa.h>
#else
# include <rsa.h>
#endif
#include "net.h"
#include "conf.h"
#include "node.h"
#include "edge.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
typedef struct connection_status_t {
int pinged:1; /* sent ping */
int active:1; /* 1 if active.. */
int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
int termreq:1; /* the termination of this connection was requested */
int remove:1; /* Set to 1 if you want this connection removed */
int timeout:1; /* 1 if gotten timeout */
int encryptout:1; /* 1 if we can encrypt outgoing traffic */
int decryptin:1; /* 1 if we have to decrypt incoming traffic */
int mst:1; /* 1 if this connection is part of a minimum spanning tree */
int unused:18;
} connection_status_t;
typedef struct connection_t {
char *name; /* name he claims to have */
sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_version; /* used protocol */
int socket; /* socket used for this connection */
long int options; /* options for this connection */
struct connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
RSA *rsa_key; /* his public/private key */
const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */
const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */
EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */
EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */
char *inkey; /* His symmetric meta key + iv */
char *outkey; /* Our symmetric meta key + iv */
int inkeylength; /* Length of his key + iv */
int outkeylength; /* Length of our key + iv */
const EVP_MD *indigest;
const EVP_MD *outdigest;
int inmaclength;
int outmaclength;
int incompression;
int outcompression;
char *mychallenge; /* challenge we received from him */
char *hischallenge; /* challenge we sent to him */
char buffer[MAXBUFSIZE]; /* metadata input buffer */
int buflen; /* bytes read into buffer */
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end */
avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;
extern avl_tree_t *connection_tree;
extern void init_connections(void);
extern void exit_connections(void);
extern connection_t *new_connection(void);
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
extern void dump_connections(void);
extern int read_connection_config(connection_t *);
#endif /* __TINC_CONNECTION_H__ */

224
src/device.c Normal file
View file

@ -0,0 +1,224 @@
/*
device.c -- Interaction with Linux ethertap and tun/tap device
Copyright (C) 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.c,v 1.1.2.8 2002/03/24 16:36:56 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <sys/ioctl.h>
#ifdef HAVE_TUNTAP
#ifdef LINUX_IF_TUN_H
#include LINUX_IF_TUN_H
#else
#include <linux/if_tun.h>
#endif
#define DEFAULT_DEVICE "/dev/misc/net/tun"
#else
#define DEFAULT_DEVICE "/dev/tap0"
#endif
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "subnet.h"
#include "system.h"
#define DEVICE_TYPE_ETHERTAP 0
#define DEVICE_TYPE_TUNTAP 1
int device_fd = -1;
int device_type;
char *device;
char *interface;
char ifrname[IFNAMSIZ];
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
extern subnet_t mymac;
/*
open the local ethertap device
*/
int setup_device(void)
{
struct ifreq ifr;
cp
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
if(!get_config_string(lookup_config(config_tree, "Interface"), &interface))
#ifdef HAVE_TUNTAP
interface = netname;
#else
interface = rindex(device, '/')?rindex(device, '/')+1:device;
#endif
cp
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
syslog(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return -1;
}
cp
/* Set default MAC address for ethertap devices */
mymac.type = SUBNET_MAC;
mymac.net.mac.address.x[0] = 0xfe;
mymac.net.mac.address.x[1] = 0xfd;
mymac.net.mac.address.x[2] = 0x00;
mymac.net.mac.address.x[3] = 0x00;
mymac.net.mac.address.x[4] = 0x00;
mymac.net.mac.address.x[5] = 0x00;
#ifdef HAVE_TUNTAP
/* Ok now check if this is an old ethertap or a new tun/tap thingie */
memset(&ifr, 0, sizeof(ifr));
cp
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (interface)
strncpy(ifr.ifr_name, interface, IFNAMSIZ);
cp
if (!ioctl(device_fd, TUNSETIFF, (void *) &ifr))
{
device_info = _("Linux tun/tap device");
device_type = DEVICE_TYPE_TUNTAP;
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
interface = ifrname;
}
else
if (!ioctl(device_fd, (('T'<< 8) | 202), (void *) &ifr))
{
syslog(LOG_WARNING, _("Old ioctl() request was needed for %s"), device);
device_type = DEVICE_TYPE_TUNTAP;
device_info = _("Linux tun/tap device");
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
interface = ifrname;
}
else
#endif
{
device_info = _("Linux ethertap device");
device_type = DEVICE_TYPE_ETHERTAP;
interface = rindex(device, '/')?rindex(device, '/')+1:device;
}
syslog(LOG_INFO, _("%s is a %s"), device, device_info);
cp
return 0;
}
void close_device(void)
{
cp
close(device_fd);
}
/*
read, encrypt and send data that is
available through the ethertap device
*/
int read_packet(vpn_packet_t *packet)
{
int lenin;
cp
if(device_type == DEVICE_TYPE_TUNTAP)
{
if((lenin = read(device_fd, packet->data, MTU)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
packet->len = lenin;
}
else /* ethertap */
{
if((lenin = read(device_fd, packet->data - 2, MTU + 2)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
packet->len = lenin - 2;
}
device_total_in += packet->len;
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, device_info);
}
return 0;
cp
}
int write_packet(vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(device_type == DEVICE_TYPE_TUNTAP)
{
if(write(device_fd, packet->data, packet->len) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
}
else/* ethertap */
{
*(short int *)(packet->data - 2) = packet->len;
if(write(device_fd, packet->data - 2, packet->len + 2) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
}
device_total_out += packet->len;
cp
return 0;
}
void dump_device_stats(void)
{
cp
syslog(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
syslog(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
syslog(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
cp
}

36
src/device.h Normal file
View file

@ -0,0 +1,36 @@
/*
net.h -- generic header for device.c
Copyright (C) 2001-2002 Ivo Timmermans <zarq@iname.com>
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.h,v 1.1.2.5 2002/02/10 21:57:54 guus Exp $
*/
#ifndef __TINC_DEVICE_H__
#define __TINC_DEVICE_H__
extern int device_fd;
extern char *device;
extern char *interface;
extern int setup_device(void);
extern void close_device(void);
extern int read_packet(vpn_packet_t *);
extern int write_packet(vpn_packet_t *);
extern void dump_device_stats(void);
#endif /* __TINC_DEVICE_H__ */

211
src/edge.c Normal file
View file

@ -0,0 +1,211 @@
/*
edge.c -- edge tree management
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: edge.c,v 1.1.2.10 2002/03/27 15:26:43 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <avl_tree.h>
#include <list.h>
#include "net.h" /* Don't ask. */
#include "netutl.h"
#include "config.h"
#include "conf.h"
#include <utils.h>
#include "subnet.h"
#include "xalloc.h"
#include "system.h"
avl_tree_t *edge_tree; /* Tree with all known edges (replaces active_tree) */
avl_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
int edge_compare(edge_t *a, edge_t *b)
{
int result;
result = strcmp(a->from.node->name, b->from.node->name);
if(result)
return result;
else
return strcmp(a->to.node->name, b->to.node->name);
}
/* Evil edge_compare() from a parallel universe ;)
int edge_compare(edge_t *a, edge_t *b)
{
int result;
return (result = strcmp(a->from.node->name, b->from.node->name)) || (result = strcmp(a->to.node->name, b->to.node->name)), result;
}
*/
int edge_name_compare(edge_t *a, edge_t *b)
{
int result;
char *name_a1, *name_a2, *name_b1, *name_b2;
if(strcmp(a->from.node->name, a->to.node->name) < 0)
name_a1 = a->from.node->name, name_a2 = a->to.node->name;
else
name_a1 = a->to.node->name, name_a2 = a->from.node->name;
if(strcmp(b->from.node->name, b->to.node->name) < 0)
name_b1 = b->from.node->name, name_b2 = b->to.node->name;
else
name_b1 = b->to.node->name, name_b2 = b->from.node->name;
result = strcmp(name_a1, name_b1);
if(result)
return result;
else
return strcmp(name_a2, name_b2);
}
int edge_weight_compare(edge_t *a, edge_t *b)
{
int result;
result = a->weight - b->weight;
if(result)
return result;
else
return edge_name_compare(a, b);
}
void init_edges(void)
{
cp
edge_tree = avl_alloc_tree((avl_compare_t)edge_compare, NULL);
edge_weight_tree = avl_alloc_tree((avl_compare_t)edge_weight_compare, NULL);
cp
}
avl_tree_t *new_edge_tree(void)
{
cp
return avl_alloc_tree((avl_compare_t)edge_name_compare, NULL);
cp
}
void free_edge_tree(avl_tree_t *edge_tree)
{
cp
avl_delete_tree(edge_tree);
cp
}
void exit_edges(void)
{
cp
avl_delete_tree(edge_tree);
cp
}
/* Creation and deletion of connection elements */
edge_t *new_edge(void)
{
edge_t *e;
cp
e = (edge_t *)xmalloc_and_zero(sizeof(*e));
cp
return e;
}
void free_edge(edge_t *e)
{
cp
free(e);
cp
}
void edge_add(edge_t *e)
{
cp
avl_insert(edge_tree, e);
avl_insert(edge_weight_tree, e);
avl_insert(e->from.node->edge_tree, e);
avl_insert(e->to.node->edge_tree, e);
cp
}
void edge_del(edge_t *e)
{
cp
avl_delete(edge_tree, e);
avl_delete(edge_weight_tree, e);
avl_delete(e->from.node->edge_tree, e);
avl_delete(e->to.node->edge_tree, e);
cp
}
edge_t *lookup_edge(node_t *from, node_t *to)
{
edge_t v, *result;
cp
v.from.node = from;
v.to.node = to;
result = avl_search(edge_tree, &v);
if(result)
return result;
cp
v.from.node = to;
v.to.node = from;
return avl_search(edge_tree, &v);
}
void dump_edges(void)
{
avl_node_t *node;
edge_t *e;
char *from_udp, *to_udp;
cp
syslog(LOG_DEBUG, _("Edges:"));
for(node = edge_tree->head; node; node = node->next)
{
e = (edge_t *)node->data;
from_udp = sockaddr2hostname(&e->from.udpaddress);
to_udp = sockaddr2hostname(&e->to.udpaddress);
syslog(LOG_DEBUG, _(" %s at %s - %s at %s options %lx weight %d"),
e->from.node->name, from_udp,
e->to.node->name, to_udp,
e->options, e->weight);
free(from_udp);
free(to_udp);
}
syslog(LOG_DEBUG, _("End of edges."));
cp
}

62
src/edge.h Normal file
View file

@ -0,0 +1,62 @@
/*
edge.h -- header for edge.c
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: edge.h,v 1.1.2.7 2002/03/22 11:43:46 guus Exp $
*/
#ifndef __TINC_EDGE_H__
#define __TINC_EDGE_H__
#include <avl_tree.h>
#include "net.h"
#include "node.h"
#include "connection.h"
typedef struct halfconnection_t {
struct node_t *node; /* node associated with this end of the connection */
// sockaddr_t tcpaddress; /* real (internet) ip on this end of the meta connection */
sockaddr_t udpaddress; /* real (internet) ip on this end of the vpn connection */
} halfconnection_t;
typedef struct edge_t {
struct halfconnection_t from;
struct halfconnection_t to;
long int options; /* options turned on for this edge */
int weight; /* weight of this edge */
struct connection_t *connection; /* connection associated with this edge, if available */
} edge_t;
extern avl_tree_t *edge_tree; /* Tree with all known edges (replaces active_tree) */
extern avl_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern void init_edges(void);
extern void exit_edges(void);
extern edge_t *new_edge(void);
extern void free_edge(edge_t *);
extern avl_tree_t *new_edge_tree(void);
extern void free_edge_tree(avl_tree_t *);
extern void edge_add(edge_t *);
extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
extern void dump_edges(void);
#endif /* __TINC_EDGE_H__ */

110
src/event.c Normal file
View file

@ -0,0 +1,110 @@
/*
event.c -- event queue
Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: event.c,v 1.1.4.2 2002/03/01 14:09:30 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <xalloc.h>
#include <string.h>
#include <utils.h>
#include <avl_tree.h>
#include <time.h>
#include "event.h"
#include "system.h"
avl_tree_t *event_tree;
extern time_t now;
int id;
int event_compare(event_t *a, event_t *b)
{
if(a->time > b->time)
return 1;
if(a->time < b->time)
return -1;
return a->id - b->id;
}
void init_events(void)
{
cp
event_tree = avl_alloc_tree((avl_compare_t)event_compare, NULL);
cp
}
void exit_events(void)
{
cp
avl_delete_tree(event_tree);
cp
}
event_t *new_event(void)
{
event_t *event;
cp
event = (event_t *)xmalloc_and_zero(sizeof(*event));
cp
return event;
}
void free_event(event_t *event)
{
cp
free(event);
cp
}
void event_add(event_t *event)
{
cp
event->id = ++id;
avl_insert(event_tree, event);
cp
}
void event_del(event_t *event)
{
cp
avl_delete(event_tree, event);
cp
}
event_t *get_expired_event(void)
{
event_t *event;
cp
if(event_tree->head)
{
event = (event_t *)event_tree->head->data;
if(event->time < now)
{
avl_delete(event_tree, event);
return event;
}
}
cp
return NULL;
}

48
src/event.h Normal file
View file

@ -0,0 +1,48 @@
/*
event.h -- header for event.c
Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: event.h,v 1.1.4.1 2002/02/11 10:05:58 guus Exp $
*/
#ifndef __TINC_EVENT_H__
#define __TINC_EVENT_H__
#include <time.h>
#include <avl_tree.h>
avl_tree_t *event_tree;
typedef void (*event_handler_t)(void *);
typedef struct {
time_t time;
int id;
event_handler_t handler;
void *data;
} event_t;
extern void init_events(void);
extern void exit_events(void);
extern event_t *new_event(void);
extern void free_event(event_t *);
extern void event_add(event_t *);
extern void event_del(event_t *);
extern event_t *get_expired_event(void);
#endif /* __TINC_EVENT_H__ */

148
src/freebsd/device.c Normal file
View file

@ -0,0 +1,148 @@
/*
device.c -- Interaction with FreeBSD tap device
Copyright (C) 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.c,v 1.1.2.4 2002/02/18 16:25:19 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <net/if.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "subnet.h"
#include "system.h"
#define DEFAULT_DEVICE "/dev/tap0"
int device_fd = -1;
int device_type;
char *device;
char *interface;
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
extern subnet_t mymac;
/*
open the local ethertap device
*/
int setup_device(void)
{
cp
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
if(!get_config_string(lookup_config(config_tree, "Interface"), &interface))
interface = rindex(device, '/')?rindex(device, '/')+1:device;
cp
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
syslog(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return -1;
}
cp
/* Set default MAC address for ethertap devices */
mymac.type = SUBNET_MAC;
mymac.net.mac.address.x[0] = 0xfe;
mymac.net.mac.address.x[1] = 0xfd;
mymac.net.mac.address.x[2] = 0x00;
mymac.net.mac.address.x[3] = 0x00;
mymac.net.mac.address.x[4] = 0x00;
mymac.net.mac.address.x[5] = 0x00;
device_info = _("FreeBSD tap device");
syslog(LOG_INFO, _("%s is a %s"), device, device_info);
cp
return 0;
}
void close_device(void)
{
cp
close(device_fd);
}
/*
read, encrypt and send data that is
available through the ethertap device
*/
int read_packet(vpn_packet_t *packet)
{
int lenin;
cp
if((lenin = read(device_fd, packet->data, MTU)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
packet->len = lenin;
device_total_in += packet->len;
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Read packet of %d bytes from %s"),
packet->len, device_info);
return 0;
cp
}
int write_packet(vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(write(device_fd, packet->data, packet->len) < 0)
{
syslog(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
device_total_out += packet->len;
cp
}
void dump_device_stats(void)
{
cp
syslog(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
syslog(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
syslog(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
cp
}

290
src/graph.c Normal file
View file

@ -0,0 +1,290 @@
/*
graph.c -- graph algorithms
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: graph.c,v 1.1.2.11 2002/03/24 16:28:27 guus Exp $
*/
/* 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 "config.h"
#include <stdio.h>
#include <syslog.h>
#include "config.h"
#include <string.h>
#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
#include <sys/param.h>
#endif
#include <netinet/in.h>
#include <avl_tree.h>
#include <utils.h>
#include "netutl.h"
#include "node.h"
#include "edge.h"
#include "connection.h"
#include "process.h"
#include "system.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 *node, *next;
edge_t *e;
node_t *n;
connection_t *c;
int nodes = 0;
int safe_edges = 0;
int skipped;
/* Clear MST status on connections */
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
c->status.mst = 0;
}
/* Do we have something to do at all? */
if(!edge_weight_tree->head)
return;
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, "Running Kruskal's algorithm:");
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
n->status.visited = 0;
nodes++;
}
/* Starting point */
((edge_t *)edge_weight_tree->head->data)->from.node->status.visited = 1;
/* Add safe edges */
for(skipped = 0, node = edge_weight_tree->head; node; node = next)
{
next = node->next;
e = (edge_t *)node->data;
if(e->from.node->status.visited == e->to.node->status.visited)
{
skipped = 1;
continue;
}
e->from.node->status.visited = 1;
e->to.node->status.visited = 1;
if(e->connection)
e->connection->status.mst = 1;
safe_edges++;
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from.node->name, e->to.node->name, e->weight);
if(skipped)
{
next = edge_weight_tree->head;
continue;
}
}
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, safe_edges);
}
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
void sssp_bfs(void)
{
avl_node_t *node, *from, *next, *to;
edge_t *e;
node_t *n;
halfconnection_t to_hc, from_hc;
avl_tree_t *todo_tree;
int indirect;
char *name;
todo_tree = avl_alloc_tree(NULL, NULL);
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
n->status.visited = 0;
n->status.indirect = 1;
}
/* Begin with myself */
myself->status.visited = 1;
myself->status.indirect = 0;
myself->nexthop = myself;
myself->via = myself;
node = avl_alloc_node();
node->data = myself;
avl_insert_top(todo_tree, node);
/* Loop while todo_tree is filled */
while(todo_tree->head)
{
for(from = todo_tree->head; from; from = next) /* "from" is the node from which we start */
{
next = from->next;
n = (node_t *)from->data;
for(to = n->edge_tree->head; to; to = to->next) /* "to" is the edge connected to "from" */
{
e = (edge_t *)to->data;
if(e->from.node == n) /* "from_hc" is the halfconnection with .node == from */
to_hc = e->to, from_hc = e->from;
else
to_hc = e->from, from_hc = e->to;
/* Situation:
/
/
------(n)from_hc-----to_hc
\
\
n->address is set to the to_hc.udpaddress of the edge left of n.
We are currently examining the edge right of n:
- If from_hc.udpaddress != n->address, then to_hc.node is probably
not reachable for the nodes left of n. We do as if the indirectdata
flag is set on edge e.
- If edge e provides for better reachability of to_hc.node, update
to_hc.node and (re)add it to the todo_tree to (re)examine the reachability
of nodes behind it.
*/
indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &from_hc.udpaddress));
if(to_hc.node->status.visited && (!to_hc.node->status.indirect || indirect))
continue;
to_hc.node->status.visited = 1;
to_hc.node->status.indirect = indirect;
to_hc.node->nexthop = (n->nexthop == myself) ? to_hc.node : n->nexthop;
to_hc.node->via = indirect ? n->via : to_hc.node;
to_hc.node->options = e->options;
if(sockaddrcmp(&to_hc.node->address, &to_hc.udpaddress))
{
node = avl_unlink(node_udp_tree, to_hc.node);
to_hc.node->address = to_hc.udpaddress;
if(to_hc.node->hostname)
free(to_hc.node->hostname);
to_hc.node->hostname = sockaddr2hostname(&to_hc.udpaddress);
avl_insert_node(node_udp_tree, node);
}
node = avl_alloc_node();
node->data = to_hc.node;
avl_insert_before(todo_tree, from, node);
}
avl_delete_node(todo_tree, from);
}
}
avl_free_tree(todo_tree);
/* Check reachability status. */
for(node = node_tree->head; node; node = next)
{
next = node->next;
n = (node_t *)node->data;
if(n->status.visited)
{
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Node %s (%s) became reachable"), n->name, n->hostname);
n->status.reachable = 1;
asprintf(&name, "hosts/%s-up", n->name);
execute_script(name);
free(name);
}
}
else
{
if(n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Node %s (%s) became unreachable"), n->name, n->hostname);
n->status.reachable = 0;
n->status.validkey = 0;
n->status.waitingforkey = 0;
n->sent_seqno = 0;
asprintf(&name, "hosts/%s-down", n->name);
execute_script(name);
free(name);
}
}
}
}
void graph(void)
{
mst_kruskal();
sssp_bfs();
}

25
src/graph.h Normal file
View file

@ -0,0 +1,25 @@
/*
graph.h -- header for graph.c
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: graph.h,v 1.1.2.3 2002/02/10 21:57:54 guus Exp $
*/
extern void graph(void);
extern void mst_kruskal(void);
extern void sssp_bfs(void);

224
src/linux/device.c Normal file
View file

@ -0,0 +1,224 @@
/*
device.c -- Interaction with Linux ethertap and tun/tap device
Copyright (C) 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.c,v 1.1.2.8 2002/03/24 16:36:56 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <sys/ioctl.h>
#ifdef HAVE_TUNTAP
#ifdef LINUX_IF_TUN_H
#include LINUX_IF_TUN_H
#else
#include <linux/if_tun.h>
#endif
#define DEFAULT_DEVICE "/dev/misc/net/tun"
#else
#define DEFAULT_DEVICE "/dev/tap0"
#endif
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "subnet.h"
#include "system.h"
#define DEVICE_TYPE_ETHERTAP 0
#define DEVICE_TYPE_TUNTAP 1
int device_fd = -1;
int device_type;
char *device;
char *interface;
char ifrname[IFNAMSIZ];
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
extern subnet_t mymac;
/*
open the local ethertap device
*/
int setup_device(void)
{
struct ifreq ifr;
cp
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
if(!get_config_string(lookup_config(config_tree, "Interface"), &interface))
#ifdef HAVE_TUNTAP
interface = netname;
#else
interface = rindex(device, '/')?rindex(device, '/')+1:device;
#endif
cp
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
syslog(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return -1;
}
cp
/* Set default MAC address for ethertap devices */
mymac.type = SUBNET_MAC;
mymac.net.mac.address.x[0] = 0xfe;
mymac.net.mac.address.x[1] = 0xfd;
mymac.net.mac.address.x[2] = 0x00;
mymac.net.mac.address.x[3] = 0x00;
mymac.net.mac.address.x[4] = 0x00;
mymac.net.mac.address.x[5] = 0x00;
#ifdef HAVE_TUNTAP
/* Ok now check if this is an old ethertap or a new tun/tap thingie */
memset(&ifr, 0, sizeof(ifr));
cp
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (interface)
strncpy(ifr.ifr_name, interface, IFNAMSIZ);
cp
if (!ioctl(device_fd, TUNSETIFF, (void *) &ifr))
{
device_info = _("Linux tun/tap device");
device_type = DEVICE_TYPE_TUNTAP;
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
interface = ifrname;
}
else
if (!ioctl(device_fd, (('T'<< 8) | 202), (void *) &ifr))
{
syslog(LOG_WARNING, _("Old ioctl() request was needed for %s"), device);
device_type = DEVICE_TYPE_TUNTAP;
device_info = _("Linux tun/tap device");
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
interface = ifrname;
}
else
#endif
{
device_info = _("Linux ethertap device");
device_type = DEVICE_TYPE_ETHERTAP;
interface = rindex(device, '/')?rindex(device, '/')+1:device;
}
syslog(LOG_INFO, _("%s is a %s"), device, device_info);
cp
return 0;
}
void close_device(void)
{
cp
close(device_fd);
}
/*
read, encrypt and send data that is
available through the ethertap device
*/
int read_packet(vpn_packet_t *packet)
{
int lenin;
cp
if(device_type == DEVICE_TYPE_TUNTAP)
{
if((lenin = read(device_fd, packet->data, MTU)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
packet->len = lenin;
}
else /* ethertap */
{
if((lenin = read(device_fd, packet->data - 2, MTU + 2)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
packet->len = lenin - 2;
}
device_total_in += packet->len;
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, device_info);
}
return 0;
cp
}
int write_packet(vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(device_type == DEVICE_TYPE_TUNTAP)
{
if(write(device_fd, packet->data, packet->len) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
}
else/* ethertap */
{
*(short int *)(packet->data - 2) = packet->len;
if(write(device_fd, packet->data - 2, packet->len + 2) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
}
device_total_out += packet->len;
cp
return 0;
}
void dump_device_stats(void)
{
cp
syslog(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
syslog(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
syslog(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
cp
}

210
src/meta.c Normal file
View file

@ -0,0 +1,210 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: meta.c,v 1.1.2.25 2002/03/01 14:09:31 guus Exp $
*/
#include "config.h"
#include <utils.h>
#include <avl_tree.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
/* This line must be below the rest for FreeBSD */
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/evp.h>
#include "net.h"
#include "connection.h"
#include "system.h"
#include "protocol.h"
int send_meta(connection_t *c, char *buffer, int length)
{
char *bufp;
int outlen;
char outbuf[MAXBUFSIZE];
cp
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length,
c->name, c->hostname);
if(c->status.encryptout)
{
EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
bufp = outbuf;
length = outlen;
}
else
bufp = buffer;
if(write(c->socket, bufp, length) < 0)
{
syslog(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name, c->hostname, strerror(errno));
return -1;
}
cp
return 0;
}
void broadcast_meta(connection_t *from, char *buffer, int length)
{
avl_node_t *node;
connection_t *c;
cp
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
cp
}
int receive_meta(connection_t *c)
{
int x, l = sizeof(x);
int oldlen, i;
int lenin, reqlen;
int decrypted = 0;
char inbuf[MAXBUFSIZE];
cp
if(getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s %s (%s)"), __FILE__, __LINE__, c->socket, strerror(errno),
c->name, c->hostname);
return -1;
}
if(x)
{
syslog(LOG_ERR, _("Metadata socket error for %s (%s): %s"),
c->name, c->hostname, strerror(x));
return -1;
}
/* Strategy:
- Read as much as possible from the TCP socket in one go.
- Decrypt it.
- Check if a full request is in the input buffer.
- If yes, process request and remove it from the buffer,
then check again.
- If not, keep stuff in buffer and exit.
*/
lenin = read(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen);
if(lenin<=0)
{
if(lenin==0)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
}
else
if(errno==EINTR)
return 0;
else
syslog(LOG_ERR, _("Metadata socket read error for %s (%s): %s"),
c->name, c->hostname, strerror(errno));
return -1;
}
oldlen = c->buflen;
c->buflen += lenin;
while(lenin)
{
/* Decrypt */
if(c->status.decryptin && !decrypted)
{
EVP_DecryptUpdate(c->inctx, inbuf, &lenin, c->buffer + oldlen, lenin);
memcpy(c->buffer + oldlen, inbuf, lenin);
decrypted = 1;
}
/* Are we receiving a TCPpacket? */
if(c->tcplen)
{
if(c->tcplen <= c->buflen)
{
receive_tcppacket(c, c->buffer, c->tcplen);
c->buflen -= c->tcplen;
lenin -= c->tcplen;
memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
oldlen = 0;
c->tcplen = 0;
continue;
}
else
{
break;
}
}
/* Otherwise we are waiting for a request */
reqlen = 0;
for(i = oldlen; i < c->buflen; i++)
{
if(c->buffer[i] == '\n')
{
c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
reqlen = i + 1;
break;
}
}
if(reqlen)
{
if(receive_request(c))
return -1;
c->buflen -= reqlen;
lenin -= reqlen;
memmove(c->buffer, c->buffer + reqlen, c->buflen);
oldlen = 0;
continue;
}
else
{
break;
}
}
if(c->buflen >= MAXBUFSIZE)
{
syslog(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"),
c->name, c->hostname);
return -1;
}
c->last_ping_time = now;
cp
return 0;
}

32
src/meta.h Normal file
View file

@ -0,0 +1,32 @@
/*
meta.h -- header for meta.c
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: meta.h,v 1.1.2.6 2002/02/10 21:57:54 guus Exp $
*/
#ifndef __TINC_META_H__
#define __TINC_META_H__
#include "connection.h"
extern int send_meta(connection_t *, const char *, int);
extern int broadcast_meta(connection_t *, const char *, int);
extern int receive_meta(connection_t *);
#endif /* __TINC_META_H__ */

449
src/net.c Normal file
View file

@ -0,0 +1,449 @@
/*
net.c -- most of the network code
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: net.c,v 1.35.4.169 2002/04/01 21:28:05 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/rand.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "system.h"
int do_purge = 0;
int sighup = 0;
int sigalrm = 0;
time_t now = 0;
/* Purge edges and subnets of unreachable nodes. Use carefully. */
void purge(void)
{
avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext, *cnode;
node_t *n;
edge_t *e;
subnet_t *s;
connection_t *c;
cp
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_DEBUG, _("Purging unreachable nodes"));
for(nnode = node_tree->head; nnode; nnode = nnext)
{
nnext = nnode->next;
n = (node_t *)nnode->data;
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Purging node %s (%s)"), n->name, n->hostname);
for(snode = n->subnet_tree->head; snode; snode = snext)
{
snext = snode->next;
s = (subnet_t *)snode->data;
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_subnet(c, s);
}
subnet_del(n, s);
}
for(enode = n->edge_tree->head; enode; enode = enext)
{
enext = enode->next;
e = (edge_t *)enode->data;
for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
c = (connection_t *)cnode->data;
if(c->status.active)
send_del_edge(c, e);
}
edge_del(e);
}
node_del(n);
}
}
cp
}
/*
put all file descriptors in an fd_set array
While we're at it, purge stuff that needs to be removed.
*/
void build_fdset(fd_set *fs)
{
avl_node_t *node, *next;
connection_t *c;
int i;
cp
FD_ZERO(fs);
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->status.remove)
connection_del(c);
else
FD_SET(c->socket, fs);
}
if(!connection_tree->head)
purge();
for(i = 0; i < listen_sockets; i++)
{
FD_SET(listen_socket[i].tcp, fs);
FD_SET(listen_socket[i].udp, fs);
}
FD_SET(device_fd, fs);
cp
}
/*
Terminate a connection:
- Close the socket
- Remove associated edge and tell other connections about it if report = 1
- Check if we need to retry making an outgoing connection
- Deactivate the host
*/
void terminate_connection(connection_t *c, int report)
{
avl_node_t *node;
connection_t *other;
cp
if(c->status.remove)
return;
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Closing connection with %s (%s)"),
c->name, c->hostname);
c->status.remove = 1;
c->status.active = 0;
if(c->node)
c->node->connection = NULL;
if(c->socket)
close(c->socket);
if(c->edge)
{
if(report)
{
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_del_edge(other, c->edge);
}
}
edge_del(c->edge);
/* Run MST and SSSP algorithms */
graph();
}
/* Check if this was our outgoing connection */
if(c->outgoing)
{
retry_outgoing(c->outgoing);
c->outgoing = NULL;
}
cp
}
/*
Check if the other end is active.
If we have sent packets, but didn't receive any,
then possibly the other end is dead. We send a
PING request over the meta connection. If the other
end does not reply in time, we consider them dead
and close the connection.
*/
void check_dead_connections(void)
{
avl_node_t *node, *next;
connection_t *c;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->last_ping_time + pingtimeout < now)
{
if(c->status.active)
{
if(c->status.pinged)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_INFO, _("%s (%s) didn't respond to PING"),
c->name, c->hostname);
c->status.timeout = 1;
terminate_connection(c, 1);
}
else
{
send_ping(c);
}
}
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_WARNING, _("Timeout from %s (%s) during authentication"),
c->name, c->hostname);
terminate_connection(c, 0);
}
}
}
cp
}
/*
check all connections to see if anything
happened on their sockets
*/
void check_network_activity(fd_set *f)
{
connection_t *c;
avl_node_t *node;
int result, i;
int len = sizeof(result);
vpn_packet_t packet;
cp
if(FD_ISSET(device_fd, f))
{
if(!read_packet(&packet))
route_outgoing(&packet);
}
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.remove)
continue;
if(FD_ISSET(c->socket, f))
{
if(c->status.connecting)
{
c->status.connecting = 0;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
if(!result)
finish_connecting(c);
else
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_DEBUG, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(result));
close(c->socket);
do_outgoing_connection(c);
continue;
}
}
if(receive_meta(c) < 0)
{
terminate_connection(c, c->status.active);
continue;
}
}
}
for(i = 0; i < listen_sockets; i++)
{
if(FD_ISSET(listen_socket[i].udp, f))
handle_incoming_vpn_data(listen_socket[i].udp);
if(FD_ISSET(listen_socket[i].tcp, f))
handle_new_meta_connection(listen_socket[i].tcp);
}
cp
}
/*
this is where it all happens...
*/
void main_loop(void)
{
fd_set fset;
struct timeval tv;
int r;
time_t last_ping_check;
event_t *event;
cp
last_ping_check = now;
srand(now);
for(;;)
{
now = time(NULL);
tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
tv.tv_usec = 0;
build_fdset(&fset);
if((r = select(FD_SETSIZE, &fset, NULL, NULL, &tv)) < 0)
{
if(errno != EINTR && errno != EAGAIN)
{
syslog(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno));
cp_trace();
dump_connections();
return;
}
continue;
}
check_network_activity(&fset);
if(do_purge)
{
purge();
do_purge = 0;
}
/* Let's check if everybody is still alive */
if(last_ping_check + pingtimeout < now)
{
check_dead_connections();
last_ping_check = now;
if(routing_mode== RMODE_SWITCH)
age_mac();
age_past_requests();
/* Should we regenerate our key? */
if(keyexpires < now)
{
if(debug_lvl >= DEBUG_STATUS)
syslog(LOG_INFO, _("Regenerating symmetric key"));
RAND_pseudo_bytes(myself->key, myself->keylength);
send_key_changed(myself->connection, myself);
keyexpires = now + keylifetime;
}
}
while((event = get_expired_event()))
{
event->handler(event->data);
free(event);
}
if(sigalrm)
{
syslog(LOG_INFO, _("Flushing event queue"));
while(event_tree->head)
{
event = (event_t *)event_tree->head->data;
event->handler(event->data);
event_del(event);
}
sigalrm = 0;
}
if(sighup)
{
sighup = 0;
close_network_connections();
exit_configuration(&config_tree);
syslog(LOG_INFO, _("Rereading configuration file and restarting in 5 seconds..."));
sleep(5);
init_configuration(&config_tree);
if(read_server_config())
{
syslog(LOG_ERR, _("Unable to reread configuration file, exitting."));
exit(1);
}
if(setup_network_connections())
return;
continue;
}
}
cp
}

153
src/net.h Normal file
View file

@ -0,0 +1,153 @@
/*
net.h -- header for net.c
Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: net.h,v 1.9.4.49 2002/03/27 15:01:36 guus Exp $
*/
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include "config.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9014 /* 9000 bytes payload + 14 bytes ethernet header */
#define MAXSIZE 9100 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
#define MAXBUFSIZE 9100 /* Must support TCP packets of length 9000. */
#else
#define MTU 1514 /* 1500 bytes payload + 14 bytes ethernet header */
#define MAXSIZE 1600 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
#define MAXBUFSIZE 2100 /* Quite large but needed for support of keys up to 8192 bits. */
#endif
#define MAXSOCKETS 128 /* Overkill... */
#define MAXQUEUELENGTH 8 /* Maximum number of packats in a single queue */
typedef struct mac_t
{
unsigned char x[6];
} mac_t;
typedef struct ipv4_t
{
unsigned char x[4];
} ipv4_t;
typedef struct ip_mask_t {
ipv4_t address;
ipv4_t mask;
} ip_mask_t;
typedef struct ipv6_t
{
unsigned short x[8];
} ipv6_t;
typedef unsigned short port_t;
typedef short length_t;
typedef union {
struct sockaddr sa;
struct sockaddr_in in;
struct sockaddr_in6 in6;
} sockaddr_t;
#ifdef SA_LEN
#define SALEN(s) SA_LEN(&s)
#else
#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
#endif
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
unsigned int seqno; /* 32 bits sequence number (network byte order of course) */
unsigned char data[MAXSIZE];
} vpn_packet_t;
typedef struct queue_element_t {
void *packet;
struct queue_element_t *prev;
struct queue_element_t *next;
} queue_element_t;
typedef struct packet_queue_t {
queue_element_t *head;
queue_element_t *tail;
} packet_queue_t;
typedef struct outgoing_t {
char *name;
int timeout;
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
} outgoing_t;
typedef struct listen_socket_t {
int tcp;
int udp;
sockaddr_t sa;
} listen_socket_t;
extern int maxtimeout;
extern int seconds_till_retry;
extern int addressfamily;
extern char *request_name[];
extern char *status_text[];
#include "connection.h" /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
extern int keyexpires;
extern int keylifetime;
extern int do_prune;
extern int do_purge;
extern char *myport;
extern time_t now;
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int);
extern void finish_connecting(connection_t *);
extern void do_outgoing_connection(connection_t *);
extern int handle_new_meta_connection(int);
extern int setup_listen_socket(sockaddr_t *);
extern int setup_vpn_in_socket(sockaddr_t *);
extern void send_packet(struct node_t *, vpn_packet_t *);
extern void receive_packet(struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(struct node_t *, vpn_packet_t *);
extern int setup_network_connections(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern void main_loop(void);
extern void terminate_connection(connection_t *, int);
extern void flush_queue(struct node_t *);
extern int read_rsa_public_key(struct connection_t *);
#endif /* __TINC_NET_H__ */

431
src/net_packet.c Normal file
View file

@ -0,0 +1,431 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: net_packet.c,v 1.1.2.13 2002/03/27 15:01:37 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#ifndef HAVE_RAND_PSEUDO_BYTES
#define RAND_pseudo_bytes RAND_bytes
#endif
#include <zlib.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "system.h"
int keylifetime = 0;
int keyexpires = 0;
#define MAX_SEQNO 1073741824
/* VPN packet I/O */
void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
int outlen, outpad;
long int complen = MTU + 12;
EVP_CIPHER_CTX ctx;
char hmac[EVP_MAX_MD_SIZE];
cp
/* Check the message authentication code */
if(myself->digest && myself->maclength)
{
inpkt->len -= myself->maclength;
HMAC(myself->digest, myself->key, myself->keylength, (char *)&inpkt->seqno, inpkt->len, hmac, NULL);
if(memcmp(hmac, (char *)&inpkt->seqno + inpkt->len, myself->maclength))
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return;
}
}
/* Decrypt the packet */
if(myself->cipher)
{
outpkt = pkt[nextpkt++];
EVP_DecryptInit(&ctx, myself->cipher, myself->key, myself->key + myself->cipher->key_len);
EVP_DecryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_DecryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Check the sequence number */
inpkt->len -= sizeof(inpkt->seqno);
inpkt->seqno = ntohl(inpkt->seqno);
if(inpkt->seqno <= n->received_seqno)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Got late or replayed packet from %s (%s), seqno %d"), n->name, n->hostname, inpkt->seqno);
return;
}
n->received_seqno = inpkt->seqno;
if(n->received_seqno > MAX_SEQNO)
keyexpires = 0;
/* Decompress the packet */
if(myself->compression)
{
outpkt = pkt[nextpkt++];
if(uncompress(outpkt->data, &complen, inpkt->data, inpkt->len) != Z_OK)
{
syslog(LOG_ERR, _("Error while uncompressing packet from %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
receive_packet(n, inpkt);
cp
}
void receive_tcppacket(connection_t *c, char *buffer, int len)
{
vpn_packet_t outpkt;
cp
outpkt.len = len;
memcpy(outpkt.data, buffer, len);
receive_packet(c->node, &outpkt);
cp
}
void receive_packet(node_t *n, vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), packet->len, n->name, n->hostname);
route_incoming(n, packet);
cp
}
void send_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
int outlen, outpad;
long int complen = MTU + 12;
EVP_CIPHER_CTX ctx;
vpn_packet_t *copy;
static int priority = 0;
int origpriority;
int sock;
cp
/* Make sure we have a valid key */
if(!n->status.validkey)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("No valid key known yet for %s (%s), queueing packet"),
n->name, n->hostname);
/* Since packet is on the stack of handle_tap_input(),
we have to make a copy of it first. */
copy = xmalloc(sizeof(vpn_packet_t));
memcpy(copy, inpkt, sizeof(vpn_packet_t));
list_insert_tail(n->queue, copy);
if(n->queue->count > MAXQUEUELENGTH)
list_delete_head(n->queue);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
n->status.waitingforkey = 1;
return;
}
origlen = inpkt->len;
origpriority = inpkt->priority;
/* Compress the packet */
if(n->compression)
{
outpkt = pkt[nextpkt++];
if(compress2(outpkt->data, &complen, inpkt->data, inpkt->len, n->compression) != Z_OK)
{
syslog(LOG_ERR, _("Error while compressing packet to %s (%s)"), n->name, n->hostname);
return;
}
outpkt->len = complen;
inpkt = outpkt;
}
/* Add sequence number */
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof(inpkt->seqno);
/* Encrypt the packet */
if(n->cipher)
{
outpkt = pkt[nextpkt++];
EVP_EncryptInit(&ctx, n->cipher, n->key, n->key + n->cipher->key_len);
EVP_EncryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
EVP_EncryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
outpkt->len = outlen + outpad;
inpkt = outpkt;
}
/* Add the message authentication code */
if(n->digest && n->maclength)
{
HMAC(n->digest, n->key, n->keylength, (char *)&inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len, &outlen);
inpkt->len += n->maclength;
}
/* Determine which socket we have to use */
for(sock = 0; sock < listen_sockets; sock++)
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
if(sock >= listen_sockets)
sock = 0; /* If none is available, just use the first and hope for the best. */
/* Send the packet */
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority && listen_socket[sock].sa.sa.sa_family == AF_INET)
{
priority = origpriority;
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority);
if(setsockopt(sock, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
syslog(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno));
}
#endif
if((sendto(listen_socket[sock].udp, (char *)&inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0)
{
syslog(LOG_ERR, _("Error sending packet to %s (%s): %s"),
n->name, n->hostname, strerror(errno));
return;
}
inpkt->len = origlen;
cp
}
/*
send a packet to the given vpn ip.
*/
void send_packet(node_t *n, vpn_packet_t *packet)
{
node_t *via;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
packet->len, n->name, n->hostname);
if(n == myself)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_NOTICE, _("Packet is looping back to us!"));
}
return;
}
if(!n->status.reachable)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Node %s (%s) is not reachable"),
n->name, n->hostname);
return;
}
via = (n->via == myself)?n->nexthop:n->via;
if(via != n && debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Sending packet to %s via %s (%s)"),
n->name, via->name, n->via->hostname);
if((myself->options | via->options) & OPTION_TCPONLY)
{
if(send_tcppacket(via->connection, packet))
terminate_connection(via->connection, 1);
}
else
send_udppacket(via, packet);
}
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(node_t *from, vpn_packet_t *packet)
{
avl_node_t *node;
connection_t *c;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
}
cp
}
void flush_queue(node_t *n)
{
list_node_t *node, *next;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname);
for(node = n->queue->head; node; node = next)
{
next = node->next;
send_udppacket(n, (vpn_packet_t *)node->data);
list_delete_node(n->queue, node);
}
cp
}
void handle_incoming_vpn_data(int sock)
{
vpn_packet_t pkt;
int x, l = sizeof(x);
char *hostname;
sockaddr_t from;
socklen_t fromlen = sizeof(from);
node_t *n;
cp
if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
{
syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s"),
__FILE__, __LINE__, sock, strerror(errno));
cp_trace();
exit(1);
}
if(x)
{
syslog(LOG_ERR, _("Incoming data socket error: %s"), strerror(x));
return;
}
if((pkt.len = recvfrom(sock, (char *)&pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen)) <= 0)
{
syslog(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno));
return;
}
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
if(!n)
{
hostname = sockaddr2hostname(&from);
syslog(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
if(n->connection)
n->connection->last_ping_time = now;
receive_udppacket(n, &pkt);
cp
}

564
src/net_setup.c Normal file
View file

@ -0,0 +1,564 @@
/*
net_setup.c -- Setup.
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: net_setup.c,v 1.1.2.14 2002/04/01 21:28:39 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "system.h"
char *myport;
int read_rsa_public_key(connection_t *c)
{
FILE *fp;
char *fname;
char *key;
cp
if(!c->rsa_key)
c->rsa_key = RSA_new();
/* First, check for simple PublicKey statement */
if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key))
{
BN_hex2bn(&c->rsa_key->n, key);
BN_hex2bn(&c->rsa_key->e, "FFFF");
free(key);
return 0;
}
/* Else, check for PublicKeyFile statement and read it */
if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
{
if(is_safe_path(fname))
{
if((fp = fopen(fname, "r")) == NULL)
{
syslog(LOG_ERR, _("Error reading RSA public key file `%s': %s"),
fname, strerror(errno));
free(fname);
return -1;
}
free(fname);
c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
fclose(fp);
if(!c->rsa_key)
{
syslog(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"),
fname, strerror(errno));
return -1;
}
return 0;
}
else
{
free(fname);
return -1;
}
}
/* Else, check if a harnessed public key is in the config file */
asprintf(&fname, "%s/hosts/%s", confbase, c->name);
if((fp = fopen(fname, "r")))
{
c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
fclose(fp);
}
free(fname);
if(c->rsa_key)
return 0;
else
{
syslog(LOG_ERR, _("No public key for %s specified!"), c->name);
return -1;
}
}
int read_rsa_private_key(void)
{
FILE *fp;
char *fname, *key;
cp
if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key))
{
myself->connection->rsa_key = RSA_new();
BN_hex2bn(&myself->connection->rsa_key->d, key);
BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
free(key);
return 0;
}
if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
asprintf(&fname, "%s/rsa_key.priv", confbase);
if(is_safe_path(fname))
{
if((fp = fopen(fname, "r")) == NULL)
{
syslog(LOG_ERR, _("Error reading RSA private key file `%s': %s"),
fname, strerror(errno));
free(fname);
return -1;
}
free(fname);
myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
if(!myself->connection->rsa_key)
{
syslog(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"),
fname, strerror(errno));
return -1;
}
return 0;
}
free(fname);
return -1;
}
/*
Configure node_t myself and set up the local sockets (listen only)
*/
int setup_myself(void)
{
config_t *cfg;
subnet_t *subnet;
char *name, *hostname, *mode, *afname, *cipher, *digest;
struct addrinfo hint, *ai, *aip;
int choice, err;
cp
myself = new_node();
myself->connection = new_connection();
init_configuration(&myself->connection->config_tree);
asprintf(&myself->hostname, _("MYSELF"));
asprintf(&myself->connection->hostname, _("MYSELF"));
myself->connection->options = 0;
myself->connection->protocol_version = PROT_CURRENT;
if(!get_config_string(lookup_config(config_tree, "Name"), &name)) /* Not acceptable */
{
syslog(LOG_ERR, _("Name for tinc daemon required!"));
return -1;
}
if(check_id(name))
{
syslog(LOG_ERR, _("Invalid name for myself!"));
free(name);
return -1;
}
myself->name = name;
myself->connection->name = xstrdup(name);
cp
if(read_rsa_private_key())
return -1;
if(read_connection_config(myself->connection))
{
syslog(LOG_ERR, _("Cannot open host configuration file for myself!"));
return -1;
}
if(read_rsa_public_key(myself->connection))
return -1;
cp
if(!get_config_string(lookup_config(myself->connection->config_tree, "Port"), &myport))
asprintf(&myport, "655");
/* Read in all the subnets specified in the host configuration file */
cfg = lookup_config(myself->connection->config_tree, "Subnet");
while(cfg)
{
if(!get_config_subnet(cfg, &subnet))
return -1;
subnet_add(myself, subnet);
cfg = lookup_config_next(myself->connection->config_tree, cfg);
}
cp
/* Check some options */
if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice))
if(choice)
myself->options |= OPTION_INDIRECT;
if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice))
if(choice)
myself->options |= OPTION_TCPONLY;
if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice))
if(choice)
myself->options |= OPTION_INDIRECT;
if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice))
if(choice)
myself->options |= OPTION_TCPONLY;
if(myself->options & OPTION_TCPONLY)
myself->options |= OPTION_INDIRECT;
if(get_config_string(lookup_config(config_tree, "Mode"), &mode))
{
if(!strcasecmp(mode, "router"))
routing_mode = RMODE_ROUTER;
else if (!strcasecmp(mode, "switch"))
routing_mode = RMODE_SWITCH;
else if (!strcasecmp(mode, "hub"))
routing_mode = RMODE_HUB;
else
{
syslog(LOG_ERR, _("Invalid routing mode!"));
return -1;
}
free(mode);
}
else
routing_mode = RMODE_ROUTER;
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
syslog(LOG_WARNING, _("PriorityInheritance not supported on this platform"));
#endif
if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
macexpire= 600;
if(get_config_int(lookup_config(myself->connection->config_tree, "MaxTimeout"), &maxtimeout))
{
if(maxtimeout <= 0)
{
syslog(LOG_ERR, _("Bogus maximum timeout!"));
return -1;
}
}
else
maxtimeout = 900;
if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname))
{
if(!strcasecmp(afname, "IPv4"))
addressfamily = AF_INET;
else if (!strcasecmp(afname, "IPv6"))
addressfamily = AF_INET6;
else if (!strcasecmp(afname, "any"))
addressfamily = AF_UNSPEC;
else
{
syslog(LOG_ERR, _("Invalid address family!"));
return -1;
}
free(afname);
}
else
addressfamily = AF_INET;
get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
cp
/* Generate packet encryption key */
if(get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
{
if(!strcasecmp(cipher, "none"))
{
myself->cipher = NULL;
}
else
{
if(!(myself->cipher = EVP_get_cipherbyname(cipher)))
{
syslog(LOG_ERR, _("Unrecognized cipher type!"));
return -1;
}
}
}
else
myself->cipher = EVP_bf_cbc();
if(myself->cipher)
myself->keylength = myself->cipher->key_len + myself->cipher->iv_len;
else
myself->keylength = 1;
myself->connection->outcipher = EVP_bf_ofb();
myself->key = (char *)xmalloc(myself->keylength);
RAND_pseudo_bytes(myself->key, myself->keylength);
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
keyexpires = now + keylifetime;
/* Check if we want to use message authentication codes... */
if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
{
if(!strcasecmp(digest, "none"))
{
myself->digest = NULL;
}
else
{
if(!(myself->digest = EVP_get_digestbyname(digest)))
{
syslog(LOG_ERR, _("Unrecognized digest type!"));
return -1;
}
}
}
else
myself->digest = EVP_sha1();
myself->connection->outdigest = EVP_sha1();
if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength))
{
if(myself->digest)
{
if(myself->maclength > myself->digest->md_size)
{
syslog(LOG_ERR, _("MAC length exceeds size of digest!"));
return -1;
}
else if (myself->maclength < 0)
{
syslog(LOG_ERR, _("Bogus MAC length!"));
return -1;
}
}
}
else
myself->maclength = 4;
myself->connection->outmaclength = 0;
/* Compression */
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression))
{
if(myself->compression < 0 || myself->compression > 9)
{
syslog(LOG_ERR, _("Bogus compression level!"));
return -1;
}
}
else
myself->compression = 0;
myself->connection->outcompression = 0;
cp
/* Done */
myself->nexthop = myself;
myself->via = myself;
myself->status.active = 1;
myself->status.reachable = 1;
node_add(myself);
graph();
cp
/* Open sockets */
memset(&hint, 0, sizeof(hint));
hint.ai_family = addressfamily;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE;
if((err = getaddrinfo(NULL, myport, &hint, &ai)) || !ai)
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(err));
return -1;
}
for(aip = ai; aip; aip = aip->ai_next)
{
if((listen_socket[listen_sockets].tcp = setup_listen_socket((sockaddr_t *)aip->ai_addr)) < 0)
continue;
if((listen_socket[listen_sockets].udp = setup_vpn_in_socket((sockaddr_t *)aip->ai_addr)) < 0)
continue;
if(debug_lvl >= DEBUG_CONNECTIONS)
{
hostname = sockaddr2hostname((sockaddr_t *)aip->ai_addr);
syslog(LOG_NOTICE, _("Listening on %s"), hostname);
free(hostname);
}
listen_socket[listen_sockets].sa.sa = *aip->ai_addr;
listen_sockets++;
}
freeaddrinfo(ai);
if(listen_sockets)
syslog(LOG_NOTICE, _("Ready"));
else
{
syslog(LOG_ERR, _("Unable to create any listening socket!"));
return -1;
}
cp
return 0;
}
/*
setup all initial network connections
*/
int setup_network_connections(void)
{
cp
now = time(NULL);
init_connections();
init_subnets();
init_nodes();
init_edges();
init_events();
init_requests();
if(get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
{
if(pingtimeout < 1)
{
pingtimeout = 86400;
}
}
else
pingtimeout = 60;
if(setup_device() < 0)
return -1;
/* Run tinc-up script to further initialize the tap interface */
execute_script("tinc-up");
if(setup_myself() < 0)
return -1;
try_outgoing_connections();
cp
return 0;
}
/*
close all open network connections
*/
void close_network_connections(void)
{
avl_node_t *node, *next;
connection_t *c;
int i;
cp
for(node = connection_tree->head; node; node = next)
{
next = node->next;
c = (connection_t *)node->data;
if(c->outgoing)
free(c->outgoing->name), free(c->outgoing), c->outgoing = NULL;
terminate_connection(c, 0);
}
if(myself && myself->connection)
terminate_connection(myself->connection, 0);
for(i = 0; i < listen_sockets; i++)
{
close(listen_socket[i].tcp);
close(listen_socket[i].udp);
}
exit_requests();
exit_events();
exit_edges();
exit_subnets();
exit_nodes();
exit_connections();
execute_script("tinc-down");
close_device();
cp
return;
}

484
src/net_socket.c Normal file
View file

@ -0,0 +1,484 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: net_socket.c,v 1.1.2.11 2002/03/27 14:02:36 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_LINUX
#include <netinet/ip.h>
#include <netinet/tcp.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <list.h>
#include "conf.h"
#include "connection.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "graph.h"
#include "process.h"
#include "route.h"
#include "device.h"
#include "event.h"
#include "system.h"
int addressfamily = AF_INET;
int maxtimeout = 900;
int seconds_till_retry = 5;
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets = 0;
/* Setup sockets */
int setup_listen_socket(sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
syslog(LOG_ERR, _("Creating metasocket failed: %s"), strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
/* Optimize TCP settings */
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_TCP) && defined(TCP_NODELAY)
setsockopt(nfd, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(nfd, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
}
#else
syslog(LOG_WARNING, _("BindToDevice not supported on this platform"));
#endif
}
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/tcp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
if(listen(nfd, 3))
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno));
return -1;
}
cp
return nfd;
}
int setup_vpn_in_socket(sockaddr_t *sa)
{
int nfd, flags;
char *addrstr;
int option;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
char *interface;
struct ifreq ifr;
#endif
cp
if((nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
syslog(LOG_ERR, _("Creating UDP socket failed: %s"), strerror(errno));
return -1;
}
flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close(nfd);
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
return -1;
}
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
{
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
{
close(nfd);
syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
return -1;
}
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa)))
{
close(nfd);
addrstr = sockaddr2hostname(sa);
syslog(LOG_ERR, _("Can't bind to %s/udp: %s"), addrstr, strerror(errno));
free(addrstr);
return -1;
}
cp
return nfd;
}
void retry_outgoing(outgoing_t *outgoing)
{
event_t *event;
cp
outgoing->timeout += 5;
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
event = new_event();
event->handler = (event_handler_t)setup_outgoing_connection;
event->time = now + outgoing->timeout;
event->data = outgoing;
event_add(event);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout);
cp
}
int setup_outgoing_socket(connection_t *c)
{
int option;
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
return -1;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Connect */
if(connect(c->socket, &c->address.sa, SALEN(c->address.sa)) == -1)
{
close(c->socket);
syslog(LOG_ERR, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(errno));
return -1;
}
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
cp
return 0;
}
void finish_connecting(connection_t *c)
{
cp
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
c->last_ping_time = now;
send_id(c);
cp
}
void do_outgoing_connection(connection_t *c)
{
char *address, *port;
int option, result, flags;
cp
begin:
if(!c->outgoing->ai)
{
if(!c->outgoing->cfg)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("Could not set up a meta connection to %s"), c->name);
c->status.remove = 1;
retry_outgoing(c->outgoing);
return;
}
get_config_string(c->outgoing->cfg, &address);
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
asprintf(&port, "655");
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
free(address);
free(port);
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
}
if(!c->outgoing->aip)
{
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
goto begin;
}
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
if(c->hostname)
free(c->hostname);
c->hostname = sockaddr2hostname(&c->address);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
goto begin;
}
/* Optimize TCP settings */
#ifdef HAVE_LINUX
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
option = IPTOS_LOWDELAY;
setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
#endif
/* Non-blocking */
flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0)
{
syslog(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
/* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1)
{
if(errno == EINPROGRESS)
{
c->status.connecting = 1;
return;
}
close(c->socket);
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_ERR, _("%s: %s"), c->hostname, strerror(errno));
goto begin;
}
finish_connecting(c);
return;
cp
}
void setup_outgoing_connection(outgoing_t *outgoing)
{
connection_t *c;
node_t *n;
cp
n = lookup_node(outgoing->name);
if(n)
if(n->connection)
{
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_INFO, _("Already connected to %s"), outgoing->name);
n->connection->outgoing = outgoing;
return;
}
c = new_connection();
c->name = xstrdup(outgoing->name);
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
init_configuration(&c->config_tree);
read_connection_config(c);
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg)
{
syslog(LOG_ERR, _("No address specified for %s"), c->name);
free_connection(c);
free(outgoing->name);
free(outgoing);
return;
}
c->outgoing = outgoing;
c->last_ping_time = now;
connection_add(c);
do_outgoing_connection(c);
}
/*
accept a new tcp connect and create a
new connection
*/
int handle_new_meta_connection(int sock)
{
connection_t *c;
sockaddr_t sa;
int fd, len = sizeof(sa);
cp
if((fd = accept(sock, &sa.sa, &len)) < 0)
{
syslog(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno));
return -1;
}
sockaddrunmap(&sa);
c = new_connection();
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now;
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection from %s"), c->hostname);
connection_add(c);
c->allow_request = ID;
send_id(c);
cp
return 0;
}
void try_outgoing_connections(void)
{
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
cp
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg))
{
get_config_string(cfg, &name);
if(check_id(name))
{
syslog(LOG_ERR, _("Invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line);
free(name);
continue;
}
outgoing = xmalloc_and_zero(sizeof(*outgoing));
outgoing->name = name;
setup_outgoing_connection(outgoing);
}
}

246
src/netutl.c Normal file
View file

@ -0,0 +1,246 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: netutl.c,v 1.12.4.34 2002/04/05 09:11:38 guus Exp $
*/
#include "config.h"
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <utils.h>
#include <xalloc.h>
#include "errno.h"
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "system.h"
int hostnames = 0;
/*
Turn a string into a struct addrinfo.
Return NULL on failure.
*/
struct addrinfo *str2addrinfo(char *address, char *service, int socktype)
{
struct addrinfo hint, *ai;
int err;
cp
memset(&hint, 0, sizeof(hint));
hint.ai_family = addressfamily;
hint.ai_socktype = socktype;
if((err = getaddrinfo(address, service, &hint, &ai)))
{
if(debug_lvl >= DEBUG_ERROR)
syslog(LOG_WARNING, _("Error looking up %s port %s: %s\n"), address, service, gai_strerror(err));
cp_trace();
return NULL;
}
cp
return ai;
}
sockaddr_t str2sockaddr(char *address, char *port)
{
struct addrinfo hint, *ai;
sockaddr_t result;
int err;
cp
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
hint.ai_socktype = SOCK_STREAM;
if((err = getaddrinfo(address, port, &hint, &ai) || !ai))
{
syslog(LOG_ERR, _("Error looking up %s port %s: %s\n"), address, port, gai_strerror(err));
cp_trace();
raise(SIGFPE);
exit(0);
}
result = *(sockaddr_t *)ai->ai_addr;
freeaddrinfo(ai);
cp
return result;
}
void sockaddr2str(sockaddr_t *sa, char **addrstr, char **portstr)
{
char address[NI_MAXHOST];
char port[NI_MAXSERV];
char *scopeid;
int err;
cp
if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)))
{
syslog(LOG_ERR, _("Error while translating addresses: %s"), gai_strerror(err));
cp_trace();
raise(SIGFPE);
exit(0);
}
#ifdef HAVE_LINUX
if((scopeid = strchr(address, '%')))
*scopeid = '\0'; /* Descope. */
#endif
*addrstr = xstrdup(address);
*portstr = xstrdup(port);
cp
}
char *sockaddr2hostname(sockaddr_t *sa)
{
char *str;
char address[NI_MAXHOST] = "unknown";
char port[NI_MAXSERV] = "unknown";
int err;
cp
if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), hostnames?0:(NI_NUMERICHOST|NI_NUMERICSERV))))
{
syslog(LOG_ERR, _("Error while looking up hostname: %s"), gai_strerror(err));
}
asprintf(&str, _("%s port %s"), address, port);
cp
return str;
}
int sockaddrcmp(sockaddr_t *a, sockaddr_t *b)
{
int result;
cp
result = a->sa.sa_family - b->sa.sa_family;
if(result)
return result;
switch(a->sa.sa_family)
{
case AF_UNSPEC:
return 0;
case AF_INET:
result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
if(result)
return result;
return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
case AF_INET6:
result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
if(result)
return result;
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
default:
syslog(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"), a->sa.sa_family);
cp_trace();
raise(SIGFPE);
exit(0);
}
cp
}
void sockaddrunmap(sockaddr_t *sa)
{
if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr))
{
sa->in.sin_addr.s_addr = ((uint32_t *)&sa->in6.sin6_addr)[3];
sa->in.sin_family = AF_INET;
}
}
/* Subnet mask handling */
int maskcmp(char *a, char *b, int masklen, int len)
{
int i, m, result;
cp
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
if((result = a[i] - b[i]))
return result;
if(m)
return (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m))));
return 0;
}
void mask(char *a, int masklen, int len)
{
int i;
cp
i = masklen / 8;
masklen %= 8;
if(masklen)
a[i++] &= (0x100 - (1 << masklen));
for(; i < len; i++)
a[i] = 0;
}
void maskcpy(char *a, char *b, int masklen, int len)
{
int i, m;
cp
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;
}
int maskcheck(char *a, int masklen, int len)
{
int i;
cp
i = masklen / 8;
masklen %= 8;
if(masklen)
if(a[i++] & (char)~(0x100 - (1 << masklen)))
return -1;
for(; i < len; i++)
if(a[i] != 0)
return -1;
return 0;
}

46
src/netutl.h Normal file
View file

@ -0,0 +1,46 @@
/*
netutl.h -- header file for netutl.c
Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: netutl.h,v 1.2.4.11 2002/03/17 15:59:29 guus Exp $
*/
#ifndef __TINC_NETUTL_H__
#define __TINC_NETUTL_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "net.h"
extern int hostnames;
extern char *hostlookup(unsigned long);
extern struct addrinfo *str2addrinfo(char *, char *, int);
extern sockaddr_t str2sockaddr(char *, char *);
extern void sockaddr2str(sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(sockaddr_t *);
extern int sockaddrcmp(sockaddr_t *, sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern int maskcmp(char *, char *, int, int);
extern void maskcpy(char *, char *, int, int);
extern void mask(char *, int, int);
extern int maskcheck(char *, int, int);
#endif /* __TINC_NETUTL_H__ */

173
src/node.c Normal file
View file

@ -0,0 +1,173 @@
/*
node.c -- node tree management
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: node.c,v 1.1.2.11 2002/03/22 13:31:18 guus Exp $
*/
#include "config.h"
#include <string.h>
#include <syslog.h>
#include <avl_tree.h>
#include "node.h"
#include "netutl.h"
#include "net.h"
#include <utils.h>
#include <xalloc.h>
#include "system.h"
avl_tree_t *node_tree; /* Known nodes, sorted by name */
avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
node_t *myself;
int node_compare(node_t *a, node_t *b)
{
return strcmp(a->name, b->name);
}
int node_udp_compare(node_t *a, node_t *b)
{
int result;
cp
result = sockaddrcmp(&a->address, &b->address);
if(result)
return result;
return (a->name && b->name)?strcmp(a->name, b->name):0;
}
void init_nodes(void)
{
cp
node_tree = avl_alloc_tree((avl_compare_t)node_compare, NULL);
node_udp_tree = avl_alloc_tree((avl_compare_t)node_udp_compare, NULL);
cp
}
void exit_nodes(void)
{
cp
avl_delete_tree(node_tree);
avl_delete_tree(node_udp_tree);
cp
}
node_t *new_node(void)
{
node_t *n = (node_t *)xmalloc_and_zero(sizeof(*n));
cp
n->subnet_tree = new_subnet_tree();
n->edge_tree = new_edge_tree();
n->queue = list_alloc((list_action_t)free);
cp
return n;
}
void free_node(node_t *n)
{
cp
if(n->queue)
list_delete_list(n->queue);
if(n->name)
free(n->name);
if(n->hostname)
free(n->hostname);
if(n->key)
free(n->key);
if(n->subnet_tree)
free_subnet_tree(n->subnet_tree);
if(n->edge_tree)
free_edge_tree(n->edge_tree);
free(n);
cp
}
void node_add(node_t *n)
{
cp
avl_insert(node_tree, n);
avl_insert(node_udp_tree, n);
cp
}
void node_del(node_t *n)
{
avl_node_t *node, *next;
edge_t *e;
subnet_t *s;
cp
for(node = n->subnet_tree->head; node; node = next)
{
next = node->next;
s = (subnet_t *)node->data;
subnet_del(n, s);
}
for(node = n->subnet_tree->head; node; node = next)
{
next = node->next;
e = (edge_t *)node->data;
edge_del(e);
}
cp
avl_delete(node_tree, n);
avl_delete(node_udp_tree, n);
cp
}
node_t *lookup_node(char *name)
{
node_t n;
cp
n.name = name;
return avl_search(node_tree, &n);
}
node_t *lookup_node_udp(sockaddr_t *sa)
{
node_t n;
cp
n.address = *sa;
n.name = NULL;
return avl_search(node_udp_tree, &n);
}
void dump_nodes(void)
{
avl_node_t *node;
node_t *n;
cp
syslog(LOG_DEBUG, _("Nodes:"));
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
syslog(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s"),
n->name, n->hostname, n->cipher?n->cipher->nid:0, n->digest?n->digest->type:0, n->maclength, n->compression, n->options,
n->status, n->nexthop?n->nexthop->name:"-", n->via?n->via->name:"-");
}
syslog(LOG_DEBUG, _("End of nodes."));
cp
}

88
src/node.h Normal file
View file

@ -0,0 +1,88 @@
/*
node.h -- header for node.c
Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: node.h,v 1.1.2.13 2002/03/19 22:48:25 guus Exp $
*/
#ifndef __TINC_NODE_H__
#define __TINC_NODE_H__
#include <avl_tree.h>
#include "subnet.h"
#include "connection.h"
typedef struct node_status_t {
int active:1; /* 1 if active.. */
int validkey:1; /* 1 if we currently have a valid key for him */
int waitingforkey:1; /* 1 if we already sent out a request */
int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
int reachable:1; /* 1 if this node is reachable in the graph */
int indirect:1; /* 1 if this node is not directly reachable by us */
int unused:26;
} node_status_t;
typedef struct node_t {
char *name; /* name of this node */
long int options; /* options turned on for this node */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
struct node_status_t status;
const EVP_CIPHER *cipher; /* Cipher type for UDP packets */
char *key; /* Cipher key and iv */
int keylength; /* Cipher key and iv length*/
const EVP_MD *digest; /* Digest type for MAC */
int maclength; /* Length of MAC */
int compression; /* Compressionlevel, 0 = no compression */
list_t *queue; /* Queue for packets awaiting to be encrypted */
struct node_t *nexthop; /* nearest node from us to him */
struct node_t *via; /* next hop for UDP packets */
avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
avl_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
unsigned int sent_seqno; /* Sequence number last sent to this node */
unsigned int received_seqno; /* Sequence number last received from this node */
} node_t;
extern struct node_t *myself;
extern avl_tree_t *node_tree;
extern avl_tree_t *node_udp_tree;
extern void init_nodes(void);
extern void exit_nodes(void);
extern node_t *new_node(void);
extern void free_node(node_t *);
extern void node_add(node_t *);
extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(sockaddr_t *);
extern void dump_nodes(void);
#endif /* __TINC_NODE_H__ */

195
src/openbsd/device.c Normal file
View file

@ -0,0 +1,195 @@
/*
device.c -- Interaction with OpenBSD tun device
Copyright (C) 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.c,v 1.1.2.8 2002/03/27 16:00:38 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <net/if.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "subnet.h"
#include "system.h"
#define DEFAULT_DEVICE "/dev/tun0"
#define DEVICE_TYPE_ETHERTAP 0
#define DEVICE_TYPE_TUNTAP 1
int device_fd = -1;
int device_type;
char *device;
char *interface;
char *device_info;
int device_total_in = 0;
int device_total_out = 0;
extern subnet_t mymac;
/*
open the local ethertap device
*/
int setup_device(void)
{
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
if(!get_config_string(lookup_config(config_tree, "Interface"), &interface))
interface = rindex(device, '/')?rindex(device, '/')+1:device;
cp
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
syslog(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return -1;
}
cp
/* Set default MAC address for ethertap devices */
mymac.type = SUBNET_MAC;
mymac.net.mac.address.x[0] = 0xfe;
mymac.net.mac.address.x[1] = 0xfd;
mymac.net.mac.address.x[2] = 0x00;
mymac.net.mac.address.x[3] = 0x00;
mymac.net.mac.address.x[4] = 0x00;
mymac.net.mac.address.x[5] = 0x00;
device_info = _("OpenBSD tun device");
syslog(LOG_INFO, _("%s is a %s"), device, device_info);
cp
return 0;
}
void close_device(void)
{
cp
close(device_fd);
cp
}
int read_packet(vpn_packet_t *packet)
{
int lenin;
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}};
cp
if((lenin = readv(device_fd, vector, 2)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
memcpy(packet->data, mymac.net.mac.address.x, 6);
memcpy(packet->data + 6, mymac.net.mac.address.x, 6);
switch(ntohl(type))
{
case AF_INET:
packet->data[12] = 0x8;
packet->data[13] = 0x0;
break;
case AF_INET6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
break;
default:
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Unknown address family %d while reading packet from %s %s"), ntohl(type), device_info, device);
return -1;
}
packet->len = lenin + 10;
device_total_in += packet->len;
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, device_info);
}
return 0;
cp
}
int write_packet(vpn_packet_t *packet)
{
u_int32_t type;
struct iovec vector[2];
int af;
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
af = (packet->data[12] << 8) + packet->data[13];
switch(af)
{
case 0x800:
type = htonl(AF_INET);
break;
case 0x86DD:
type = htonl(AF_INET6);
break;
default:
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_ERR, _("Unknown address family %d while writing packet to %s %s"), af, device_info, device);
return -1;
}
vector[0].iov_base = &type;
vector[0].iov_len = sizeof(type);
vector[1].iov_base = packet->data + 14;
vector[1].iov_len = packet->len - 14;
if(writev(device_fd, vector, 2) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
device_total_out += packet->len;
cp
}
void dump_device_stats(void)
{
cp
syslog(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
syslog(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
syslog(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
cp
}

507
src/process.c Normal file
View file

@ -0,0 +1,507 @@
/*
process.c -- process management functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: process.c,v 1.1.2.39 2002/03/26 12:00:38 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <pidfile.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "process.h"
#include "subnet.h"
#include "device.h"
#include "connection.h"
#include "device.h"
#include "system.h"
/* If zero, don't detach from the terminal. */
int do_detach = 1;
extern char *identname;
extern char *pidfilename;
extern char **g_argv;
sigset_t emptysigset;
static int saved_debug_lvl = 0;
extern int sighup;
extern int sigalrm;
extern int do_purge;
void memory_full(int size)
{
syslog(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size);
cp_trace();
exit(1);
}
/* Some functions the less gifted operating systems might lack... */
#ifndef HAVE_FCLOSEALL
int fcloseall(void)
{
fflush(stdin);
fflush(stdout);
fflush(stderr);
fclose(stdin);
fclose(stdout);
fclose(stderr);
return 0;
}
#endif
/*
Close network connections, and terminate neatly
*/
void cleanup_and_exit(int c)
{
cp
close_network_connections();
if(debug_lvl > DEBUG_NOTHING)
dump_device_stats();
syslog(LOG_NOTICE, _("Terminating"));
closelog();
exit(c);
}
/*
check for an existing tinc for this net, and write pid to pidfile
*/
int write_pidfile(void)
{
int pid;
cp
if((pid = check_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
netname, pid);
else
fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
return 1;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename))
return 1;
cp
return 0;
}
/*
kill older tincd for this net
*/
int kill_other(int signal)
{
int pid;
cp
if(!(pid = read_pid(pidfilename)))
{
if(netname)
fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
else
fprintf(stderr, _("No other tincd is running.\n"));
return 1;
}
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(netname)
fprintf(stderr, _("The tincd for net `%s' is no longer running. "), netname);
else
fprintf(stderr, _("The tincd is no longer running. "));
fprintf(stderr, _("Removing stale lock file.\n"));
remove_pid(pidfilename);
}
cp
return 0;
}
/*
Detach from current terminal, write pidfile, kill parent
*/
int detach(void)
{
cp
setup_signals();
/* First check if we can open a fresh new pidfile */
if(write_pidfile())
return -1;
/* If we succeeded in doing that, detach */
closelog();
if(do_detach)
{
if(daemon(0, 0) < 0)
{
fprintf(stderr, _("Couldn't detach from terminal: %s"), strerror(errno));
return -1;
}
/* Now UPDATE the pid in the pidfile, because we changed it... */
if(!write_pid(pidfilename))
return -1;
}
openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
VERSION, __DATE__, __TIME__, debug_lvl);
else
syslog(LOG_NOTICE, _("tincd %s starting"), VERSION);
xalloc_fail_func = memory_full;
cp
return 0;
}
/*
Execute the program name, with sane environment. All output will be
redirected to syslog.
*/
void _execute_script(const char *scriptname) __attribute__ ((noreturn));
void _execute_script(const char *scriptname)
{
char *s;
cp
#ifdef HAVE_UNSETENV
unsetenv("NETNAME");
unsetenv("DEVICE");
unsetenv("INTERFACE");
#endif
if(netname)
{
asprintf(&s, "NETNAME=%s", netname);
putenv(s); /* Don't free s! see man 3 putenv */
}
if(device)
{
asprintf(&s, "DEVICE=%s", device);
putenv(s); /* Don't free s! see man 3 putenv */
}
if(interface)
{
asprintf(&s, "INTERFACE=%s", interface);
putenv(s); /* Don't free s! see man 3 putenv */
}
chdir("/");
/* Close all file descriptors */
closelog(); /* <- this means we cannot use syslog() here anymore! */
fcloseall();
execl(scriptname, NULL);
/* No return on success */
if(errno != ENOENT) /* Ignore if the file does not exist */
exit(1); /* Some error while trying execl(). */
else
exit(0);
}
/*
Fork and execute the program pointed to by name.
*/
int execute_script(const char *name)
{
pid_t pid;
int status;
struct stat s;
char *scriptname;
cp
asprintf(&scriptname, "%s/%s", confbase, name);
/* First check if there is a script */
if(stat(scriptname, &s))
return 0;
if((pid = fork()) < 0)
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "fork", strerror(errno));
return -1;
}
if(pid)
{
if(debug_lvl >= DEBUG_STATUS)
syslog(LOG_INFO, _("Executing script %s"), name);
free(scriptname);
if(waitpid(pid, &status, 0) == pid)
{
if(WIFEXITED(status)) /* Child exited by itself */
{
if(WEXITSTATUS(status))
{
syslog(LOG_ERR, _("Process %d (%s) exited with non-zero status %d"), pid, name, WEXITSTATUS(status));
return -1;
}
else
return 0;
}
else if(WIFSIGNALED(status)) /* Child was killed by a signal */
{
syslog(LOG_ERR, _("Process %d (%s) was killed by signal %d (%s)"),
pid, name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return -1;
}
else /* Something strange happened */
{
syslog(LOG_ERR, _("Process %d (%s) terminated abnormally"), pid, name);
return -1;
}
}
else
{
syslog(LOG_ERR, _("System call `%s' failed: %s"), "waitpid", strerror(errno));
return -1;
}
}
cp
/* Child here */
_execute_script(scriptname);
}
/*
Signal handlers.
*/
RETSIGTYPE
sigterm_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got TERM signal"));
cleanup_and_exit(0);
}
RETSIGTYPE
sigquit_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got QUIT signal"));
cleanup_and_exit(0);
}
RETSIGTYPE
fatal_signal_square(int a)
{
syslog(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a));
cp_trace();
exit(1);
}
RETSIGTYPE
fatal_signal_handler(int a)
{
struct sigaction act;
syslog(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
cp_trace();
if(do_detach)
{
syslog(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
act.sa_handler = fatal_signal_square;
act.sa_mask = emptysigset;
act.sa_flags = 0;
sigaction(SIGSEGV, &act, NULL);
close_network_connections();
sleep(5);
remove_pid(pidfilename);
execvp(g_argv[0], g_argv);
}
else
{
syslog(LOG_NOTICE, _("Not restarting."));
exit(1);
}
}
RETSIGTYPE
sighup_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got HUP signal"));
sighup = 1;
}
RETSIGTYPE
sigint_handler(int a)
{
if(saved_debug_lvl)
{
syslog(LOG_NOTICE, _("Reverting to old debug level (%d)"),
saved_debug_lvl);
debug_lvl = saved_debug_lvl;
saved_debug_lvl = 0;
}
else
{
syslog(LOG_NOTICE, _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."),
debug_lvl);
saved_debug_lvl = debug_lvl;
debug_lvl = 5;
}
}
RETSIGTYPE
sigalrm_handler(int a)
{
if(debug_lvl > DEBUG_NOTHING)
syslog(LOG_NOTICE, _("Got ALRM signal"));
sigalrm = 1;
}
RETSIGTYPE
sigusr1_handler(int a)
{
dump_connections();
}
RETSIGTYPE
sigusr2_handler(int a)
{
dump_device_stats();
dump_nodes();
dump_edges();
dump_subnets();
}
RETSIGTYPE
sigwinch_handler(int a)
{
extern int do_purge;
do_purge = 1;
}
RETSIGTYPE
unexpected_signal_handler(int a)
{
syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
cp_trace();
}
RETSIGTYPE
ignore_signal_handler(int a)
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
syslog(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
cp_trace();
}
}
struct {
int signal;
void (*handler)(int);
} sighandlers[] = {
{ SIGHUP, sighup_handler },
{ SIGTERM, sigterm_handler },
{ SIGQUIT, sigquit_handler },
{ SIGSEGV, fatal_signal_handler },
{ 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 }
};
void
setup_signals(void)
{
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++)
{
if(!do_detach)
act.sa_handler = SIG_DFL;
else
act.sa_handler = unexpected_signal_handler;
sigaction(i, &act, NULL);
}
/* If we didn't detach, allow coredumps */
if(!do_detach)
sighandlers[3].handler = SIG_DFL;
/* 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));
}
}

36
src/process.h Normal file
View file

@ -0,0 +1,36 @@
/*
process.h -- header file for process.c
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: process.h,v 1.1.2.10 2002/02/10 21:57:54 guus Exp $
*/
#ifndef __TINC_PROCESS_H__
#define __TINC_PROCESS_H__
#include "config.h"
extern int do_detach;
extern void setup_signals(void);
extern int execute_script(const char *);
extern int detach(void);
extern int kill_other(int);
extern void cleanup_and_exit(int);
#endif /* __TINC_PROCESS_H__ */

244
src/protocol.c Normal file
View file

@ -0,0 +1,244 @@
/*
protocol.c -- handle the meta-protocol, basic functions
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
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: protocol.c,v 1.28.4.128 2002/03/27 15:26:43 guus Exp $
*/
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "system.h"
avl_tree_t *past_request_tree;
int check_id(char *id)
{
int i;
for (i = 0; i < strlen(id); i++)
if(!isalnum(id[i]) && id[i] != '_')
return -1;
return 0;
}
/* Generic request routines - takes care of logging and error
detection as well */
int send_request(connection_t *c, const char *format, ...)
{
va_list args;
char buffer[MAXBUFSIZE];
int len, request;
cp
/* Use vsnprintf instead of vasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the
input buffer anyway */
va_start(args, format);
len = vsnprintf(buffer, MAXBUFSIZE, format, args);
va_end(args);
if(len < 0 || len > MAXBUFSIZE-1)
{
syslog(LOG_ERR, _("Output buffer overflow while sending request to %s (%s)"), c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_PROTOCOL)
{
sscanf(buffer, "%d", &request);
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Sending %s to %s (%s): %s"), request_name[request], c->name, c->hostname, buffer);
else
syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], c->name, c->hostname);
}
buffer[len++] = '\n';
cp
return send_meta(c, buffer, len);
}
int receive_request(connection_t *c)
{
int request;
cp
if(sscanf(c->buffer, "%d", &request) == 1)
{
if((request < 0) || (request >= LAST) || (request_handlers[request] == NULL))
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Unknown request from %s (%s): %s"),
c->name, c->hostname, c->buffer);
else
syslog(LOG_ERR, _("Unknown request from %s (%s)"),
c->name, c->hostname);
return -1;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
{
if(debug_lvl >= DEBUG_META)
syslog(LOG_DEBUG, _("Got %s from %s (%s): %s"),
request_name[request], c->name, c->hostname, c->buffer);
else
syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
request_name[request], c->name, c->hostname);
}
}
if((c->allow_request != ALL) && (c->allow_request != request))
{
syslog(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name, c->hostname);
return -1;
}
if(request_handlers[request](c))
/* Something went wrong. Probably scriptkiddies. Terminate. */
{
syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
request_name[request], c->name, c->hostname);
return -1;
}
}
else
{
syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
c->name, c->hostname);
return -1;
}
cp
return 0;
}
int past_request_compare(past_request_t *a, past_request_t *b)
{
cp
return strcmp(a->request, b->request);
}
void free_past_request(past_request_t *r)
{
cp
if(r->request)
free(r->request);
free(r);
cp
}
void init_requests(void)
{
cp
past_request_tree = avl_alloc_tree((avl_compare_t)past_request_compare, (avl_action_t)free_past_request);
cp
}
void exit_requests(void)
{
cp
avl_delete_tree(past_request_tree);
cp
}
int seen_request(char *request)
{
past_request_t p, *new;
cp
p.request = request;
if(avl_search(past_request_tree, &p))
{
if(debug_lvl >= DEBUG_SCARY_THINGS)
syslog(LOG_DEBUG, _("Already seen request"));
return 1;
}
else
{
new = (past_request_t *)xmalloc(sizeof(*new));
new->request = xstrdup(request);
new->firstseen = now;
avl_insert(past_request_tree, new);
return 0;
}
cp
}
void age_past_requests(void)
{
avl_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
cp
for(node = past_request_tree->head; node; node = next)
{
next = node->next;
p = (past_request_t *)node->data;
if(p->firstseen + pingtimeout < now)
avl_delete_node(past_request_tree, node), deleted++;
else
left++;
}
if(debug_lvl >= DEBUG_SCARY_THINGS && left + deleted)
syslog(LOG_DEBUG, _("Aging past requests: deleted %d, left %d\n"), deleted, left);
cp
}
/* Jumptable for the request handlers */
int (*request_handlers[])(connection_t*) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h,
tcppacket_h,
};
/* Request names */
char (*request_name[]) = {
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE",
"KEY_CHANGED", "REQ_KEY", "ANS_KEY",
"PACKET",
};

120
src/protocol.h Normal file
View file

@ -0,0 +1,120 @@
/*
protocol.h -- header for protocol.c
Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
2000,2001 Guus Sliepen <guus@sliepen.warande.net>
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: protocol.h,v 1.5.4.29 2002/03/22 11:43:48 guus Exp $
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#include "net.h"
#include "node.h"
#include "subnet.h"
/* Protocol version. Different versions are incompatible,
incompatible version have different protocols.
*/
#define PROT_CURRENT 14
/* Request numbers */
enum {
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
// ADD_NODE, DEL_NODE,
ADD_SUBNET, DEL_SUBNET,
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
LAST /* Guardian for the highest request number */
};
typedef struct past_request_t {
char *request;
time_t firstseen;
} past_request_t;
/* Maximum size of strings in a request */
#define MAX_STRING_SIZE 2048
#define MAX_STRING "%2048s"
/* Basic functions */
extern int send_request(connection_t*, const char*, ...);
extern int receive_request(connection_t *);
extern int check_id(char *);
extern void init_requests(void);
extern void exit_requests(void);
extern int seen_request(char *);
extern void age_past_requests(void);
/* Requests */
extern int send_id(connection_t *);
extern int send_metakey(connection_t *);
extern int send_challenge(connection_t *);
extern int send_chal_reply(connection_t *);
extern int send_ack(connection_t *);
extern int send_status(connection_t *, int, char *);
extern int send_error(connection_t *, int, char *);
extern int send_termreq(connection_t *);
extern int send_ping(connection_t *);
extern int send_pong(connection_t *);
// extern int send_add_node(connection_t *, node_t *);
// extern int send_del_node(connection_t *, node_t *);
extern int send_add_subnet(connection_t *, subnet_t *);
extern int send_del_subnet(connection_t *, subnet_t *);
extern int send_add_edge(connection_t *, edge_t *);
extern int send_del_edge(connection_t *, edge_t *);
extern int send_key_changed(connection_t *, node_t *);
extern int send_req_key(connection_t *, node_t *, node_t *);
extern int send_ans_key(connection_t *, node_t *, node_t *);
extern int send_tcppacket(connection_t *, vpn_packet_t *);
/* Request handlers */
extern int (*request_handlers[])(connection_t *);
extern int id_h(connection_t *);
extern int metakey_h(connection_t *);
extern int challenge_h(connection_t *);
extern int chal_reply_h(connection_t *);
extern int ack_h(connection_t *);
extern int status_h(connection_t *);
extern int error_h(connection_t *);
extern int termreq_h(connection_t *);
extern int ping_h(connection_t *);
extern int pong_h(connection_t *);
// extern int add_node_h(connection_t *);
// extern int del_node_h(connection_t *);
extern int add_subnet_h(connection_t *);
extern int del_subnet_h(connection_t *);
extern int add_edge_h(connection_t *);
extern int del_edge_h(connection_t *);
extern int key_changed_h(connection_t *);
extern int req_key_h(connection_t *);
extern int ans_key_h(connection_t *);
extern int tcppacket_h(connection_t *);
#endif /* __TINC_PROTOCOL_H__ */

605
src/protocol_auth.c Normal file
View file

@ -0,0 +1,605 @@
/*
protocol_auth.c -- handle the meta-protocol, authentication
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: protocol_auth.c,v 1.1.4.8 2002/03/27 15:26:44 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#ifndef HAVE_RAND_PSEUDO_BYTES
#define RAND_pseudo_bytes RAND_bytes
#endif
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "system.h"
int send_id(connection_t *c)
{
cp
return send_request(c, "%d %s %d", ID, myself->connection->name, myself->connection->protocol_version);
}
int id_h(connection_t *c)
{
char name[MAX_STRING_SIZE];
int bla;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" %d", name, &c->protocol_version) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ID", c->name, c->hostname);
return -1;
}
/* Check if identity is a valid name */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ID", c->name, c->hostname, "invalid name");
return -1;
}
/* If we set c->name in advance, make sure we are connected to the right host */
if(c->name)
{
if(strcmp(c->name, name))
{
syslog(LOG_ERR, _("Peer %s is %s instead of %s"), c->hostname, name, c->name);
return -1;
}
}
else
c->name = xstrdup(name);
/* Check if version matches */
if(c->protocol_version != myself->connection->protocol_version)
{
syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
c->name, c->hostname, c->protocol_version);
return -1;
}
if(bypass_security)
{
if(!c->config_tree)
init_configuration(&c->config_tree);
c->allow_request = ACK;
return send_ack(c);
}
if(!c->config_tree)
{
init_configuration(&c->config_tree);
if((bla = read_connection_config(c)))
{
syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), c->hostname, c->name);
return -1;
}
}
if(read_rsa_public_key(c))
{
return -1;
}
/* Check some options */
if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &bla) && bla) || myself->options & OPTION_INDIRECT)
c->options |= OPTION_INDIRECT;
if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &bla) && bla) || myself->options & OPTION_TCPONLY)
c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
c->allow_request = METAKEY;
cp
return send_metakey(c);
}
int send_metakey(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len, x;
cp
len = RSA_size(c->rsa_key);
/* Allocate buffers for the meta key */
if(!c->outkey)
c->outkey = xmalloc(len);
if(!c->outctx)
c->outctx = xmalloc(sizeof(*c->outctx));
cp
/* Copy random data to the buffer */
RAND_bytes(c->outkey, len);
/* The message we send must be smaller than the modulus of the RSA key.
By definition, for a key of k bits, the following formula holds:
2^(k-1) <= modulus < 2^(k)
Where ^ means "to the power of", not "xor".
This means that to be sure, we must choose our message < 2^(k-1).
This can be done by setting the most significant bit to zero.
*/
c->outkey[0] &= 0x7F;
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(c->outkey, buffer, len);
buffer[len*2] = '\0';
syslog(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), buffer);
}
/* Encrypt the random data
We do not use one of the PKCS padding schemes here.
This is allowed, because we encrypt a totally random string
with a length equal to that of the modulus of the RSA key.
*/
if(RSA_public_encrypt(len, c->outkey, buffer, c->rsa_key, RSA_NO_PADDING) != len)
{
syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
return -1;
}
cp
/* Convert the encrypted random data to a hexadecimal formatted string */
bin2hex(buffer, buffer, len);
buffer[len*2] = '\0';
/* Send the meta key */
x = send_request(c, "%d %d %d %d %d %s", METAKEY,
c->outcipher?c->outcipher->nid:0, c->outdigest?c->outdigest->type:0,
c->outmaclength, c->outcompression, buffer);
/* Further outgoing requests are encrypted with the key we just generated */
if(c->outcipher)
{
EVP_EncryptInit(c->outctx, c->outcipher,
c->outkey + len - c->outcipher->key_len,
c->outkey + len - c->outcipher->key_len - c->outcipher->iv_len);
c->status.encryptout = 1;
}
cp
return x;
}
int metakey_h(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
int len;
cp
if(sscanf(c->buffer, "%*d %d %d %d %d "MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname);
return -1;
}
cp
len = RSA_size(myself->connection->rsa_key);
/* Check if the length of the meta key is all right */
if(strlen(buffer) != len*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength");
return -1;
}
/* Allocate buffers for the meta key */
cp
if(!c->inkey)
c->inkey = xmalloc(len);
if(!c->inctx)
c->inctx = xmalloc(sizeof(*c->inctx));
/* Convert the challenge from hexadecimal back to binary */
cp
hex2bin(buffer,buffer,len);
/* Decrypt the meta key */
cp
if(RSA_private_decrypt(len, buffer, c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
{
syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(c->inkey, buffer, len);
buffer[len*2] = '\0';
syslog(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer);
}
/* All incoming requests will now be encrypted. */
cp
/* Check and lookup cipher and digest algorithms */
if(cipher)
{
c->incipher = EVP_get_cipherbynid(cipher);
if(!c->incipher)
{
syslog(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname);
return -1;
}
EVP_DecryptInit(c->inctx, c->incipher,
c->inkey + len - c->incipher->key_len,
c->inkey + len - c->incipher->key_len - c->incipher->iv_len);
c->status.decryptin = 1;
}
else
{
c->incipher = NULL;
}
c->inmaclength = maclength;
if(digest)
{
c->indigest = EVP_get_digestbynid(digest);
if(!c->indigest)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname);
return -1;
}
if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0)
{
syslog(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname);
return -1;
}
}
else
{
c->indigest = NULL;
}
c->incompression = compression;
c->allow_request = CHALLENGE;
cp
return send_challenge(c);
}
int send_challenge(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len, x;
cp
/* CHECKME: what is most reasonable value for len? */
len = RSA_size(c->rsa_key);
/* Allocate buffers for the challenge */
if(!c->hischallenge)
c->hischallenge = xmalloc(len);
cp
/* Copy random data to the buffer */
RAND_bytes(c->hischallenge, len);
cp
/* Convert to hex */
bin2hex(c->hischallenge, buffer, len);
buffer[len*2] = '\0';
cp
/* Send the challenge */
x = send_request(c, "%d %s", CHALLENGE, buffer);
cp
return x;
}
int challenge_h(connection_t *c)
{
char buffer[MAX_STRING_SIZE];
int len;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING, buffer) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname);
return -1;
}
len = RSA_size(myself->connection->rsa_key);
/* Check if the length of the challenge is all right */
if(strlen(buffer) != len*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length");
return -1;
}
/* Allocate buffers for the challenge */
if(!c->mychallenge)
c->mychallenge = xmalloc(len);
/* Convert the challenge from hexadecimal back to binary */
hex2bin(buffer,c->mychallenge,len);
c->allow_request = CHAL_REPLY;
/* Rest is done by send_chal_reply() */
cp
return send_chal_reply(c);
}
int send_chal_reply(connection_t *c)
{
char hash[EVP_MAX_MD_SIZE*2+1];
EVP_MD_CTX ctx;
cp
/* Calculate the hash from the challenge we received */
EVP_DigestInit(&ctx, c->indigest);
EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key));
EVP_DigestFinal(&ctx, hash, NULL);
/* Convert the hash to a hexadecimal formatted string */
bin2hex(hash,hash,c->indigest->md_size);
hash[c->indigest->md_size*2] = '\0';
/* Send the reply */
cp
return send_request(c, "%d %s", CHAL_REPLY, hash);
}
int chal_reply_h(connection_t *c)
{
char hishash[MAX_STRING_SIZE];
char myhash[EVP_MAX_MD_SIZE];
EVP_MD_CTX ctx;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING, hishash) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHAL_REPLY", c->name, c->hostname);
return -1;
}
/* Check if the length of the hash is all right */
if(strlen(hishash) != c->outdigest->md_size*2)
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length"));
return -1;
}
/* Convert the hash to binary format */
hex2bin(hishash, hishash, c->outdigest->md_size);
/* Calculate the hash from the challenge we sent */
EVP_DigestInit(&ctx, c->outdigest);
EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key));
EVP_DigestFinal(&ctx, myhash, NULL);
/* Verify the incoming hash with the calculated hash */
if(memcmp(hishash, myhash, c->outdigest->md_size))
{
syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply"));
if(debug_lvl >= DEBUG_SCARY_THINGS)
{
bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
hishash[SHA_DIGEST_LENGTH*2] = '\0';
syslog(LOG_DEBUG, _("Expected challenge reply: %s"), hishash);
}
return -1;
}
/* Identity has now been positively verified.
Send an acknowledgement with the rest of the information needed.
*/
c->allow_request = ACK;
cp
return send_ack(c);
}
int send_ack(connection_t *c)
{
/* ACK message contains rest of the information the other end needs
to create node_t and edge_t structures. */
int x;
char *address, *port;
struct timeval now;
cp
/* Estimate weight */
gettimeofday(&now, NULL);
c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
sockaddr2str(&c->address, &address, &port);
x = send_request(c, "%d %s %s %d %lx", ACK, myport, address, c->estimated_weight, c->options);
free(address);
free(port);
cp
return x;
}
void send_everything(connection_t *c)
{
avl_node_t *node, *node2;
node_t *n;
subnet_t *s;
edge_t *e;
/* Send all known subnets */
for(node = node_tree->head; node; node = node->next)
{
n = (node_t *)node->data;
for(node2 = n->subnet_tree->head; node2; node2 = node2->next)
{
s = (subnet_t *)node2->data;
send_add_subnet(c, s);
}
}
/* Send all known edges */
for(node = edge_tree->head; node; node = node->next)
{
e = (edge_t *)node->data;
if(e == c->edge)
continue;
send_add_edge(c, e);
}
}
int ack_h(connection_t *c)
{
char myaddress[MAX_STRING_SIZE];
char hisport[MAX_STRING_SIZE];
char *hisaddress, *dummy;
int weight;
long int options;
node_t *n;
connection_t *other;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" %d %lx", hisport, myaddress, &weight, &options) != 4)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name, c->hostname);
return -1;
}
/* Check if we already have a node_t for him */
n = lookup_node(c->name);
if(!n)
{
n = new_node();
n->name = xstrdup(c->name);
node_add(n);
}
else
{
if(n->connection)
{
/* Oh dear, we already have a connection to this node. */
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"), n->name, n->hostname);
terminate_connection(n->connection, 0);
}
/* FIXME: check if information in existing node matches that of the other end of this connection */
}
n->connection = c;
c->node = n;
c->options |= options;
/* Create an edge_t for this connection */
c->edge = new_edge();
cp
c->edge->from.node = myself;
c->edge->from.udpaddress = str2sockaddr(myaddress, myport);
c->edge->to.node = n;
sockaddr2str(&c->address, &hisaddress, &dummy);
c->edge->to.udpaddress = str2sockaddr(hisaddress, hisport);
free(hisaddress);
free(dummy);
c->edge->weight = (weight + c->estimated_weight) / 2;
c->edge->connection = c;
c->edge->options = c->options;
cp
edge_add(c->edge);
/* Activate this connection */
c->allow_request = ALL;
c->status.active = 1;
if(debug_lvl >= DEBUG_CONNECTIONS)
syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), c->name, c->hostname);
cp
/* Send him everything we know */
send_everything(c);
/* Notify others of this connection */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_add_edge(other, c->edge);
}
/* Run MST and SSSP algorithms */
graph();
cp
return 0;
}

298
src/protocol_edge.c Normal file
View file

@ -0,0 +1,298 @@
/*
protocol_edge.c -- handle the meta-protocol, edges
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: protocol_edge.c,v 1.1.4.7 2002/03/27 15:26:44 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "system.h"
int send_add_edge(connection_t *c, edge_t *e)
{
int x;
char *from_udpaddress, *from_udpport;
char *to_udpaddress, *to_udpport;
cp
sockaddr2str(&e->from.udpaddress, &from_udpaddress, &from_udpport);
sockaddr2str(&e->to.udpaddress, &to_udpaddress, &to_udpport);
x = send_request(c, "%d %lx %s %s %s %s %s %s %lx %d", ADD_EDGE, random(),
e->from.node->name, from_udpaddress, from_udpport,
e->to.node->name, to_udpaddress, to_udpport,
e->options, e->weight);
free(from_udpaddress);
free(from_udpport);
free(to_udpaddress);
free(to_udpport);
cp
return x;
}
int add_edge_h(connection_t *c)
{
connection_t *other;
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char from_address[MAX_STRING_SIZE];
char from_udpport[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
char to_udpport[MAX_STRING_SIZE];
sockaddr_t from_udpaddress;
sockaddr_t to_udpaddress;
long int options;
int weight;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
from_name, from_address, from_udpport,
to_name, to_address, to_udpport,
&options, &weight) != 8)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name, c->hostname);
return -1;
}
/* Check if names are valid */
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Lookup nodes */
from = lookup_node(from_name);
if(!from)
{
from = new_node();
from->name = xstrdup(from_name);
node_add(from);
}
to = lookup_node(to_name);
if(!to)
{
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
/* Convert addresses */
from_udpaddress = str2sockaddr(from_address, from_udpport);
to_udpaddress = str2sockaddr(to_address, to_udpport);
/* Check if edge already exists */
e = lookup_edge(from, to);
if(e)
{
if(e->weight != weight || e->options != options
|| ((e->from.node == from) && (sockaddrcmp(&e->from.udpaddress, &from_udpaddress)|| sockaddrcmp(&e->to.udpaddress, &to_udpaddress)))
|| ((e->from.node == to) && (sockaddrcmp(&e->from.udpaddress, &to_udpaddress) || sockaddrcmp(&e->to.udpaddress, &from_udpaddress)))
)
{
if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
send_add_edge(c, e);
return 0;
}
else
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
edge_del(e);
}
}
else
return 0;
}
else if(from == myself || to == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not exist"), "ADD_EDGE", c->name, c->hostname);
e = new_edge();
e->from.node = from;
e->to.node = to;
send_del_edge(c, e);
free_edge(e);
return 0;
}
e = new_edge();
e->from.node = from;
e->from.udpaddress = from_udpaddress;
e->to.node = to;
e->to.udpaddress = to_udpaddress;
e->options = options;
e->weight = weight;
edge_add(e);
/* Tell the rest about the new edge */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Run MST before or after we tell the rest? */
graph();
cp
return 0;
}
int send_del_edge(connection_t *c, edge_t *e)
{
cp
return send_request(c, "%d %lx %s %s", DEL_EDGE, random(),
e->from.node->name, e->to.node->name);
}
int del_edge_h(connection_t *c)
{
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
connection_t *other;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING"", from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE",
c->name, c->hostname);
return -1;
}
/* Check if names are valid */
if(check_id(from_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(check_id(to_name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Lookup nodes */
from = lookup_node(from_name);
if(!from)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
to = lookup_node(to_name);
if(!to)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
/* Check if edge exists */
e = lookup_edge(from, to);
if(!e)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
return 0;
}
if(e->from.node == myself || e->to.node == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_EDGE", c->name, c->hostname);
send_add_edge(c, e); /* Send back a correction */
return 0;
}
/* Tell the rest about the deleted edge */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Delete the edge */
edge_del(e);
/* Run MST before or after we tell the rest? */
graph();
cp
return 0;
}

286
src/protocol_key.c Normal file
View file

@ -0,0 +1,286 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: protocol_key.c,v 1.1.4.6 2002/03/22 13:31:18 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "system.h"
int mykeyused = 0;
int send_key_changed(connection_t *c, node_t *n)
{
connection_t *other;
avl_node_t *node;
cp
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
if(n == myself && !mykeyused)
return 0;
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%d %lx %s", KEY_CHANGED, random(), n->name);
}
cp
return 0;
}
int key_changed_h(connection_t *c)
{
char name[MAX_STRING_SIZE];
avl_node_t *node;
connection_t *other;
node_t *n;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING, name) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
c->name, c->hostname);
return -1;
}
if(seen_request(c->buffer))
return 0;
n = lookup_node(name);
if(!n)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"), "KEY_CHANGED",
c->name, c->hostname, name);
return -1;
}
n->status.validkey = 0;
n->status.waitingforkey = 0;
n->sent_seqno = 0;
/* Tell the others */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
}
int send_req_key(connection_t *c, node_t *from, node_t *to)
{
cp
return send_request(c, "%d %s %s", REQ_KEY,
from->name, to->name);
}
int req_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, from_name, to_name) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "REQ_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Check if this key request is for us */
if(to == myself) /* Yes, send our own key back */
{
mykeyused = 1;
from->received_seqno = 0;
send_ans_key(c, myself, from);
}
else
{
/* Proxy keys
if(to->status.validkey)
{
send_ans_key(c, to, from);
}
else
*/
send_req_key(to->nexthop->connection, from, to);
}
cp
return 0;
}
int send_ans_key(connection_t *c, node_t *from, node_t *to)
{
char key[MAX_STRING_SIZE];
cp
bin2hex(from->key, key, from->keylength);
key[from->keylength * 2] = '\0';
cp
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
}
int ans_key_h(connection_t *c)
{
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
node_t *from, *to;
cp
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
c->name, c->hostname);
return -1;
}
from = lookup_node(from_name);
if(!from)
{
syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, from_name);
return -1;
}
to = lookup_node(to_name);
if(!to)
{
syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
c->name, c->hostname, to_name);
return -1;
}
/* Forward it if necessary */
if(to != myself)
{
return send_request(to->nexthop->connection, "%s", c->buffer);
}
/* Update our copy of the origin's packet key */
if(from->key)
free(from->key);
from->key = xstrdup(key);
from->keylength = strlen(key) / 2;
hex2bin(from->key, from->key, from->keylength);
from->key[from->keylength] = '\0';
from->status.validkey = 1;
from->status.waitingforkey = 0;
/* Check and lookup cipher and digest algorithms */
if(cipher)
{
from->cipher = EVP_get_cipherbynid(cipher);
if(!from->cipher)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
return -1;
}
if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
{
syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
return -1;
}
}
else
{
from->cipher = NULL;
}
from->maclength = maclength;
if(digest)
{
from->digest = EVP_get_digestbynid(digest);
if(!from->digest)
{
syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
return -1;
}
if(from->maclength > from->digest->md_size || from->maclength < 0)
{
syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
return -1;
}
}
else
{
from->digest = NULL;
}
from->compression = compression;
flush_queue(from);
cp
return 0;
}

198
src/protocol_misc.c Normal file
View file

@ -0,0 +1,198 @@
/*
protocol_misc.c -- handle the meta-protocol, miscellaneous functions
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: protocol_misc.c,v 1.1.4.3 2002/03/23 20:21:10 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "system.h"
/* Status and error notification routines */
int send_status(connection_t *c, int statusno, char *statusstring)
{
cp
if(!statusstring)
statusstring = status_text[statusno];
cp
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
}
int status_h(connection_t *c)
{
int statusno;
char statusstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &statusno, statusstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS",
c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_STATUS)
{
syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
c->name, c->hostname, status_text[statusno], statusstring);
}
cp
return 0;
}
int send_error(connection_t *c, int err, char *errstring)
{
cp
if(!errstring)
errstring = strerror(err);
return send_request(c, "%d %d %s", ERROR, err, errstring);
}
int error_h(connection_t *c)
{
int err;
char errorstring[MAX_STRING_SIZE];
cp
if(sscanf(c->buffer, "%*d %d "MAX_STRING, &err, errorstring) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR",
c->name, c->hostname);
return -1;
}
if(debug_lvl >= DEBUG_ERROR)
{
syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
c->name, c->hostname, strerror(err), errorstring);
}
terminate_connection(c, c->status.active);
cp
return 0;
}
int send_termreq(connection_t *c)
{
cp
return send_request(c, "%d", TERMREQ);
}
int termreq_h(connection_t *c)
{
cp
terminate_connection(c, c->status.active);
cp
return 0;
}
int send_ping(connection_t *c)
{
cp
c->status.pinged = 1;
c->last_ping_time = now;
cp
return send_request(c, "%d", PING);
}
int ping_h(connection_t *c)
{
cp
return send_pong(c);
}
int send_pong(connection_t *c)
{
cp
return send_request(c, "%d", PONG);
}
int pong_h(connection_t *c)
{
cp
c->status.pinged = 0;
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing)
c->outgoing->timeout = 0;
cp
return 0;
}
/* Sending and receiving packets via TCP */
int send_tcppacket(connection_t *c, vpn_packet_t *packet)
{
int x;
cp
/* Evil hack. */
x = send_request(c, "%d %hd", PACKET, packet->len);
if(x)
return x;
cp
return send_meta(c, packet->data, packet->len);
}
int tcppacket_h(connection_t *c)
{
short int len;
cp
if(sscanf(c->buffer, "%*d %hd", &len) != 1)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name, c->hostname);
return -1;
}
/* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
c->tcplen = len;
cp
return 0;
}
/* Status strings */
char (*status_text[]) = {
"Warning",
};
/* Error strings */
char (*error_text[]) = {
"Error",
};

237
src/protocol_subnet.c Normal file
View file

@ -0,0 +1,237 @@
/*
protocol_subnet.c -- handle the meta-protocol, subnets
Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: protocol_subnet.c,v 1.1.4.3 2002/03/22 13:31:18 guus Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "meta.h"
#include "connection.h"
#include "node.h"
#include "edge.h"
#include "graph.h"
#include "system.h"
int send_add_subnet(connection_t *c, subnet_t *subnet)
{
int x;
char *netstr;
cp
x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(),
subnet->owner->name, netstr = net2str(subnet));
free(netstr);
cp
return x;
}
int add_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname);
return -1;
}
/* Check if owner name is a valid */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if subnet string is valid */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* Check if the owner of the new subnet is in the connection list */
owner = lookup_node(name);
if(!owner)
{
owner = new_node();
owner->name = xstrdup(name);
node_add(owner);
}
/* Check if we already know this subnet */
if(lookup_subnet(owner, s))
{
free_subnet(s);
return 0;
}
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "ADD_SUBNET", c->name, c->hostname);
s->owner = myself;
send_del_subnet(c, s);
return 0;
}
/* If everything is correct, add the subnet to the list of the owner */
subnet_add(owner, s);
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
cp
return 0;
}
int send_del_subnet(connection_t *c, subnet_t *s)
{
int x;
char *netstr;
cp
netstr = net2str(s);
x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr);
free(netstr);
cp
return x;
}
int del_subnet_h(connection_t *c)
{
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
connection_t *other;
subnet_t *s, *find;
avl_node_t *node;
cp
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
{
syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname);
return -1;
}
/* Check if owner name is a valid */
if(check_id(name))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid name"));
return -1;
}
/* Check if the owner of the new subnet is in the connection list */
if(!(owner = lookup_node(name)))
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
/* Check if subnet string is valid */
if(!(s = str2net(subnetstr)))
{
syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid subnet string"));
return -1;
}
if(seen_request(c->buffer))
return 0;
/* If everything is correct, delete the subnet from the list of the owner */
s->owner = owner;
find = lookup_subnet(owner, s);
free_subnet(s);
if(!find)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return 0;
}
/* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
if(owner == myself)
{
if(debug_lvl >= DEBUG_PROTOCOL)
syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return 0;
}
/* Tell the rest */
for(node = connection_tree->head; node; node = node->next)
{
other = (connection_t *)node->data;
if(other->status.active && other != c)
send_request(other, "%s", c->buffer);
}
/* Finally, delete it. */
subnet_del(owner, find);
cp
return 0;
}

493
src/route.c Normal file
View file

@ -0,0 +1,493 @@
/*
route.c -- routing
Copyright (C) 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: route.c,v 1.1.2.37 2002/03/19 00:08:23 guus Exp $
*/
#include "config.h"
#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
#include <sys/param.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD)
#include <net/if.h>
#define ETHER_ADDR_LEN 6
#else
#include <net/ethernet.h>
#endif
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/if_ether.h>
#include <utils.h>
#include <xalloc.h>
#include <syslog.h>
#include <string.h>
#include <avl_tree.h>
#include "net.h"
#include "connection.h"
#include "subnet.h"
#include "route.h"
#include "protocol.h"
#include "device.h"
#include "system.h"
int routing_mode = RMODE_ROUTER;
int priorityinheritance = 0;
int macexpire = 600;
subnet_t mymac;
void learn_mac(mac_t *address)
{
subnet_t *subnet;
avl_node_t *node;
connection_t *c;
cp
subnet = lookup_subnet_mac(address);
/* If we don't know this MAC address yet, store it */
if(!subnet || subnet->owner!=myself)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(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 = new_subnet();
subnet->type = SUBNET_MAC;
memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
subnet_add(myself, subnet);
/* And tell all other tinc daemons it's our MAC */
for(node = connection_tree->head; node; node = node->next)
{
c = (connection_t *)node->data;
if(c->status.active)
send_add_subnet(c, subnet);
}
}
subnet->net.mac.lastseen = now;
}
void age_mac(void)
{
subnet_t *s;
connection_t *c;
avl_node_t *node, *next, *node2;
cp
for(node = myself->subnet_tree->head; node; node = next)
{
next = node->next;
s = (subnet_t *)node->data;
if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
s->net.mac.address.x[0], s->net.mac.address.x[1], s->net.mac.address.x[2], s->net.mac.address.x[3], s->net.mac.address.x[4], s->net.mac.address.x[5]);
for(node2 = connection_tree->head; node2; node2 = node2->next)
{
c = (connection_t *)node2->data;
if(c->status.active)
send_del_subnet(c, s);
}
subnet_del(myself, s);
}
}
cp
}
node_t *route_mac(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
/* Learn source address */
learn_mac((mac_t *)(&packet->data[6]));
/* Lookup destination address */
subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
if(subnet)
return subnet->owner;
else
return NULL;
}
node_t *route_ipv4(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
if(priorityinheritance)
packet->priority = packet->data[15];
subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
cp
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
}
return NULL;
}
cp
return subnet->owner;
}
node_t *route_ipv6(vpn_packet_t *packet)
{
subnet_t *subnet;
cp
subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
cp
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
ntohs(*(short unsigned int *)&packet->data[38]),
ntohs(*(short unsigned int *)&packet->data[40]),
ntohs(*(short unsigned int *)&packet->data[42]),
ntohs(*(short unsigned int *)&packet->data[44]),
ntohs(*(short unsigned int *)&packet->data[46]),
ntohs(*(short unsigned int *)&packet->data[48]),
ntohs(*(short unsigned int *)&packet->data[50]),
ntohs(*(short unsigned int *)&packet->data[52]));
}
return NULL;
}
cp
return subnet->owner;
}
unsigned short int inet_checksum(unsigned short int *data, int len, unsigned short int prevsum)
{
unsigned long int checksum = prevsum ^ 0xFFFF;
while(len--)
checksum += ntohs(*data++);
while(checksum >> 16)
checksum = (checksum & 0xFFFF) + (checksum >> 16);
return checksum ^ 0xFFFF;
}
void route_neighborsol(vpn_packet_t *packet)
{
struct ip6_hdr *hdr;
struct nd_neighbor_solicit *ns;
struct nd_opt_hdr *opt;
subnet_t *subnet;
short unsigned int checksum;
struct {
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
uint32_t length;
uint8_t junk[4];
} pseudo;
cp
hdr = (struct ip6_hdr *)(packet->data + 14);
ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
/* First, snatch the source address from the neighbor solicitation packet */
memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
/* 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)
{
if(debug_lvl > DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
}
return;
}
/* Create pseudo header */
memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
pseudo.junk[3] = IPPROTO_ICMPV6;
/* Generate checksum */
checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
if(checksum)
{
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
return;
}
/* Check if the IPv6 address exists on the VPN */
subnet = lookup_subnet_ipv6((ipv6_t *)&ns->nd_ns_target);
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(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(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16); /* swap destination and source protocol address */
memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16); /* ... */
memcpy((char *)opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
ns->nd_ns_hdr.icmp6_cksum = 0;
ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40; /* Set solicited flag */
ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
/* Create pseudo header */
memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
pseudo.junk[3] = IPPROTO_ICMPV6;
/* Generate checksum */
checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
write_packet(packet);
cp
}
void route_arp(vpn_packet_t *packet)
{
struct ether_arp *arp;
subnet_t *subnet;
unsigned char ipbuf[4];
cp
/* First, snatch the source address from the ARP packet */
memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
/* This routine generates replies to ARP requests.
You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
*/
arp = (struct ether_arp *)(packet->data + 14);
/* Check if this is a valid ARP request */
if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
ntohs(arp->arp_pro) != ETHERTYPE_IP ||
(int) (arp->arp_hln) != ETHER_ADDR_LEN ||
(int) (arp->arp_pln) != 4 ||
ntohs(arp->arp_op) != ARPOP_REQUEST )
{
if(debug_lvl > DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
}
return;
}
/* Check if the IPv4 address exists on the VPN */
subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
if(!subnet)
{
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(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(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
memcpy(arp->arp_spa, ipbuf, 4); /* ... */
memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
arp->arp_op = htons(ARPOP_REPLY);
write_packet(packet);
cp
}
void route_outgoing(vpn_packet_t *packet)
{
unsigned short int type;
node_t *n = NULL;
cp
/* FIXME: multicast? */
switch(routing_mode)
{
case RMODE_ROUTER:
type = ntohs(*((unsigned short*)(&packet->data[12])));
switch(type)
{
case 0x0800:
n = route_ipv4(packet);
break;
case 0x86DD:
if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] == ND_NEIGHBOR_SOLICIT)
{
route_neighborsol(packet);
return;
}
n = route_ipv6(packet);
break;
case 0x0806:
route_arp(packet);
return;
default:
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
}
return;
}
if(n)
send_packet(n, packet);
break;
case RMODE_SWITCH:
n = route_mac(packet);
if(n)
send_packet(n, packet);
else
broadcast_packet(myself, packet);
break;
case RMODE_HUB:
broadcast_packet(myself, packet);
break;
}
}
void route_incoming(node_t *source, vpn_packet_t *packet)
{
switch(routing_mode)
{
case RMODE_ROUTER:
{
node_t *n = NULL;
unsigned short int type;
type = ntohs(*((unsigned short*)(&packet->data[12])));
switch(type)
{
case 0x0800:
n = route_ipv4(packet);
break;
case 0x86DD:
n = route_ipv6(packet);
break;
default:
n = myself;
break;
}
if(n)
{
if(n == myself)
{
memcpy(packet->data, mymac.net.mac.address.x, 6);
write_packet(packet);
}
else
send_packet(n, packet);
}
}
break;
case RMODE_SWITCH:
{
subnet_t *subnet;
subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
if(subnet)
{
if(subnet->owner == myself)
write_packet(packet);
else
send_packet(subnet->owner, packet);
}
else
{
broadcast_packet(source, packet);
write_packet(packet);
}
}
break;
case RMODE_HUB:
broadcast_packet(source, packet); /* Spread it on */
write_packet(packet);
break;
}
}

41
src/route.h Normal file
View file

@ -0,0 +1,41 @@
/*
route.h -- header file for route.c
Copyright (C) 2000-2002 Ivo Timmermans <zarq@iname.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: route.h,v 1.1.2.7 2002/03/01 14:09:31 guus Exp $
*/
#ifndef __TINC_ROUTE_H__
#define __TINC_ROUTE_H__
enum
{
RMODE_HUB = 0,
RMODE_SWITCH,
RMODE_ROUTER,
};
extern int routing_mode;
extern int priorityinheritance;
extern int macexpire;
extern void age_mac(void);
extern void route_incoming(node_t *, vpn_packet_t *);
extern void route_outgoing(vpn_packet_t *);
#endif /* __TINC_ROUTE_H__ */

196
src/solaris/device.c Normal file
View file

@ -0,0 +1,196 @@
/*
device.c -- Interaction with Solaris tun device
Copyright (C) 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
2001-2002 Guus Sliepen <guus@sliepen.warande.net>
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: device.c,v 1.1.2.7 2002/02/18 16:25:19 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stropts.h>
#include <sys/sockio.h>
#include <net/if_tun.h>
#define DEFAULT_DEVICE "/dev/tun"
#include <utils.h>
#include "conf.h"
#include "net.h"
#include "subnet.h"
#include "system.h"
int device_fd = -1;
int device_type;
char *device = NULL;
char *interface = NULL;
char ifrname[IFNAMSIZ];
char *device_info = NULL;
int device_total_in = 0;
int device_total_out = 0;
subnet_t mymac;
int setup_device(void)
{
int ip_fd = -1, if_fd = -1;
int ppa;
char *ptr;
cp
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = DEFAULT_DEVICE;
cp
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
syslog(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno));
return -1;
}
cp
ppa = 0;
ptr = device;
while(*ptr && !isdigit((int)*ptr)) ptr++;
ppa = atoi(ptr);
if( (ip_fd = open("/dev/ip", O_RDWR, 0)) < 0){
syslog(LOG_ERR, _("Could not open /dev/ip: %s"), strerror(errno));
return -1;
}
/* Assign a new PPA and get its unit number. */
if( (ppa = ioctl(device_fd, TUNNEWPPA, ppa)) < 0){
syslog(LOG_ERR, _("Can't assign new interface: %s"), strerror(errno));
return -1;
}
if( (if_fd = open(device, O_RDWR, 0)) < 0){
syslog(LOG_ERR, _("Could not open %s twice: %s"), device, strerror(errno));
return -1;
}
if(ioctl(if_fd, I_PUSH, "ip") < 0){
syslog(LOG_ERR, _("Can't push IP module: %s"), strerror(errno));
return -1;
}
/* Assign ppa according to the unit number returned by tun device */
if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0){
syslog(LOG_ERR, _("Can't set PPA %d: %s"), ppa, strerror(errno));
return -1;
}
if(ioctl(ip_fd, I_LINK, if_fd) < 0){
syslog(LOG_ERR, _("Can't link TUN device to IP: %s"), strerror(errno));
return -1;
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &interface))
asprintf(&interface, "tun%d", ppa);
device_info = _("Solaris tun device");
/* Set default MAC address for ethertap devices */
mymac.type = SUBNET_MAC;
mymac.net.mac.address.x[0] = 0xfe;
mymac.net.mac.address.x[1] = 0xfd;
mymac.net.mac.address.x[2] = 0x00;
mymac.net.mac.address.x[3] = 0x00;
mymac.net.mac.address.x[4] = 0x00;
mymac.net.mac.address.x[5] = 0x00;
syslog(LOG_INFO, _("%s is a %s"), device, device_info);
cp
return 0;
}
void close_device(void)
{
cp
close(device_fd);
}
int read_packet(vpn_packet_t *packet)
{
int lenin;
cp
if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0)
{
syslog(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno));
return -1;
}
memcpy(packet->data, mymac.net.mac.address.x, 6);
memcpy(packet->data + 6, mymac.net.mac.address.x, 6);
packet->data[12] = 0x08;
packet->data[13] = 0x00;
packet->len = lenin + 14;
device_total_in += packet->len;
if(debug_lvl >= DEBUG_TRAFFIC)
{
syslog(LOG_DEBUG, _("Read packet of %d bytes from %s"), packet->len, device_info);
}
return 0;
cp
}
int write_packet(vpn_packet_t *packet)
{
cp
if(debug_lvl >= DEBUG_TRAFFIC)
syslog(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info);
if(write(device_fd, packet->data + 14, packet->len - 14) < 0)
{
syslog(LOG_ERR, _("Can't write to %s %s: %s"), device_info, packet->len, strerror(errno));
return -1;
}
device_total_out += packet->len;
cp
return 0;
}
void dump_device_stats(void)
{
cp
syslog(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);
syslog(LOG_DEBUG, _(" total bytes in: %10d"), device_total_in);
syslog(LOG_DEBUG, _(" total bytes out: %10d"), device_total_out);
cp
}

394
src/subnet.c Normal file
View file

@ -0,0 +1,394 @@
/*
subnet.c -- handle subnet lookups and lists
Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
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: subnet.c,v 1.1.2.34 2002/04/09 11:42:48 guus Exp $
*/
#include "config.h"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <utils.h>
#include <xalloc.h>
#include <avl_tree.h>
#include "conf.h"
#include "net.h"
#include "node.h"
#include "subnet.h"
#include "netutl.h"
#include "system.h"
/* lists type of subnet */
avl_tree_t *subnet_tree;
/* Subnet comparison */
int subnet_compare_mac(subnet_t *a, subnet_t *b)
{
cp
return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
}
int subnet_compare_ipv4(subnet_t *a, subnet_t *b)
{
int result;
cp
result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
if(result)
return result;
return a->net.ipv4.prefixlength - b->net.ipv4.prefixlength;
}
int subnet_compare_ipv6(subnet_t *a, subnet_t *b)
{
int result;
cp
result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
if(result)
return result;
return a->net.ipv6.prefixlength - b->net.ipv6.prefixlength;
}
int subnet_compare(subnet_t *a, subnet_t *b)
{
int result;
cp
result = a->type - b->type;
if(result)
return result;
switch(a->type)
{
case SUBNET_MAC:
return subnet_compare_mac(a, b);
case SUBNET_IPV4:
return subnet_compare_ipv4(a, b);
case SUBNET_IPV6:
return subnet_compare_ipv6(a, b);
default:
syslog(LOG_ERR, _("subnet_compare() was called with unknown subnet type %d, exitting!"), a->type);
cp_trace();
exit(0);
}
return 0;
}
/* Initialising trees */
void init_subnets(void)
{
cp
subnet_tree = avl_alloc_tree((avl_compare_t)subnet_compare, (avl_action_t)free_subnet);
cp
}
void exit_subnets(void)
{
cp
avl_delete_tree(subnet_tree);
cp
}
avl_tree_t *new_subnet_tree(void)
{
cp
return avl_alloc_tree((avl_compare_t)subnet_compare, NULL);
cp
}
void free_subnet_tree(avl_tree_t *subnet_tree)
{
cp
avl_delete_tree(subnet_tree);
cp
}
/* Allocating and freeing space for subnets */
subnet_t *new_subnet(void)
{
cp
return (subnet_t *)xmalloc(sizeof(subnet_t));
}
void free_subnet(subnet_t *subnet)
{
cp
free(subnet);
}
/* Adding and removing subnets */
void subnet_add(node_t *n, subnet_t *subnet)
{
cp
subnet->owner = n;
avl_insert(subnet_tree, subnet);
cp
avl_insert(n->subnet_tree, subnet);
cp
}
void subnet_del(node_t *n, subnet_t *subnet)
{
cp
avl_delete(n->subnet_tree, subnet);
cp
avl_delete(subnet_tree, subnet);
cp
}
/* Ascii representation of subnets */
subnet_t *str2net(char *subnetstr)
{
int i, l;
subnet_t *subnet;
unsigned short int x[8];
cp
subnet = new_subnet();
cp
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
&x[0], &x[1], &x[2], &x[3],
&l) == 5)
{
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = l;
for(i = 0; i < 4; i++)
subnet->net.ipv4.address.x[i] = x[i];
return 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_IPV6;
subnet->net.ipv6.prefixlength = l;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return subnet;
}
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu",
&x[0], &x[1], &x[2], &x[3]) == 4)
{
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = 32;
for(i = 0; i < 4; i++)
subnet->net.ipv4.address.x[i] = x[i];
return 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_IPV6;
subnet->net.ipv6.prefixlength = 128;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return 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_MAC;
for(i = 0; i < 6; i++)
subnet->net.mac.address.x[i] = x[i];
return subnet;
}
free(subnet);
return NULL;
}
char *net2str(subnet_t *subnet)
{
char *netstr;
cp
switch(subnet->type)
{
case SUBNET_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_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_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:
syslog(LOG_ERR, _("net2str() was called with unknown subnet type %d, exitting!"), subnet->type);
cp_trace();
exit(0);
}
cp
return netstr;
}
/* Subnet lookup routines */
subnet_t *lookup_subnet(node_t *owner, subnet_t *subnet)
{
cp
return avl_search(owner->subnet_tree, subnet);
}
subnet_t *lookup_subnet_mac(mac_t *address)
{
subnet_t subnet, *p;
cp
subnet.type = SUBNET_MAC;
memcpy(&subnet.net.mac.address, address, sizeof(mac_t));
p = (subnet_t *)avl_search(subnet_tree, &subnet);
cp
return p;
}
subnet_t *lookup_subnet_ipv4(ipv4_t *address)
{
subnet_t subnet, *p;
cp
subnet.type = SUBNET_IPV4;
memcpy(&subnet.net.ipv4.address, address, sizeof(ipv4_t));
subnet.net.ipv4.prefixlength = 32;
do
{
/* Go find subnet */
p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
/* Check if the found subnet REALLY matches */
cp
if(p)
{
if(p->type != SUBNET_IPV4)
{
p = NULL;
break;
}
if (!maskcmp((char *)address, (char *)&p->net.ipv4.address, p->net.ipv4.prefixlength, sizeof(ipv4_t)))
break;
else
{
/* Otherwise, see if there is a bigger enclosing subnet */
subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1;
maskcpy((char *)&subnet.net.ipv4.address, (char *)&p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t));
}
}
} while (p);
cp
return p;
}
subnet_t *lookup_subnet_ipv6(ipv6_t *address)
{
subnet_t subnet, *p;
cp
subnet.type = SUBNET_IPV6;
memcpy(&subnet.net.ipv6.address, address, sizeof(ipv6_t));
subnet.net.ipv6.prefixlength = 128;
do
{
/* Go find subnet */
p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
/* Check if the found subnet REALLY matches */
cp
if(p)
{
if(p->type != SUBNET_IPV6)
return NULL;
if (!maskcmp((char *)address, (char *)&p->net.ipv6.address, p->net.ipv6.prefixlength, sizeof(ipv6_t)))
break;
else
{
/* Otherwise, see if there is a bigger enclosing subnet */
subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1;
maskcpy((char *)&subnet.net.ipv6.address, (char *)&p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t));
}
}
} while (p);
cp
return p;
}
void dump_subnets(void)
{
char *netstr;
subnet_t *subnet;
avl_node_t *node;
cp
syslog(LOG_DEBUG, _("Subnet list:"));
for(node = subnet_tree->head; node; node = node->next)
{
subnet = (subnet_t *)node->data;
netstr = net2str(subnet);
syslog(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
free(netstr);
}
syslog(LOG_DEBUG, _("End of subnet list."));
cp
}

88
src/subnet.h Normal file
View file

@ -0,0 +1,88 @@
/*
subnet.h -- header for subnet.c
Copyright (C) 2000,2001 Guus Sliepen <guus@sliepen.warande.net>,
2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>
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: subnet.h,v 1.1.2.18 2002/04/09 11:42:48 guus Exp $
*/
#ifndef __TINC_SUBNET_H__
#define __TINC_SUBNET_H__
#include "net.h"
enum
{
SUBNET_MAC = 0,
SUBNET_IPV4,
SUBNET_IPV6,
SUBNET_TYPES /* Guardian */
};
typedef struct subnet_mac_t
{
mac_t address;
time_t lastseen;
} subnet_mac_t;
typedef struct subnet_ipv4_t
{
ipv4_t address;
int prefixlength;
} subnet_ipv4_t;
typedef struct subnet_ipv6_t
{
ipv6_t address;
int prefixlength;
} subnet_ipv6_t;
#include "node.h"
typedef struct subnet_t {
struct node_t *owner; /* the owner of this subnet */
struct node_t *uplink; /* the uplink which we should send packets to for this subnet */
int type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
/* And now for the actual subnet: */
union net
{
subnet_mac_t mac;
subnet_ipv4_t ipv4;
subnet_ipv6_t ipv6;
} net;
} subnet_t;
extern subnet_t *new_subnet(void);
extern void free_subnet(subnet_t *);
extern void init_subnets(void);
extern void exit_subnets(void);
extern avl_tree_t *new_subnet_tree(void);
extern void free_subnet_tree(avl_tree_t *);
extern void subnet_add(struct node_t *, subnet_t *);
extern void subnet_del(struct node_t *, subnet_t *);
extern char *net2str(subnet_t *);
extern subnet_t *str2net(char *);
extern subnet_t *lookup_subnet(struct node_t *, subnet_t *);
extern subnet_t *lookup_subnet_mac(mac_t *);
extern subnet_t *lookup_subnet_ipv4(ipv4_t *);
extern subnet_t *lookup_subnet_ipv6(ipv6_t *);
extern void dump_subnets(void);
#endif /* __TINC_SUBNET_H__ */

400
src/tincd.c Normal file
View file

@ -0,0 +1,400 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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: tincd.c,v 1.10.4.58 2002/03/11 11:23:04 guus Exp $
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "net.h"
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "subnet.h"
#include "system.h"
/* The name this program was run with. */
char *program_name;
/* If nonzero, display usage information and exit. */
int show_help;
/* If nonzero, print the version on standard output and exit. */
int show_version;
/* If nonzero, it will attempt to kill a running tincd and exit. */
int kill_tincd = 0;
/* If nonzero, generate public/private keypair for this host/net. */
int generate_keys = 0;
/* If nonzero, use null ciphers and skip all key exchanges. */
int bypass_security = 0;
char *identname; /* program name for syslog */
char *pidfilename; /* pid file location */
char **g_argv; /* a copy of the cmdline arguments */
char **environment; /* A pointer to the environment on
startup */
static struct option const long_options[] =
{
{ "config", required_argument, NULL, 'c' },
{ "kill", optional_argument, NULL, 'k' },
{ "net", required_argument, NULL, 'n' },
{ "help", no_argument, &show_help, 1 },
{ "version", no_argument, &show_version, 1 },
{ "no-detach", no_argument, &do_detach, 0 },
{ "generate-keys", optional_argument, NULL, 'K'},
{ "debug", optional_argument, NULL, 'd'},
{ "bypass-security", no_argument, &bypass_security, 1 },
{ NULL, 0, NULL, 0 }
};
static void
usage(int status)
{
if(status != 0)
fprintf(stderr, _("Try `%s --help\' for more information.\n"), program_name);
else
{
printf(_("Usage: %s [option]...\n\n"), program_name);
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"));
printf(_(" -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
" --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@nl.linux.org.\n"));
}
exit(status);
}
void
parse_options(int argc, char **argv, char **envp)
{
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "c:Dd::k::n:K::", long_options, &option_index)) != EOF)
{
switch(r)
{
case 0: /* long option */
break;
case 'c': /* config file */
confbase = xmalloc(strlen(optarg)+1);
strcpy(confbase, optarg);
break;
case 'D': /* no detach */
do_detach = 0;
break;
case 'd': /* inc debug level */
if(optarg)
debug_lvl = atoi(optarg);
else
debug_lvl++;
break;
case 'k': /* kill old tincds */
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(1);
}
}
}
else
kill_tincd = SIGTERM;
break;
case 'n': /* net name given */
netname = xmalloc(strlen(optarg)+1);
strcpy(netname, optarg);
break;
case 'K': /* generate public/private keypair */
if(optarg)
{
generate_keys = atoi(optarg);
if(generate_keys < 512)
{
fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
optarg);
usage(1);
}
generate_keys &= ~7; /* Round it to bytes */
}
else
generate_keys = 1024;
break;
case '?':
usage(1);
default:
break;
}
}
}
/* This function prettyprints the key generation process */
void indicator(int a, int b, void *p)
{
switch(a)
{
case 0:
fprintf(stderr, ".");
break;
case 1:
fprintf(stderr, "+");
break;
case 2:
fprintf(stderr, "-");
break;
case 3:
switch(b)
{
case 0:
fprintf(stderr, " p\n");
break;
case 1:
fprintf(stderr, " q\n");
break;
default:
fprintf(stderr, "?");
}
break;
default:
fprintf(stderr, "?");
}
}
/*
Generate a public/private RSA keypair, and ask for a file to store
them in.
*/
int keygen(int bits)
{
RSA *rsa_key;
FILE *f;
char *name = NULL;
char *filename;
fprintf(stderr, _("Generating %d bits keys:\n"), bits);
rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
if(!rsa_key)
{
fprintf(stderr, _("Error during key generation!\n"));
return -1;
}
else
fprintf(stderr, _("Done.\n"));
get_config_string(lookup_config(config_tree, "Name"), &name);
if(name)
asprintf(&filename, "%s/hosts/%s", confbase, name);
else
asprintf(&filename, "%s/rsa_key.pub", confbase);
if((f = ask_and_safe_open(filename, _("public RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPublicKey(f, rsa_key);
fclose(f);
free(filename);
asprintf(&filename, "%s/rsa_key.priv", confbase);
if((f = ask_and_safe_open(filename, _("private RSA key"), "a")) == NULL)
return -1;
if(ftell(f))
fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
fclose(f);
free(filename);
return 0;
}
/*
Set all files and paths according to netname
*/
void make_names(void)
{
if(netname)
{
if(!pidfilename)
asprintf(&pidfilename, LOCALSTATEDIR "/run/tinc.%s.pid", netname);
if(!confbase)
asprintf(&confbase, "%s/tinc/%s", CONFDIR, netname);
else
syslog(LOG_INFO, _("Both netname and configuration directory given, using the latter..."));
if(!identname)
asprintf(&identname, "tinc.%s", netname);
}
else
{
if(!pidfilename)
pidfilename = LOCALSTATEDIR "/run/tinc.pid";
if(!confbase)
asprintf(&confbase, "%s/tinc", CONFDIR);
if(!identname)
identname = "tinc";
}
}
int
main(int argc, char **argv, char **envp)
{
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
environment = envp;
parse_options(argc, argv, envp);
if(show_version)
{
printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT);
printf(_("Copyright (C) 1998-2002 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(0);
#ifdef HAVE_SOLARIS
openlog("tinc", LOG_CONS, LOG_DAEMON); /* Catch all syslog() calls issued before detaching */
#else
openlog("tinc", LOG_PERROR, LOG_DAEMON); /* Catch all syslog() calls issued before detaching */
#endif
g_argv = argv;
make_names();
init_configuration(&config_tree);
/* Slllluuuuuuurrrrp! */
cp
RAND_load_file("/dev/urandom", 1024);
#ifdef HAVE_SSLEAY_ADD_ALL_ALGORITHMS
SSLeay_add_all_algorithms();
#else
OpenSSL_add_all_algorithms();
#endif
cp
if(generate_keys)
{
read_server_config();
exit(keygen(generate_keys));
}
if(kill_tincd)
exit(kill_other(kill_tincd));
if(read_server_config())
exit(1);
cp
if(detach())
exit(0);
cp
for(;;)
{
if(!setup_network_connections())
{
main_loop();
cleanup_and_exit(1);
}
syslog(LOG_ERR, _("Unrecoverable error"));
cp_trace();
if(do_detach)
{
syslog(LOG_NOTICE, _("Restarting in %d seconds!"), maxtimeout);
sleep(maxtimeout);
}
else
{
syslog(LOG_ERR, _("Not restarting."));
exit(1);
}
}
}