Import Upstream version 1.1~pre3

This commit is contained in:
Guus Sliepen 2019-08-26 13:44:49 +02:00
parent 02de1cd2f1
commit 34d5939212
136 changed files with 13943 additions and 4867 deletions

View file

@ -1,30 +1,43 @@
## Produce this file with automake to get Makefile.in
sbin_PROGRAMS = tincd tincctl
sbin_PROGRAMS = tincd tincctl sptps_test
EXTRA_DIST = linux bsd solaris cygwin mingw raw_socket uml_socket openssl gcrypt
EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt
tincd_SOURCES = \
utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c hash.c \
buffer.c conf.c connection.c control.c edge.c graph.c logger.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
protocol_key.c protocol_subnet.c route.c sptps.c subnet.c subnet_parse.c tincd.c \
dummy_device.c raw_socket_device.c multicast_device.c
if UML
tincd_SOURCES += uml_device.c
endif
if VDE
tincd_SOURCES += vde_device.c
endif
nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
tincctl_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \
list.c tincctl.c top.c
info.c list.c subnet_parse.c tincctl.c top.c
nodist_tincctl_SOURCES = \
ecdsagen.c rsagen.c
sptps_test_SOURCES = \
logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \
sptps.c sptps_test.c utils.c
if TUNEMU
tincd_SOURCES += bsd/tunemu.c
endif
tincctl_LDADD = $(CURSES_LIBS)
tincctl_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
DEFAULT_INCLUDES =
@ -32,8 +45,8 @@ INCLUDES = @INCLUDES@ -I$(top_builddir)
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h tincctl.h top.h bsd/tunemu.h
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h info.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h

View file

@ -1,9 +1,9 @@
# Makefile.in generated by automake 1.11.1 from Makefile.am.
# Makefile.in generated by automake 1.11.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
# Inc.
# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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.
@ -17,6 +17,23 @@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@ -35,9 +52,11 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
sbin_PROGRAMS = tincd$(EXEEXT) tincctl$(EXEEXT)
@TUNEMU_TRUE@am__append_1 = bsd/tunemu.c
@TUNEMU_TRUE@am__append_2 = -lpcap
sbin_PROGRAMS = tincd$(EXEEXT) tincctl$(EXEEXT) sptps_test$(EXEEXT)
@UML_TRUE@am__append_1 = uml_device.c
@VDE_TRUE@am__append_2 = vde_device.c
@TUNEMU_TRUE@am__append_3 = bsd/tunemu.c
@TUNEMU_TRUE@am__append_4 = -lpcap
subdir = src
DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in
@ -45,7 +64,8 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/libevent.m4 \
$(top_srcdir)/m4/lzo.m4 $(top_srcdir)/m4/openssl.m4 \
$(top_srcdir)/m4/zlib.m4 $(top_srcdir)/configure.in
$(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/zlib.m4 \
$(top_srcdir)/configure.in
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
@ -54,34 +74,47 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(sbindir)"
PROGRAMS = $(sbin_PROGRAMS)
am_sptps_test_OBJECTS = logger.$(OBJEXT) cipher.$(OBJEXT) \
crypto.$(OBJEXT) ecdh.$(OBJEXT) ecdsa.$(OBJEXT) \
digest.$(OBJEXT) prf.$(OBJEXT) sptps.$(OBJEXT) \
sptps_test.$(OBJEXT) utils.$(OBJEXT)
sptps_test_OBJECTS = $(am_sptps_test_OBJECTS)
sptps_test_LDADD = $(LDADD)
am_tincctl_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) \
getopt1.$(OBJEXT) dropin.$(OBJEXT) list.$(OBJEXT) \
tincctl.$(OBJEXT) top.$(OBJEXT)
getopt1.$(OBJEXT) dropin.$(OBJEXT) info.$(OBJEXT) \
list.$(OBJEXT) subnet_parse.$(OBJEXT) tincctl.$(OBJEXT) \
top.$(OBJEXT)
nodist_tincctl_OBJECTS = ecdsagen.$(OBJEXT) rsagen.$(OBJEXT)
tincctl_OBJECTS = $(am_tincctl_OBJECTS) $(nodist_tincctl_OBJECTS)
am__DEPENDENCIES_1 =
tincctl_DEPENDENCIES = $(am__DEPENDENCIES_1)
tincctl_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
am__tincd_SOURCES_DIST = utils.c getopt.c getopt1.c list.c \
splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
buffer.c conf.c connection.c control.c edge.c graph.c logger.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 bsd/tunemu.c
@TUNEMU_TRUE@am__objects_1 = tunemu.$(OBJEXT)
hash.c buffer.c conf.c connection.c control.c edge.c graph.c \
logger.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 sptps.c subnet.c subnet_parse.c \
tincd.c dummy_device.c raw_socket_device.c multicast_device.c \
uml_device.c vde_device.c bsd/tunemu.c
@UML_TRUE@am__objects_1 = uml_device.$(OBJEXT)
@VDE_TRUE@am__objects_2 = vde_device.$(OBJEXT)
@TUNEMU_TRUE@am__objects_3 = tunemu.$(OBJEXT)
am_tincd_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
list.$(OBJEXT) splay_tree.$(OBJEXT) dropin.$(OBJEXT) \
fake-getaddrinfo.$(OBJEXT) fake-getnameinfo.$(OBJEXT) \
buffer.$(OBJEXT) conf.$(OBJEXT) connection.$(OBJEXT) \
control.$(OBJEXT) edge.$(OBJEXT) graph.$(OBJEXT) \
logger.$(OBJEXT) meta.$(OBJEXT) net.$(OBJEXT) \
hash.$(OBJEXT) buffer.$(OBJEXT) conf.$(OBJEXT) \
connection.$(OBJEXT) control.$(OBJEXT) edge.$(OBJEXT) \
graph.$(OBJEXT) logger.$(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) \
$(am__objects_1)
route.$(OBJEXT) sptps.$(OBJEXT) subnet.$(OBJEXT) \
subnet_parse.$(OBJEXT) tincd.$(OBJEXT) dummy_device.$(OBJEXT) \
raw_socket_device.$(OBJEXT) multicast_device.$(OBJEXT) \
$(am__objects_1) $(am__objects_2) $(am__objects_3)
nodist_tincd_OBJECTS = device.$(OBJEXT) cipher.$(OBJEXT) \
crypto.$(OBJEXT) ecdh.$(OBJEXT) ecdsa.$(OBJEXT) \
digest.$(OBJEXT) prf.$(OBJEXT) rsa.$(OBJEXT)
@ -94,9 +127,16 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(tincctl_SOURCES) $(nodist_tincctl_SOURCES) \
$(tincd_SOURCES) $(nodist_tincd_SOURCES)
DIST_SOURCES = $(tincctl_SOURCES) $(am__tincd_SOURCES_DIST)
SOURCES = $(sptps_test_SOURCES) $(tincctl_SOURCES) \
$(nodist_tincctl_SOURCES) $(tincd_SOURCES) \
$(nodist_tincd_SOURCES)
DIST_SOURCES = $(sptps_test_SOURCES) $(tincctl_SOURCES) \
$(am__tincd_SOURCES_DIST)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(nodist_noinst_HEADERS) $(noinst_HEADERS)
ETAGS = etags
CTAGS = ctags
@ -133,7 +173,7 @@ LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@ @LIBGCRYPT_LIBS@ $(am__append_2)
LIBS = @LIBS@ @LIBGCRYPT_LIBS@ $(am__append_4)
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
@ -149,6 +189,7 @@ PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
READLINE_LIBS = @READLINE_LIBS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
@ -203,30 +244,36 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
EXTRA_DIST = linux bsd solaris cygwin mingw raw_socket uml_socket openssl gcrypt
EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt
tincd_SOURCES = utils.c getopt.c getopt1.c list.c splay_tree.c \
dropin.c fake-getaddrinfo.c fake-getnameinfo.c buffer.c conf.c \
connection.c control.c edge.c graph.c logger.c meta.c net.c \
net_packet.c net_setup.c net_socket.c netutl.c node.c \
dropin.c fake-getaddrinfo.c fake-getnameinfo.c hash.c buffer.c \
conf.c connection.c control.c edge.c graph.c logger.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 $(am__append_1)
sptps.c subnet.c subnet_parse.c tincd.c dummy_device.c \
raw_socket_device.c multicast_device.c $(am__append_1) \
$(am__append_2) $(am__append_3)
nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
tincctl_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \
list.c tincctl.c top.c
info.c list.c subnet_parse.c tincctl.c top.c
nodist_tincctl_SOURCES = \
ecdsagen.c rsagen.c
tincctl_LDADD = $(CURSES_LIBS)
sptps_test_SOURCES = \
logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \
sptps.c sptps_test.c utils.c
tincctl_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
DEFAULT_INCLUDES =
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h tincctl.h top.h bsd/tunemu.h
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h info.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h
@ -268,8 +315,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
install-sbinPROGRAMS: $(sbin_PROGRAMS)
@$(NORMAL_INSTALL)
test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p; \
@ -303,10 +353,13 @@ uninstall-sbinPROGRAMS:
clean-sbinPROGRAMS:
-test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
tincctl$(EXEEXT): $(tincctl_OBJECTS) $(tincctl_DEPENDENCIES)
sptps_test$(EXEEXT): $(sptps_test_OBJECTS) $(sptps_test_DEPENDENCIES) $(EXTRA_sptps_test_DEPENDENCIES)
@rm -f sptps_test$(EXEEXT)
$(LINK) $(sptps_test_OBJECTS) $(sptps_test_LDADD) $(LIBS)
tincctl$(EXEEXT): $(tincctl_OBJECTS) $(tincctl_DEPENDENCIES) $(EXTRA_tincctl_DEPENDENCIES)
@rm -f tincctl$(EXEEXT)
$(LINK) $(tincctl_OBJECTS) $(tincctl_LDADD) $(LIBS)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES) $(EXTRA_tincd_DEPENDENCIES)
@rm -f tincd$(EXEEXT)
$(LINK) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS)
@ -325,6 +378,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dropin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdh.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsagen.Po@am__quote@
@ -334,9 +388,12 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/graph.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meta.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multicast_device.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@
@ -351,16 +408,22 @@ distclean-compile:
@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)/raw_socket_device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splay_tree.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sptps.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sptps_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnet_parse.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tincctl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tincd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/top.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tunemu.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uml_device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vde_device.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@ -492,10 +555,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction BSD tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2011 Guus Sliepen <guus@tinc-vpn.org>
2001-2012 Guus Sliepen <guus@tinc-vpn.org>
2009 Grzegorz Dymarek <gregd72002@googlemail.com>
This program is free software; you can redistribute it and/or modify
@ -33,7 +33,12 @@
#include "bsd/tunemu.h"
#endif
#define DEFAULT_DEVICE "/dev/tun0"
#define DEFAULT_TUN_DEVICE "/dev/tun0"
#if defined(HAVE_FREEBSD) || defined(HAVE_NETBSD)
#define DEFAULT_TAP_DEVICE "/dev/tap0"
#else
#define DEFAULT_TAP_DEVICE "/dev/tun0"
#endif
typedef enum device_type {
DEVICE_TYPE_TUN,
@ -58,18 +63,22 @@ static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD;
static device_type_t device_type = DEVICE_TYPE_TUN;
#endif
bool setup_device(void) {
static bool setup_device(void) {
char *type;
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
if(routing_mode == RMODE_ROUTER)
device = xstrdup(DEFAULT_TUN_DEVICE);
else
device = xstrdup(DEFAULT_TAP_DEVICE);
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
if(!strcasecmp(type, "tun"))
/* use default */;
/* use default */;
#ifdef HAVE_TUNEMU
else if(!strcasecmp(type, "tunemu"))
device_type = DEVICE_TYPE_TUNEMU;
@ -81,7 +90,7 @@ bool setup_device(void) {
else if(!strcasecmp(type, "tap"))
device_type = DEVICE_TYPE_TAP;
else {
logger(LOG_ERR, "Unknown device type %s!", type);
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown device type %s!", type);
return false;
}
} else {
@ -93,7 +102,7 @@ bool setup_device(void) {
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU: {
char dynamic_name[256] = "";
device_fd = tunemu_open(dynamic_name);
device_fd = tunemu_open(dynamic_name);
}
break;
#endif
@ -102,19 +111,23 @@ bool setup_device(void) {
}
if(device_fd < 0) {
logger(LOG_ERR, "Could not open %s: %s", device, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
switch(device_type) {
default:
device_type = DEVICE_TYPE_TUN;
case DEVICE_TYPE_TUN:
#ifdef TUNSIFHEAD
{
{
const int zero = 0;
if(ioctl(device_fd, TUNSIFHEAD, &zero, sizeof zero) == -1) {
logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
return false;
}
}
@ -133,7 +146,7 @@ bool setup_device(void) {
{
const int one = 1;
if(ioctl(device_fd, TUNSIFHEAD, &one, sizeof one) == -1) {
logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
return false;
}
}
@ -160,22 +173,22 @@ bool setup_device(void) {
iface = xstrdup(ifr.ifr_name);
}
}
#endif
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
device_info = "BSD tunemu device";
device_info = "BSD tunemu device";
break;
#endif
}
logger(LOG_INFO, "%s is a %s", device, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
switch(device_type) {
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
@ -190,7 +203,7 @@ void close_device(void) {
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
int inlen;
switch(device_type) {
@ -204,7 +217,7 @@ bool read_packet(vpn_packet_t *packet) {
inlen = read(device_fd, packet->data + 14, MTU - 14);
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -219,12 +232,13 @@ bool read_packet(vpn_packet_t *packet) {
packet->data[13] = 0xDD;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
memset(packet->data, 0, 12);
packet->len = inlen + 14;
break;
@ -233,7 +247,7 @@ bool read_packet(vpn_packet_t *packet) {
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}};
if((inlen = readv(device_fd, vector, 2)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -250,19 +264,20 @@ bool read_packet(vpn_packet_t *packet) {
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown address family %x while reading packet from %s %s",
ntohl(type), device_info, device);
return false;
}
memset(packet->data, 0, 12);
packet->len = inlen + 10;
break;
}
case DEVICE_TYPE_TAP:
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -273,23 +288,23 @@ bool read_packet(vpn_packet_t *packet) {
default:
return false;
}
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s",
packet->len, device_info);
return true;
}
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
static bool write_packet(vpn_packet_t *packet) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
switch(device_type) {
case DEVICE_TYPE_TUN:
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -299,7 +314,7 @@ bool write_packet(vpn_packet_t *packet) {
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}};
int af;
af = (packet->data[12] << 8) + packet->data[13];
switch (af) {
@ -310,23 +325,23 @@ bool write_packet(vpn_packet_t *packet) {
type = htonl(AF_INET6);
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown address family %x while writing packet to %s %s",
af, device_info, device);
return false;
}
if(writev(device_fd, vector, 2) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
break;
}
case DEVICE_TYPE_TAP:
if(write(device_fd, packet->data, packet->len) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -335,7 +350,7 @@ bool write_packet(vpn_packet_t *packet) {
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -351,8 +366,16 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,20 +1,20 @@
/*
* tunemu - Tun device emulation for Darwin
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.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 3 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "tunemu.h"
@ -36,18 +36,18 @@
#define PPPPROTO_CTL 1
#define PPP_IP 0x21
#define PPP_IPV6 0x57
#define PPP_IP 0x21
#define PPP_IPV6 0x57
#define SC_LOOP_TRAFFIC 0x00000200
#define PPPIOCNEWUNIT _IOWR('t', 62, int)
#define PPPIOCSFLAGS _IOW('t', 89, int)
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
#define PPPIOCATTCHAN _IOW('t', 56, int)
#define PPPIOCGCHAN _IOR('t', 55, int)
#define PPPIOCCONNECT _IOW('t', 58, int)
#define PPPIOCGUNIT _IOR('t', 86, int)
#define PPPIOCNEWUNIT _IOWR('t', 62, int)
#define PPPIOCSFLAGS _IOW('t', 89, int)
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
#define PPPIOCATTCHAN _IOW('t', 56, int)
#define PPPIOCGCHAN _IOR('t', 55, int)
#define PPPIOCCONNECT _IOW('t', 58, int)
#define PPPIOCGUNIT _IOR('t', 86, int)
struct sockaddr_ppp
{

View file

@ -1,20 +1,20 @@
/*
* tunemu - Tun device emulation for Darwin
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.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 3 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef TUNEMU_H

View file

@ -57,7 +57,7 @@ char *buffer_prepare(buffer_t *buffer, int size) {
}
// Copy data into the buffer.
void buffer_add(buffer_t *buffer, const char *data, int size) {
memcpy(buffer_prepare(buffer, size), data, size);
}

218
src/cipher.c Normal file
View file

@ -0,0 +1,218 @@
/*
cipher.c -- Symmetric block cipher handling
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/rand.h>
#include <openssl/err.h>
#include "cipher.h"
#include "logger.h"
#include "xalloc.h"
typedef struct cipher_counter {
unsigned char counter[EVP_MAX_IV_LENGTH];
unsigned char block[EVP_MAX_IV_LENGTH];
int n;
} cipher_counter_t;
static bool cipher_open(cipher_t *cipher) {
EVP_CIPHER_CTX_init(&cipher->ctx);
return true;
}
bool cipher_open_by_name(cipher_t *cipher, const char *name) {
cipher->cipher = EVP_get_cipherbyname(name);
if(cipher->cipher)
return cipher_open(cipher);
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown cipher name '%s'!", name);
return false;
}
bool cipher_open_by_nid(cipher_t *cipher, int nid) {
cipher->cipher = EVP_get_cipherbynid(nid);
if(cipher->cipher)
return cipher_open(cipher);
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown cipher nid %d!", nid);
return false;
}
bool cipher_open_blowfish_ofb(cipher_t *cipher) {
cipher->cipher = EVP_bf_ofb();
return cipher_open(cipher);
}
void cipher_close(cipher_t *cipher) {
EVP_CIPHER_CTX_cleanup(&cipher->ctx);
free(cipher->counter);
cipher->counter = NULL;
}
size_t cipher_keylength(const cipher_t *cipher) {
return cipher->cipher->key_len + cipher->cipher->block_size;
}
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
bool result;
if(encrypt)
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
else
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
if(result)
return true;
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
bool result;
if(encrypt)
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
else
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
if(result)
return true;
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_set_counter(cipher_t *cipher, const void *counter, size_t len) {
if(len > cipher->cipher->block_size - 4) {
logger(DEBUG_ALWAYS, LOG_ERR, "Counter too long");
abort();
}
memcpy(cipher->counter->counter + cipher->cipher->block_size - len, counter, len);
memset(cipher->counter->counter, 0, 4);
cipher->counter->n = 0;
return true;
}
bool cipher_set_counter_key(cipher_t *cipher, void *key) {
int result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, NULL);
if(!result) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(!cipher->counter)
cipher->counter = xmalloc_and_zero(sizeof *cipher->counter);
else
cipher->counter->n = 0;
memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->block_size);
return true;
}
bool cipher_counter_xor(cipher_t *cipher, const void *indata, size_t inlen, void *outdata) {
if(!cipher->counter) {
logger(DEBUG_ALWAYS, LOG_ERR, "Counter not initialized");
return false;
}
const unsigned char *in = indata;
unsigned char *out = outdata;
while(inlen--) {
// Encrypt the new counter value if we need it
if(!cipher->counter->n) {
int len;
if(!EVP_EncryptUpdate(&cipher->ctx, cipher->counter->block, &len, cipher->counter->counter, cipher->cipher->block_size)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
// Increase the counter value
for(int i = 0; i < cipher->cipher->block_size; i++)
if(++cipher->counter->counter[i])
break;
}
*out++ = *in++ ^ cipher->counter->counter[cipher->counter->n++];
if(cipher->counter->n >= cipher->cipher->block_size)
cipher->counter->n = 0;
}
return true;
}
bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
if(oneshot) {
int len, pad;
if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
if(outlen) *outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
if(outlen) *outlen = len;
return true;
}
}
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
if(oneshot) {
int len, pad;
if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
if(outlen) *outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
if(outlen) *outlen = len;
return true;
}
}
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int cipher_get_nid(const cipher_t *cipher) {
return cipher->cipher ? cipher->cipher->nid : 0;
}
bool cipher_active(const cipher_t *cipher) {
return cipher->cipher && cipher->cipher->nid != 0;
}

View file

@ -2,9 +2,9 @@
conf.c -- configuration code
Copyright (C) 1998 Robert van der Meulen
1998-2005 Ivo Timmermans
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2010-2011 Julien Muchembled <jm@jmuchemb.eu>
2000 Cris van Pelt
2000 Cris van Pelt
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -28,18 +28,18 @@
#include "conf.h"
#include "list.h"
#include "logger.h"
#include "netutl.h" /* for str2address */
#include "netutl.h" /* for str2address */
#include "protocol.h"
#include "utils.h" /* for cp */
#include "utils.h" /* for cp */
#include "xalloc.h"
splay_tree_t *config_tree;
int pinginterval = 0; /* seconds between pings */
int pingtimeout = 0; /* seconds to wait for response */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
list_t *cmdline_conf = NULL; /* global/host configuration values given at the command line */
int pinginterval = 0; /* seconds between pings */
int pingtimeout = 0; /* seconds to wait for response */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
list_t *cmdline_conf = NULL; /* global/host configuration values given at the command line */
static int config_compare(const config_t *a, const config_t *b) {
@ -141,7 +141,7 @@ bool get_config_bool(const config_t *cfg, bool *result) {
return true;
}
logger(LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
logger(DEBUG_ALWAYS, LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
return false;
@ -154,7 +154,7 @@ bool get_config_int(const config_t *cfg, int *result) {
if(sscanf(cfg->value, "%d", result) == 1)
return true;
logger(LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
logger(DEBUG_ALWAYS, LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
return false;
@ -182,7 +182,7 @@ bool get_config_address(const config_t *cfg, struct addrinfo **result) {
return true;
}
logger(LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
logger(DEBUG_ALWAYS, LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
return false;
@ -195,7 +195,7 @@ bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
return false;
if(!str2net(&subnet, cfg->value)) {
logger(LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
logger(DEBUG_ALWAYS, LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
return false;
}
@ -206,7 +206,7 @@ bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
&& !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
|| ((subnet.type == SUBNET_IPV6)
&& !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
logger(DEBUG_ALWAYS, LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
return false;
}
@ -236,8 +236,9 @@ static char *readline(FILE * fp, char *buf, size_t buflen) {
if(!newline)
return buf;
*newline = '\0'; /* kill newline */
if(newline > p && newline[-1] == '\r') /* and carriage return if necessary */
/* kill newline and carriage return if necessary */
*newline = '\0';
if(newline > p && newline[-1] == '\r')
newline[-1] = '\0';
return buf;
@ -265,10 +266,10 @@ config_t *parse_config_line(char *line, const char *fname, int lineno) {
if(!*value) {
const char err[] = "No value for variable";
if (fname)
logger(LOG_ERR, "%s `%s' on line %d while reading config file %s",
logger(DEBUG_ALWAYS, LOG_ERR, "%s `%s' on line %d while reading config file %s",
err, variable, lineno, fname);
else
logger(LOG_ERR, "%s `%s' in command line option %d",
logger(DEBUG_ALWAYS, LOG_ERR, "%s `%s' in command line option %d",
err, variable, lineno);
return NULL;
}
@ -298,7 +299,7 @@ bool read_config_file(splay_tree_t *config_tree, const char *fname) {
fp = fopen(fname, "r");
if(!fp) {
logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
return false;
}
@ -321,7 +322,7 @@ bool read_config_file(splay_tree_t *config_tree, const char *fname) {
ignore = false;
continue;
}
if(!strncmp(line, "-----BEGIN", 10)) {
ignore = true;
continue;
@ -339,33 +340,31 @@ bool read_config_file(splay_tree_t *config_tree, const char *fname) {
}
void read_config_options(splay_tree_t *config_tree, const char *prefix) {
list_node_t *node, *next;
size_t prefix_len = prefix ? strlen(prefix) : 0;
for(node = cmdline_conf->tail; node; node = next) {
config_t *orig_cfg, *cfg = (config_t *)node->data;
next = node->prev;
for(const list_node_t *node = cmdline_conf->tail; node; node = node->prev) {
const config_t *cfg = node->data;
config_t *new;
if(!prefix) {
if(strchr(cfg->variable, '.'))
continue;
node->data = NULL;
list_unlink_node(cmdline_conf, node);
} else {
if(strncmp(prefix, cfg->variable, prefix_len) ||
cfg->variable[prefix_len] != '.')
continue;
/* Because host configuration is parsed again when
reconnecting, nodes must not be freed when a prefix
is given. */
orig_cfg = cfg;
cfg = new_config();
cfg->variable = xstrdup(orig_cfg->variable + prefix_len + 1);
cfg->value = xstrdup(orig_cfg->value);
cfg->file = NULL;
cfg->line = orig_cfg->line;
}
config_add(config_tree, cfg);
new = new_config();
if(prefix)
new->variable = xstrdup(cfg->variable + prefix_len + 1);
else
new->variable = xstrdup(cfg->variable);
new->value = xstrdup(cfg->value);
new->file = NULL;
new->line = cfg->line;
config_add(config_tree, new);
}
}
@ -375,26 +374,25 @@ bool read_server_config(void) {
read_config_options(config_tree, NULL);
xasprintf(&fname, "%s/tinc.conf", confbase);
xasprintf(&fname, "%s" SLASH "tinc.conf", confbase);
x = read_config_file(config_tree, fname);
if(!x) { /* System error: complain */
logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
}
if(!x)
logger(DEBUG_ALWAYS, LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
free(fname);
return x;
}
bool read_connection_config(connection_t *c) {
bool read_host_config(splay_tree_t *config_tree, const char *name) {
char *fname;
bool x;
read_config_options(c->config_tree, c->name);
read_config_options(config_tree, name);
xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
x = read_config_file(c->config_tree, fname);
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
x = read_config_file(config_tree, fname);
free(fname);
return x;
@ -402,12 +400,12 @@ bool read_connection_config(connection_t *c) {
bool append_config_file(const char *name, const char *key, const char *value) {
char *fname;
xasprintf(&fname, "%s/hosts/%s", confbase, name);
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
FILE *fp = fopen(fname, "a");
if(!fp) {
logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
} else {
fprintf(fp, "\n# The following line was automatically added by tinc\n%s = %s\n", key, value);
fclose(fp);
@ -415,45 +413,5 @@ bool append_config_file(const char *name, const char *key, const char *value) {
free(fname);
return fp;
}
bool disable_old_keys(FILE *f) {
char buf[100];
long pos;
bool disabled = false;
rewind(f);
pos = ftell(f);
if(pos < 0)
return false;
while(fgets(buf, sizeof buf, f)) {
if(!strncmp(buf, "-----BEGIN RSA", 14)) {
buf[11] = 'O';
buf[12] = 'L';
buf[13] = 'D';
if(fseek(f, pos, SEEK_SET))
break;
if(fputs(buf, f) <= 0)
break;
disabled = true;
}
else if(!strncmp(buf, "-----END RSA", 12)) {
buf[ 9] = 'O';
buf[10] = 'L';
buf[11] = 'D';
if(fseek(f, pos, SEEK_SET))
break;
if(fputs(buf, f) <= 0)
break;
disabled = true;
}
pos = ftell(f);
if(pos < 0)
break;
}
return disabled;
return fp != NULL;
}

View file

@ -1,7 +1,7 @@
/*
conf.h -- header for conf.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -60,10 +60,7 @@ extern config_t *parse_config_line(char *, const char *, int);
extern bool read_config_file(splay_tree_t *, const char *);
extern void read_config_options(splay_tree_t *, const char *);
extern bool read_server_config(void);
extern bool read_connection_config(struct connection_t *);
extern bool read_host_config(splay_tree_t *, const char *);
extern bool append_config_file(const char *, const char *, const char *);
extern FILE *ask_and_open(const char *, const char *, const char *);
extern bool is_safe_path(const char *);
extern bool disable_old_keys(FILE *);
#endif /* __TINC_CONF_H__ */
#endif /* __TINC_CONF_H__ */

View file

@ -1,6 +1,6 @@
/*
connection.c -- connection list management
Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
2008 Max Rijevski <maksuf@gmail.com>
@ -21,7 +21,7 @@
#include "system.h"
#include "splay_tree.h"
#include "list.h"
#include "cipher.h"
#include "conf.h"
#include "control_common.h"
@ -31,23 +31,19 @@
#include "utils.h"
#include "xalloc.h"
splay_tree_t *connection_tree; /* Meta connections */
connection_t *broadcast;
static int connection_compare(const connection_t *a, const connection_t *b) {
return a < b ? -1 : a == b ? 0 : 1;
}
list_t *connection_list;
connection_t *everyone;
void init_connections(void) {
connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection);
broadcast = new_connection();
broadcast->name = xstrdup("everyone");
broadcast->hostname = xstrdup("BROADCAST");
connection_list = list_alloc((list_action_t) free_connection);
everyone = new_connection();
everyone->name = xstrdup("everyone");
everyone->hostname = xstrdup("BROADCAST");
}
void exit_connections(void) {
splay_delete_tree(connection_tree);
free_connection(broadcast);
list_delete_list(connection_list);
free_connection(everyone);
}
connection_t *new_connection(void) {
@ -58,30 +54,20 @@ void free_connection(connection_t *c) {
if(!c)
return;
if(c->name)
free(c->name);
if(c->hostname)
free(c->hostname);
cipher_close(&c->incipher);
digest_close(&c->indigest);
cipher_close(&c->outcipher);
digest_close(&c->outdigest);
ecdh_free(&c->ecdh);
sptps_stop(&c->sptps);
ecdsa_free(&c->ecdsa);
rsa_free(&c->rsa);
if(c->hischallenge)
free(c->hischallenge);
if(c->config_tree)
exit_configuration(&c->config_tree);
free(c->hischallenge);
buffer_clear(&c->inbuf);
buffer_clear(&c->outbuf);
if(event_initialized(&c->inevent))
event_del(&c->inevent);
@ -91,24 +77,26 @@ void free_connection(connection_t *c) {
if(c->socket > 0)
closesocket(c->socket);
free(c->name);
free(c->hostname);
if(c->config_tree)
exit_configuration(&c->config_tree);
free(c);
}
void connection_add(connection_t *c) {
splay_insert(connection_tree, c);
list_insert_tail(connection_list, c);
}
void connection_del(connection_t *c) {
splay_delete(connection_tree, c);
list_delete(connection_list, c);
}
bool dump_connections(connection_t *cdump) {
splay_node_t *node;
connection_t *c;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
for list_each(connection_t, c, connection_list) {
send_request(cdump, "%d %d %s %s %x %d %x",
CONTROL, REQ_DUMP_CONNECTIONS,
c->name, c->hostname, c->options, c->socket,
bitfield_to_int(&c->status, sizeof c->status));

View file

@ -1,6 +1,6 @@
/*
connection.h -- header for connection.c
Copyright (C) 2000-2010 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -25,81 +25,83 @@
#include "cipher.h"
#include "digest.h"
#include "rsa.h"
#include "splay_tree.h"
#include "list.h"
#include "sptps.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
#define OPTION_CLAMP_MSS 0x0008
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
#define OPTION_CLAMP_MSS 0x0008
#define OPTION_VERSION(x) ((x) >> 24) /* Top 8 bits are for protocol minor version */
typedef struct connection_status_t {
unsigned int pinged:1; /* sent ping */
unsigned int active:1; /* 1 if active.. */
unsigned int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
unsigned int termreq:1; /* the termination of this connection was requested */
unsigned int remove_unused:1; /* Set to 1 if you want this connection removed */
unsigned int timeout_unused:1; /* 1 if gotten timeout */
unsigned int encryptout:1; /* 1 if we can encrypt outgoing traffic */
unsigned int decryptin:1; /* 1 if we have to decrypt incoming traffic */
unsigned int mst:1; /* 1 if this connection is part of a minimum spanning tree */
unsigned int control:1;
unsigned int pcap:1;
unsigned int unused:21;
unsigned int pinged:1; /* sent ping */
unsigned int active:1; /* 1 if active.. */
unsigned int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
unsigned int unused_termreq:1; /* the termination of this connection was requested */
unsigned int remove_unused:1; /* Set to 1 if you want this connection removed */
unsigned int timeout_unused:1; /* 1 if gotten timeout */
unsigned int encryptout:1; /* 1 if we can encrypt outgoing traffic */
unsigned int decryptin:1; /* 1 if we have to decrypt incoming traffic */
unsigned int mst:1; /* 1 if this connection is part of a minimum spanning tree */
unsigned int control:1; /* 1 if this is a control connection */
unsigned int pcap:1; /* 1 if this is a control connection requesting packet capture */
unsigned int log:1; /* 1 if this is a control connection requesting log dump */
unsigned int unused:20;
} connection_status_t;
#include "ecdh.h"
#include "ecdsa.h"
#include "edge.h"
#include "net.h"
#include "node.h"
typedef struct connection_t {
char *name; /* name he claims to have */
char *name; /* name he claims to have */
union sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_major; /* used protocol */
int protocol_minor; /* used protocol */
union sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_major; /* used protocol */
int protocol_minor; /* used protocol */
int socket; /* socket used for this connection */
uint32_t options; /* options for this connection */
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 */
int socket; /* socket used for this connection */
uint32_t options; /* options for this connection */
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 */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
rsa_t rsa; /* his public RSA key */
ecdsa_t ecdsa; /* his public ECDSA key */
ecdsa_t ecdh; /* state for ECDH key exchange */
cipher_t incipher; /* Cipher he will use to send data to us */
cipher_t outcipher; /* Cipher we will use to send data to him */
rsa_t rsa; /* his public RSA key */
ecdsa_t ecdsa; /* his public ECDSA key */
cipher_t incipher; /* Cipher he will use to send data to us */
cipher_t outcipher; /* Cipher we will use to send data to him */
digest_t indigest;
digest_t outdigest;
sptps_t sptps;
int inmaclength;
int outmaclength;
int incompression;
int outcompression;
char *hischallenge; /* The challenge we sent to him */
char *hischallenge; /* The challenge we sent to him */
struct buffer_t inbuf;
struct buffer_t outbuf;
struct event inevent; /* input event on this metadata connection */
struct event outevent; /* output event on this metadata connection */
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
struct event inevent; /* input event on this metadata connection */
struct event outevent; /* output event on this metadata connection */
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 or pinged them */
time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */
splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;
extern splay_tree_t *connection_tree;
extern connection_t *broadcast;
extern list_t *connection_list;
extern connection_t *everyone;
extern void init_connections(void);
extern void exit_connections(void);
@ -109,4 +111,4 @@ extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
extern bool dump_connections(struct connection_t *);
#endif /* __TINC_CONNECTION_H__ */
#endif /* __TINC_CONNECTION_H__ */

View file

@ -1,6 +1,6 @@
/*
control.c -- Control socket handling.
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,7 +29,6 @@
#include "netutl.h"
#include "protocol.h"
#include "route.h"
#include "splay_tree.h"
#include "utils.h"
#include "xalloc.h"
@ -44,16 +43,16 @@ static bool control_ok(connection_t *c, int type) {
return control_return(c, type, 0);
}
bool control_h(connection_t *c, char *request) {
bool control_h(connection_t *c, const char *request) {
int type;
if(!c->status.control || c->allow_request != CONTROL) {
logger(LOG_ERR, "Unauthorized control request from %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Unauthorized control request from %s (%s)", c->name, c->hostname);
return false;
}
if(sscanf(request, "%*d %d", &type) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CONTROL", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "CONTROL", c->name, c->hostname);
return false;
}
@ -64,7 +63,7 @@ bool control_h(connection_t *c, char *request) {
case REQ_DUMP_NODES:
return dump_nodes(c);
case REQ_DUMP_EDGES:
return dump_edges(c);
@ -93,22 +92,18 @@ bool control_h(connection_t *c, char *request) {
return control_ok(c, REQ_RETRY);
case REQ_RELOAD:
logger(LOG_NOTICE, "Got '%s' command", "reload");
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got '%s' command", "reload");
int result = reload_configuration();
return control_return(c, REQ_RELOAD, result);
case REQ_DISCONNECT: {
char name[MAX_STRING_SIZE];
connection_t *other;
splay_node_t *node, *next;
bool found = false;
if(sscanf(request, "%*d %*d " MAX_STRING, name) != 1)
return control_return(c, REQ_DISCONNECT, -1);
for(node = connection_tree->head; node; node = next) {
next = node->next;
other = node->data;
for list_each(connection_t, other, connection_list) {
if(strcmp(other->name, name))
continue;
terminate_connection(other, other->status.active);
@ -122,10 +117,17 @@ bool control_h(connection_t *c, char *request) {
return dump_traffic(c);
case REQ_PCAP:
sscanf(request, "%*d %*d %d", &c->outmaclength);
c->status.pcap = true;
pcap = true;
return true;
case REQ_LOG:
sscanf(request, "%*d %*d %d", &c->outcompression);
c->status.log = true;
logcontrol = true;
return true;
default:
return send_request(c, "%d %d", CONTROL, REQ_INVALID);
}
@ -137,7 +139,7 @@ bool init_control(void) {
FILE *f = fopen(pidfilename, "w");
if(!f) {
logger(LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
return false;
}

View file

@ -1,6 +1,7 @@
/*
control_protocol.h -- control socket protocol.
Copyright (C) 2007 Scott Lamb <slamb@slamb.org>
Copyright (C) 2007 Scott Lamb <slamb@slamb.org>
2009-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -39,6 +40,7 @@ enum request_type {
REQ_DISCONNECT,
REQ_DUMP_TRAFFIC,
REQ_PCAP,
REQ_LOG,
};
#define TINC_CTL_VERSION_CURRENT 0

43
src/crypto.c Normal file
View file

@ -0,0 +1,43 @@
/*
crypto.c -- Cryptographic miscellaneous functions and initialisation
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include "crypto.h"
void crypto_init(void) {
RAND_load_file("/dev/urandom", 1024);
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
OpenSSL_add_all_algorithms();
}
void crypto_exit(void) {
EVP_cleanup();
}
void randomize(void *out, size_t outlen) {
RAND_pseudo_bytes(out, outlen);
}

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a Cygwin environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2009 Guus Sliepen <guus@tinc-vpn.org>
2002-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ static uint64_t device_total_out = 0;
static pid_t reader_pid;
static int sp[2];
bool setup_device(void) {
static bool setup_device(void) {
HKEY key, key2;
int i, err;
@ -64,7 +64,7 @@ bool setup_device(void) {
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
logger(LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
return false;
}
@ -77,7 +77,7 @@ bool setup_device(void) {
snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
len = sizeof adaptername;
@ -116,7 +116,7 @@ bool setup_device(void) {
RegCloseKey(key);
if(!found) {
logger(LOG_ERR, "No Windows tap device found!");
logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
return false;
}
@ -127,22 +127,22 @@ bool setup_device(void) {
iface = xstrdup(adaptername);
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
/* Now we are going to open this device twice: once for reading and once for writing.
We do this because apparently it isn't possible to check for activity in the select() loop.
Furthermore I don't really know how to do it the "Windows" way. */
if(socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, sp)) {
logger(LOG_DEBUG, "System call `%s' failed: %s", "socketpair", strerror(errno));
logger(DEBUG_ALWAYS, LOG_DEBUG, "System call `%s' failed: %s", "socketpair", strerror(errno));
return false;
}
/* The parent opens the tap device for writing. */
device_handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, "Could not open Windows tap device %s (%s) for writing: %s", device, iface, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open Windows tap device %s (%s) for writing: %s", device, iface, winerror(GetLastError()));
return false;
}
@ -151,7 +151,7 @@ bool setup_device(void) {
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
return false;
}
@ -164,14 +164,14 @@ bool setup_device(void) {
reader_pid = fork();
if(reader_pid == -1) {
logger(LOG_DEBUG, "System call `%s' failed: %s", "fork", strerror(errno));
logger(DEBUG_ALWAYS, LOG_DEBUG, "System call `%s' failed: %s", "fork", strerror(errno));
return false;
}
if(!reader_pid) {
/* The child opens the tap device for reading, blocking.
It passes everything it reads to the socket. */
char buf[MTU];
long inlen;
@ -180,13 +180,13 @@ bool setup_device(void) {
device_handle = CreateFile(tapname, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, "Could not open Windows tap device %s (%s) for reading: %s", device, iface, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open Windows tap device %s (%s) for reading: %s", device, iface, winerror(GetLastError()));
buf[0] = 0;
write(sp[1], buf, 1);
exit(1);
}
logger(LOG_DEBUG, "Tap reader forked and running.");
logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader forked and running.");
/* Notify success */
@ -203,18 +203,18 @@ bool setup_device(void) {
read(device_fd, &gelukt, 1);
if(gelukt != 1) {
logger(LOG_DEBUG, "Tap reader failed!");
logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader failed!");
return false;
}
device_info = "Windows tap device";
logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
close(sp[0]);
close(sp[1]);
CloseHandle(device_handle);
@ -225,33 +225,33 @@ void close_device(void) {
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
packet->len = inlen;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet) {
static bool write_packet(vpn_packet_t *packet) {
long outlen;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
@ -260,8 +260,16 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,7 +1,7 @@
/*
net.h -- generic header for device.c
device.h -- generic header for device.c
Copyright (C) 2001-2005 Ivo Timmermans
2001-2006 Guus Sliepen <guus@tinc-vpn.org>
2001-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -32,10 +32,20 @@ extern uint64_t device_in_bytes;
extern uint64_t device_out_packets;
extern uint64_t device_out_bytes;
extern bool setup_device(void);
extern void close_device(void);
extern bool read_packet(struct vpn_packet_t *);
extern bool write_packet(struct vpn_packet_t *);
extern void dump_device_stats(void);
typedef struct devops_t {
bool (*setup)(void);
void (*close)(void);
bool (*read)(struct vpn_packet_t *);
bool (*write)(struct vpn_packet_t *);
void (*dump_stats)(void);
} devops_t;
#endif /* __TINC_DEVICE_H__ */
extern const devops_t os_devops;
extern const devops_t dummy_devops;
extern const devops_t raw_socket_devops;
extern const devops_t multicast_devops;
extern const devops_t uml_devops;
extern const devops_t vde_devops;
extern devops_t devops;
#endif /* __TINC_DEVICE_H__ */

127
src/digest.c Normal file
View file

@ -0,0 +1,127 @@
/*
digest.c -- Digest handling
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "utils.h"
#include "xalloc.h"
#include <openssl/err.h>
#include <openssl/hmac.h>
#include "digest.h"
#include "logger.h"
static void set_maclength(digest_t *digest, int maclength) {
int digestlen = EVP_MD_size(digest->digest);
if(maclength > digestlen || maclength < 0)
digest->maclength = digestlen;
else
digest->maclength = maclength;
}
bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
digest->digest = EVP_get_digestbyname(name);
digest->key = NULL;
if(!digest->digest) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest name '%s'!", name);
return false;
}
set_maclength(digest, maclength);
return true;
}
bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
digest->digest = EVP_get_digestbynid(nid);
digest->key = NULL;
if(!digest->digest) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest nid %d!", nid);
return false;
}
set_maclength(digest, maclength);
return true;
}
bool digest_open_sha1(digest_t *digest, int maclength) {
digest->digest = EVP_sha1();
digest->key = NULL;
set_maclength(digest, maclength);
return true;
}
bool digest_set_key(digest_t *digest, const void *key, size_t len) {
digest->key = xrealloc(digest->key, len);
memcpy(digest->key, key, len);
digest->keylength = len;
return true;
}
void digest_close(digest_t *digest) {
free(digest->key);
digest->key = NULL;
}
bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
size_t len = EVP_MD_size(digest->digest);
unsigned char tmpdata[len];
if(digest->key) {
HMAC(digest->digest, digest->key, digest->keylength, indata, inlen, tmpdata, NULL);
} else {
EVP_MD_CTX ctx;
if(!EVP_DigestInit(&ctx, digest->digest)
|| !EVP_DigestUpdate(&ctx, indata, inlen)
|| !EVP_DigestFinal(&ctx, tmpdata, NULL)) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
}
memcpy(outdata, tmpdata, digest->maclength);
return true;
}
bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
size_t len = digest->maclength;
unsigned char outdata[len];
return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, digest->maclength);
}
int digest_get_nid(const digest_t *digest) {
return digest->digest ? digest->digest->type : 0;
}
size_t digest_keylength(const digest_t *digest) {
return digest->digest->md_size;
}
size_t digest_length(const digest_t *digest) {
return digest->maclength;
}
bool digest_active(const digest_t *digest) {
return digest->digest && digest->digest->type != 0;
}

View file

@ -25,7 +25,7 @@
#ifndef HAVE_DAEMON
/*
Replacement for the daemon() function.
The daemon() function is for programs wishing to detach themselves
from the controlling terminal and run in the background as system
daemons.
@ -104,14 +104,14 @@ char *get_current_dir_name(void) {
size = 100;
buf = xmalloc(size);
errno = 0; /* Success */
errno = 0; /* Success */
r = getcwd(buf, size);
/* getcwd returns NULL and sets errno to ERANGE if the bufferspace
is insufficient to contain the entire working directory. */
while(r == NULL && errno == ERANGE) {
free(buf);
size <<= 1; /* double the size */
size <<= 1; /* double the size */
buf = xmalloc(size);
r = getcwd(buf, size);
}

View file

@ -45,4 +45,4 @@ extern int gettimeofday(struct timeval *, void *);
extern int usleep(long long usec);
#endif
#endif /* __DROPIN_H__ */
#endif /* __DROPIN_H__ */

62
src/dummy_device.c Normal file
View file

@ -0,0 +1,62 @@
/*
device.c -- Dummy device
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "device.h"
#include "logger.h"
#include "net.h"
static char *device_info = "dummy device";
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
device = "dummy";
iface = "dummy";
logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
static void close_device(void) {
}
static bool read_packet(vpn_packet_t *packet) {
return false;
}
static bool write_packet(vpn_packet_t *packet) {
device_total_out += packet->len;
return true;
}
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t dummy_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

96
src/ecdh.c Normal file
View file

@ -0,0 +1,96 @@
/*
ecdh.c -- Diffie-Hellman key exchange handling
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "utils.h"
#include "xalloc.h"
#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include "ecdh.h"
#include "logger.h"
bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey) {
*ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!*ecdh) {
logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(!EC_KEY_generate_key(*ecdh)) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
const EC_POINT *point = EC_KEY_get0_public_key(*ecdh);
if(!point) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
size_t result = EC_POINT_point2oct(EC_KEY_get0_group(*ecdh), point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL);
if(!result) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(*ecdh));
if(!point) {
logger(DEBUG_ALWAYS, LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int result = EC_POINT_oct2point(EC_KEY_get0_group(*ecdh), point, pubkey, ECDH_SIZE, NULL);
if(!result) {
EC_POINT_free(point);
logger(DEBUG_ALWAYS, LOG_ERR, "Converting binary to EC_POINT failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
result = ECDH_compute_key(shared, ECDH_SIZE, point, *ecdh, NULL);
EC_POINT_free(point);
EC_KEY_free(*ecdh);
*ecdh = NULL;
if(!result) {
logger(DEBUG_ALWAYS, LOG_ERR, "Computing Elliptic Curve Diffie-Hellman shared key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
void ecdh_free(ecdh_t *ecdh) {
if(*ecdh) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
}
}

130
src/ecdsa.c Normal file
View file

@ -0,0 +1,130 @@
/*
ecdsa.c -- ECDSA key handling
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include "logger.h"
#include "ecdsa.h"
#include "utils.h"
// Get and set ECDSA keys
//
bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
*ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!*ecdsa) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "EC_KEY_new_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int len = strlen(p);
unsigned char pubkey[len / 4 * 3 + 3];
const unsigned char *ppubkey = pubkey;
len = b64decode(p, (char *)pubkey, len);
if(!o2i_ECPublicKey(ecdsa, &ppubkey, len)) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
unsigned char *pubkey = NULL;
int len = i2o_ECPublicKey(*ecdsa, &pubkey);
char *base64 = malloc(len * 4 / 3 + 5);
b64encode((char *)pubkey, base64, len);
free(pubkey);
return base64;
}
// Read PEM ECDSA keys
bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
*ecdsa = PEM_read_EC_PUBKEY(fp, ecdsa, NULL, NULL);
if(*ecdsa)
return true;
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
*ecdsa = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);
if(*ecdsa)
return true;
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
size_t ecdsa_size(ecdsa_t *ecdsa) {
return ECDSA_size(*ecdsa);
}
// TODO: standardise output format?
bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t len, void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
memset(sig, 0, siglen);
if(!ECDSA_sign(0, hash, sizeof hash, sig, &siglen, *ecdsa)) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "ECDSA_sign() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t len, const void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
if(!ECDSA_verify(0, hash, sizeof hash, sig, siglen, *ecdsa)) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "ECDSA_verify() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
bool ecdsa_active(ecdsa_t *ecdsa) {
return *ecdsa;
}
void ecdsa_free(ecdsa_t *ecdsa) {
if(*ecdsa) {
EC_KEY_free(*ecdsa);
*ecdsa = NULL;
}
}

View file

@ -1,6 +1,6 @@
/*
edge.c -- edge tree management
Copyright (C) 2000-2006 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -29,7 +29,7 @@
#include "utils.h"
#include "xalloc.h"
splay_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
splay_tree_t *edge_weight_tree;
static int edge_compare(const edge_t *a, const edge_t *b) {
return strcmp(a->to->name, b->to->name);
@ -99,7 +99,7 @@ void edge_del(edge_t *e) {
edge_t *lookup_edge(node_t *from, node_t *to) {
edge_t v;
v.from = from;
v.to = to;
@ -107,17 +107,10 @@ edge_t *lookup_edge(node_t *from, node_t *to) {
}
bool dump_edges(connection_t *c) {
splay_node_t *node, *node2;
node_t *n;
edge_t *e;
char *address;
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
e = node2->data;
address = sockaddr2hostname(&e->address);
send_request(c, "%d %d %s to %s at %s options %x weight %d",
for splay_each(node_t, n, node_tree) {
for splay_each(edge_t, e, n->edge_tree) {
char *address = sockaddr2hostname(&e->address);
send_request(c, "%d %d %s %s %s %x %d",
CONTROL, REQ_DUMP_EDGES,
e->from->name, e->to->name, address,
e->options, e->weight);

View file

@ -1,6 +1,6 @@
/*
edge.h -- header for edge.c
Copyright (C) 2001-2006 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -31,14 +31,14 @@ typedef struct edge_t {
struct node_t *to;
sockaddr_t address;
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
} edge_t;
extern splay_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern splay_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern void init_edges(void);
extern void exit_edges(void);
@ -51,4 +51,4 @@ extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
extern bool dump_edges(struct connection_t *);
#endif /* __TINC_EDGE_H__ */
#endif /* __TINC_EDGE_H__ */

View file

@ -54,17 +54,17 @@ struct arphdr {
uint16_t ar_hrd;
uint16_t ar_pro;
uint8_t ar_hln;
uint8_t ar_pln;
uint16_t ar_op;
uint8_t ar_pln;
uint16_t ar_op;
} __attribute__ ((__packed__));
#define ARPOP_REQUEST 1
#define ARPOP_REPLY 2
#define ARPOP_RREQUEST 3
#define ARPOP_RREPLY 4
#define ARPOP_InREQUEST 8
#define ARPOP_InREPLY 9
#define ARPOP_NAK 10
#define ARPOP_REQUEST 1
#define ARPOP_REPLY 2
#define ARPOP_RREQUEST 3
#define ARPOP_RREPLY 4
#define ARPOP_InREQUEST 8
#define ARPOP_InREPLY 9
#define ARPOP_NAK 10
#endif
#ifndef HAVE_STRUCT_ETHER_ARP

View file

@ -7,13 +7,13 @@
/* for old netdb.h */
#ifndef EAI_NODATA
#define EAI_NODATA 1
#define EAI_NODATA 1
#endif
#ifndef EAI_MEMORY
#define EAI_MEMORY 2
#define EAI_MEMORY 2
#endif
#ifndef EAI_FAMILY
#define EAI_FAMILY 3
#define EAI_FAMILY 3
#endif

View file

@ -29,7 +29,7 @@ char *gai_strerror(int ecode) {
default:
return "Unknown error";
}
}
}
#endif /* !HAVE_GAI_STRERROR */
#if !HAVE_DECL_FREEADDRINFO
@ -49,14 +49,14 @@ static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr) {
struct addrinfo *ai;
ai = xmalloc_and_zero(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
ai->ai_addr = (struct sockaddr *)(ai + 1);
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_addr->sa_family = ai->ai_family = AF_INET;
((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
return ai;
}
@ -77,12 +77,12 @@ int getaddrinfo(const char *hostname, const char *servname, const struct addrinf
*res = malloc_ai(port, htonl(0x00000000));
return 0;
}
if (!hostname) {
*res = malloc_ai(port, htonl(0x7f000001));
return 0;
}
hp = gethostbyname(hostname);
if(!hp || !hp->h_addr_list || !hp->h_addr_list[0])

View file

@ -15,25 +15,24 @@
#endif
#ifndef AI_NUMERICHOST
#define AI_NUMERICHOST 4
#define AI_NUMERICHOST 4
#endif
#ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
#endif /* !HAVE_STRUCT_ADDRINFO */
#if !HAVE_DECL_GETADDRINFO
int getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res);
int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
#endif /* !HAVE_GETADDRINFO */
#if !HAVE_DECL_GAI_STRERROR

View file

@ -41,10 +41,10 @@ int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t host
}
hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
if(!hp || !hp->h_name || !hp->h_name[0])
return EAI_NODATA;
len = snprintf(host, hostlen, "%s", hp->h_name);
if(len < 0 || len >= hostlen)
return EAI_MEMORY;

View file

@ -2,8 +2,7 @@
#define _FAKE_GETNAMEINFO_H
#if !HAVE_DECL_GETNAMEINFO
int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
size_t hostlen, char *serv, size_t servlen, int flags);
int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
#endif /* !HAVE_GETNAMEINFO */
#ifndef NI_MAXSERV

View file

@ -1,6 +1,6 @@
/*
cipher.c -- Symmetric block cipher handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -97,12 +97,12 @@ static bool cipher_open(cipher_t *cipher, int algo, int mode) {
gcry_error_t err;
if(!ciphertonid(algo, mode, &cipher->nid)) {
logger(LOG_DEBUG, "Cipher %d mode %d has no corresponding nid!", algo, mode);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Cipher %d mode %d has no corresponding nid!", algo, mode);
return false;
}
if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) {
logger(LOG_DEBUG, "Unable to intialise cipher %d mode %d: %s", algo, mode, gcry_strerror(err));
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unable to intialise cipher %d mode %d: %s", algo, mode, gcry_strerror(err));
return false;
}
@ -118,7 +118,7 @@ bool cipher_open_by_name(cipher_t *cipher, const char *name) {
int algo, mode;
if(!nametocipher(name, &algo, &mode)) {
logger(LOG_DEBUG, "Unknown cipher name '%s'!", name);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown cipher name '%s'!", name);
return false;
}
@ -129,7 +129,7 @@ bool cipher_open_by_nid(cipher_t *cipher, int nid) {
int algo, mode;
if(!nidtocipher(nid, &algo, &mode)) {
logger(LOG_DEBUG, "Unknown cipher ID %d!", nid);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown cipher ID %d!", nid);
return false;
}
@ -199,7 +199,7 @@ bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
size_t reqlen = ((inlen + cipher->blklen) / cipher->blklen) * cipher->blklen;
if(*outlen < reqlen) {
logger(LOG_ERR, "Error while encrypting: not enough room for padding");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: not enough room for padding");
return false;
}
@ -212,18 +212,18 @@ bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
else
pad[i] = padbyte;
}
if(oneshot)
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
if((err = gcry_cipher_encrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
return false;
}
if(cipher->padding) {
if((err = gcry_cipher_encrypt(cipher->handle, outdata + inlen, cipher->blklen, pad, cipher->blklen))) {
logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
return false;
}
@ -241,7 +241,7 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
if((err = gcry_cipher_decrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
logger(LOG_ERR, "Error while decrypting: %s", gcry_strerror(err));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", gcry_strerror(err));
return false;
}
@ -252,7 +252,7 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
uint8_t padbyte = ((uint8_t *)outdata)[inlen - 1];
if(padbyte == 0 || padbyte > cipher->blklen || padbyte > inlen) {
logger(LOG_ERR, "Error while decrypting: invalid padding");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: invalid padding");
return false;
}
@ -260,7 +260,7 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
for(int i = inlen - 1; i >= origlen; i--)
if(((uint8_t *)outdata)[i] != padbyte) {
logger(LOG_ERR, "Error while decrypting: invalid padding");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: invalid padding");
return false;
}

View file

@ -1,6 +1,6 @@
/*
cipher.h -- header file cipher.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
crypto.h -- header for crypto.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
digest.c -- Digest handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -75,7 +75,7 @@ static bool digesttonid(int algo, int *nid) {
static bool digest_open(digest_t *digest, int algo, int maclength) {
if(!digesttonid(algo, &digest->nid)) {
logger(LOG_DEBUG, "Digest %d has no corresponding nid!", algo);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Digest %d has no corresponding nid!", algo);
return false;
}
@ -85,7 +85,7 @@ static bool digest_open(digest_t *digest, int algo, int maclength) {
digest->maclength = len;
else
digest->maclength = maclength;
digest->algo = algo;
digest->hmac = NULL;
@ -96,7 +96,7 @@ bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
int algo;
if(!nametodigest(name, &algo)) {
logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest name '%s'!", name);
return false;
}
@ -107,7 +107,7 @@ bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
int algo;
if(!nidtodigest(nid, &algo)) {
logger(LOG_DEBUG, "Unknown digest ID %d!", nid);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest ID %d!", nid);
return false;
}

View file

@ -1,6 +1,6 @@
/*
digest.h -- header file digest.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
rsa.c -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -148,7 +148,7 @@ static size_t ber_read_len(unsigned char **p, size_t *buflen) {
return *(*p)++;
}
}
static bool ber_read_sequence(unsigned char **p, size_t *buflen, size_t *result) {
int tag = ber_read_id(p, buflen);
@ -173,7 +173,7 @@ static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) {
if(mpi)
err = gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, *p, len, NULL);
*p += len;
*buflen -= len;
@ -187,7 +187,7 @@ bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
?: gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL);
if(err) {
logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
return false;
}
@ -202,7 +202,7 @@ bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
?: gcry_mpi_scan(&rsa->d, GCRYMPI_FMT_HEX, d, 0, NULL);
if(err) {
logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
return false;
}
@ -216,7 +216,7 @@ bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
size_t derlen;
if(!pem_decode(fp, "RSA PUBLIC KEY", derbuf, sizeof derbuf, &derlen)) {
logger(LOG_ERR, "Unable to read RSA public key: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA public key: %s", strerror(errno));
return NULL;
}
@ -224,7 +224,7 @@ bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
|| !ber_read_mpi(&derp, &derlen, &rsa->n)
|| !ber_read_mpi(&derp, &derlen, &rsa->e)
|| derlen) {
logger(LOG_ERR, "Error while decoding RSA public key");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decoding RSA public key");
return NULL;
}
@ -236,7 +236,7 @@ bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
size_t derlen;
if(!pem_decode(fp, "RSA PRIVATE KEY", derbuf, sizeof derbuf, &derlen)) {
logger(LOG_ERR, "Unable to read RSA private key: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA private key: %s", strerror(errno));
return NULL;
}
@ -251,7 +251,7 @@ bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
|| !ber_read_mpi(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, NULL) // u
|| derlen) {
logger(LOG_ERR, "Error while decoding RSA private key");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decoding RSA private key");
return NULL;
}
@ -267,7 +267,7 @@ size_t rsa_size(rsa_t *rsa) {
*/
// TODO: get rid of this macro, properly clean up gcry_ structures after use
#define check(foo) { gcry_error_t err = (foo); if(err) {logger(LOG_ERR, "gcrypt error %s/%s at %s:%d", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }}
#define check(foo) { gcry_error_t err = (foo); if(err) {logger(DEBUG_ALWAYS, LOG_ERR, "gcrypt error %s/%s at %s:%d", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }}
bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
gcry_mpi_t inmpi;

View file

@ -1,6 +1,6 @@
/*
rsagen.c -- RSA key generation and export
Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2008-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -164,12 +164,12 @@ bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
if(!ber_write_mpi(&derp1, &derlen1, &rsa->n)
|| !ber_write_mpi(&derp1, &derlen1, &rsa->e)
|| !ber_write_sequence(&derp2, &derlen2, derbuf1, derlen1)) {
logger(LOG_ERR, "Error while encoding RSA public key");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA public key");
return false;
}
if(!pem_encode(fp, "RSA PUBLIC KEY", derbuf2, derlen2)) {
logger(LOG_ERR, "Unable to write RSA public key: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA public key: %s", strerror(errno));
return false;
}
@ -193,12 +193,12 @@ bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
|| ber_write_mpi(&derp1, &derlen1, &exp1)
|| ber_write_mpi(&derp1, &derlen1, &exp2)
|| ber_write_mpi(&derp1, &derlen1, &coeff))
logger(LOG_ERR, "Error while encoding RSA private key");
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA private key");
return false;
}
if(!pem_encode(fp, "RSA PRIVATE KEY", derbuf2, derlen2)) {
logger(LOG_ERR, "Unable to write RSA private key: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA private key: %s", strerror(errno));
return false;
}

View file

@ -1,6 +1,6 @@
/*
graph.c -- graph algorithms
Copyright (C) 2001-2011 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -44,12 +44,12 @@
#include "system.h"
#include "splay_tree.h"
#include "config.h"
#include "connection.h"
#include "device.h"
#include "edge.h"
#include "graph.h"
#include "list.h"
#include "logger.h"
#include "netutl.h"
#include "node.h"
@ -65,34 +65,22 @@
Please note that sorting on weight is already done by add_edge().
*/
void mst_kruskal(void) {
splay_node_t *node, *next;
edge_t *e;
node_t *n;
connection_t *c;
static void mst_kruskal(void) {
/* Clear MST status on connections */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
for list_each(connection_t, c, connection_list)
c->status.mst = false;
}
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:");
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Running Kruskal's algorithm:");
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for splay_each(node_t, n, node_tree)
n->status.visited = false;
}
/* Add safe edges */
for(node = edge_weight_tree->head; node; node = next) {
next = node->next;
e = node->data;
for splay_each(edge_t, e, edge_weight_tree) {
if(!e->reverse || (e->from->status.visited && e->to->status.visited))
continue;
@ -105,31 +93,21 @@ void mst_kruskal(void) {
if(e->reverse->connection)
e->reverse->connection->status.mst = true;
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name,
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name,
e->to->name, e->weight);
}
}
/* Implementation of Dijkstra's algorithm.
Running time: O(N^2)
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
static void sssp_dijkstra(void) {
splay_node_t *node, *to;
edge_t *e;
node_t *n, *m;
list_t *todo_list;
list_node_t *lnode, *nnode;
bool indirect;
todo_list = list_alloc(NULL);
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Dijkstra's algorithm:");
static void sssp_bfs(void) {
list_t *todo_list = list_alloc(NULL);
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for splay_each(node_t, n, node_tree) {
n->status.visited = false;
n->status.indirect = true;
n->distance = -1;
@ -137,119 +115,23 @@ static void sssp_dijkstra(void) {
/* Begin with myself */
myself->status.visited = true;
myself->status.indirect = false;
myself->nexthop = myself;
myself->prevedge = NULL;
myself->via = myself;
myself->distance = 0;
list_insert_head(todo_list, myself);
/* Loop while todo_list is filled */
while(todo_list->head) {
n = NULL;
nnode = NULL;
for list_each(node_t, n, todo_list) { /* "n" is the node from which we start */
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Examining edges from %s", n->name);
/* Select node from todo_list with smallest distance */
for(lnode = todo_list->head; lnode; lnode = lnode->next) {
m = lnode->data;
if(!n || m->status.indirect < n->status.indirect || m->distance < n->distance) {
n = m;
nnode = lnode;
}
}
/* Mark this node as visited and remove it from the todo_list */
n->status.visited = true;
list_unlink_node(todo_list, nnode);
/* Update distance of neighbours and add them to the todo_list */
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
e = to->data;
if(e->to->status.visited || !e->reverse)
continue;
/* Situation:
/
/
----->(n)---e-->(e->to)
\
\
Where e is an edge, (n) and (e->to) are nodes.
n->address is set to the e->address of the edge left of n to n.
We are currently examining the edge e right of n from n:
- If edge e provides for better reachability of e->to, update e->to.
*/
if(e->to->distance < 0)
list_insert_tail(todo_list, e->to);
indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address));
if(e->to->distance >= 0 && (!e->to->status.indirect || indirect) && e->to->distance <= n->distance + e->weight)
continue;
e->to->distance = n->distance + e->weight;
e->to->status.indirect = indirect;
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
update_node_udp(e->to, &e->address);
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Updating edge %s - %s weight %d distance %d", e->from->name,
e->to->name, e->weight, e->to->distance);
}
}
list_free(todo_list);
}
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
void sssp_bfs(void) {
splay_node_t *node, *to;
edge_t *e;
node_t *n;
list_t *todo_list;
list_node_t *from, *todonext;
bool indirect;
todo_list = list_alloc(NULL);
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = node->data;
n->status.visited = false;
n->status.indirect = true;
}
/* Begin with myself */
myself->status.visited = true;
myself->status.indirect = false;
myself->nexthop = myself;
myself->via = myself;
list_insert_head(todo_list, myself);
/* Loop while todo_list is filled */
for(from = todo_list->head; from; from = todonext) { /* "from" is the node from which we start */
n = from->data;
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
e = to->data;
if(n->distance < 0)
abort();
for splay_each(edge_t, e, n->edge_tree) { /* "e" is the edge connected to "from" */
if(!e->reverse)
continue;
@ -270,61 +152,63 @@ void sssp_bfs(void) {
of nodes behind it.
*/
indirect = n->status.indirect || e->options & OPTION_INDIRECT;
bool indirect = n->status.indirect || e->options & OPTION_INDIRECT;
if(e->to->status.visited
&& (!e->to->status.indirect || indirect))
&& (!e->to->status.indirect || indirect)
&& (e->to->distance != n->distance + 1 || e->weight >= e->to->prevedge->weight))
continue;
e->to->status.visited = true;
e->to->status.indirect = indirect;
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
e->to->prevedge = e;
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
e->to->distance = n->distance + 1;
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
if(!e->to->status.reachable || (e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN))
update_node_udp(e->to, &e->address);
list_insert_tail(todo_list, e->to);
}
todonext = from->next;
list_delete_node(todo_list, from);
next = node->next; /* Because the list_insert_tail() above could have added something extra for us! */
list_delete_node(todo_list, node);
}
list_free(todo_list);
}
static void check_reachability(void) {
splay_node_t *node, *next;
node_t *n;
char *name;
char *address, *port;
char *envp[7];
int i;
/* Check reachability status. */
for(node = node_tree->head; node; node = next) {
next = node->next;
n = node->data;
for splay_each(node_t, n, node_tree) {
if(n->status.visited != n->status.reachable) {
n->status.reachable = !n->status.reachable;
n->last_state_change = time(NULL);
if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became reachable",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable",
n->name, n->hostname);
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became unreachable",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became unreachable",
n->name, n->hostname);
}
if(experimental && OPTION_VERSION(n->options) >= 2)
n->status.sptps = true;
/* TODO: only clear status.validkey if node is unreachable? */
n->status.validkey = false;
if(n->status.sptps) {
sptps_stop(&n->sptps);
n->status.waitingforkey = false;
}
n->last_req_key = 0;
n->status.udp_confirmed = false;
n->maxmtu = MTU;
n->minmtu = 0;
n->mtuprobes = 0;
@ -332,6 +216,11 @@ static void check_reachability(void) {
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
char *name;
char *address;
char *port;
char *envp[7];
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
@ -343,31 +232,37 @@ static void check_reachability(void) {
execute_script(n->status.reachable ? "host-up" : "host-down", envp);
xasprintf(&name,
n->status.reachable ? "hosts/%s-up" : "hosts/%s-down",
n->name);
xasprintf(&name, n->status.reachable ? "hosts/%s-up" : "hosts/%s-down", n->name);
execute_script(name, envp);
free(name);
free(address);
free(port);
for(i = 0; i < 6; i++)
for(int i = 0; i < 6; i++)
free(envp[i]);
subnet_update(n, NULL, n->status.reachable);
if(!n->status.reachable)
if(!n->status.reachable) {
update_node_udp(n, NULL);
else if(n->connection)
send_ans_key(n);
memset(&n->status, 0, sizeof n->status);
n->options = 0;
} else if(n->connection) {
if(n->status.sptps) {
if(n->connection->outgoing)
send_req_key(n);
} else {
send_ans_key(n);
}
}
}
}
}
void graph(void) {
subnet_cache_flush();
sssp_dijkstra();
sssp_bfs();
check_reachability();
mst_kruskal();
}

View file

@ -1,6 +1,6 @@
/*
graph.h -- header for graph.c
Copyright (C) 2001-2006 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify

105
src/hash.c Normal file
View file

@ -0,0 +1,105 @@
/*
hash.c -- hash table management
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "hash.h"
#include "xalloc.h"
/* Generic hash function */
static uint32_t hash_function(const void *p, size_t len) {
const uint8_t *q = p;
uint32_t hash = 0;
while(true) {
for(int i = len > 4 ? 4 : len; --i;)
hash += q[i] << (8 * i);
hash *= 0x9e370001UL; // Golden ratio prime.
if(len <= 4)
break;
len -= 4;
}
return hash;
}
/* Map 32 bits int onto 0..n-1, without throwing away too many bits if n is 2^8 or 2^16 */
static uint32_t modulo(uint32_t hash, size_t n) {
if(n == 0x100)
return (hash >> 24) ^ ((hash >> 16) & 0xff) ^ ((hash >> 8) & 0xff) ^ (hash & 0xff);
else if(n == 0x10000)
return (hash >> 16) ^ (hash & 0xffff);
else
return hash % n;
}
/* (De)allocation */
hash_t *hash_alloc(size_t n, size_t size) {
hash_t *hash = xmalloc_and_zero(sizeof *hash);
hash->n = n;
hash->size = size;
hash->keys = xmalloc(hash->n * hash->size);
hash->values = xmalloc_and_zero(hash->n * sizeof *hash->values);
return hash;
}
void hash_free(hash_t *hash) {
free(hash->keys);
free(hash->values);
free(hash);
}
/* Searching and inserting */
void hash_insert(hash_t *hash, const void *key, const void *value) {
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
memcpy(hash->keys + i * hash->size, key, hash->size);
hash->values[i] = value;
}
void *hash_search(const hash_t *hash, const void *key) {
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
if(hash->values[i] && !memcmp(key, hash->keys + i * hash->size, hash->size)) {
return (void *)hash->values[i];
}
return NULL;
}
void *hash_search_or_insert(hash_t *hash, const void *key, const void *value) {
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
if(hash->values[i] && !memcmp(key, hash->keys + i * hash->size, hash->size))
return (void *)hash->values[i];
memcpy(hash->keys + i * hash->size, key, hash->size);
hash->values[i] = value;
return NULL;
}
/* Utility functions */
void hash_clear(hash_t *hash) {
memset(hash->values, 0, hash->n * sizeof *hash->values);
}
void hash_resize(hash_t *hash, size_t n) {
hash->keys = xrealloc(hash->keys, n * hash->size);
hash->values = xrealloc(hash->values, n * sizeof *hash->values);
if(n > hash->n)
memset(hash->values + hash->n, 0, (n - hash->n) * sizeof *hash->values);
}

41
src/hash.h Normal file
View file

@ -0,0 +1,41 @@
/*
hash.h -- header file for hash.c
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_HASH_H__
#define __TINC_HASH_H__
typedef struct hash_t {
size_t n;
size_t size;
char *keys;
const void **values;
} hash_t;
extern hash_t *hash_alloc(size_t n, size_t size) __attribute__ ((__malloc__));
extern void hash_free(hash_t *);
extern void hash_insert(hash_t *, const void *key, const void *value);
extern void *hash_search(const hash_t *, const void *key);
extern void *hash_search_or_insert(hash_t *, const void *key, const void *value);
extern void hash_clear(hash_t *);
extern void hash_resize(hash_t *, size_t n);
#endif /* __TINC_HASH_H__ */

266
src/info.c Normal file
View file

@ -0,0 +1,266 @@
/*
info.c -- Show information about a node, subnet or address
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "control_common.h"
#include "list.h"
#include "subnet.h"
#include "tincctl.h"
#include "info.h"
#include "xalloc.h"
void logger(int level, int priority, const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
char *strip_weight(char *netstr) {
int len = strlen(netstr);
if(len >= 3 && !strcmp(netstr + len - 3, "#10"))
netstr[len - 3] = 0;
return netstr;
}
static int info_node(int fd, const char *item) {
// Check the list of nodes
sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_NODES, item);
bool found = false;
char line[4096];
char node[4096];
char from[4096];
char to[4096];
char subnet[4096];
char host[4096];
char port[4096];
char via[4096];
char nexthop[4096];
int code, req, cipher, digest, maclength, compression, distance;
short int pmtu, minmtu, maxmtu;
unsigned int options;
node_status_t status;
long int last_state_change;
while(recvline(fd, line, sizeof line)) {
int n = sscanf(line, "%d %d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", &code, &req, node, host, port, &cipher, &digest, &maclength, &compression, &options, (unsigned *)&status, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
if(n == 2)
break;
if(n != 18) {
fprintf(stderr, "Unable to parse node dump from tincd.\n");
return 1;
}
if(!strcmp(node, item)) {
found = true;
break;
}
}
if(!found) {
fprintf(stderr, "Unknown node %s.\n", item);
return 1;
}
while(recvline(fd, line, sizeof line)) {
if(sscanf(line, "%d %d %s", &code, &req, node) == 2)
break;
}
printf("Node: %s\n", item);
printf("Address: %s port %s\n", host, port);
char timestr[32] = "never";
if(last_state_change)
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&last_state_change));
if(status.reachable)
printf("Online since: %s\n", timestr);
else
printf("Last seen: %s\n", timestr);
printf("Status: ");
if(status.validkey)
printf(" validkey");
if(status.visited)
printf(" visited");
if(status.reachable)
printf(" reachable");
if(status.indirect)
printf(" indirect");
if(status.sptps)
printf(" sptps");
if(status.udp_confirmed)
printf(" udp_confirmed");
printf("\n");
printf("Options: ");
if(options & OPTION_INDIRECT)
printf(" indirect");
if(options & OPTION_TCPONLY)
printf(" tcponly");
if(options & OPTION_PMTU_DISCOVERY)
printf(" pmtu_discovery");
if(options & OPTION_CLAMP_MSS)
printf(" clamp_mss");
printf("\n");
printf("Protocol: %d.%d\n", PROT_MAJOR, OPTION_VERSION(options));
printf("Reachability: ");
if(!strcmp(host, "MYSELF"))
printf("can reach itself\n");
else if(!status.reachable)
printf("unreachable\n");
else if(strcmp(via, item))
printf("indirectly via %s\n", via);
else if(!status.validkey)
printf("unknown\n");
else if(minmtu > 0)
printf("directly with UDP\nPMTU: %d\n", pmtu);
else if(!strcmp(nexthop, item))
printf("directly with TCP\n");
else
printf("none, forwarded via %s\n", nexthop);
// List edges
printf("Edges: ");
sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_EDGES, item);
while(recvline(fd, line, sizeof line)) {
int n = sscanf(line, "%d %d %s %s", &code, &req, from, to);
if(n == 2)
break;
if(n != 4) {
fprintf(stderr, "Unable to parse edge dump from tincd.\n%s\n", line);
return 1;
}
if(!strcmp(from, item))
printf(" %s", to);
}
printf("\n");
// List subnets
printf("Subnets: ");
sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
while(recvline(fd, line, sizeof line)) {
int n = sscanf(line, "%d %d %s %s", &code, &req, subnet, from);
if(n == 2)
break;
if(n != 4) {
fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
return 1;
}
if(!strcmp(from, item))
printf(" %s", strip_weight(subnet));
}
printf("\n");
return 0;
}
static int info_subnet(int fd, const char *item) {
subnet_t subnet, find;
if(!str2net(&find, item)) {
fprintf(stderr, "Could not parse subnet or address '%s'.\n", item);
return 1;
}
bool address = !strchr(item, '/');
bool weight = strchr(item, '#');
bool found = false;
char line[4096];
char netstr[4096];
char owner[4096];
int code, req;
sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
while(recvline(fd, line, sizeof line)) {
int n = sscanf(line, "%d %d %s %s", &code, &req, netstr, owner);
if(n == 2)
break;
if(n != 4 || !str2net(&subnet, netstr)) {
fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
return 1;
}
if(find.type != subnet.type)
continue;
if(weight) {
if(find.weight != subnet.weight)
continue;
}
if(find.type == SUBNET_IPV4) {
if(address) {
if(maskcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, subnet.net.ipv4.prefixlength))
continue;
} else {
if(find.net.ipv4.prefixlength != subnet.net.ipv4.prefixlength)
continue;
if(memcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, sizeof subnet.net.ipv4))
continue;
}
} else if(find.type == SUBNET_IPV6) {
if(address) {
if(maskcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, subnet.net.ipv6.prefixlength))
continue;
} else {
if(find.net.ipv6.prefixlength != subnet.net.ipv6.prefixlength)
continue;
if(memcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, sizeof subnet.net.ipv6))
continue;
}
} if(find.type == SUBNET_MAC) {
if(memcmp(&find.net.mac.address, &subnet.net.mac.address, sizeof subnet.net.mac))
continue;
}
found = true;
printf("Subnet: %s\n", strip_weight(netstr));
printf("Owner: %s\n", owner);
}
if(!found) {
if(address)
fprintf(stderr, "Unknown address %s.\n", item);
else
fprintf(stderr, "Unknown subnet %s.\n", item);
return 1;
}
return 0;
}
int info(int fd, const char *item) {
if(check_id(item))
return info_node(fd, item);
if(strchr(item, '.') || strchr(item, ':'))
return info_subnet(fd, item);
fprintf(stderr, "Argument is not a node name, subnet or address.\n");
return 1;
}

27
src/info.h Normal file
View file

@ -0,0 +1,27 @@
/*
info.h -- header for info.c.
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_INFO_H__
#define __TINC_INFO_H__
extern int info(int fd, const char *item);
extern char *strip_weight(char *);
#endif

View file

@ -1,7 +1,7 @@
/*
ipv4.h -- missing IPv4 related definitions
Copyright (C) 2005 Ivo Timmermans
2006 Guus Sliepen <guus@tinc-vpn.org>
2006-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -41,6 +41,14 @@
#define ICMP_NET_UNKNOWN 6
#endif
#ifndef ICMP_TIME_EXCEEDED
#define ICMP_TIME_EXCEEDED 11
#endif
#ifndef ICMP_EXC_TTL
#define ICMP_EXC_TTL 0
#endif
#ifndef ICMP_NET_UNREACH
#define ICMP_NET_UNREACH 0
#endif
@ -64,7 +72,7 @@ struct ip {
#endif
uint8_t ip_tos;
uint16_t ip_len;
uint16_t ip_id;
uint16_t ip_id;
uint16_t ip_off;
#define IP_RF 0x8000
#define IP_DF 0x4000

View file

@ -1,7 +1,7 @@
/*
ipv6.h -- missing IPv6 related definitions
Copyright (C) 2005 Ivo Timmermans
2006 Guus Sliepen <guus@tinc-vpn.org>
2006-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -54,9 +54,9 @@ struct sockaddr_in6 {
#ifndef IN6_IS_ADDR_V4MAPPED
#define IN6_IS_ADDR_V4MAPPED(a) \
((((__const uint32_t *) (a))[0] == 0) \
&& (((__const uint32_t *) (a))[1] == 0) \
&& (((__const uint32_t *) (a))[2] == htonl (0xffff)))
((((__const uint32_t *) (a))[0] == 0) \
&& (((__const uint32_t *) (a))[1] == 0) \
&& (((__const uint32_t *) (a))[2] == htonl (0xffff)))
#endif
#ifndef HAVE_STRUCT_IP6_HDR
@ -95,8 +95,10 @@ struct icmp6_hdr {
#define ICMP6_DST_UNREACH_NOROUTE 0
#define ICMP6_DST_UNREACH 1
#define ICMP6_PACKET_TOO_BIG 2
#define ICMP6_TIME_EXCEEDED 3
#define ICMP6_DST_UNREACH_ADMIN 1
#define ICMP6_DST_UNREACH_ADDR 3
#define ICMP6_TIME_EXCEED_TRANSIT 0
#define ND_NEIGHBOR_SOLICIT 135
#define ND_NEIGHBOR_ADVERT 136
#define icmp6_data32 icmp6_dataun.icmp6_un_data32

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Linux ethertap and tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2009 Guus Sliepen <guus@tinc-vpn.org>
2001-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -41,6 +41,7 @@ int device_fd = -1;
static device_type_t device_type;
char *device = NULL;
char *iface = NULL;
static char *type = NULL;
static char ifrname[IFNAMSIZ];
static char *device_info;
@ -49,27 +50,35 @@ uint64_t device_in_bytes = 0;
uint64_t device_out_packets = 0;
uint64_t device_out_bytes = 0;
bool setup_device(void) {
static bool setup_device(void) {
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
#ifdef HAVE_LINUX_IF_TUN_H
if (netname != NULL)
if(netname)
iface = xstrdup(netname);
#else
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
#endif
device_fd = open(device, O_RDWR | O_NONBLOCK);
if(device_fd < 0) {
logger(LOG_ERR, "Could not open %s: %s", device, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
struct ifreq ifr = {{{0}}};
if(routing_mode == RMODE_ROUTER) {
get_config_string(lookup_config(config_tree, "DeviceType"), &type);
if(type && strcasecmp(type, "tun") && strcasecmp(type, "tap")) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown device type %s!", type);
return false;
}
if((type && !strcasecmp(type, "tun")) || (!type && routing_mode == RMODE_ROUTER)) {
ifr.ifr_flags = IFF_TUN;
device_type = DEVICE_TYPE_TUN;
device_info = "Linux tun/tap device (tun mode)";
@ -92,47 +101,44 @@ bool setup_device(void) {
if(!ioctl(device_fd, TUNSETIFF, &ifr)) {
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
if(iface) free(iface);
iface = xstrdup(ifrname);
} else if(!ioctl(device_fd, (('T' << 8) | 202), &ifr)) {
logger(LOG_WARNING, "Old ioctl() request was needed for %s", device);
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
if(iface) free(iface);
free(iface);
iface = xstrdup(ifrname);
}
logger(LOG_INFO, "%s is a %s", device, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
close(device_fd);
free(type);
free(device);
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
int inlen;
switch(device_type) {
case DEVICE_TYPE_TUN:
inlen = read(device_fd, packet->data + 10, MTU - 10);
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
memset(packet->data, 0, 12);
packet->len = inlen + 10;
break;
case DEVICE_TYPE_TAP:
inlen = read(device_fd, packet->data, MTU);
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
@ -146,28 +152,28 @@ bool read_packet(vpn_packet_t *packet) {
device_in_packets++;
device_in_bytes += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
static bool write_packet(vpn_packet_t *packet) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
switch(device_type) {
case DEVICE_TYPE_TUN:
packet->data[10] = packet->data[11] = 0;
if(write(device_fd, packet->data + 10, packet->len - 10) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
break;
case DEVICE_TYPE_TAP:
if(write(device_fd, packet->data, packet->len) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
@ -182,8 +188,16 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_in_bytes);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_in_bytes);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,7 +1,7 @@
/*
list.c -- functions to deal with double linked lists
Copyright (C) 2000-2005 Ivo Timmermans
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,9 +26,7 @@
/* (De)constructors */
list_t *list_alloc(list_action_t delete) {
list_t *list;
list = xmalloc_and_zero(sizeof(list_t));
list_t *list = xmalloc_and_zero(sizeof(list_t));
list->delete = delete;
return list;
@ -52,9 +50,7 @@ void list_free_node(list_t *list, list_node_t *node) {
/* Insertion and deletion */
list_node_t *list_insert_head(list_t *list, void *data) {
list_node_t *node;
node = list_alloc_node();
list_node_t *node = list_alloc_node();
node->data = data;
node->prev = NULL;
@ -72,9 +68,7 @@ list_node_t *list_insert_head(list_t *list, void *data) {
}
list_node_t *list_insert_tail(list_t *list, void *data) {
list_node_t *node;
node = list_alloc_node();
list_node_t *node = list_alloc_node();
node->data = data;
node->next = NULL;
@ -92,9 +86,7 @@ list_node_t *list_insert_tail(list_t *list, void *data) {
}
list_node_t *list_insert_after(list_t *list, list_node_t *after, void *data) {
list_node_t *node;
node = list_alloc_node();
list_node_t *node = list_alloc_node();
node->data = data;
node->next = after->next;
@ -158,6 +150,12 @@ void list_delete_tail(list_t *list) {
list_delete_node(list, list->tail);
}
void list_delete(list_t *list, const void *data) {
for(list_node_t *node = list->head, *next; next = node ? node->next : NULL, node; node = next)
if(node->data == data)
list_delete_node(list, node);
}
/* Head/tail lookup */
void *list_get_head(list_t *list) {
@ -177,12 +175,8 @@ void *list_get_tail(list_t *list) {
/* Fast list deletion */
void list_delete_list(list_t *list) {
list_node_t *node, *next;
for(node = list->head; node; node = next) {
next = node->next;
for(list_node_t *node = list->head, *next; next = node ? node->next : NULL, node; node = next)
list_free_node(list, node);
}
list_free(list);
}
@ -190,20 +184,12 @@ void list_delete_list(list_t *list) {
/* Traversing */
void list_foreach_node(list_t *list, list_action_node_t action) {
list_node_t *node, *next;
for(node = list->head; node; node = next) {
next = node->next;
for(list_node_t *node = list->head, *next; next = node ? node->next : NULL, node; node = next)
action(node);
}
}
void list_foreach(list_t *list, list_action_t action) {
list_node_t *node, *next;
for(node = list->head; node; node = next) {
next = node->next;
for(list_node_t *node = list->head, *next; next = node ? node->next : NULL, node; node = next)
if(node->data)
action(node->data);
}
}

View file

@ -1,7 +1,7 @@
/*
list.h -- header file for list.c
Copyright (C) 2000-2005 Ivo Timmermans
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -57,6 +57,8 @@ extern list_node_t *list_insert_tail(list_t *, void *);
extern list_node_t *list_insert_after(list_t *, list_node_t *, void *);
extern list_node_t *list_insert_before(list_t *, list_node_t *, void *);
extern void list_delete(list_t *, const void *);
extern void list_unlink_node(list_t *, list_node_t *);
extern void list_delete_node(list_t *, list_node_t *);
@ -77,4 +79,6 @@ extern void list_delete_list(list_t *);
extern void list_foreach(list_t *, list_action_t);
extern void list_foreach_node(list_t *, list_action_node_t);
#endif /* __TINC_LIST_H__ */
#define list_each(type, item, list) (type *item = (type *)1; item; item = NULL) for(list_node_t *node = (list)->head, *next; item = node ? node->data : NULL, next = node ? node->next : NULL, node; node = next)
#endif /* __TINC_LIST_H__ */

View file

@ -1,6 +1,6 @@
/*
logger.c -- logging code
Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2004-2012 Guus Sliepen <guus@tinc-vpn.org>
2004-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -21,7 +21,11 @@
#include "system.h"
#include "conf.h"
#include "meta.h"
#include "logger.h"
#include "connection.h"
#include "control_common.h"
#include "sptps.h"
debug_t debug_level = DEBUG_NOTHING;
static logmode_t logmode = LOGMODE_STDERR;
@ -32,11 +36,94 @@ static FILE *logfile = NULL;
static HANDLE loghandle = NULL;
#endif
static const char *logident = NULL;
bool logcontrol = false;
static void real_logger(int level, int priority, const char *message) {
char timestr[32] = "";
time_t now;
static bool suppress = false;
// Bail out early if there is nothing to do.
if(suppress)
return;
if(!logcontrol && (level > debug_level || logmode == LOGMODE_NULL))
return;
if(level <= debug_level) {
switch(logmode) {
case LOGMODE_STDERR:
fprintf(stderr, "%s\n", message);
fflush(stderr);
break;
case LOGMODE_FILE:
now = time(NULL);
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(logfile, "%s %s[%ld]: %s\n", timestr, logident, (long)logpid, message);
fflush(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
{
const char *messages[] = {message};
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
#else
#ifdef HAVE_SYSLOG_H
syslog(priority, "%s", message);
#endif
#endif
break;
case LOGMODE_NULL:
break;
}
}
if(logcontrol) {
suppress = true;
logcontrol = false;
for list_each(connection_t, c, connection_list) {
if(!c->status.log)
continue;
logcontrol = true;
if(level > (c->outcompression >= 0 ? c->outcompression : debug_level))
continue;
int len = strlen(message);
if(send_request(c, "%d %d %d", CONTROL, REQ_LOG, len))
send_meta(c, message, len);
}
suppress = false;
}
}
void logger(int level, int priority, const char *format, ...) {
va_list ap;
char message[1024] = "";
va_start(ap, format);
int len = vsnprintf(message, sizeof message, format, ap);
va_end(ap);
if(len > 0 && len < sizeof message && message[len - 1] == '\n')
message[len - 1] = 0;
real_logger(level, priority, message);
}
static void sptps_logger(sptps_t *s, int s_errno, const char *format, va_list ap) {
char message[1024] = "";
int len = vsnprintf(message, sizeof message, format, ap);
if(len > 0 && len < sizeof message && message[len - 1] == '\n')
message[len - 1] = 0;
real_logger(DEBUG_ALWAYS, LOG_ERR, message);
}
void openlogger(const char *ident, logmode_t mode) {
logident = ident;
logmode = mode;
switch(mode) {
case LOGMODE_STDERR:
logpid = getpid();
@ -66,6 +153,11 @@ void openlogger(const char *ident, logmode_t mode) {
case LOGMODE_NULL:
break;
}
if(logmode != LOGMODE_NULL)
sptps_log = sptps_logger;
else
sptps_log = sptps_log_quiet;
}
void reopenlogger() {
@ -75,62 +167,13 @@ void reopenlogger() {
fflush(logfile);
FILE *newfile = fopen(logfilename, "a");
if(!newfile) {
logger(LOG_ERR, "Unable to reopen log file %s: %s\n", logfilename, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
return;
}
fclose(logfile);
logfile = newfile;
}
void logger(int priority, const char *format, ...) {
va_list ap;
char timestr[32] = "";
time_t now;
va_start(ap, format);
switch(logmode) {
case LOGMODE_STDERR:
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
break;
case LOGMODE_FILE:
now = time(NULL);
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(logfile, "%s %s[%ld]: ", timestr, logident, (long)logpid);
vfprintf(logfile, format, ap);
fprintf(logfile, "\n");
fflush(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
{
char message[4096];
const char *messages[] = {message};
vsnprintf(message, sizeof message, format, ap);
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
#else
#ifdef HAVE_SYSLOG_H
#ifdef HAVE_VSYSLOG
vsyslog(priority, format, ap);
#else
{
char message[4096];
vsnprintf(message, sizeof message, format, ap);
syslog(priority, "%s", message);
}
#endif
break;
#endif
#endif
case LOGMODE_NULL:
break;
}
va_end(ap);
}
void closelogger(void) {
switch(logmode) {

View file

@ -1,17 +1,37 @@
/*
logger.h -- header file for logger.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_LOGGER_H__
#define __TINC_LOGGER_H__
typedef enum debug_t {
DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
DEBUG_ALWAYS = 0,
DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
DEBUG_ERROR = 2, /* Show error messages received from other hosts */
DEBUG_STATUS = 2, /* Show status messages received from other hosts */
DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
DEBUG_META = 4, /* Show contents of every request that is sent/received */
DEBUG_TRAFFIC = 5, /* Show network traffic information */
DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
DEBUG_SCARY_THINGS = 10 /* You have been warned */
DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
DEBUG_ERROR = 2, /* Show error messages received from other hosts */
DEBUG_STATUS = 2, /* Show status messages received from other hosts */
DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
DEBUG_META = 4, /* Show contents of every request that is sent/received */
DEBUG_TRAFFIC = 5, /* Show network traffic information */
DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
DEBUG_SCARY_THINGS = 10 /* You have been warned */
} debug_t;
typedef enum logmode_t {
@ -46,11 +66,10 @@ enum {
#endif
extern debug_t debug_level;
extern bool logcontrol;
extern void openlogger(const char *, logmode_t);
extern void reopenlogger(void);
extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void logger(int, int, const char *, ...) __attribute__ ((__format__(printf, 3, 4)));
extern void closelogger(void);
#define ifdebug(l) if(debug_level >= DEBUG_##l)
#endif /* __TINC_LOGGER_H__ */

View file

@ -1,6 +1,6 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
2006 Scott Lamb <slamb@slamb.org>
@ -21,7 +21,6 @@
#include "system.h"
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "logger.h"
@ -31,21 +30,38 @@
#include "utils.h"
#include "xalloc.h"
bool send_meta(connection_t *c, const char *buffer, int length) {
bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t length) {
connection_t *c = handle;
if(!c) {
logger(LOG_ERR, "send_meta() called with NULL pointer!");
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta_sptps() called with NULL pointer!");
abort();
}
ifdebug(META) logger(LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
buffer_add(&c->outbuf, buffer, length);
event_add(&c->outevent, NULL);
return true;
}
bool send_meta(connection_t *c, const char *buffer, int length) {
if(!c) {
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta() called with NULL pointer!");
abort();
}
logger(DEBUG_META, LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
c->name, c->hostname);
if(c->protocol_minor >= 2)
return sptps_send_record(&c->sptps, 0, buffer, length);
/* Add our data to buffer */
if(c->status.encryptout) {
size_t outlen = length;
if(!cipher_encrypt(&c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
logger(LOG_ERR, "Error while encrypting metadata to %s (%s)",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting metadata to %s (%s)",
c->name, c->hostname);
return false;
}
@ -60,15 +76,47 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
}
void broadcast_meta(connection_t *from, const char *buffer, int length) {
splay_node_t *node;
connection_t *c;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
for list_each(connection_t, c, connection_list)
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
bool receive_meta_sptps(void *handle, uint8_t type, const char *data, uint16_t length) {
connection_t *c = handle;
if(!c) {
logger(DEBUG_ALWAYS, LOG_ERR, "receive_meta_sptps() called with NULL pointer!");
abort();
}
if(type == SPTPS_HANDSHAKE) {
if(c->allow_request == ACK)
return send_ack(c);
else
return true;
}
if(!data)
return true;
/* Are we receiving a TCPpacket? */
if(c->tcplen) {
if(length != c->tcplen)
return false;
receive_tcppacket(c, data, length);
c->tcplen = 0;
return true;
}
/* Change newline to null byte, just like non-SPTPS requests */
if(data[length - 1] == '\n')
((char *)data)[length - 1] = 0;
/* Otherwise we are waiting for a request */
return receive_request(c, data);
}
bool receive_meta(connection_t *c) {
@ -88,7 +136,7 @@ bool receive_meta(connection_t *c) {
buffer_compact(&c->inbuf, MAXBUFSIZE);
if(sizeof inbuf <= c->inbuf.len) {
logger(LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
return false;
}
@ -96,17 +144,20 @@ bool receive_meta(connection_t *c) {
if(inlen <= 0) {
if(!inlen || !errno) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)",
c->name, c->hostname);
} else if(sockwouldblock(sockerrno))
return true;
else
logger(LOG_ERR, "Metadata socket read error for %s (%s): %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Metadata socket read error for %s (%s): %s",
c->name, c->hostname, sockstrerror(sockerrno));
return false;
}
do {
if(c->protocol_minor >= 2)
return sptps_receive_data(&c->sptps, bufp, inlen);
if(!c->status.decryptin) {
endp = memchr(bufp, '\n', inlen);
if(endp)
@ -120,10 +171,9 @@ bool receive_meta(connection_t *c) {
bufp = endp;
} else {
size_t outlen = inlen;
ifdebug(META) logger(LOG_DEBUG, "Received encrypted %d bytes", inlen);
if(!cipher_decrypt(&c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
logger(LOG_ERR, "Error while decrypting metadata from %s (%s)",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting metadata from %s (%s)",
c->name, c->hostname);
return false;
}
@ -137,7 +187,15 @@ bool receive_meta(connection_t *c) {
if(c->tcplen) {
char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
if(tcpbuffer) {
receive_tcppacket(c, tcpbuffer, c->tcplen);
if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
if(tcpbuffer[0] == 0 && tcpbuffer[1] == 0x5a) {
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
} else {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
return false;
}
} else
receive_tcppacket(c, tcpbuffer, c->tcplen);
c->tcplen = 0;
continue;
} else {

View file

@ -1,6 +1,6 @@
/*
meta.h -- header for meta.c
Copyright (C) 2000-2006 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -24,7 +24,9 @@
#include "connection.h"
extern bool send_meta(struct connection_t *, const char *, int);
extern bool send_meta_sptps(void *, uint8_t, const char *, size_t);
extern bool receive_meta_sptps(void *, uint8_t, const char *, uint16_t);
extern void broadcast_meta(struct connection_t *, const char *, int);
extern bool receive_meta(struct connection_t *);
#endif /* __TINC_META_H__ */
#endif /* __TINC_META_H__ */

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a MinGW environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2011 Guus Sliepen <guus@tinc-vpn.org>
2002-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -50,7 +50,7 @@ static DWORD WINAPI tapreader(void *bla) {
OVERLAPPED overlapped;
vpn_packet_t packet;
logger(LOG_DEBUG, "Tap reader running");
logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader running");
/* Read from tap device and send to parent */
@ -69,7 +69,7 @@ static DWORD WINAPI tapreader(void *bla) {
if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
continue;
} else {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return -1;
}
@ -83,7 +83,7 @@ static DWORD WINAPI tapreader(void *bla) {
}
}
bool setup_device(void) {
static bool setup_device(void) {
HKEY key, key2;
int i;
@ -105,7 +105,7 @@ bool setup_device(void) {
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
logger(LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
return false;
}
@ -118,7 +118,7 @@ bool setup_device(void) {
snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
len = sizeof adaptername;
@ -156,7 +156,7 @@ bool setup_device(void) {
RegCloseKey(key);
if(!found) {
logger(LOG_ERR, "No Windows tap device found!");
logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
return false;
}
@ -172,16 +172,16 @@ bool setup_device(void) {
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
}
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
return false;
}
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
return false;
}
@ -194,7 +194,7 @@ bool setup_device(void) {
thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
if(!thread) {
logger(LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
return false;
}
@ -205,31 +205,31 @@ bool setup_device(void) {
device_info = "Windows tap device";
logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
CloseHandle(device_handle);
free(device);
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
return false;
}
bool write_packet(vpn_packet_t *packet) {
static bool write_packet(vpn_packet_t *packet) {
long outlen;
OVERLAPPED overlapped = {0};
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
@ -238,8 +238,16 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

235
src/multicast_device.c Normal file
View file

@ -0,0 +1,235 @@
/*
device.c -- multicast socket
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "conf.h"
#include "device.h"
#include "net.h"
#include "logger.h"
#include "netutl.h"
#include "utils.h"
#include "route.h"
#include "xalloc.h"
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static struct addrinfo *ai = NULL;
static mac_t ignore_src = {{0}};
static bool setup_device(void) {
char *host = NULL;
char *port;
char *space;
int ttl = 1;
device_info = "multicast socket";
get_config_string(lookup_config(config_tree, "Interface"), &iface);
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Device variable required for %s", device_info);
goto error;
}
host = xstrdup(device);
space = strchr(host, ' ');
if(!space) {
logger(DEBUG_ALWAYS, LOG_ERR, "Port number required for %s", device_info);
goto error;
}
*space++ = 0;
port = space;
space = strchr(port, ' ');
if(space) {
*space++ = 0;
ttl = atoi(space);
}
ai = str2addrinfo(host, port, SOCK_DGRAM);
if(!ai)
goto error;
device_fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
if(device_fd < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Creating socket failed: %s", sockstrerror(sockerrno));
goto error;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
static const int one = 1;
setsockopt(device_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
if(bind(device_fd, ai->ai_addr, ai->ai_addrlen)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s %s: %s", host, port, sockstrerror(sockerrno));
goto error;
}
switch(ai->ai_family) {
#ifdef IP_ADD_MEMBERSHIP
case AF_INET: {
struct ip_mreq mreq;
struct sockaddr_in in;
memcpy(&in, ai->ai_addr, sizeof in);
mreq.imr_multiaddr.s_addr = in.sin_addr.s_addr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(device_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof mreq)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
goto error;
}
#ifdef IP_MULTICAST_LOOP
setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&one, sizeof one);
#endif
#ifdef IP_MULTICAST_TTL
setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof ttl);
#endif
} break;
#endif
#ifdef IPV6_JOIN_GROUP
case AF_INET6: {
struct ipv6_mreq mreq;
struct sockaddr_in6 in6;
memcpy(&in6, ai->ai_addr, sizeof in6);
memcpy(&mreq.ipv6mr_multiaddr, &in6.sin6_addr, sizeof mreq.ipv6mr_multiaddr);
mreq.ipv6mr_interface = in6.sin6_scope_id;
if(setsockopt(device_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mreq, sizeof mreq)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
goto error;
}
#ifdef IPV6_MULTICAST_LOOP
setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&one, sizeof one);
#endif
#ifdef IPV6_MULTICAST_HOPS
setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof ttl);
#endif
} break;
#endif
default:
logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %hx unsupported", ai->ai_family);
goto error;
}
freeaddrinfo(ai);
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
error:
if(device_fd >= 0)
closesocket(device_fd);
if(ai)
freeaddrinfo(ai);
free(host);
return false;
}
static void close_device(void) {
close(device_fd);
free(device);
free(iface);
if(ai)
freeaddrinfo(ai);
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
if((lenin = recv(device_fd, packet->data, MTU, 0)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
if(!memcmp(&ignore_src, packet->data + 6, sizeof ignore_src)) {
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
packet->len = 0;
return true;
}
packet->len = lenin;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
if(sendto(device_fd, packet->data, packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
device_total_out += packet->len;
memcpy(&ignore_src, packet->data + 6, sizeof ignore_src);
return true;
}
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t multicast_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};
#if 0
static bool not_supported(void) {
logger(DEBUG_ALWAYS, LOG_ERR, "Raw socket device not supported on this platform");
return false;
}
const devops_t multicast_devops = {
.setup = not_supported,
.close = NULL,
.read = NULL,
.write = NULL,
.dump_stats = NULL,
};
#endif

231
src/net.c
View file

@ -1,9 +1,9 @@
/*
net.c -- most of the network code
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2011 Loïc Grenié <loic.grenie@gmail.com>
2011 Loïc Grenié <loic.grenie@gmail.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
@ -23,7 +23,6 @@
#include "system.h"
#include "utils.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "device.h"
@ -40,40 +39,28 @@
int contradicting_add_edge = 0;
int contradicting_del_edge = 0;
static int sleeptime = 10;
time_t last_config_check = 0;
/* Purge edges and subnets of unreachable nodes. Use carefully. */
void purge(void) {
splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
node_t *n;
edge_t *e;
subnet_t *s;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "Purging unreachable nodes");
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Purging unreachable nodes");
/* Remove all edges and subnets owned by unreachable nodes. */
for(nnode = node_tree->head; nnode; nnode = nnext) {
nnext = nnode->next;
n = nnode->data;
for splay_each(node_t, n, node_tree) {
if(!n->status.reachable) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Purging node %s (%s)", n->name,
n->hostname);
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Purging node %s (%s)", n->name, n->hostname);
for(snode = n->subnet_tree->head; snode; snode = snext) {
snext = snode->next;
s = snode->data;
send_del_subnet(broadcast, s);
for splay_each(subnet_t, s, n->subnet_tree) {
send_del_subnet(everyone, s);
if(!strictsubnets)
subnet_del(n, s);
}
for(enode = n->edge_tree->head; enode; enode = enext) {
enext = enode->next;
e = enode->data;
for splay_each(edge_t, e, n->edge_tree) {
if(!tunnelserver)
send_del_edge(broadcast, e);
send_del_edge(everyone, e);
edge_del(e);
}
}
@ -81,20 +68,13 @@ void purge(void) {
/* Check if anyone else claims to have an edge to an unreachable node. If not, delete node. */
for(nnode = node_tree->head; nnode; nnode = nnext) {
nnext = nnode->next;
n = nnode->data;
for splay_each(node_t, n, node_tree) {
if(!n->status.reachable) {
for(enode = edge_weight_tree->head; enode; enode = enext) {
enext = enode->next;
e = enode->data;
for splay_each(edge_t, e, edge_weight_tree)
if(e->to == n)
break;
}
return;
if(!enode && (!strictsubnets || !n->subnet_tree->head))
if(!strictsubnets || !n->subnet_tree->head)
/* in strictsubnets mode do not delete nodes with subnets */
node_del(n);
}
@ -103,25 +83,25 @@ void purge(void) {
/*
Terminate a connection:
- Close the socket
- Remove associated edge and tell other connections about it if report = true
- Mark it as inactive
- Remove the edge representing this connection
- Kill it with fire
- Check if we need to retry making an outgoing connection
- Deactivate the host
*/
void terminate_connection(connection_t *c, bool report) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Closing connection with %s (%s)",
c->name, c->hostname);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
c->status.active = false;
if(c->node)
if(c->node && c->node->connection == c)
c->node->connection = NULL;
if(c->edge) {
if(report && !tunnelserver)
send_del_edge(broadcast, c->edge);
send_del_edge(everyone, c->edge);
edge_del(c->edge);
c->edge = NULL;
/* Run MST and SSSP algorithms */
@ -134,18 +114,19 @@ void terminate_connection(connection_t *c, bool report) {
e = lookup_edge(c->node, myself);
if(e) {
if(!tunnelserver)
send_del_edge(broadcast, e);
send_del_edge(everyone, e);
edge_del(e);
}
}
}
outgoing_t *outgoing = c->outgoing;
connection_del(c);
/* Check if this was our outgoing connection */
if(c->outgoing)
retry_outgoing(c->outgoing);
connection_del(c);
if(outgoing)
do_outgoing_connection(outgoing);
}
/*
@ -157,42 +138,34 @@ void terminate_connection(connection_t *c, bool report) {
and close the connection.
*/
static void timeout_handler(int fd, short events, void *event) {
splay_node_t *node, *next;
connection_t *c;
time_t now = time(NULL);
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = node->data;
for list_each(connection_t, c, connection_list) {
if(c->status.control)
continue;
if(c->last_ping_time + pingtimeout <= now) {
if(c->status.active) {
if(c->status.pinged) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
c->name, c->hostname, now - c->last_ping_time);
terminate_connection(c, true);
continue;
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now) {
send_ping(c);
}
} else {
if(c->status.connecting) {
ifdebug(CONNECTIONS)
logger(LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
c->status.connecting = false;
closesocket(c->socket);
do_outgoing_connection(c);
continue;
} else {
ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
terminate_connection(c, false);
continue;
}
} else {
if(c->status.connecting)
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
else
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
}
terminate_connection(c, c->status.active);
}
}
if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
usleep(sleeptime * 1000000LL);
sleeptime *= 2;
if(sleeptime < 0)
@ -222,11 +195,8 @@ void handle_meta_connection_data(int fd, short events, void *data) {
if(!result)
finish_connecting(c);
else {
ifdebug(CONNECTIONS) logger(LOG_DEBUG,
"Error while connecting to %s (%s): %s",
c->name, c->hostname, sockstrerror(result));
closesocket(c->socket);
do_outgoing_connection(c);
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(result));
terminate_connection(c, false);
return;
}
}
@ -238,27 +208,23 @@ void handle_meta_connection_data(int fd, short events, void *data) {
}
static void sigterm_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
event_loopexit(NULL);
}
static void sighup_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
reopenlogger();
reload_configuration();
}
static void sigalrm_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
retry();
}
int reload_configuration(void) {
connection_t *c;
splay_node_t *node, *next;
char *fname;
struct stat s;
static time_t last_config_check = 0;
/* Reread our own configuration file */
@ -266,85 +232,112 @@ int reload_configuration(void) {
init_configuration(&config_tree);
if(!read_server_config()) {
logger(LOG_ERR, "Unable to reread configuration file, exitting.");
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reread configuration file, exitting.");
event_loopexit(NULL);
return EINVAL;
}
/* Close connections to hosts that have a changed or deleted host config file */
for(node = connection_tree->head; node; node = next) {
c = node->data;
next = node->next;
if(c->outgoing) {
free(c->outgoing->name);
if(c->outgoing->ai)
freeaddrinfo(c->outgoing->ai);
free(c->outgoing);
c->outgoing = NULL;
}
xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
if(stat(fname, &s) || s.st_mtime > last_config_check)
terminate_connection(c, c->status.active);
free(fname);
}
read_config_options(config_tree, NULL);
last_config_check = time(NULL);
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, myself->name);
read_config_file(config_tree, fname);
free(fname);
/* Parse some options that are allowed to be changed while tinc is running */
setup_myself_reloadable();
/* If StrictSubnet is set, expire deleted Subnets and read new ones in */
if(strictsubnets) {
subnet_t *subnet;
for(node = subnet_tree->head; node; node = node->next) {
subnet = node->data;
for splay_each(subnet_t, subnet, subnet_tree)
subnet->expires = 1;
}
load_all_subnets();
for(node = subnet_tree->head; node; node = next) {
next = node->next;
subnet = node->data;
for splay_each(subnet_t, subnet, subnet_tree) {
if(subnet->expires == 1) {
send_del_subnet(broadcast, subnet);
send_del_subnet(everyone, subnet);
if(subnet->owner->status.reachable)
subnet_update(subnet->owner, subnet, false);
subnet_del(subnet->owner, subnet);
} else if(subnet->expires == -1) {
subnet->expires = 0;
} else {
send_add_subnet(broadcast, subnet);
send_add_subnet(everyone, subnet);
if(subnet->owner->status.reachable)
subnet_update(subnet->owner, subnet, true);
}
}
} else { /* Only read our own subnets back in */
for splay_each(subnet_t, subnet, myself->subnet_tree)
if(!subnet->expires)
subnet->expires = 1;
config_t *cfg = lookup_config(config_tree, "Subnet");
while(cfg) {
subnet_t *subnet, *s2;
if(!get_config_subnet(cfg, &subnet))
continue;
if((s2 = lookup_subnet(myself, subnet))) {
if(s2->expires == 1)
s2->expires = 0;
free_subnet(subnet);
} else {
subnet_add(myself, subnet);
send_add_subnet(everyone, subnet);
subnet_update(myself, subnet, true);
}
cfg = lookup_config_next(config_tree, cfg);
}
for splay_each(subnet_t, subnet, myself->subnet_tree) {
if(subnet->expires == 1) {
send_del_subnet(everyone, subnet);
subnet_update(myself, subnet, false);
subnet_del(myself, subnet);
}
}
}
/* Try to make outgoing connections */
try_outgoing_connections();
/* Close connections to hosts that have a changed or deleted host config file */
for list_each(connection_t, c, connection_list) {
if(c->status.control)
continue;
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
struct stat s;
if(stat(fname, &s) || s.st_mtime > last_config_check) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Host config file of %s has been changed", c->name);
terminate_connection(c, c->status.active);
}
free(fname);
}
last_config_check = time(NULL);
return 0;
}
void retry(void) {
connection_t *c;
splay_node_t *node;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
for list_each(connection_t, c, connection_list) {
if(c->outgoing && !c->node) {
if(timeout_initialized(&c->outgoing->ev))
event_del(&c->outgoing->ev);
if(c->status.connecting)
close(c->socket);
c->outgoing->timeout = 0;
do_outgoing_connection(c);
terminate_connection(c, c->status.active);
}
}
}
@ -375,7 +368,7 @@ int main_loop(void) {
#endif
if(event_loop(0) < 0) {
logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
return 1;
}

View file

@ -1,7 +1,7 @@
/*
net.h -- header for net.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,15 +26,18 @@
#include "digest.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#else
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#endif
#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + padding + HMAC + compressor overhead */
#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)
#define MAXSOCKETS 8 /* Probably overkill... */
/* MAXBUFSIZE is the maximum size of a request: enough for a MAXSIZEd packet or a 8192 bits RSA key */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128)
#define MAXSOCKETS 8 /* Probably overkill... */
typedef struct mac_t {
uint8_t x[6];
@ -77,12 +80,24 @@ typedef union sockaddr_t {
#endif
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
uint8_t data[MAXSIZE];
} vpn_packet_t;
/* Packet types when using SPTPS */
#define PKT_COMPRESSED 1
#define PKT_MAC 2
#define PKT_PROBE 4
typedef enum packet_type_t {
PACKET_NORMAL,
PACKET_COMPRESSED,
PACKET_PROBE
} packet_type_t;
typedef struct listen_socket_t {
struct event ev_tcp;
struct event ev_udp;
@ -97,6 +112,7 @@ typedef struct listen_socket_t {
typedef struct outgoing_t {
char *name;
int timeout;
splay_tree_t *config_tree;
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
@ -109,6 +125,7 @@ extern int maxoutbufsize;
extern int seconds_till_retry;
extern int addressfamily;
extern unsigned replaywin;
extern bool localdiscovery;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
@ -119,6 +136,24 @@ extern bool do_prune;
extern char *myport;
extern int contradicting_add_edge;
extern int contradicting_del_edge;
extern time_t last_config_check;
extern char *proxyhost;
extern char *proxyport;
extern char *proxyuser;
extern char *proxypass;
typedef enum proxytype_t {
PROXY_NONE = 0,
PROXY_SOCKS4,
PROXY_SOCKS4A,
PROXY_SOCKS5,
PROXY_HTTP,
PROXY_EXEC,
} proxytype_t;
extern proxytype_t proxytype;
extern char *scriptinterpreter;
extern char *scriptextension;
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
#include "connection.h"
@ -127,20 +162,23 @@ extern int contradicting_del_edge;
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int, short, void *);
extern void finish_connecting(struct connection_t *);
extern bool do_outgoing_connection(struct connection_t *);
extern bool do_outgoing_connection(struct outgoing_t *);
extern void handle_new_meta_connection(int, short, void *);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
extern bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len);
extern bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len);
extern void send_packet(struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, const char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
extern char *get_name(void);
extern bool setup_myself_reloadable(void);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern int main_loop(void);
extern void terminate_connection(struct connection_t *, bool);
extern void flush_queue(struct node_t *);
extern bool node_read_ecdsa_public_key(struct node_t *);
extern bool read_ecdsa_public_key(struct connection_t *);
extern bool read_rsa_public_key(struct connection_t *);
@ -159,4 +197,4 @@ extern void load_all_subnets(void);
extern CRITICAL_SECTION mutex;
#endif
#endif /* __TINC_NET_H__ */
#endif /* __TINC_NET_H__ */

View file

@ -1,7 +1,7 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2010 Timothy Redaelli <timothy@redaelli.eu>
2010 Brandon Black <blblack@gmail.com>
@ -36,7 +36,6 @@
#include LZO1X_H
#endif
#include "splay_tree.h"
#include "cipher.h"
#include "conf.h"
#include "connection.h"
@ -62,24 +61,30 @@ static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999
static void send_udppacket(node_t *, vpn_packet_t *);
unsigned replaywin = 16;
bool localdiscovery = false;
#define MAX_SEQNO 1073741824
// mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
// mtuprobes == 31: sleep pinginterval seconds
// mtuprobes == 32: send 1 burst, sleep pingtimeout second
// mtuprobes == 33: no response from other side, restart PMTU discovery process
/* mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
mtuprobes == 31: sleep pinginterval seconds
mtuprobes == 32: send 1 burst, sleep pingtimeout second
mtuprobes == 33: no response from other side, restart PMTU discovery process
Probes are sent in batches of three, with random sizes between the lower and
upper boundaries for the MTU thus far discovered.
In case local discovery is enabled, a fourth packet is added to each batch,
which will be broadcast to the local network.
*/
static void send_mtu_probe_handler(int fd, short events, void *data) {
node_t *n = data;
vpn_packet_t packet;
int len, i;
int timeout = 1;
n->mtuprobes++;
if(!n->status.reachable || !n->status.validkey) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
n->mtuprobes = 0;
return;
}
@ -91,14 +96,15 @@ static void send_mtu_probe_handler(int fd, short events, void *data) {
goto end;
}
ifdebug(TRAFFIC) logger(LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
n->status.udp_confirmed = false;
n->mtuprobes = 1;
n->minmtu = 0;
n->maxmtu = MTU;
}
if(n->mtuprobes >= 10 && n->mtuprobes < 32 && !n->minmtu) {
ifdebug(TRAFFIC) logger(LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
n->mtuprobes = 31;
}
@ -108,7 +114,7 @@ static void send_mtu_probe_handler(int fd, short events, void *data) {
else
n->maxmtu = n->minmtu;
n->mtu = n->minmtu;
ifdebug(TRAFFIC) logger(LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
logger(DEBUG_TRAFFIC, LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
n->mtuprobes = 31;
}
@ -119,7 +125,9 @@ static void send_mtu_probe_handler(int fd, short events, void *data) {
timeout = pingtimeout;
}
for(i = 0; i < 3; i++) {
for(int i = 0; i < 3 + localdiscovery; i++) {
int len;
if(n->maxmtu <= n->minmtu)
len = n->maxmtu;
else
@ -127,13 +135,17 @@ static void send_mtu_probe_handler(int fd, short events, void *data) {
if(len < 64)
len = 64;
vpn_packet_t packet;
memset(packet.data, 0, 14);
randomize(packet.data + 14, len - 14);
packet.len = len;
packet.priority = 0;
if(i >= 3 && n->mtuprobes <= 10)
packet.priority = -1;
else
packet.priority = 0;
ifdebug(TRAFFIC) logger(LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
send_udppacket(n, &packet);
}
@ -149,12 +161,14 @@ void send_mtu_probe(node_t *n) {
}
static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
if(!packet->data[0]) {
packet->data[0] = 1;
send_udppacket(n, packet);
} else {
n->status.udp_confirmed = true;
if(n->mtuprobes > 30) {
if(n->minmtu)
n->mtuprobes = 30;
@ -198,7 +212,7 @@ static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t l
return -1;
#endif
}
return -1;
}
@ -231,7 +245,7 @@ static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t
/* VPN packet I/O */
static void receive_packet(node_t *n, vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
packet->len, n->name, n->hostname);
n->in_packets++;
@ -241,6 +255,9 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
}
static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
if(n->status.sptps)
return sptps_verify_datagram(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
return false;
@ -254,8 +271,13 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
vpn_packet_t *outpkt = pkt[0];
size_t outlen;
if(n->status.sptps) {
sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
return;
}
if(!cipher_active(&n->incipher)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
n->name, n->hostname);
return;
}
@ -263,7 +285,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Check packet length */
if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got too short packet from %s (%s)",
n->name, n->hostname);
return;
}
@ -272,8 +294,8 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
if(digest_active(&n->indigest)) {
inpkt->len -= n->indigest.maclength;
if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
return;
}
}
@ -284,10 +306,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
outlen = MAXSIZE;
if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
return;
}
outpkt->len = outlen;
inpkt = outpkt;
}
@ -301,17 +323,17 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
if(inpkt->seqno != n->received_seqno + 1) {
if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
if(n->farfuture++ < replaywin >> 2) {
logger(LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
logger(DEBUG_ALWAYS, LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
n->name, n->hostname, inpkt->seqno - n->received_seqno - 1, n->farfuture);
return;
}
logger(LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
logger(DEBUG_ALWAYS, LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
memset(n->late, 0, replaywin);
} else if (inpkt->seqno <= n->received_seqno) {
if((n->received_seqno >= replaywin * 8 && inpkt->seqno <= n->received_seqno - replaywin * 8) || !(n->late[(inpkt->seqno / 8) % replaywin] & (1 << inpkt->seqno % 8))) {
logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
n->name, n->hostname, inpkt->seqno, n->received_seqno);
logger(DEBUG_ALWAYS, LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
n->name, n->hostname, inpkt->seqno, n->received_seqno);
return;
}
} else {
@ -326,7 +348,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
if(inpkt->seqno > n->received_seqno)
n->received_seqno = inpkt->seqno;
if(n->received_seqno > MAX_SEQNO)
regenerate_key();
@ -338,8 +360,8 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while uncompressing packet from %s (%s)",
n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)",
n->name, n->hostname);
return;
}
@ -369,6 +391,53 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
receive_packet(c->node, &outpkt);
}
static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
if(!n->status.validkey) {
logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
if(!n->status.waitingforkey)
send_req_key(n);
else if(n->last_req_key + 10 < time(NULL)) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
sptps_stop(&n->sptps);
n->status.waitingforkey = false;
send_req_key(n);
}
return;
}
uint8_t type = 0;
int offset = 0;
if(!(origpkt->data[12] | origpkt->data[13])) {
sptps_send_record(&n->sptps, PKT_PROBE, (char *)origpkt->data, origpkt->len);
return;
}
if(routing_mode == RMODE_ROUTER)
offset = 14;
else
type = PKT_MAC;
if(origpkt->len < offset)
return;
vpn_packet_t outpkt;
if(n->outcompression) {
int len = compress_packet(outpkt.data + offset, origpkt->data + offset, origpkt->len - offset, n->outcompression);
if(len < 0) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)", n->name, n->hostname);
} else if(len < origpkt->len - offset) {
outpkt.len = len + offset;
origpkt = &outpkt;
type |= PKT_COMPRESSED;
}
}
sptps_send_record(&n->sptps, type, (char *)origpkt->data + offset, origpkt->len - offset);
return;
}
static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
@ -379,21 +448,23 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
size_t outlen;
#if defined(SOL_IP) && defined(IP_TOS)
static int priority = 0;
int origpriority = origpkt->priority;
#endif
int sock;
int origpriority = origpkt->priority;
if(!n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
return;
}
if(n->status.sptps)
return send_sptps_packet(n, origpkt);
/* Make sure we have a valid key */
if(!n->status.validkey) {
time_t now = time(NULL);
ifdebug(TRAFFIC) logger(LOG_INFO,
logger(DEBUG_TRAFFIC, LOG_INFO,
"No valid key known yet for %s (%s), forwarding via TCP",
n->name, n->hostname);
@ -408,7 +479,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
}
if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
ifdebug(TRAFFIC) logger(LOG_INFO,
logger(DEBUG_TRAFFIC, LOG_INFO,
"Packet for %s (%s) larger than minimum MTU, forwarding via %s",
n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
@ -426,7 +497,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
outpkt = pkt[nextpkt++];
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while compressing packet to %s (%s)",
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)",
n->name, n->hostname);
return;
}
@ -446,7 +517,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
outlen = MAXSIZE;
if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
goto end;
}
@ -461,41 +532,221 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
inpkt->len += digest_length(&n->outdigest);
}
/* Send the packet */
sockaddr_t *sa;
int sock;
/* Overloaded use of priority field: -1 means local broadcast */
if(origpriority == -1 && n->prevedge) {
sockaddr_t broadcast;
broadcast.in.sin_family = AF_INET;
broadcast.in.sin_addr.s_addr = -1;
broadcast.in.sin_port = n->prevedge->address.in.sin_port;
sa = &broadcast;
sock = 0;
} else {
if(origpriority == -1)
origpriority = 0;
if(n->status.udp_confirmed) {
/* Address of this node is confirmed, so use it. */
sa = &n->address;
sock = n->sock;
} else {
/* Otherwise, go through the list of known addresses of
this node. The first address we try is always the
one in n->address; that could be set to the node's
reflexive UDP address discovered during key
exchange. The other known addresses are those found
in edges to this node. */
static unsigned int i;
int j = 0;
edge_t *candidate = NULL;
if(i) {
for splay_each(edge_t, e, edge_weight_tree) {
if(e->to != n)
continue;
j++;
if(!candidate || j == i)
candidate = e;
}
}
if(!candidate) {
sa = &n->address;
sock = n->sock;
} else {
sa = &candidate->address;
sock = rand() % listen_sockets;
}
if(i++)
if(i > j)
i = 0;
}
}
/* 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(sa->sa.sa_family != listen_socket[sock].sa.sa.sa_family)
for(sock = 0; sock < listen_sockets; sock++)
if(sa->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. */
sock = 0;
/* Send the packet */
if(!n->status.udp_confirmed)
n->sock = sock;
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority
&& listen_socket[sock].sa.sa.sa_family == AF_INET) {
&& listen_socket[n->sock].sa.sa.sa_family == AF_INET) {
priority = origpriority;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof priority)) /* SO_PRIORITY doesn't seem to work */
logger(LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
if(setsockopt(listen_socket[n->sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
logger(DEBUG_ALWAYS, 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 && !sockwouldblock(sockerrno)) {
socklen_t sl = SALEN(n->address.sa);
if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &sa->sa, sl) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
if(n->mtu >= origlen)
n->mtu = origlen - 1;
} else
logger(LOG_ERR, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
}
end:
origpkt->len = origlen;
}
bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
node_t *to = handle;
/* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */
if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && len > to->minmtu)) {
char buf[len * 4 / 3 + 5];
b64encode(data, buf, len);
/* If no valid key is known yet, send the packets using ANS_KEY requests,
to ensure we get to learn the reflexive UDP address. */
if(!to->status.validkey)
return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, myself->incompression);
else
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
}
/* Otherwise, send the packet via UDP */
struct sockaddr *sa;
socklen_t sl;
int sock;
sa = &(to->address.sa);
sl = SALEN(to->address.sa);
sock = to->sock;
if(sendto(listen_socket[sock].udp, data, len, 0, sa, sl) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(to->maxmtu >= len)
to->maxmtu = len - 1;
if(to->mtu >= len)
to->mtu = len - 1;
} else {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
return false;
}
}
return true;
}
bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) {
node_t *from = handle;
if(type == SPTPS_HANDSHAKE) {
if(!from->status.validkey) {
from->status.validkey = true;
from->status.waitingforkey = false;
logger(DEBUG_META, LOG_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
}
return true;
}
if(len > MTU) {
logger(DEBUG_ALWAYS, LOG_ERR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
return false;
}
vpn_packet_t inpkt;
if(type == PKT_PROBE) {
inpkt.len = len;
memcpy(inpkt.data, data, len);
mtu_probe_h(from, &inpkt, len);
return true;
}
if(type & ~(PKT_COMPRESSED | PKT_MAC)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unexpected SPTPS record type %d len %d from %s (%s)", type, len, from->name, from->hostname);
return false;
}
/* Check if we have the headers we need */
if(routing_mode != RMODE_ROUTER && !(type & PKT_MAC)) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Received packet from %s (%s) without MAC header (maybe Mode is not set correctly)", from->name, from->hostname);
return false;
} else if(routing_mode == RMODE_ROUTER && (type & PKT_MAC)) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Received packet from %s (%s) with MAC header (maybe Mode is not set correctly)", from->name, from->hostname);
}
int offset = (type & PKT_MAC) ? 0 : 14;
if(type & PKT_COMPRESSED) {
len = uncompress_packet(inpkt.data + offset, (const uint8_t *)data, len, from->incompression);
if(len < 0) {
return false;
} else {
inpkt.len = len + offset;
}
if(inpkt.len > MAXSIZE)
abort();
} else {
memcpy(inpkt.data + offset, data, len);
inpkt.len = len + offset;
}
/* Generate the Ethernet packet type if necessary */
if(offset) {
switch(inpkt.data[14] >> 4) {
case 4:
inpkt.data[12] = 0x08;
inpkt.data[13] = 0x00;
break;
case 6:
inpkt.data[12] = 0x86;
inpkt.data[13] = 0xDD;
break;
default:
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown IP version %d while reading packet from %s (%s)",
inpkt.data[14] >> 4, from->name, from->hostname);
return false;
}
}
receive_packet(from, &inpkt);
return true;
}
/*
send a packet to the given vpn ip.
*/
@ -507,15 +758,15 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
memcpy(packet->data, mymac.x, ETH_ALEN);
n->out_packets++;
n->out_bytes += packet->len;
write_packet(packet);
devops.write(packet);
return;
}
ifdebug(TRAFFIC) logger(LOG_ERR, "Sending packet of %d bytes to %s (%s)",
logger(DEBUG_TRAFFIC, LOG_ERR, "Sending packet of %d bytes to %s (%s)",
packet->len, n->name, n->hostname);
if(!n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Node %s (%s) is not reachable",
logger(DEBUG_TRAFFIC, LOG_INFO, "Node %s (%s) is not reachable",
n->name, n->hostname);
return;
}
@ -523,10 +774,15 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
n->out_packets++;
n->out_bytes += packet->len;
if(n->status.sptps) {
send_sptps_packet(n, packet);
return;
}
via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
if(via != n)
ifdebug(TRAFFIC) logger(LOG_INFO, "Sending packet to %s via %s (%s)",
logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet to %s via %s (%s)",
n->name, via->name, n->via->hostname);
if(packet->priority == -1 || ((myself->options | via->options) & OPTION_TCPONLY)) {
@ -539,46 +795,53 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
splay_node_t *node;
connection_t *c;
ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
packet->len, from->name, from->hostname);
if(from != myself) {
// Always give ourself a copy of the packet.
if(from != myself)
send_packet(myself, packet);
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver)
return;
}
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver || broadcast_mode == BMODE_NONE)
return;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
packet->len, from->name, from->hostname);
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
switch(broadcast_mode) {
// In MST mode, broadcast packets travel via the Minimum Spanning Tree.
// This guarantees all nodes receive the broadcast packet, and
// usually distributes the sending of broadcast packets over all nodes.
case BMODE_MST:
for list_each(connection_t, c, connection_list)
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
break;
// In direct mode, we send copies to each node we know of.
// However, this only reaches nodes that can be reached in a single hop.
// We don't have enough information to forward broadcast packets in this case.
case BMODE_DIRECT:
if(from != myself)
break;
for splay_each(node_t, n, node_tree)
if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n))
send_packet(n, packet);
break;
default:
break;
}
}
static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
splay_node_t *node;
edge_t *e;
node_t *n = NULL;
bool hard = false;
static time_t last_hard_try = 0;
time_t now = time(NULL);
if(last_hard_try == now)
return NULL;
else
last_hard_try = now;
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
if(e->to == myself)
for splay_each(edge_t, e, edge_weight_tree) {
if(!e->to->status.reachable || e->to == myself)
continue;
if(sockaddrcmp_noport(from, &e->address)) {
@ -597,13 +860,14 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
if(hard)
last_hard_try = now;
last_hard_try = now;
return n;
}
void handle_incoming_vpn_data(int sock, short events, void *data) {
vpn_packet_t pkt;
char *hostname;
sockaddr_t from;
sockaddr_t from = {{0}};
socklen_t fromlen = sizeof from;
node_t *n;
int len;
@ -612,13 +876,13 @@ void handle_incoming_vpn_data(int sock, short events, void *data) {
if(len <= 0 || len > MAXSIZE) {
if(!sockwouldblock(sockerrno))
logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
return;
}
pkt.len = len;
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
@ -626,9 +890,9 @@ void handle_incoming_vpn_data(int sock, short events, void *data) {
n = try_harder(&from, &pkt);
if(n)
update_node_udp(n, &from);
else ifdebug(PROTOCOL) {
else if(debug_level >= DEBUG_PROTOCOL) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
free(hostname);
return;
}
@ -636,6 +900,8 @@ void handle_incoming_vpn_data(int sock, short events, void *data) {
return;
}
n->sock = (intptr_t)data;
receive_udppacket(n, &pkt);
}
@ -644,7 +910,7 @@ void handle_device_data(int sock, short events, void *data) {
packet.priority = 0;
if(read_packet(&packet)) {
if(devops.read(&packet)) {
myself->in_packets++;
myself->in_bytes += packet.len;
route(myself, &packet);

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2009 Florian Forster <octo@verplant.org>
@ -22,9 +22,9 @@
#include "system.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "list.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
@ -33,8 +33,6 @@
#include "utils.h"
#include "xalloc.h"
#include <assert.h>
/* Needed on Mac OS/X */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
@ -59,13 +57,13 @@ static void configure_tcp(connection_t *c) {
int flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, "fcntl for %s: %s", c->hostname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "fcntl for %s: %s", c->hostname, strerror(errno));
}
#elif defined(WIN32)
unsigned long arg = 1;
if(ioctlsocket(c->socket, FIONBIO, &arg) != 0) {
logger(LOG_ERR, "ioctlsocket for %s: %d", c->hostname, sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "ioctlsocket for %s: %s", c->hostname, sockstrerror(sockerrno));
}
#endif
@ -98,74 +96,17 @@ static bool bind_to_interface(int sd) {
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
if(status) {
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(errno));
return false;
}
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
logger(LOG_WARNING, "%s not supported on this platform", "BindToInterface");
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "BindToInterface");
#endif
return true;
}
static bool bind_to_address(connection_t *c) {
char *node;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
int status;
assert(c != NULL);
assert(c->socket >= 0);
node = NULL;
if(!get_config_string(lookup_config(config_tree, "BindToAddress"),
&node))
return true;
assert(node != NULL);
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = c->address.sa.sa_family;
/* We're called from `do_outgoing_connection' only. */
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
ai_list = NULL;
status = getaddrinfo(node, /* service = */ NULL,
&ai_hints, &ai_list);
if(status) {
logger(LOG_WARNING, "Error looking up %s port %s: %s",
node, "any", gai_strerror(status));
free(node);
return false;
}
assert(ai_list != NULL);
status = -1;
for(ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
status = bind(c->socket,
ai_list->ai_addr, ai_list->ai_addrlen);
if(!status)
break;
}
if(status) {
logger(LOG_ERR, "Can't bind to %s/tcp: %s", node, sockstrerror(sockerrno));
} else ifdebug(CONNECTIONS) {
logger(LOG_DEBUG, "Successfully bound outgoing "
"TCP socket to %s", node);
}
free(node);
freeaddrinfo(ai_list);
return status ? false : true;
}
int setup_listen_socket(const sockaddr_t *sa) {
int nfd;
char *addrstr;
@ -175,10 +116,14 @@ int setup_listen_socket(const sockaddr_t *sa) {
nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(nfd < 0) {
ifdebug(STATUS) logger(LOG_ERR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
logger(DEBUG_STATUS, LOG_ERR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
return -1;
}
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
/* Optimize TCP settings */
option = 1;
@ -199,26 +144,26 @@ int setup_listen_socket(const sockaddr_t *sa) {
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(sockerrno));
return -1;
}
#else
logger(LOG_WARNING, "%s not supported on this platform", "BindToInterface");
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "BindToInterface");
#endif
}
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
logger(LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
free(addrstr);
return -1;
}
if(listen(nfd, 3)) {
closesocket(nfd);
logger(LOG_ERR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
return -1;
}
@ -233,17 +178,21 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
if(nfd < 0) {
logger(LOG_ERR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
return -1;
}
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
#ifdef O_NONBLOCK
{
int flags = fcntl(nfd, F_GETFL);
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(nfd);
logger(LOG_ERR, "System call `%s' failed: %s", "fcntl",
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "fcntl",
strerror(errno));
return -1;
}
@ -253,7 +202,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
unsigned long arg = 1;
if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
closesocket(nfd);
logger(LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
return -1;
}
}
@ -261,12 +210,13 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof option);
if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
logger(LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
@ -313,7 +263,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
logger(LOG_ERR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
free(addrstr);
return -1;
}
@ -334,15 +284,16 @@ void retry_outgoing(outgoing_t *outgoing) {
timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
ifdebug(CONNECTIONS) logger(LOG_NOTICE,
logger(DEBUG_CONNECTIONS, LOG_NOTICE,
"Trying to re-establish outgoing connection in %d seconds",
outgoing->timeout);
}
void finish_connecting(connection_t *c) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
configure_tcp(c);
if(proxytype != PROXY_EXEC)
configure_tcp(c);
c->last_ping_time = time(NULL);
c->status.connecting = false;
@ -350,113 +301,68 @@ void finish_connecting(connection_t *c) {
send_id(c);
}
bool do_outgoing_connection(connection_t *c) {
char *address, *port, *space;
int result;
static void do_outgoing_pipe(connection_t *c, char *command) {
#ifndef HAVE_MINGW
int fd[2];
if(!c->outgoing) {
logger(LOG_ERR, "do_outgoing_connection() for %s called without c->outgoing", c->name);
abort();
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", strerror(errno));
return;
}
begin:
if(!c->outgoing->ai) {
if(!c->outgoing->cfg) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
c->name);
retry_outgoing(c->outgoing);
c->outgoing = NULL;
connection_del(c);
return false;
}
get_config_string(c->outgoing->cfg, &address);
space = strchr(address, ' ');
if(space) {
port = xstrdup(space + 1);
*space = 0;
} else {
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
port = xstrdup("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(fork()) {
c->socket = fd[0];
close(fd[1]);
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
return;
}
if(!c->outgoing->aip) {
if(c->outgoing->ai)
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
goto begin;
}
close(0);
close(1);
close(fd[0]);
dup2(fd[1], 0);
dup2(fd[1], 1);
close(fd[1]);
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
// Other filedescriptors should be closed automatically by CLOEXEC
if(c->hostname)
free(c->hostname);
char *host = NULL;
char *port = NULL;
c->hostname = sockaddr2hostname(&c->address);
sockaddr2str(&c->address, &host, &port);
setenv("REMOTEADDRESS", host, true);
setenv("REMOTEPORT", port, true);
setenv("NODE", c->name, true);
setenv("NAME", myself->name, true);
if(netname)
setenv("NETNAME", netname, true);
ifdebug(CONNECTIONS) logger(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) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
int option = 1;
if(c->address.sa.sa_family == AF_INET6)
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
int result = system(command);
if(result < 0)
logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s", command, strerror(errno));
else if(result)
logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
exit(result);
#else
logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
return;
#endif
bind_to_interface(c->socket);
bind_to_address(c);
/* Optimize TCP settings */
configure_tcp(c);
/* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1) {
if(sockinprogress(sockerrno)) {
c->status.connecting = true;
return true;
}
closesocket(c->socket);
ifdebug(CONNECTIONS) logger(LOG_ERR, "%s: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
finish_connecting(c);
return true;
}
static void handle_meta_write(int sock, short events, void *data) {
ifdebug(META) logger(LOG_DEBUG, "handle_meta_write() called");
connection_t *c = data;
ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
if(outlen <= 0) {
logger(LOG_ERR, "Onoes, outlen = %d (%s)", (int)outlen, strerror(errno));
if(!errno || errno == EPIPE) {
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)", c->name, c->hostname);
} else if(sockwouldblock(sockerrno)) {
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Sending %d bytes to %s (%s) would block", c->outbuf.len - c->outbuf.offset, c->name, c->hostname);
return;
} else {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, strerror(errno));
}
terminate_connection(c, c->status.active);
return;
}
@ -466,51 +372,151 @@ static void handle_meta_write(int sock, short events, void *data) {
event_del(&c->outevent);
}
void setup_outgoing_connection(outgoing_t *outgoing) {
connection_t *c;
node_t *n;
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
bool do_outgoing_connection(outgoing_t *outgoing) {
char *address, *port, *space;
struct addrinfo *proxyai = NULL;
int result;
n = lookup_node(outgoing->name);
if(n)
if(n->connection) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "Already connected to %s", outgoing->name);
n->connection->outgoing = outgoing;
return;
begin:
if(!outgoing->ai) {
if(!outgoing->cfg) {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not set up a meta connection to %s", outgoing->name);
retry_outgoing(outgoing);
return false;
}
c = new_connection();
get_config_string(outgoing->cfg, &address);
space = strchr(address, ' ');
if(space) {
port = xstrdup(space + 1);
*space = 0;
} else {
if(!get_config_string(lookup_config(outgoing->config_tree, "Port"), &port))
port = xstrdup("655");
}
outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
free(address);
free(port);
outgoing->aip = outgoing->ai;
outgoing->cfg = lookup_config_next(outgoing->config_tree, outgoing->cfg);
}
if(!outgoing->aip) {
if(outgoing->ai)
freeaddrinfo(outgoing->ai);
outgoing->ai = NULL;
goto begin;
}
connection_t *c = new_connection();
c->outgoing = outgoing;
memcpy(&c->address, outgoing->aip->ai_addr, outgoing->aip->ai_addrlen);
outgoing->aip = outgoing->aip->ai_next;
c->hostname = sockaddr2hostname(&c->address);
logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", outgoing->name, c->hostname);
if(!proxytype) {
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
configure_tcp(c);
} else if(proxytype == PROXY_EXEC) {
do_outgoing_pipe(c, proxyhost);
} else {
proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
if(!proxyai) {
free_connection(c);
goto begin;
}
logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
}
if(c->socket == -1) {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
free_connection(c);
goto begin;
}
#ifdef FD_CLOEXEC
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
#endif
if(proxytype != PROXY_EXEC) {
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
int option = 1;
if(c->address.sa.sa_family == AF_INET6)
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
#endif
bind_to_interface(c->socket);
}
/* Connect */
if(!proxytype) {
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
} else if(proxytype == PROXY_EXEC) {
result = 0;
} else {
result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
freeaddrinfo(proxyai);
}
if(result == -1 && !sockinprogress(sockerrno)) {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not connect to %s (%s): %s", outgoing->name, c->hostname, sockstrerror(sockerrno));
free_connection(c);
goto begin;
}
/* Now that there is a working socket, fill in the rest and register this connection. */
c->status.connecting = true;
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) {
logger(LOG_ERR, "No address specified for %s", c->name);
free_connection(c);
return;
}
c->outgoing = outgoing;
c->last_ping_time = time(NULL);
connection_add(c);
if (do_outgoing_connection(c)) {
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
return true;
}
void setup_outgoing_connection(outgoing_t *outgoing) {
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
node_t *n = lookup_node(outgoing->name);
if(n && n->connection) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Already connected to %s", outgoing->name);
n->connection->outgoing = outgoing;
return;
}
init_configuration(&outgoing->config_tree);
read_host_config(outgoing->config_tree, outgoing->name);
outgoing->cfg = lookup_config(outgoing->config_tree, "Address");
if(!outgoing->cfg) {
logger(DEBUG_ALWAYS, LOG_ERR, "No address specified for %s", outgoing->name);
return;
}
do_outgoing_connection(outgoing);
}
/*
@ -526,7 +532,7 @@ void handle_new_meta_connection(int sock, short events, void *data) {
fd = accept(sock, &sa.sa, &len);
if(fd < 0) {
logger(LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
logger(DEBUG_ALWAYS, LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
return;
}
@ -544,12 +550,12 @@ void handle_new_meta_connection(int sock, short events, void *data) {
c->socket = fd;
c->last_ping_time = time(NULL);
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection from %s", c->hostname);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
configure_tcp(c);
connection_add(c);
@ -559,9 +565,15 @@ void handle_new_meta_connection(int sock, short events, void *data) {
}
static void free_outgoing(outgoing_t *outgoing) {
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
if(outgoing->ai)
freeaddrinfo(outgoing->ai);
if(outgoing->config_tree)
exit_configuration(&outgoing->config_tree);
if(outgoing->name)
free(outgoing->name);
@ -569,26 +581,60 @@ static void free_outgoing(outgoing_t *outgoing) {
}
void try_outgoing_connections(void) {
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
outgoing_list = list_alloc((list_action_t)free_outgoing);
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
/* If there is no outgoing list yet, create one. Otherwise, mark all outgoings as deleted. */
if(!outgoing_list) {
outgoing_list = list_alloc((list_action_t)free_outgoing);
} else {
for list_each(outgoing_t, outgoing, outgoing_list)
outgoing->timeout = -1;
}
/* Make sure there is one outgoing_t in the list for each ConnectTo. */
for(config_t *cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
char *name;
get_config_string(cfg, &name);
if(!check_id(name)) {
logger(LOG_ERR,
logger(DEBUG_ALWAYS, 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;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing);
bool found = false;
for list_each(outgoing_t, outgoing, outgoing_list) {
if(!strcmp(outgoing->name, name)) {
found = true;
outgoing->timeout = 0;
break;
}
}
if(!found) {
outgoing_t *outgoing = xmalloc_and_zero(sizeof *outgoing);
outgoing->name = name;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing);
}
}
/* Terminate any connections whose outgoing_t is to be deleted. */
for list_each(connection_t, c, connection_list) {
if(c->outgoing && c->outgoing->timeout == -1) {
c->outgoing = NULL;
logger(DEBUG_CONNECTIONS, LOG_INFO, "No more outgoing connection to %s", c->name);
terminate_connection(c, c->status.active);
}
}
/* Delete outgoing_ts for which there is no ConnectTo. */
for list_each(outgoing_t, outgoing, outgoing_list)
if(outgoing->timeout == -1)
list_delete_node(outgoing_list, node);
}

View file

@ -1,7 +1,7 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998-2005 Ivo Timmermans
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -42,7 +42,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
err = getaddrinfo(address, service, &hint, &ai);
if(err) {
logger(LOG_WARNING, "Error looking up %s port %s: %s", address,
logger(DEBUG_ALWAYS, LOG_WARNING, "Error looking up %s port %s: %s", address,
service, gai_strerror(err));
return NULL;
}
@ -62,8 +62,7 @@ sockaddr_t str2sockaddr(const char *address, const char *port) {
err = getaddrinfo(address, port, &hint, &ai);
if(err || !ai) {
ifdebug(SCARY_THINGS)
logger(LOG_DEBUG, "Unknown type address %s port %s", address, port);
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Unknown type address %s port %s", address, port);
result.sa.sa_family = AF_UNKNOWN;
result.unknown.address = xstrdup(address);
result.unknown.port = xstrdup(port);
@ -83,15 +82,17 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
int err;
if(sa->sa.sa_family == AF_UNKNOWN) {
*addrstr = xstrdup(sa->unknown.address);
*portstr = xstrdup(sa->unknown.port);
if(addrstr)
*addrstr = xstrdup(sa->unknown.address);
if(portstr)
*portstr = xstrdup(sa->unknown.port);
return;
}
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV);
if(err) {
logger(LOG_ERR, "Error while translating addresses: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while translating addresses: %s",
gai_strerror(err));
abort();
}
@ -99,7 +100,7 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
scopeid = strchr(address, '%');
if(scopeid)
*scopeid = '\0'; /* Descope. */
*scopeid = '\0'; /* Descope. */
if(addrstr)
*addrstr = xstrdup(address);
@ -121,7 +122,7 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port,
hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
if(err) {
logger(LOG_ERR, "Error while looking up hostname: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Error while looking up hostname: %s",
gai_strerror(err));
}
@ -152,7 +153,7 @@ int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b) {
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
logger(DEBUG_ALWAYS, LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
}
@ -195,7 +196,7 @@ int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof a->in6.sin6_port);
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
logger(DEBUG_ALWAYS, LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
}
@ -217,78 +218,10 @@ void sockaddrfree(sockaddr_t *a) {
free(a->unknown.port);
}
}
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(const void *va, const void *vb, int masklen) {
int i, m, result;
const char *a = va;
const char *b = vb;
for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
result = a[i] - b[i];
if(result)
return result;
}
if(m)
return (a[i] & (0x100 - (1 << (8 - m)))) -
(b[i] & (0x100 - (1 << (8 - m))));
return 0;
}
void mask(void *va, int masklen, int len) {
int i;
char *a = va;
i = masklen / 8;
masklen %= 8;
if(masklen)
a[i++] &= (0x100 - (1 << (8 - masklen)));
for(; i < len; i++)
a[i] = 0;
}
void maskcpy(void *va, const void *vb, int masklen, int len) {
int i, m;
char *a = va;
const char *b = vb;
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
a[i] = b[i];
if(m) {
a[i] = b[i] & (0x100 - (1 << (8 - m)));
i++;
}
for(; i < len; i++)
a[i] = 0;
}
bool maskcheck(const void *va, int masklen, int len) {
int i;
const char *a = va;
i = masklen / 8;
masklen %= 8;
if(masklen && a[i++] & (0xff >> masklen))
return false;
for(; i < len; i++)
if(a[i] != 0)
return false;
return true;
}

View file

@ -1,7 +1,7 @@
/*
netutl.h -- header file for netutl.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,9 +34,5 @@ extern int sockaddrcmp_noport(const sockaddr_t *, const sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);
extern int maskcmp(const void *, const void *, int);
extern void maskcpy(void *, const void *, int, int);
extern void mask(void *, int, int);
extern bool maskcheck(const void *, int, int);
#endif /* __TINC_NETUTL_H__ */
#endif /* __TINC_NETUTL_H__ */

View file

@ -1,6 +1,6 @@
/*
node.c -- node tree management
Copyright (C) 2001-2011 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -21,16 +21,17 @@
#include "system.h"
#include "control_common.h"
#include "splay_tree.h"
#include "hash.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "splay_tree.h"
#include "utils.h"
#include "xalloc.h"
splay_tree_t *node_tree; /* Known nodes, sorted by name */
splay_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
splay_tree_t *node_tree;
static hash_t *node_udp_cache;
node_t *myself;
@ -38,24 +39,13 @@ static int node_compare(const node_t *a, const node_t *b) {
return strcmp(a->name, b->name);
}
static int node_udp_compare(const node_t *a, const node_t *b) {
int result;
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) {
node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL);
node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t));
}
void exit_nodes(void) {
splay_delete_tree(node_udp_tree);
hash_free(node_udp_cache);
splay_delete_tree(node_tree);
}
@ -85,12 +75,12 @@ void free_node(node_t *n) {
cipher_close(&n->outcipher);
digest_close(&n->outdigest);
ecdh_free(&n->ecdh);
ecdsa_free(&n->ecdsa);
sptps_stop(&n->sptps);
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
if(n->hostname)
free(n->hostname);
@ -108,23 +98,12 @@ void node_add(node_t *n) {
}
void node_del(node_t *n) {
splay_node_t *node, *next;
edge_t *e;
subnet_t *s;
for(node = n->subnet_tree->head; node; node = next) {
next = node->next;
s = node->data;
for splay_each(subnet_t, s, n->subnet_tree)
subnet_del(n, s);
}
for(node = n->edge_tree->head; node; node = next) {
next = node->next;
e = node->data;
for splay_each(edge_t, e, n->edge_tree)
edge_del(e);
}
splay_delete(node_udp_tree, n);
splay_delete(node_tree, n);
}
@ -137,62 +116,41 @@ node_t *lookup_node(char *name) {
}
node_t *lookup_node_udp(const sockaddr_t *sa) {
node_t n = {NULL};
n.address = *sa;
n.name = NULL;
return splay_search(node_udp_tree, &n);
return hash_search(node_udp_cache, sa);
}
void update_node_udp(node_t *n, const sockaddr_t *sa) {
if(n == myself) {
logger(LOG_WARNING, "Trying to update UDP address of myself!");
logger(DEBUG_ALWAYS, LOG_WARNING, "Trying to update UDP address of myself!");
return;
}
splay_delete(node_udp_tree, n);
if(n->hostname)
free(n->hostname);
hash_insert(node_udp_cache, &n->address, NULL);
if(sa) {
n->address = *sa;
hash_insert(node_udp_cache, sa, n);
free(n->hostname);
n->hostname = sockaddr2hostname(&n->address);
splay_insert(node_udp_tree, n);
ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
} else {
memset(&n->address, 0, sizeof n->address);
n->hostname = NULL;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s cleared", n->name);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
}
}
bool dump_nodes(connection_t *c) {
splay_node_t *node;
node_t *n;
for(node = node_tree->head; node; node = node->next) {
n = node->data;
send_request(c, "%d %d %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)", CONTROL, REQ_DUMP_NODES,
n->name, n->hostname, cipher_get_nid(&n->outcipher),
for splay_each(node_t, n, node_tree)
send_request(c, "%d %d %s %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", CONTROL, REQ_DUMP_NODES,
n->name, n->hostname ?: "unknown port unknown", cipher_get_nid(&n->outcipher),
digest_get_nid(&n->outdigest), (int)digest_length(&n->outdigest), n->outcompression,
n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name ?: "-" : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
}
n->via ? n->via->name ?: "-" : "-", n->distance, n->mtu, n->minmtu, n->maxmtu, (long)n->last_state_change);
return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
}
bool dump_traffic(connection_t *c) {
splay_node_t *node;
node_t *n;
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for splay_each(node_t, n, node_tree)
send_request(c, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, CONTROL, REQ_DUMP_TRAFFIC,
n->name, n->in_packets, n->in_bytes, n->out_packets, n->out_bytes);
}
return send_request(c, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
}

View file

@ -1,6 +1,6 @@
/*
node.h -- header for node.c
Copyright (C) 2001-2010 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -25,62 +25,65 @@
#include "cipher.h"
#include "connection.h"
#include "digest.h"
#include "ecdh.h"
#include "subnet.h"
typedef struct node_status_t {
unsigned int unused_active:1; /* 1 if active (not used for nodes) */
unsigned int validkey:1; /* 1 if we currently have a valid key for him */
unsigned int unused_waitingforkey:1; /* 1 if we already sent out a request */
unsigned int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
unsigned int reachable:1; /* 1 if this node is reachable in the graph */
unsigned int indirect:1; /* 1 if this node is not directly reachable by us */
unsigned int ecdh:1; /* 1 if this node supports ECDH key exchange */
unsigned int unused:25;
unsigned int unused_active:1; /* 1 if active (not used for nodes) */
unsigned int validkey:1; /* 1 if we currently have a valid key for him */
unsigned int waitingforkey:1; /* 1 if we already sent out a request */
unsigned int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
unsigned int reachable:1; /* 1 if this node is reachable in the graph */
unsigned int indirect:1; /* 1 if this node is not directly reachable by us */
unsigned int sptps:1; /* 1 if this node supports SPTPS */
unsigned int udp_confirmed:1; /* 1 if the address is one that we received UDP traffic on */
unsigned int unused:24;
} node_status_t;
typedef struct node_t {
char *name; /* name of this node */
uint32_t options; /* options turned on for this node */
char *name; /* name of this node */
uint32_t 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 */
int sock; /* Socket to use for outgoing UDP packets */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
node_status_t status;
time_t last_state_change;
time_t last_req_key;
ecdsa_t ecdsa; /* His public ECDSA key */
ecdh_t ecdh; /* State for ECDH key exchange */
ecdsa_t ecdsa; /* His public ECDSA key */
sptps_t sptps;
cipher_t incipher; /* Cipher for UDP packets */
digest_t indigest; /* Digest for UDP packets */
cipher_t incipher; /* Cipher for UDP packets */
digest_t indigest; /* Digest for UDP packets */
cipher_t outcipher; /* Cipher for UDP packets */
digest_t outdigest; /* Digest for UDP packets */
cipher_t outcipher; /* Cipher for UDP packets */
digest_t outdigest; /* Digest for UDP packets */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int distance;
struct node_t *nexthop; /* nearest node from us to him */
struct node_t *via; /* next hop for UDP packets */
struct node_t *nexthop; /* nearest node from us to him */
struct edge_t *prevedge; /* nearest node from him to us */
struct node_t *via; /* next hop for UDP packets */
splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
splay_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) */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
uint32_t farfuture; /* Packets in a row that have arrived from the far future */
unsigned char* late; /* Bitfield marking late packets */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
uint32_t farfuture; /* Packets in a row that have arrived from the far future */
unsigned char* late; /* Bitfield marking late packets */
length_t mtu; /* Maximum size of packets to send to this node */
length_t minmtu; /* Probed minimum MTU */
length_t maxmtu; /* Probed maximum MTU */
int mtuprobes; /* Number of probes */
struct event mtuevent; /* Probe event */
length_t mtu; /* Maximum size of packets to send to this node */
length_t minmtu; /* Probed minimum MTU */
length_t maxmtu; /* Probed maximum MTU */
int mtuprobes; /* Number of probes */
struct event mtuevent; /* Probe event */
uint64_t in_packets;
uint64_t in_bytes;
@ -90,7 +93,6 @@ typedef struct node_t {
extern struct node_t *myself;
extern splay_tree_t *node_tree;
extern splay_tree_t *node_udp_tree;
extern void init_nodes(void);
extern void exit_nodes(void);
@ -104,4 +106,4 @@ extern bool dump_nodes(struct connection_t *);
extern bool dump_traffic(struct connection_t *);
extern void update_node_udp(node_t *, const sockaddr_t *);
#endif /* __TINC_NODE_H__ */
#endif /* __TINC_NODE_H__ */

View file

@ -1,6 +1,6 @@
/*
cipher.c -- Symmetric block cipher handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,6 +26,12 @@
#include "logger.h"
#include "xalloc.h"
typedef struct cipher_counter {
unsigned char counter[EVP_MAX_IV_LENGTH];
unsigned char block[EVP_MAX_IV_LENGTH];
int n;
} cipher_counter_t;
static bool cipher_open(cipher_t *cipher) {
EVP_CIPHER_CTX_init(&cipher->ctx);
@ -38,7 +44,7 @@ bool cipher_open_by_name(cipher_t *cipher, const char *name) {
if(cipher->cipher)
return cipher_open(cipher);
logger(LOG_ERR, "Unknown cipher name '%s'!", name);
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown cipher name '%s'!", name);
return false;
}
@ -48,7 +54,7 @@ bool cipher_open_by_nid(cipher_t *cipher, int nid) {
if(cipher->cipher)
return cipher_open(cipher);
logger(LOG_ERR, "Unknown cipher nid %d!", nid);
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown cipher nid %d!", nid);
return false;
}
@ -59,10 +65,12 @@ bool cipher_open_blowfish_ofb(cipher_t *cipher) {
void cipher_close(cipher_t *cipher) {
EVP_CIPHER_CTX_cleanup(&cipher->ctx);
free(cipher->counter);
cipher->counter = NULL;
}
size_t cipher_keylength(const cipher_t *cipher) {
return cipher->cipher->key_len + cipher->cipher->iv_len;
return cipher->cipher->key_len + cipher->cipher->block_size;
}
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
@ -76,7 +84,7 @@ bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
if(result)
return true;
logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -91,28 +99,92 @@ bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encry
if(result)
return true;
logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_set_counter(cipher_t *cipher, const void *counter, size_t len) {
if(len > cipher->cipher->block_size - 4) {
logger(DEBUG_ALWAYS, LOG_ERR, "Counter too long");
abort();
}
memcpy(cipher->counter->counter + cipher->cipher->block_size - len, counter, len);
memset(cipher->counter->counter, 0, 4);
cipher->counter->n = 0;
return true;
}
bool cipher_set_counter_key(cipher_t *cipher, void *key) {
int result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, NULL);
if(!result) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(!cipher->counter)
cipher->counter = xmalloc_and_zero(sizeof *cipher->counter);
else
cipher->counter->n = 0;
memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->block_size);
return true;
}
bool cipher_counter_xor(cipher_t *cipher, const void *indata, size_t inlen, void *outdata) {
if(!cipher->counter) {
logger(DEBUG_ALWAYS, LOG_ERR, "Counter not initialized");
return false;
}
const unsigned char *in = indata;
unsigned char *out = outdata;
while(inlen--) {
// Encrypt the new counter value if we need it
if(!cipher->counter->n) {
int len;
if(!EVP_EncryptUpdate(&cipher->ctx, cipher->counter->block, &len, cipher->counter->counter, cipher->cipher->block_size)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
// Increase the counter value
for(int i = 0; i < cipher->cipher->block_size; i++)
if(++cipher->counter->counter[i])
break;
}
*out++ = *in++ ^ cipher->counter->counter[cipher->counter->n++];
if(cipher->counter->n >= cipher->cipher->block_size)
cipher->counter->n = 0;
}
return true;
}
bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
if(oneshot) {
int len, pad;
if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
*outlen = len + pad;
if(outlen) *outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
*outlen = len;
if(outlen) *outlen = len;
return true;
}
}
logger(LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -122,18 +194,18 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
*outlen = len + pad;
if(outlen) *outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
*outlen = len;
if(outlen) *outlen = len;
return true;
}
}
logger(LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}

View file

@ -1,6 +1,6 @@
/*
cipher.h -- header file cipher.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@
typedef struct cipher {
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher;
struct cipher_counter *counter;
} cipher_t;
extern bool cipher_open_by_name(cipher_t *, const char *);
@ -38,8 +39,11 @@ extern void cipher_close(cipher_t *);
extern size_t cipher_keylength(const cipher_t *);
extern bool cipher_set_key(cipher_t *, void *, bool);
extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool);
extern bool cipher_set_counter(cipher_t *, const void *, size_t);
extern bool cipher_set_counter_key(cipher_t *, void *);
extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
extern bool cipher_counter_xor(cipher_t *, const void *indata, size_t inlen, void *outdata);
extern int cipher_get_nid(const cipher_t *);
extern bool cipher_active(const cipher_t *);

View file

@ -26,12 +26,12 @@
#include "crypto.h"
void crypto_init(void) {
RAND_load_file("/dev/urandom", 1024);
RAND_load_file("/dev/urandom", 1024);
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_algorithms();
}
void crypto_exit(void) {

View file

@ -1,6 +1,6 @@
/*
digest.c -- Digest handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -41,7 +41,7 @@ bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
digest->key = NULL;
if(!digest->digest) {
logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest name '%s'!", name);
return false;
}
@ -54,7 +54,7 @@ bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
digest->key = NULL;
if(!digest->digest) {
logger(LOG_DEBUG, "Unknown digest nid %d!", nid);
logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest nid %d!", nid);
return false;
}
@ -78,8 +78,7 @@ bool digest_set_key(digest_t *digest, const void *key, size_t len) {
}
void digest_close(digest_t *digest) {
if(digest->key)
free(digest->key);
free(digest->key);
digest->key = NULL;
}
@ -95,7 +94,7 @@ bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *out
if(!EVP_DigestInit(&ctx, digest->digest)
|| !EVP_DigestUpdate(&ctx, indata, inlen)
|| !EVP_DigestFinal(&ctx, tmpdata, NULL)) {
logger(LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
}
@ -115,6 +114,10 @@ int digest_get_nid(const digest_t *digest) {
return digest->digest ? digest->digest->type : 0;
}
size_t digest_keylength(const digest_t *digest) {
return digest->digest->md_size;
}
size_t digest_length(const digest_t *digest) {
return digest->maclength;
}

View file

@ -1,6 +1,6 @@
/*
digest.h -- header file digest.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -39,6 +39,7 @@ extern bool digest_create(struct digest *, const void *indata, size_t inlen, voi
extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern bool digest_set_key(struct digest *, const void *key, size_t len);
extern int digest_get_nid(const struct digest *);
extern size_t digest_keylength(const struct digest *);
extern size_t digest_length(const struct digest *);
extern bool digest_active(const struct digest *);

View file

@ -1,6 +1,6 @@
/*
ecdh.c -- Diffie-Hellman key exchange handling
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -30,21 +30,32 @@
bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey) {
*ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!EC_KEY_generate_key(*ecdh)) {
logger(LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
if(!*ecdh) {
logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(!EC_KEY_generate_key(*ecdh)) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
const EC_POINT *point = EC_KEY_get0_public_key(*ecdh);
if(!point) {
logger(LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
size_t result = EC_POINT_point2oct(EC_KEY_get0_group(*ecdh), point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL);
if(!result) {
logger(LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
EC_KEY_free(*ecdh);
*ecdh = NULL;
logger(DEBUG_ALWAYS, LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
@ -53,14 +64,15 @@ bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey) {
bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(*ecdh));
if(!point) {
logger(LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
logger(DEBUG_ALWAYS, LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int result = EC_POINT_oct2point(EC_KEY_get0_group(*ecdh), point, pubkey, ECDH_SIZE, NULL);
if(!result) {
logger(LOG_ERR, "Converting binary to EC_POINT failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
EC_POINT_free(point);
logger(DEBUG_ALWAYS, LOG_ERR, "Converting binary to EC_POINT failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
result = ECDH_compute_key(shared, ECDH_SIZE, point, *ecdh, NULL);
@ -69,7 +81,7 @@ bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
*ecdh = NULL;
if(!result) {
logger(LOG_ERR, "Computing Elliptic Curve Diffie-Hellman shared key failed: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Computing Elliptic Curve Diffie-Hellman shared key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}

View file

@ -1,6 +1,6 @@
/*
ecdsa.c -- ECDSA key handling
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -30,15 +30,19 @@
//
bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
*ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!*ecdsa) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "EC_KEY_new_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int len = strlen(p);
unsigned char pubkey[len / 4 * 3 + 3];
const unsigned char *ppubkey = pubkey;
len = b64decode(p, pubkey, len);
len = b64decode(p, (char *)pubkey, len);
if(!o2i_ECPublicKey(ecdsa, &ppubkey, len)) {
logger(LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
logger(DEBUG_ALWAYS, LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
@ -49,7 +53,7 @@ char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
int len = i2o_ECPublicKey(*ecdsa, &pubkey);
char *base64 = malloc(len * 4 / 3 + 5);
b64encode(pubkey, base64, len);
b64encode((char *)pubkey, base64, len);
free(pubkey);
@ -64,7 +68,7 @@ bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
if(*ecdsa)
return true;
logger(LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -73,8 +77,8 @@ bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
if(*ecdsa)
return true;
logger(LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -87,31 +91,27 @@ size_t ecdsa_size(ecdsa_t *ecdsa) {
bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t len, void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
char hash[SHA512_DIGEST_LENGTH];
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
memset(sig, 0, siglen);
if(!ECDSA_sign(0, hash, sizeof hash, sig, &siglen, *ecdsa)) {
logger(LOG_DEBUG, "ECDSA_sign() failed: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_DEBUG, "ECDSA_sign() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(siglen != ECDSA_size(*ecdsa)) {
logger(LOG_ERR, "Signature length %d != %d", siglen, ECDSA_size(*ecdsa));
}
return true;
}
bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t len, const void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
char hash[SHA512_DIGEST_LENGTH];
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
if(!ECDSA_verify(0, hash, sizeof hash, sig, siglen, *ecdsa)) {
logger(LOG_DEBUG, "ECDSA_verify() failed: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_DEBUG, "ECDSA_verify() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}

View file

@ -67,7 +67,7 @@ char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
int len = i2o_ECPublicKey(*ecdsa, &pubkey);
char *base64 = malloc(len * 4 / 3 + 5);
b64encode(pubkey, base64, len);
b64encode((char *)pubkey, base64, len);
free(pubkey);

View file

@ -1,6 +1,6 @@
/*
prf.c -- Pseudo-Random Function for key material generation
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,16 +19,18 @@
#include "system.h"
#include <openssl/obj_mac.h>
#include "digest.h"
#include "prf.h"
/* Generate key material from a master secret and a seed, based on RFC 4346 section 5.
We use SHA512 and Whirlpool instead of MD5 and SHA1.
We use SHA512 instead of MD5 and SHA1.
*/
static bool prf_xor(int nid, char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
static bool prf_xor(int nid, const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
digest_t digest;
if(!digest_open_by_nid(&digest, nid, -1))
return false;
@ -65,12 +67,9 @@ static bool prf_xor(int nid, char *secret, size_t secretlen, char *seed, size_t
return true;
}
bool prf(char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
/* Split secret in half, generate outlen bits with two different hash algorithms,
and XOR the results. */
bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
/* This construction allows us to easily switch back to a scheme where the PRF is calculated using two different digest algorithms. */
memset(out, 0, outlen);
return prf_xor(NID_sha512, secret, (secretlen + 1) / 2, seed, seedlen, out, outlen)
&& prf_xor(NID_whirlpool, secret + secretlen / 2, (secretlen + 1) / 2, seed, seedlen, out, outlen);
return prf_xor(NID_sha512, secret, secretlen, seed, seedlen, out, outlen);
}

View file

@ -20,6 +20,6 @@
#ifndef __TINC_PRF_H__
#define __TINC_PRF_H__
extern bool prf(char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen);
extern bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen);
#endif

View file

@ -1,6 +1,6 @@
/*
rsa.c -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,16 +29,21 @@
bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
*rsa = RSA_new();
BN_hex2bn(&(*rsa)->n, n);
BN_hex2bn(&(*rsa)->e, e);
if(BN_hex2bn(&(*rsa)->n, n) != strlen(n))
return false;
if(BN_hex2bn(&(*rsa)->e, e) != strlen(e))
return false;
return true;
}
bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
*rsa = RSA_new();
BN_hex2bn(&(*rsa)->n, n);
BN_hex2bn(&(*rsa)->e, e);
BN_hex2bn(&(*rsa)->d, d);
if(BN_hex2bn(&(*rsa)->n, n) != strlen(n))
return false;
if(BN_hex2bn(&(*rsa)->e, e) != strlen(e))
return false;
if(BN_hex2bn(&(*rsa)->d, d) != strlen(d))
return false;
return true;
}
@ -49,13 +54,13 @@ bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
if(*rsa)
return true;
*rsa = PEM_read_RSA_PUBKEY(fp, rsa, NULL, NULL);
if(*rsa)
return true;
logger(LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -64,8 +69,8 @@ bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
if(*rsa)
return true;
logger(LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
@ -77,16 +82,16 @@ bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
if(RSA_public_encrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
return true;
logger(LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
if(RSA_private_decrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
return true;
logger(LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool rsa_active(rsa_t *rsa) {

View file

@ -1,6 +1,6 @@
/*
rsa.h -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

75
src/prf.c Normal file
View file

@ -0,0 +1,75 @@
/*
prf.c -- Pseudo-Random Function for key material generation
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/obj_mac.h>
#include "digest.h"
#include "prf.h"
/* Generate key material from a master secret and a seed, based on RFC 4346 section 5.
We use SHA512 instead of MD5 and SHA1.
*/
static bool prf_xor(int nid, const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
digest_t digest;
if(!digest_open_by_nid(&digest, nid, -1))
return false;
if(!digest_set_key(&digest, secret, secretlen))
return false;
size_t len = digest_length(&digest);
/* Data is what the "inner" HMAC function processes.
It consists of the previous HMAC result plus the seed.
*/
char data[len + seedlen];
memset(data, 0, len);
memcpy(data + len, seed, seedlen);
char hash[len];
while(outlen > 0) {
/* Inner HMAC */
digest_create(&digest, data, len + seedlen, data);
/* Outer HMAC */
digest_create(&digest, data, len + seedlen, hash);
/* XOR the results of the outer HMAC into the out buffer */
for(int i = 0; i < len && i < outlen; i++)
*out++ ^= hash[i];
outlen -= len;
}
digest_close(&digest);
return true;
}
bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
/* This construction allows us to easily switch back to a scheme where the PRF is calculated using two different digest algorithms. */
memset(out, 0, outlen);
return prf_xor(NID_sha512, secret, secretlen, seed, seedlen, out, outlen);
}

View file

@ -1,7 +1,7 @@
/*
process.c -- process management functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -55,13 +55,11 @@ static SERVICE_STATUS_HANDLE statushandle = 0;
static bool install_service(void) {
char command[4096] = "\"";
char **argp;
bool space;
SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!manager) {
logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
return false;
}
@ -74,13 +72,13 @@ static bool install_service(void) {
strncat(command, "\"", sizeof command - strlen(command));
for(argp = g_argv + 1; *argp; argp++) {
space = strchr(*argp, ' ');
for(char **argp = g_argv + 1; *argp; argp++) {
char *space = strchr(*argp, ' ');
strncat(command, " ", sizeof command - strlen(command));
if(space)
strncat(command, "\"", sizeof command - strlen(command));
strncat(command, *argp, sizeof command - strlen(command));
if(space)
@ -90,25 +88,25 @@ static bool install_service(void) {
service = CreateService(manager, identname, identname,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
command, NULL, NULL, NULL, NULL, NULL);
if(!service) {
DWORD lasterror = GetLastError();
logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
if(lasterror != ERROR_SERVICE_EXISTS)
return false;
}
if(service) {
ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
logger(LOG_INFO, "%s service installed", identname);
logger(DEBUG_ALWAYS, LOG_INFO, "%s service installed", identname);
} else {
service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
}
if(!StartService(service, 0, NULL))
logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
else
logger(LOG_INFO, "%s service started", identname);
logger(DEBUG_ALWAYS, LOG_INFO, "%s service started", identname);
return true;
}
@ -119,53 +117,49 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
SetServiceStatus(statushandle, &status);
return NO_ERROR;
case SERVICE_CONTROL_STOP:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
break;
case SERVICE_CONTROL_SHUTDOWN:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
break;
default:
logger(LOG_WARNING, "Got unexpected request %d", request);
logger(DEBUG_ALWAYS, LOG_WARNING, "Got unexpected request %d", (int)request);
return ERROR_CALL_NOT_IMPLEMENTED;
}
event_loopexit(NULL);
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
return NO_ERROR;
}
VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
int err = 1;
extern int main2(int argc, char **argv);
status.dwServiceType = SERVICE_WIN32;
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL);
statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL);
if (!statushandle) {
logger(LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
err = 1;
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
} else {
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_START_PENDING;
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(statushandle, &status);
status.dwWaitHint = 0;
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(statushandle, &status);
err = main2(argc, argv);
main2(argc, argv);
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_STOPPED;
//status.dwWin32ExitCode = err;
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(statushandle, &status);
}
@ -183,7 +177,7 @@ bool init_service(void) {
return false;
}
else
logger(LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
}
return true;
@ -206,19 +200,18 @@ bool detach(void) {
if(do_detach) {
#ifndef HAVE_MINGW
if(daemon(0, 0)) {
fprintf(stderr, "Couldn't detach from terminal: %s",
strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Couldn't detach from terminal: %s", strerror(errno));
return false;
}
#else
if(!statushandle)
exit(install_service());
exit(!install_service());
#endif
}
openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
logger(DEBUG_ALWAYS, LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
VERSION, __DATE__, __TIME__, debug_level);
return true;
@ -226,46 +219,40 @@ bool detach(void) {
bool execute_script(const char *name, char **envp) {
#ifdef HAVE_SYSTEM
int status, len;
char *scriptname;
int i;
char *command;
#ifndef HAVE_MINGW
len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
#else
len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
#endif
if(len < 0)
return false;
xasprintf(&scriptname, "%s" SLASH "%s%s", confbase, name, scriptextension);
scriptname[len - 1] = '\0';
#ifndef HAVE_TUNEMU
/* First check if there is a script */
if(access(scriptname + 1, F_OK)) {
if(access(scriptname, F_OK)) {
free(scriptname);
return true;
}
#endif
ifdebug(STATUS) logger(LOG_INFO, "Executing script %s", name);
logger(DEBUG_STATUS, LOG_INFO, "Executing script %s", name);
#ifdef HAVE_PUTENV
/* Set environment */
for(i = 0; envp[i]; i++)
for(int i = 0; envp[i]; i++)
putenv(envp[i]);
#endif
scriptname[len - 1] = '\"';
status = system(scriptname);
if(scriptinterpreter)
xasprintf(&command, "%s \"%s\"", scriptinterpreter, scriptname);
else
xasprintf(&command, "\"%s\"", scriptname);
int status = system(command);
free(command);
free(scriptname);
/* Unset environment */
for(i = 0; envp[i]; i++) {
for(int i = 0; envp[i]; i++) {
char *e = strchr(envp[i], '=');
if(e) {
char p[e - envp[i] + 1];
@ -277,22 +264,22 @@ bool execute_script(const char *name, char **envp) {
#ifdef WEXITSTATUS
if(status != -1) {
if(WIFEXITED(status)) { /* Child exited by itself */
if(WIFEXITED(status)) { /* Child exited by itself */
if(WEXITSTATUS(status)) {
logger(LOG_ERR, "Script %s exited with non-zero status %d",
logger(DEBUG_ALWAYS, LOG_ERR, "Script %s exited with non-zero status %d",
name, WEXITSTATUS(status));
return false;
}
} else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
logger(LOG_ERR, "Script %s was killed by signal %d (%s)",
} else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
logger(DEBUG_ALWAYS, LOG_ERR, "Script %s was killed by signal %d (%s)",
name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return false;
} else { /* Something strange happened */
logger(LOG_ERR, "Script %s terminated abnormally", name);
} else { /* Something strange happened */
logger(DEBUG_ALWAYS, LOG_ERR, "Script %s terminated abnormally", name);
return false;
}
} else {
logger(LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
return false;
}
#endif

View file

@ -1,7 +1,7 @@
/*
process.h -- header file for process.c
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -33,4 +33,4 @@ extern bool kill_other(int);
extern bool init_service(void);
#endif
#endif /* __TINC_PROCESS_H__ */
#endif /* __TINC_PROCESS_H__ */

View file

@ -1,7 +1,7 @@
/*
protocol.c -- handle the meta-protocol, basic functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,7 +34,7 @@ bool experimental = false;
/* Jumptable for the request handlers */
static bool (*request_handlers[])(connection_t *, char *) = {
static bool (*request_handlers[])(connection_t *, const char *) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
@ -56,6 +56,9 @@ static char (*request_name[]) = {
static splay_tree_t *past_request_tree;
bool check_id(const char *id) {
if(!id || !*id)
return false;
for(; *id; id++)
if(!isalnum(*id) && *id != '_')
return false;
@ -80,85 +83,71 @@ bool send_request(connection_t *c, const char *format, ...) {
va_end(args);
if(len < 0 || len > MAXBUFSIZE - 1) {
logger(LOG_ERR, "Output buffer overflow while sending request to %s (%s)",
logger(DEBUG_ALWAYS, LOG_ERR, "Output buffer overflow while sending request to %s (%s)",
c->name, c->hostname);
return false;
}
ifdebug(PROTOCOL) {
ifdebug(META)
logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
request_name[atoi(request)], c->name, c->hostname, request);
else
logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[atoi(request)],
c->name, c->hostname);
}
logger(DEBUG_META, LOG_DEBUG, "Sending %s to %s (%s): %s", request_name[atoi(request)], c->name, c->hostname, request);
request[len++] = '\n';
if(c == broadcast) {
if(c == everyone) {
broadcast_meta(NULL, request, len);
return true;
} else
return send_meta(c, request, len);
}
void forward_request(connection_t *from, char *request) {
/* Note: request is not zero terminated anymore after a call to this function! */
ifdebug(PROTOCOL) {
ifdebug(META)
logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
request_name[atoi(request)], from->name, from->hostname, request);
else
logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
request_name[atoi(request)], from->name, from->hostname);
}
void forward_request(connection_t *from, const char *request) {
logger(DEBUG_META, LOG_DEBUG, "Forwarding %s from %s (%s): %s", request_name[atoi(request)], from->name, from->hostname, request);
// Create a temporary newline-terminated copy of the request
int len = strlen(request);
request[len++] = '\n';
broadcast_meta(from, request, len);
char tmp[len + 1];
memcpy(tmp, request, len);
tmp[len] = '\n';
broadcast_meta(from, tmp, sizeof tmp);
}
bool receive_request(connection_t *c, char *request) {
bool receive_request(connection_t *c, const char *request) {
if(proxytype == PROXY_HTTP && c->allow_request == ID) {
if(!request[0] || request[0] == '\r')
return true;
if(!strncasecmp(request, "HTTP/1.1 ", 9)) {
if(!strncmp(request + 9, "200", 3)) {
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
return true;
} else {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
return false;
}
}
}
int reqno = atoi(request);
if(reqno || *request == '0') {
if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
ifdebug(META)
logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
c->name, c->hostname, request);
else
logger(LOG_ERR, "Unknown request from %s (%s)",
c->name, c->hostname);
logger(DEBUG_META, LOG_DEBUG, "Unknown request from %s (%s): %s", c->name, c->hostname, request);
return false;
} else {
ifdebug(PROTOCOL) {
ifdebug(META)
logger(LOG_DEBUG, "Got %s from %s (%s): %s",
request_name[reqno], c->name, c->hostname, request);
else
logger(LOG_DEBUG, "Got %s from %s (%s)",
request_name[reqno], c->name, c->hostname);
}
logger(DEBUG_META, LOG_DEBUG, "Got %s from %s (%s): %s", request_name[reqno], c->name, c->hostname, request);
}
if((c->allow_request != ALL) && (c->allow_request != reqno)) {
logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Unauthorized request from %s (%s)", c->name, c->hostname);
return false;
}
if(!request_handlers[reqno](c, request)) {
/* Something went wrong. Probably scriptkiddies. Terminate. */
logger(LOG_ERR, "Error while processing %s from %s (%s)",
request_name[reqno], c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Error while processing %s from %s (%s)", request_name[reqno], c->name, c->hostname);
return false;
}
} else {
logger(LOG_ERR, "Bogus data received from %s (%s)",
c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Bogus data received from %s (%s)", c->name, c->hostname);
return false;
}
@ -171,20 +160,20 @@ static int past_request_compare(const past_request_t *a, const past_request_t *b
static void free_past_request(past_request_t *r) {
if(r->request)
free(r->request);
free((char *)r->request);
free(r);
}
static struct event past_request_event;
bool seen_request(char *request) {
bool seen_request(const char *request) {
past_request_t *new, p = {NULL};
p.request = request;
if(splay_search(past_request_tree, &p)) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Already seen request");
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Already seen request");
return true;
} else {
new = xmalloc(sizeof *new);
@ -197,15 +186,10 @@ bool seen_request(char *request) {
}
static void age_past_requests(int fd, short events, void *data) {
splay_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
time_t now = time(NULL);
for(node = past_request_tree->head; node; node = next) {
next = node->next;
p = node->data;
for splay_each(past_request_t, p, past_request_tree) {
if(p->firstseen + pinginterval <= now)
splay_delete_node(past_request_tree, node), deleted++;
else
@ -213,7 +197,7 @@ static void age_past_requests(int fd, short events, void *data) {
}
if(left || deleted)
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Aging past requests: deleted %d, left %d",
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Aging past requests: deleted %d, left %d",
deleted, left);
if(left)

View file

@ -1,7 +1,7 @@
/*
protocol.h -- header for protocol.c
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -24,7 +24,7 @@
/* Protocol version. Different major versions are incompatible. */
#define PROT_MAJOR 17
#define PROT_MINOR 2
#define PROT_MINOR 2 /* Should not exceed 255! */
/* Silly Windows */
@ -35,7 +35,7 @@
/* Request numbers */
typedef enum request_t {
ALL = -1, /* Guardian for allow_request */
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
@ -43,12 +43,15 @@ typedef enum request_t {
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
/* Tinc 1.1 requests */
CONTROL,
LAST /* Guardian for the highest request number */
REQ_PUBKEY, ANS_PUBKEY,
REQ_SPTPS,
LAST /* Guardian for the highest request number */
} request_t;
typedef struct past_request_t {
char *request;
const char *request;
time_t firstseen;
} past_request_t;
@ -72,13 +75,13 @@ extern bool experimental;
/* Basic functions */
extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void forward_request(struct connection_t *, char *);
extern bool receive_request(struct connection_t *, char *);
extern void forward_request(struct connection_t *, const char *);
extern bool receive_request(struct connection_t *, const char *);
extern bool check_id(const char *);
extern void init_requests(void);
extern void exit_requests(void);
extern bool seen_request(char *);
extern bool seen_request(const char *);
/* Requests */
@ -89,7 +92,7 @@ extern bool send_challenge(struct connection_t *);
extern bool send_chal_reply(struct connection_t *);
extern bool send_ack(struct connection_t *);
extern bool send_status(struct connection_t *, int, const char *);
extern bool send_error(struct connection_t *, int,const char *);
extern bool send_error(struct connection_t *, int, const char *);
extern bool send_termreq(struct connection_t *);
extern bool send_ping(struct connection_t *);
extern bool send_pong(struct connection_t *);
@ -104,24 +107,24 @@ extern bool send_tcppacket(struct connection_t *, const struct vpn_packet_t *);
/* Request handlers */
extern bool id_h(struct connection_t *, char *);
extern bool metakey_h(struct connection_t *, char *);
extern bool challenge_h(struct connection_t *, char *);
extern bool chal_reply_h(struct connection_t *, char *);
extern bool ack_h(struct connection_t *, char *);
extern bool status_h(struct connection_t *, char *);
extern bool error_h(struct connection_t *, char *);
extern bool termreq_h(struct connection_t *, char *);
extern bool ping_h(struct connection_t *, char *);
extern bool pong_h(struct connection_t *, char *);
extern bool add_subnet_h(struct connection_t *, char *);
extern bool del_subnet_h(struct connection_t *, char *);
extern bool add_edge_h(struct connection_t *, char *);
extern bool del_edge_h(struct connection_t *, char *);
extern bool key_changed_h(struct connection_t *, char *);
extern bool req_key_h(struct connection_t *, char *);
extern bool ans_key_h(struct connection_t *, char *);
extern bool tcppacket_h(struct connection_t *, char *);
extern bool control_h(struct connection_t *, char *);
extern bool id_h(struct connection_t *, const char *);
extern bool metakey_h(struct connection_t *, const char *);
extern bool challenge_h(struct connection_t *, const char *);
extern bool chal_reply_h(struct connection_t *, const char *);
extern bool ack_h(struct connection_t *, const char *);
extern bool status_h(struct connection_t *, const char *);
extern bool error_h(struct connection_t *, const char *);
extern bool termreq_h(struct connection_t *, const char *);
extern bool ping_h(struct connection_t *, const char *);
extern bool pong_h(struct connection_t *, const char *);
extern bool add_subnet_h(struct connection_t *, const char *);
extern bool del_subnet_h(struct connection_t *, const char *);
extern bool add_edge_h(struct connection_t *, const char *);
extern bool del_edge_h(struct connection_t *, const char *);
extern bool key_changed_h(struct connection_t *, const char *);
extern bool req_key_h(struct connection_t *, const char *);
extern bool ans_key_h(struct connection_t *, const char *);
extern bool tcppacket_h(struct connection_t *, const char *);
extern bool control_h(struct connection_t *, const char *);
#endif /* __TINC_PROTOCOL_H__ */
#endif /* __TINC_PROTOCOL_H__ */

View file

@ -1,7 +1,7 @@
/*
protocol_auth.c -- handle the meta-protocol, authentication
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,6 @@
#include "system.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "control.h"
@ -31,15 +30,103 @@
#include "edge.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "prf.h"
#include "protocol.h"
#include "rsa.h"
#include "sptps.h"
#include "utils.h"
#include "xalloc.h"
static bool send_proxyrequest(connection_t *c) {
switch(proxytype) {
case PROXY_HTTP: {
char *host;
char *port;
sockaddr2str(&c->address, &host, &port);
send_request(c, "CONNECT %s:%s HTTP/1.1\r\n\r", host, port);
free(host);
free(port);
return true;
}
case PROXY_SOCKS4: {
if(c->address.sa.sa_family != AF_INET) {
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
return false;
}
char s4req[9 + (proxyuser ? strlen(proxyuser) : 0)];
s4req[0] = 4;
s4req[1] = 1;
memcpy(s4req + 2, &c->address.in.sin_port, 2);
memcpy(s4req + 4, &c->address.in.sin_addr, 4);
if(proxyuser)
memcpy(s4req + 8, proxyuser, strlen(proxyuser));
s4req[sizeof s4req - 1] = 0;
c->tcplen = 8;
return send_meta(c, s4req, sizeof s4req);
}
case PROXY_SOCKS5: {
int len = 3 + 6 + (c->address.sa.sa_family == AF_INET ? 4 : 16);
c->tcplen = 2;
if(proxypass)
len += 3 + strlen(proxyuser) + strlen(proxypass);
char s5req[len];
int i = 0;
s5req[i++] = 5;
s5req[i++] = 1;
if(proxypass) {
s5req[i++] = 2;
s5req[i++] = 1;
s5req[i++] = strlen(proxyuser);
memcpy(s5req + i, proxyuser, strlen(proxyuser));
i += strlen(proxyuser);
s5req[i++] = strlen(proxypass);
memcpy(s5req + i, proxypass, strlen(proxypass));
i += strlen(proxypass);
c->tcplen += 2;
} else {
s5req[i++] = 0;
}
s5req[i++] = 5;
s5req[i++] = 1;
s5req[i++] = 0;
if(c->address.sa.sa_family == AF_INET) {
s5req[i++] = 1;
memcpy(s5req + i, &c->address.in.sin_addr, 4);
i += 4;
memcpy(s5req + i, &c->address.in.sin_port, 2);
i += 2;
c->tcplen += 10;
} else if(c->address.sa.sa_family == AF_INET6) {
s5req[i++] = 3;
memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
i += 16;
memcpy(s5req + i, &c->address.in6.sin6_port, 2);
i += 2;
c->tcplen += 22;
} else {
logger(DEBUG_ALWAYS, LOG_ERR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
return false;
}
if(i > len)
abort();
return send_meta(c, s5req, sizeof s5req);
}
case PROXY_SOCKS4A:
logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
return false;
case PROXY_EXEC:
return true;
default:
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
return false;
}
}
bool send_id(connection_t *c) {
gettimeofday(&c->start, NULL);
@ -52,14 +139,18 @@ bool send_id(connection_t *c) {
minor = myself->connection->protocol_minor;
}
if(proxytype)
if(!send_proxyrequest(c))
return false;
return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
}
bool id_h(connection_t *c, char *request) {
bool id_h(connection_t *c, const char *request) {
char name[MAX_STRING_SIZE];
if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
c->hostname);
return false;
}
@ -70,13 +161,17 @@ bool id_h(connection_t *c, char *request) {
c->status.control = true;
c->allow_request = CONTROL;
c->last_ping_time = time(NULL) + 3600;
free(c->name);
c->name = xstrdup("<control>");
return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
}
/* Check if identity is a valid name */
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name,
c->hostname, "invalid name");
return false;
}
@ -85,7 +180,7 @@ bool id_h(connection_t *c, char *request) {
if(c->outgoing) {
if(strcmp(c->name, name)) {
logger(LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name,
logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name,
c->name);
return false;
}
@ -98,7 +193,7 @@ bool id_h(connection_t *c, char *request) {
/* Check if version matches */
if(c->protocol_major != myself->connection->protocol_major) {
logger(LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
c->name, c->hostname, c->protocol_major, c->protocol_minor);
return false;
}
@ -110,52 +205,41 @@ bool id_h(connection_t *c, char *request) {
return send_ack(c);
}
if(!c->config_tree) {
init_configuration(&c->config_tree);
if(!read_connection_config(c)) {
logger(LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname,
c->name);
return false;
}
if(experimental && c->protocol_minor >= 2)
if(!read_ecdsa_public_key(c))
return false;
} else {
if(!ecdsa_active(&c->ecdsa))
c->protocol_minor = 1;
}
if(!experimental)
c->protocol_minor = 0;
if(!c->config_tree) {
init_configuration(&c->config_tree);
if(!read_host_config(c->config_tree, c->name)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname, c->name);
return false;
}
if(experimental && c->protocol_minor >= 2) {
if(!read_ecdsa_public_key(c))
return false;
}
} else {
if(c->protocol_minor && !ecdsa_active(&c->ecdsa))
c->protocol_minor = 1;
}
c->allow_request = METAKEY;
if(c->protocol_minor >= 2)
return send_metakey_ec(c);
else
if(c->protocol_minor >= 2) {
c->allow_request = ACK;
char label[25 + strlen(myself->name) + strlen(c->name)];
if(c->outgoing)
snprintf(label, sizeof label, "tinc TCP key expansion %s %s", myself->name, c->name);
else
snprintf(label, sizeof label, "tinc TCP key expansion %s %s", c->name, myself->name);
return sptps_start(&c->sptps, c, c->outgoing, false, myself->connection->ecdsa, c->ecdsa, label, sizeof label, send_meta_sptps, receive_meta_sptps);
} else {
return send_metakey(c);
}
bool send_metakey_ec(connection_t *c) {
logger(LOG_DEBUG, "Sending ECDH metakey to %s", c->name);
size_t siglen = ecdsa_size(&myself->connection->ecdsa);
char key[(ECDH_SIZE + siglen) * 2 + 1];
// TODO: include nonce? Use relevant parts of SSH or TLS protocol
if(!ecdh_generate_public(&c->ecdh, key))
return false;
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
return false;
b64encode(key, key, ECDH_SIZE + siglen);
return send_request(c, "%d %s", METAKEY, key);
}
}
bool send_metakey(connection_t *c) {
@ -164,7 +248,7 @@ bool send_metakey(connection_t *c) {
if(!cipher_open_blowfish_ofb(&c->outcipher))
return false;
if(!digest_open_sha1(&c->outdigest, -1))
return false;
@ -191,9 +275,9 @@ bool send_metakey(connection_t *c) {
cipher_set_key_from_rsa(&c->outcipher, key, len, true);
ifdebug(SCARY_THINGS) {
if(debug_level >= DEBUG_SCARY_THINGS) {
bin2hex(key, hexkey, len);
logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
}
/* Encrypt the random data
@ -204,7 +288,7 @@ bool send_metakey(connection_t *c) {
*/
if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) {
logger(LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
@ -218,90 +302,12 @@ bool send_metakey(connection_t *c) {
cipher_get_nid(&c->outcipher),
digest_get_nid(&c->outdigest), c->outmaclength,
c->outcompression, hexkey);
c->status.encryptout = true;
return result;
}
static bool metakey_ec_h(connection_t *c, const char *request) {
size_t siglen = ecdsa_size(&c->ecdsa);
char key[MAX_STRING_SIZE];
char sig[siglen];
logger(LOG_DEBUG, "Got ECDH metakey from %s", c->name);
if(sscanf(request, "%*d " MAX_STRING, key) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
return false;
}
int inlen = b64decode(key, key, sizeof key);
if(inlen != (ECDH_SIZE + siglen)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
return false;
}
if(!ecdsa_verify(&c->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "invalid ECDSA signature");
return false;
}
char shared[ECDH_SHARED_SIZE];
if(!ecdh_compute_shared(&c->ecdh, key, shared))
return false;
/* Update our crypto end */
if(!cipher_open_by_name(&c->incipher, "aes-256-ofb"))
return false;
if(!digest_open_by_name(&c->indigest, "sha512", -1))
return false;
if(!cipher_open_by_name(&c->outcipher, "aes-256-ofb"))
return false;
if(!digest_open_by_name(&c->outdigest, "sha512", -1))
return false;
size_t mykeylen = cipher_keylength(&c->incipher);
size_t hiskeylen = cipher_keylength(&c->outcipher);
char *mykey;
char *hiskey;
char *seed;
if(strcmp(myself->name, c->name) < 0) {
mykey = key;
hiskey = key + mykeylen * 2;
xasprintf(&seed, "tinc TCP key expansion %s %s", myself->name, c->name);
} else {
mykey = key + hiskeylen * 2;
hiskey = key;
xasprintf(&seed, "tinc TCP key expansion %s %s", c->name, myself->name);
}
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
return false;
free(seed);
cipher_set_key(&c->incipher, mykey, false);
digest_set_key(&c->indigest, mykey + mykeylen, mykeylen);
cipher_set_key(&c->outcipher, hiskey, true);
digest_set_key(&c->outdigest, hiskey + hiskeylen, hiskeylen);
c->status.decryptin = true;
c->status.encryptout = true;
c->allow_request = CHALLENGE;
return send_challenge(c);
}
bool metakey_h(connection_t *c, char *request) {
if(c->protocol_minor >= 2)
return metakey_ec_h(c, request);
bool metakey_h(connection_t *c, const char *request) {
char hexkey[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
size_t len = rsa_size(&myself->connection->rsa);
@ -309,7 +315,7 @@ bool metakey_h(connection_t *c, char *request) {
char key[len];
if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
return false;
}
@ -320,31 +326,31 @@ bool metakey_h(connection_t *c, char *request) {
/* Check if the length of the meta key is all right */
if(inlen != len) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
return false;
}
/* Decrypt the meta key */
if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) {
logger(LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
ifdebug(SCARY_THINGS) {
if(debug_level >= DEBUG_SCARY_THINGS) {
bin2hex(key, hexkey, len);
logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
}
/* Check and lookup cipher and digest algorithms */
if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) {
logger(LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
return false;
}
if(!digest_open_by_nid(&c->indigest, digest, -1)) {
logger(LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
return false;
}
@ -356,7 +362,7 @@ bool metakey_h(connection_t *c, char *request) {
}
bool send_challenge(connection_t *c) {
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa);
size_t len = rsa_size(&c->rsa);
char buffer[len * 2 + 1];
if(!c->hischallenge)
@ -375,14 +381,14 @@ bool send_challenge(connection_t *c) {
return send_request(c, "%d %s", CHALLENGE, buffer);
}
bool challenge_h(connection_t *c, char *request) {
bool challenge_h(connection_t *c, const char *request) {
char buffer[MAX_STRING_SIZE];
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&myself->connection->rsa);
size_t len = rsa_size(&myself->connection->rsa);
size_t digestlen = digest_length(&c->indigest);
char digest[digestlen];
if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
return false;
}
@ -393,7 +399,7 @@ bool challenge_h(connection_t *c, char *request) {
/* Check if the length of the challenge is all right */
if(inlen != len) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
return false;
}
@ -412,11 +418,11 @@ bool challenge_h(connection_t *c, char *request) {
return send_request(c, "%d %s", CHAL_REPLY, buffer);
}
bool chal_reply_h(connection_t *c, char *request) {
bool chal_reply_h(connection_t *c, const char *request) {
char hishash[MAX_STRING_SIZE];
if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
c->hostname);
return false;
}
@ -428,15 +434,15 @@ bool chal_reply_h(connection_t *c, char *request) {
/* Check if the length of the hash is all right */
if(inlen != digest_length(&c->outdigest)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
return false;
}
/* Verify the hash */
if(!digest_verify(&c->outdigest, c->hischallenge, c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa), hishash)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
return false;
}
@ -498,61 +504,48 @@ bool send_ack(connection_t *c) {
get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight);
return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, c->options);
return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, (c->options & 0xffffff) | (experimental ? (PROT_MINOR << 24) : 0));
}
static void send_everything(connection_t *c) {
splay_node_t *node, *node2;
node_t *n;
subnet_t *s;
edge_t *e;
/* Send all known subnets and edges */
if(tunnelserver) {
for(node = myself->subnet_tree->head; node; node = node->next) {
s = node->data;
for splay_each(subnet_t, s, myself->subnet_tree)
send_add_subnet(c, s);
}
return;
}
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for(node2 = n->subnet_tree->head; node2; node2 = node2->next) {
s = node2->data;
for splay_each(node_t, n, node_tree) {
for splay_each(subnet_t, s, n->subnet_tree)
send_add_subnet(c, s);
}
for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
e = node2->data;
for splay_each(edge_t, e, n->edge_tree)
send_add_edge(c, e);
}
}
}
static bool upgrade_h(connection_t *c, char *request) {
static bool upgrade_h(connection_t *c, const char *request) {
char pubkey[MAX_STRING_SIZE];
if(sscanf(request, "%*d " MAX_STRING, pubkey) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
return false;
}
if(ecdsa_active(&c->ecdsa) || read_ecdsa_public_key(c)) {
logger(LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
return false;
}
logger(LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
logger(DEBUG_ALWAYS, LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
append_config_file(c->name, "ECDSAPublicKey", pubkey);
c->allow_request = TERMREQ;
return send_termreq(c);
}
bool ack_h(connection_t *c, char *request) {
bool ack_h(connection_t *c, const char *request) {
if(c->protocol_minor == 1)
return upgrade_h(c, request);
@ -564,7 +557,7 @@ bool ack_h(connection_t *c, char *request) {
bool choice;
if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
c->hostname);
return false;
}
@ -580,11 +573,11 @@ bool ack_h(connection_t *c, char *request) {
} else {
if(n->connection) {
/* Oh dear, we already have a connection to this node. */
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
if(n->connection->outgoing) {
if(c->outgoing)
logger(LOG_WARNING, "Two outgoing connections to the same node!");
logger(DEBUG_ALWAYS, LOG_WARNING, "Two outgoing connections to the same node!");
else
c->outgoing = n->connection->outgoing;
@ -618,15 +611,12 @@ bool ack_h(connection_t *c, char *request) {
c->options &= ~OPTION_CLAMP_MSS;
}
if(c->protocol_minor > 0)
c->node->status.ecdh = true;
/* Activate this connection */
c->allow_request = ALL;
c->status.active = true;
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection with %s (%s) activated", c->name,
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
c->hostname);
/* Send him everything we know */
@ -652,7 +642,7 @@ bool ack_h(connection_t *c, char *request) {
if(tunnelserver)
send_add_edge(c, c->edge);
else
send_add_edge(broadcast, c->edge);
send_add_edge(everyone, c->edge);
/* Run MST and SSSP algorithms */

View file

@ -1,7 +1,7 @@
/*
protocol_edge.c -- handle the meta-protocol, edges
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2009 Michael Tokarev <mjt@corpit.ru>
This program is free software; you can redistribute it and/or modify
@ -21,7 +21,6 @@
#include "system.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "edge.h"
@ -50,7 +49,7 @@ bool send_add_edge(connection_t *c, const edge_t *e) {
return x;
}
bool add_edge_h(connection_t *c, char *request) {
bool add_edge_h(connection_t *c, const char *request) {
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
@ -63,7 +62,7 @@ bool add_edge_h(connection_t *c, char *request) {
if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
from_name, to_name, to_address, to_port, &options, &weight) != 6) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
c->hostname);
return false;
}
@ -71,7 +70,7 @@ bool add_edge_h(connection_t *c, char *request) {
/* Check if names are valid */
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
c->hostname, "invalid name");
return false;
}
@ -88,7 +87,7 @@ bool add_edge_h(connection_t *c, char *request) {
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
logger(DEBUG_PROTOCOL, LOG_WARNING,
"Ignoring indirect %s from %s (%s)",
"ADD_EDGE", c->name, c->hostname);
return true;
@ -118,12 +117,12 @@ bool add_edge_h(connection_t *c, char *request) {
if(e) {
if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
logger(DEBUG_PROTOCOL, 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 true;
} else {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) which does not match existing entry",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) which does not match existing entry",
"ADD_EDGE", c->name, c->hostname);
edge_del(e);
graph();
@ -131,7 +130,7 @@ bool add_edge_h(connection_t *c, char *request) {
} else
return true;
} else if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself which does not exist",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself which does not exist",
"ADD_EDGE", c->name, c->hostname);
contradicting_add_edge++;
e = new_edge();
@ -167,14 +166,14 @@ bool send_del_edge(connection_t *c, const edge_t *e) {
e->from->name, e->to->name);
}
bool del_edge_h(connection_t *c, char *request) {
bool del_edge_h(connection_t *c, const char *request) {
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
c->hostname);
return false;
}
@ -182,7 +181,7 @@ bool del_edge_h(connection_t *c, char *request) {
/* Check if names are valid */
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
c->hostname, "invalid name");
return false;
}
@ -199,20 +198,20 @@ bool del_edge_h(connection_t *c, char *request) {
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
logger(DEBUG_PROTOCOL, LOG_WARNING,
"Ignoring indirect %s from %s (%s)",
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
ifdebug(PROTOCOL) logger(LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
logger(DEBUG_PROTOCOL, LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!to) {
ifdebug(PROTOCOL) logger(LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
logger(DEBUG_PROTOCOL, LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
return true;
}
@ -222,16 +221,16 @@ bool del_edge_h(connection_t *c, char *request) {
e = lookup_edge(from, to);
if(!e) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) which does not appear in the edge tree",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(e->from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
"DEL_EDGE", c->name, c->hostname);
contradicting_del_edge++;
send_add_edge(c, e); /* Send back a correction */
send_add_edge(c, e); /* Send back a correction */
return true;
}
@ -254,7 +253,7 @@ bool del_edge_h(connection_t *c, char *request) {
e = lookup_edge(to, myself);
if(e) {
if(!tunnelserver)
send_del_edge(broadcast, e);
send_del_edge(everyone, e);
edge_del(e);
}
}

View file

@ -1,7 +1,7 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,43 +20,45 @@
#include "system.h"
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "crypto.h"
#include "ecdh.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "prf.h"
#include "protocol.h"
#include "sptps.h"
#include "utils.h"
#include "xalloc.h"
static bool mykeyused = false;
void send_key_changed(void) {
splay_node_t *node;
connection_t *c;
send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name);
/* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c->status.active && c->node && c->node->status.reachable)
for list_each(connection_t, c, connection_list)
if(c->status.active && c->node && c->node->status.reachable && !c->node->status.sptps)
send_ans_key(c->node);
/* Force key exchange for connections using SPTPS */
if(experimental) {
for splay_each(node_t, n, node_tree)
if(n->status.reachable && n->status.validkey && n->status.sptps)
sptps_force_kex(&n->sptps);
}
}
bool key_changed_h(connection_t *c, char *request) {
bool key_changed_h(connection_t *c, const char *request) {
char name[MAX_STRING_SIZE];
node_t *n;
if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
c->name, c->hostname);
return false;
}
@ -67,13 +69,15 @@ bool key_changed_h(connection_t *c, char *request) {
n = lookup_node(name);
if(!n) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
"KEY_CHANGED", c->name, c->hostname, name);
return true;
}
n->status.validkey = false;
n->last_req_key = 0;
if(!n->status.sptps) {
n->status.validkey = false;
n->last_req_key = 0;
}
/* Tell the others */
@ -83,31 +87,137 @@ bool key_changed_h(connection_t *c, char *request) {
return true;
}
bool send_req_key(node_t *to) {
return send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, experimental ? 1 : 0);
static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
node_t *to = handle;
to->sptps.send_data = send_sptps_data;
char buf[len * 4 / 3 + 5];
b64encode(data, buf, len);
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_KEY, buf);
}
bool req_key_h(connection_t *c, char *request) {
bool send_req_key(node_t *to) {
if(to->status.sptps) {
if(!node_read_ecdsa_public_key(to)) {
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
return true;
}
if(to->sptps.label)
logger(DEBUG_ALWAYS, LOG_DEBUG, "send_req_key(%s) called while sptps->label != NULL!", to->name);
char label[25 + strlen(myself->name) + strlen(to->name)];
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", myself->name, to->name);
sptps_stop(&to->sptps);
to->status.validkey = false;
to->status.waitingforkey = true;
to->last_req_key = time(NULL);
to->incompression = myself->incompression;
return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record);
}
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
}
/* REQ_KEY is overloaded to allow arbitrary requests to be routed between two nodes. */
static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, int reqno) {
switch(reqno) {
case REQ_PUBKEY: {
char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
send_request(from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, from->name, ANS_PUBKEY, pubkey);
free(pubkey);
return true;
}
case ANS_PUBKEY: {
if(node_read_ecdsa_public_key(from)) {
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got ANS_PUBKEY from %s (%s) even though we already have his pubkey", from->name, from->hostname);
return true;
}
char pubkey[MAX_STRING_SIZE];
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !ecdsa_set_base64_public_key(&from->ecdsa, pubkey)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
return true;
}
logger(DEBUG_PROTOCOL, LOG_INFO, "Learned ECDSA public key from %s (%s)", from->name, from->hostname);
append_config_file(from->name, "ECDSAPublicKey", pubkey);
return true;
}
case REQ_KEY: {
if(!node_read_ecdsa_public_key(from)) {
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
return true;
}
if(from->sptps.label)
logger(DEBUG_ALWAYS, LOG_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name);
char buf[MAX_STRING_SIZE];
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1) {
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS_START", from->name, from->hostname, "invalid SPTPS data");
return true;
}
int len = b64decode(buf, buf, strlen(buf));
char label[25 + strlen(from->name) + strlen(myself->name)];
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", from->name, myself->name);
sptps_stop(&from->sptps);
from->status.validkey = false;
from->status.waitingforkey = true;
from->last_req_key = time(NULL);
sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record);
sptps_receive_data(&from->sptps, buf, len);
return true;
}
case REQ_SPTPS: {
if(!from->status.validkey) {
logger(DEBUG_PROTOCOL, LOG_ERR, "Got REQ_SPTPS from %s (%s) but we don't have a valid key yet", from->name, from->hostname);
return true;
}
char buf[MAX_STRING_SIZE];
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1) {
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS", from->name, from->hostname, "invalid SPTPS data");
return true;
}
int len = b64decode(buf, buf, strlen(buf));
sptps_receive_data(&from->sptps, buf, len);
return true;
}
default:
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
return true;
}
}
bool req_key_h(connection_t *c, const char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
int kx_version = 0;
int reqno = 0;
if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &kx_version) < 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &reqno) < 2) {
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
c->hostname);
return false;
}
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
return false;
}
from = lookup_node(from_name);
if(!from) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
logger(DEBUG_ALWAYS, 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 true;
}
@ -115,25 +225,26 @@ bool req_key_h(connection_t *c, char *request) {
to = lookup_node(to_name);
if(!to) {
logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
logger(DEBUG_ALWAYS, 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 true;
}
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
if(experimental && kx_version >= 1) {
logger(LOG_DEBUG, "Got ECDH key request from %s", from->name);
from->status.ecdh = true;
}
if(to == myself) { /* Yes */
/* Is this an extended REQ_KEY message? */
if(experimental && reqno)
return req_key_ext_h(c, request, from, reqno);
/* No, just send our key back */
send_ans_key(from);
} else {
if(tunnelserver)
return true;
if(!to->status.reachable) {
logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
"REQ_KEY", c->name, c->hostname, to_name);
return true;
}
@ -144,41 +255,16 @@ bool req_key_h(connection_t *c, char *request) {
return true;
}
bool send_ans_key_ecdh(node_t *to) {
int siglen = ecdsa_size(&myself->connection->ecdsa);
char key[(ECDH_SIZE + siglen) * 2 + 1];
if(!ecdh_generate_public(&to->ecdh, key))
return false;
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
return false;
b64encode(key, key, ECDH_SIZE + siglen);
char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
if(!pubkey)
return false;
int result = send_request(to->nexthop->connection, "%d %s %s ECDH:%s:%s %d %d %zu %d", ANS_KEY,
myself->name, to->name, key, pubkey,
cipher_get_nid(&myself->incipher),
digest_get_nid(&myself->indigest),
digest_length(&myself->indigest),
myself->incompression);
free(pubkey);
return result;
}
bool send_ans_key(node_t *to) {
if(experimental && to->status.ecdh)
return send_ans_key_ecdh(to);
if(to->status.sptps)
abort();
size_t keylen = cipher_keylength(&myself->incipher);
char key[keylen * 2 + 1];
cipher_close(&to->incipher);
digest_close(&to->indigest);
cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
to->incompression = myself->incompression;
@ -194,40 +280,40 @@ bool send_ans_key(node_t *to) {
to->received_seqno = 0;
if(replaywin) memset(to->late, 0, replaywin);
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
myself->name, to->name, key,
cipher_get_nid(&to->incipher),
digest_get_nid(&to->indigest),
digest_length(&to->indigest),
(int)digest_length(&to->indigest),
to->incompression);
}
bool ans_key_h(connection_t *c, char *request) {
bool ans_key_h(connection_t *c, const char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
char address[MAX_STRING_SIZE] = "";
char port[MAX_STRING_SIZE] = "";
char address[MAX_STRING_SIZE] = "";
char port[MAX_STRING_SIZE] = "";
int cipher, digest, maclength, compression, keylen;
node_t *from, *to;
if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
from_name, to_name, key, &cipher, &digest, &maclength,
&compression, address, port) < 7) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
c->hostname);
return false;
}
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
return false;
}
from = lookup_node(from_name);
if(!from) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
logger(DEBUG_ALWAYS, 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 true;
}
@ -235,7 +321,7 @@ bool ans_key_h(connection_t *c, char *request) {
to = lookup_node(to_name);
if(!to) {
logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
logger(DEBUG_ALWAYS, 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 true;
}
@ -247,14 +333,14 @@ bool ans_key_h(connection_t *c, char *request) {
return true;
if(!to->status.reachable) {
logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
logger(DEBUG_ALWAYS, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
"ANS_KEY", c->name, c->hostname, to_name);
return true;
}
if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
char *address, *port;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
sockaddr2str(&from->address, &address, &port);
send_request(to->nexthop->connection, "%s %s %s", request, address, port);
free(address);
@ -265,142 +351,77 @@ bool ans_key_h(connection_t *c, char *request) {
return send_request(to->nexthop->connection, "%s", request);
}
/* Check and lookup cipher and digest algorithms */
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
return false;
}
if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
return false;
}
if(maclength != digest_length(&from->outdigest)) {
logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
return false;
}
/* Don't use key material until every check has passed. */
cipher_close(&from->outcipher);
digest_close(&from->outdigest);
from->status.validkey = false;
if(compression < 0 || compression > 11) {
logger(LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
return true;
}
from->outcompression = compression;
/* ECDH or old-style key exchange? */
if(experimental && !strncmp(key, "ECDH:", 5)) {
char *pubkey = strchr(key + 5, ':');
if(pubkey)
*pubkey++ = 0;
/* Check if we already have an ECDSA public key for this node.
* If not, use the one from the key exchange, and store it. */
/* SPTPS or old-style key exchange? */
if(!node_read_ecdsa_public_key(from)) {
if(!pubkey) {
logger(LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
return true;
if(from->status.sptps) {
char buf[strlen(key)];
int len = b64decode(key, buf, strlen(key));
if(!sptps_receive_data(&from->sptps, buf, len))
logger(DEBUG_ALWAYS, LOG_ERR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
if(from->status.validkey) {
if(*address && *port) {
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
sockaddr_t sa = str2sockaddr(address, port);
update_node_udp(from, &sa);
}
if(!ecdsa_set_base64_public_key(&from->ecdsa, pubkey))
return true;
append_config_file(from->name, "ECDSAPublicKey", pubkey);
if(from->options & OPTION_PMTU_DISCOVERY)
send_mtu_probe(from);
}
int siglen = ecdsa_size(&from->ecdsa);
int keylen = b64decode(key + 5, key + 5, sizeof key - 5);
if(keylen != ECDH_SIZE + siglen) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
return true;
}
if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
return true;
}
if(!ecdsa_verify(&from->ecdsa, key + 5, ECDH_SIZE, key + 5 + ECDH_SIZE)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
return true;
}
if(!from->ecdh) {
from->status.ecdh = true;
if(!send_ans_key(from))
return true;
}
char shared[ECDH_SHARED_SIZE * 2 + 1];
if(!ecdh_compute_shared(&from->ecdh, key + 5, shared))
return true;
/* Update our crypto end */
size_t mykeylen = cipher_keylength(&myself->incipher);
size_t hiskeylen = cipher_keylength(&from->outcipher);
char *mykey;
char *hiskey;
char *seed;
if(strcmp(myself->name, from->name) < 0) {
mykey = key;
hiskey = key + mykeylen * 2;
xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
} else {
mykey = key + hiskeylen * 2;
hiskey = key;
xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
}
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
return true;
free(seed);
cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
from->incompression = myself->incompression;
cipher_set_key(&from->incipher, mykey, false);
digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
cipher_set_key(&from->outcipher, hiskey, true);
digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
// Reset sequence number and late packet window
mykeyused = true;
from->received_seqno = 0;
if(replaywin)
memset(from->late, 0, replaywin);
if(strcmp(myself->name, from->name) < 0)
memmove(key, key + mykeylen * 2, hiskeylen * 2);
} else {
keylen = hex2bin(key, key, sizeof key);
if(keylen != cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
return true;
}
/* Update our copy of the origin's packet key */
cipher_set_key(&from->outcipher, key, true);
digest_set_key(&from->outdigest, key, keylen);
return true;
}
/* Check and lookup cipher and digest algorithms */
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
return false;
}
if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
return false;
}
if(maclength != digest_length(&from->outdigest)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
return false;
}
/* Process key */
keylen = hex2bin(key, key, sizeof key);
if(keylen != cipher_keylength(&from->outcipher)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
return true;
}
/* Update our copy of the origin's packet key */
cipher_set_key(&from->outcipher, key, true);
digest_set_key(&from->outdigest, key, keylen);
from->status.validkey = true;
from->sent_seqno = 0;
if(*address && *port) {
ifdebug(PROTOCOL) logger(LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
sockaddr_t sa = str2sockaddr(address, port);
update_node_udp(from, &sa);
}

View file

@ -1,7 +1,7 @@
/*
protocol_misc.c -- handle the meta-protocol, miscellaneous functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -40,17 +40,17 @@ bool send_status(connection_t *c, int statusno, const char *statusstring) {
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
}
bool status_h(connection_t *c, char *request) {
bool status_h(connection_t *c, const char *request) {
int statusno;
char statusstring[MAX_STRING_SIZE];
if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
c->name, c->hostname);
return false;
}
ifdebug(STATUS) logger(LOG_NOTICE, "Status message from %s (%s): %d: %s",
logger(DEBUG_STATUS, LOG_NOTICE, "Status message from %s (%s): %d: %s",
c->name, c->hostname, statusno, statusstring);
return true;
@ -63,17 +63,17 @@ bool send_error(connection_t *c, int err, const char *errstring) {
return send_request(c, "%d %d %s", ERROR, err, errstring);
}
bool error_h(connection_t *c, char *request) {
bool error_h(connection_t *c, const char *request) {
int err;
char errorstring[MAX_STRING_SIZE];
if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
c->name, c->hostname);
return false;
}
ifdebug(ERROR) logger(LOG_NOTICE, "Error message from %s (%s): %d: %s",
logger(DEBUG_ERROR, LOG_NOTICE, "Error message from %s (%s): %d: %s",
c->name, c->hostname, err, errorstring);
return false;
@ -83,7 +83,7 @@ bool send_termreq(connection_t *c) {
return send_request(c, "%d", TERMREQ);
}
bool termreq_h(connection_t *c, char *request) {
bool termreq_h(connection_t *c, const char *request) {
return false;
}
@ -94,7 +94,7 @@ bool send_ping(connection_t *c) {
return send_request(c, "%d", PING);
}
bool ping_h(connection_t *c, char *request) {
bool ping_h(connection_t *c, const char *request) {
return send_pong(c);
}
@ -102,13 +102,19 @@ bool send_pong(connection_t *c) {
return send_request(c, "%d", PONG);
}
bool pong_h(connection_t *c, char *request) {
bool pong_h(connection_t *c, const char *request) {
c->status.pinged = false;
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing)
if(c->outgoing) {
c->outgoing->timeout = 0;
c->outgoing->cfg = NULL;
if(c->outgoing->ai)
freeaddrinfo(c->outgoing->ai);
c->outgoing->ai = NULL;
c->outgoing->aip = NULL;
}
return true;
}
@ -117,7 +123,7 @@ bool pong_h(connection_t *c, char *request) {
bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
/* If there already is a lot of data in the outbuf buffer, discard this packet.
We use a very simple Random Early Drop algorithm. */
We use a very simple Random Early Drop algorithm. */
if(2.0 * c->outbuf.len / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
return true;
@ -128,11 +134,11 @@ bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
return send_meta(c, (char *)packet->data, packet->len);
}
bool tcppacket_h(connection_t *c, char *request) {
bool tcppacket_h(connection_t *c, const char *request) {
short int len;
if(sscanf(request, "%*d %hd", &len) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
c->hostname);
return false;
}

View file

@ -1,7 +1,7 @@
/*
protocol_subnet.c -- handle the meta-protocol, subnets
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2009 Michael Tokarev <mjt@tls.msk.ru>
This program is free software; you can redistribute it and/or modify
@ -41,14 +41,14 @@ bool send_add_subnet(connection_t *c, const subnet_t *subnet) {
return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
}
bool add_subnet_h(connection_t *c, char *request) {
bool add_subnet_h(connection_t *c, const char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t s = {NULL}, *new, *old;
if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
c->hostname);
return false;
}
@ -56,7 +56,7 @@ bool add_subnet_h(connection_t *c, char *request) {
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
c->hostname, "invalid name");
return false;
}
@ -64,7 +64,7 @@ bool add_subnet_h(connection_t *c, char *request) {
/* Check if subnet string is valid */
if(!str2net(&s, subnetstr)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
c->hostname, "invalid subnet string");
return false;
}
@ -78,7 +78,7 @@ bool add_subnet_h(connection_t *c, char *request) {
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet registrations */
ifdebug(PROTOCOL) logger(LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
@ -97,7 +97,7 @@ bool add_subnet_h(connection_t *c, char *request) {
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
"ADD_SUBNET", c->name, c->hostname);
s.owner = myself;
send_del_subnet(c, &s);
@ -107,7 +107,7 @@ bool add_subnet_h(connection_t *c, char *request) {
/* In tunnel server mode, we should already know all allowed subnets */
if(tunnelserver) {
logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
@ -115,7 +115,7 @@ bool add_subnet_h(connection_t *c, char *request) {
/* Ignore if strictsubnets is true, but forward it to others */
if(strictsubnets) {
logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
forward_request(c, request);
return true;
@ -151,14 +151,14 @@ bool send_del_subnet(connection_t *c, const subnet_t *s) {
return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
}
bool del_subnet_h(connection_t *c, char *request) {
bool del_subnet_h(connection_t *c, const char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t s = {NULL}, *find;
if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
c->hostname);
return false;
}
@ -166,7 +166,7 @@ bool del_subnet_h(connection_t *c, char *request) {
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
c->hostname, "invalid name");
return false;
}
@ -174,7 +174,7 @@ bool del_subnet_h(connection_t *c, char *request) {
/* Check if subnet string is valid */
if(!str2net(&s, subnetstr)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
c->hostname, "invalid subnet string");
return false;
}
@ -188,13 +188,13 @@ bool del_subnet_h(connection_t *c, char *request) {
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet deletion */
ifdebug(PROTOCOL) logger(LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
"DEL_SUBNET", c->name, c->hostname, subnetstr);
logger(DEBUG_PROTOCOL, LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
"DEL_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for %s which is not in our node tree",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for %s which is not in our node tree",
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
@ -206,7 +206,7 @@ bool del_subnet_h(connection_t *c, char *request) {
find = lookup_subnet(owner, &s);
if(!find) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for %s which does not appear in his subnet tree",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for %s which does not appear in his subnet tree",
"DEL_SUBNET", c->name, c->hostname, name);
if(strictsubnets)
forward_request(c, request);
@ -216,7 +216,7 @@ bool del_subnet_h(connection_t *c, char *request) {
/* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
"DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return true;

View file

@ -1,7 +1,7 @@
/*
device.c -- raw socket
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2009 Guus Sliepen <guus@tinc-vpn.org>
2002-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,9 @@
#include "system.h"
#ifdef HAVE_NETPACKET_PACKET_H
#include <netpacket/packet.h>
#endif
#include "conf.h"
#include "device.h"
@ -30,16 +32,13 @@
#include "route.h"
#include "xalloc.h"
int device_fd = -1;
char *device = NULL;
char *iface = NULL;
static char ifrname[IFNAMSIZ];
#if defined(PF_PACKET) && defined(ETH_P_ALL) && defined(AF_PACKET)
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
bool setup_device(void) {
static bool setup_device(void) {
struct ifreq ifr;
struct sockaddr_ll sa;
@ -52,16 +51,21 @@ bool setup_device(void) {
device_info = "raw socket";
if((device_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
logger(LOG_ERR, "Could not open %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device_info,
strerror(errno));
return false;
}
memset(&ifr, 0, sizeof ifr);
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
close(device_fd);
logger(LOG_ERR, "Can't find interface %s: %s", iface,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't find interface %s: %s", iface,
strerror(errno));
return false;
}
@ -72,27 +76,27 @@ bool setup_device(void) {
sa.sll_ifindex = ifr.ifr_ifindex;
if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof sa)) {
logger(LOG_ERR, "Could not bind %s to %s: %s", device, iface, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind %s to %s: %s", device, iface, strerror(errno));
return false;
}
logger(LOG_INFO, "%s is a %s", device, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
close(device_fd);
free(device);
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -101,18 +105,18 @@ bool read_packet(vpn_packet_t *packet) {
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
static bool write_packet(vpn_packet_t *packet) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
if(write(device_fd, packet->data, packet->len) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
@ -122,8 +126,32 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t raw_socket_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};
#else
static bool not_supported(void) {
logger(DEBUG_ALWAYS, LOG_ERR, "Raw socket device not supported on this platform");
return false;
}
const devops_t raw_socket_devops = {
.setup = not_supported,
.close = NULL,
.read = NULL,
.write = NULL,
.dump_stats = NULL,
};
#endif

View file

@ -1,7 +1,7 @@
/*
route.c -- routing
Copyright (C) 2000-2005 Ivo Timmermans,
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,6 @@
#include "system.h"
#include "splay_tree.h"
#include "connection.h"
#include "control_common.h"
#include "ethernet.h"
@ -36,6 +35,8 @@
rmode_t routing_mode = RMODE_ROUTER;
fmode_t forwarding_mode = FMODE_INTERNAL;
bmode_t broadcast_mode = BMODE_MST;
bool decrement_ttl = false;
bool directonly = false;
bool priorityinheritance = false;
int macexpire = 600;
@ -70,7 +71,7 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
checksum += *p++;
len -= 2;
}
if(len)
checksum += *(uint8_t *)p;
@ -84,21 +85,22 @@ static bool ratelimit(int frequency) {
static time_t lasttime = 0;
static int count = 0;
time_t now = time(NULL);
if(lasttime == now) {
if(++count > frequency)
if(count >= frequency)
return true;
} else {
lasttime = now;
count = 0;
}
count++;
return false;
}
static bool checklength(node_t *source, vpn_packet_t *packet, length_t length) {
if(packet->len < length) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Got too short packet from %s (%s)", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Got too short packet from %s (%s)", source->name, source->hostname);
return false;
} else
return true;
@ -160,8 +162,8 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
if(oldmss <= newmss)
break;
ifdebug(TRAFFIC) logger(LOG_INFO, "Clamping MSS of packet from %s to %s to %d", source->name, via->name, newmss);
logger(DEBUG_TRAFFIC, LOG_INFO, "Clamping MSS of packet from %s to %s to %d", source->name, via->name, newmss);
/* Update the MSS value and the checksum */
packet->data[start + 22 + i] = newmss >> 8;
@ -182,29 +184,22 @@ static void swap_mac_addresses(vpn_packet_t *packet) {
memcpy(&packet->data[0], &packet->data[6], sizeof tmp);
memcpy(&packet->data[6], &tmp, sizeof tmp);
}
static void age_subnets(int fd, short events, void *data) {
subnet_t *s;
connection_t *c;
splay_node_t *node, *next, *node2;
bool left = false;
time_t now = time(NULL);
for(node = myself->subnet_tree->head; node; node = next) {
next = node->next;
s = node->data;
for splay_each(subnet_t, s, myself->subnet_tree) {
if(s->expires && s->expires < now) {
ifdebug(TRAFFIC) {
if(debug_level >= DEBUG_TRAFFIC) {
char netstr[MAXNETSTR];
if(net2str(netstr, sizeof netstr, s))
logger(LOG_INFO, "Subnet %s expired", netstr);
logger(DEBUG_TRAFFIC, LOG_INFO, "Subnet %s expired", netstr);
}
for(node2 = connection_tree->head; node2; node2 = node2->next) {
c = node2->data;
for list_each(connection_t, c, connection_list)
if(c->status.active)
send_del_subnet(c, s);
}
subnet_del(myself, s);
} else {
@ -218,16 +213,12 @@ static void age_subnets(int fd, short events, void *data) {
}
static void learn_mac(mac_t *address) {
subnet_t *subnet;
splay_node_t *node;
connection_t *c;
subnet = lookup_subnet_mac(myself, address);
subnet_t *subnet = lookup_subnet_mac(myself, address);
/* If we don't know this MAC address yet, store it */
if(!subnet) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx",
logger(DEBUG_TRAFFIC, 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]);
@ -241,11 +232,9 @@ static void learn_mac(mac_t *address) {
/* And tell all other tinc daemons it's our MAC */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
for list_each(connection_t, c, connection_list)
if(c->status.active)
send_add_subnet(c, subnet);
}
if(!timeout_initialized(&age_subnets_event))
timeout_set(&age_subnets_event, age_subnets, NULL);
@ -261,14 +250,14 @@ static void learn_mac(mac_t *address) {
static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) {
struct ip ip = {0};
struct icmp icmp = {0};
struct in_addr ip_src;
struct in_addr ip_dst;
uint32_t oldlen;
if(ratelimit(3))
return;
/* Swap Ethernet source and destination addresses */
swap_mac_addresses(packet);
@ -278,7 +267,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
memcpy(&ip, packet->data + ether_size, ip_size);
/* Remember original source and destination */
ip_src = ip.ip_src;
ip_dst = ip.ip_dst;
@ -289,13 +278,13 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
if(oldlen >= IP_MSS - ip_size - icmp_size)
oldlen = IP_MSS - ip_size - icmp_size;
/* Copy first part of original contents to ICMP message */
memmove(packet->data + ether_size + ip_size + icmp_size, packet->data + ether_size, oldlen);
/* Fill in IPv4 header */
ip.ip_v = 4;
ip.ip_hl = ip_size / 4;
ip.ip_tos = 0;
@ -309,13 +298,13 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
ip.ip_dst = ip_src;
ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
/* Fill in ICMP header */
icmp.icmp_type = type;
icmp.icmp_code = code;
icmp.icmp_cksum = 0;
icmp.icmp_cksum = inet_checksum(&icmp, icmp_size, ~0);
icmp.icmp_cksum = inet_checksum(packet->data + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
@ -323,7 +312,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
memcpy(packet->data + ether_size, &ip, ip_size);
memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size);
packet->len = ether_size + ip_size + icmp_size + oldlen;
send_packet(source, packet);
@ -337,28 +326,28 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) {
int len, maxlen, todo;
uint8_t *offset;
uint16_t ip_off, origf;
memcpy(&ip, packet->data + ether_size, ip_size);
fragment.priority = packet->priority;
if(ip.ip_hl != ip_size / 4)
return;
todo = ntohs(ip.ip_len) - ip_size;
if(ether_size + ip_size + todo != packet->len) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Length of packet (%d) doesn't match length in IPv4 header (%d)", packet->len, (int)(ether_size + ip_size + todo));
logger(DEBUG_TRAFFIC, LOG_WARNING, "Length of packet (%d) doesn't match length in IPv4 header (%d)", packet->len, (int)(ether_size + ip_size + todo));
return;
}
ifdebug(TRAFFIC) logger(LOG_INFO, "Fragmenting packet of %d bytes to %s (%s)", packet->len, dest->name, dest->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Fragmenting packet of %d bytes to %s (%s)", packet->len, dest->name, dest->hostname);
offset = packet->data + ether_size + ip_size;
maxlen = (dest->mtu - ether_size - ip_size) & ~0x7;
ip_off = ntohs(ip.ip_off);
origf = ip_off & ~IP_OFFMASK;
ip_off &= IP_OFFMASK;
while(todo) {
len = todo > maxlen ? maxlen : todo;
memcpy(fragment.data + ether_size + ip_size, offset, len);
@ -376,7 +365,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) {
send_packet(dest, &fragment);
ip_off += len / 8;
}
}
}
static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
@ -388,7 +377,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
subnet = lookup_subnet_ipv4(&dest);
if(!subnet) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%d",
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%d",
source->name, source->hostname,
dest.x[0],
dest.x[1],
@ -398,9 +387,9 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
return;
}
if(subnet->owner == source) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
}
@ -414,12 +403,17 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
packet->priority = packet->data[15];
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
if(via == source) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
return;
}
if(directonly && subnet->owner != via)
return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_ANO);
if(via && packet->len > MAX(via->mtu, 590) && via != myself) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
if(packet->data[20] & 0x40) {
packet->len = MAX(via->mtu, 590);
route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
@ -431,7 +425,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
}
clamp_mss(source, via, packet);
send_packet(subnet->owner, packet);
}
@ -439,11 +433,11 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size + ip_size))
return;
if(((packet->data[30] & 0xf0) == 0xe0) || (
if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
packet->data[30] == 255 &&
packet->data[31] == 255 &&
packet->data[32] == 255 &&
packet->data[33] == 255))
packet->data[33] == 255)))
broadcast_packet(source, packet);
else
route_ipv4_unicast(source, packet);
@ -454,18 +448,18 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) {
struct ip6_hdr ip6;
struct icmp6_hdr icmp6 = {0};
uint16_t checksum;
uint16_t checksum;
struct {
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
uint32_t length;
uint32_t next;
} pseudo;
if(ratelimit(3))
return;
/* Swap Ethernet source and destination addresses */
swap_mac_addresses(packet);
@ -475,7 +469,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
memcpy(&ip6, packet->data + ether_size, ip6_size);
/* Remember original source and destination */
pseudo.ip6_src = ip6.ip6_dst;
pseudo.ip6_dst = ip6.ip6_src;
@ -483,16 +477,16 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
if(type == ICMP6_PACKET_TOO_BIG)
icmp6.icmp6_mtu = htonl(pseudo.length);
if(pseudo.length >= IP_MSS - ip6_size - icmp6_size)
pseudo.length = IP_MSS - ip6_size - icmp6_size;
/* Copy first part of original contents to ICMP message */
memmove(packet->data + ether_size + ip6_size + icmp6_size, packet->data + ether_size, pseudo.length);
/* Fill in IPv6 header */
ip6.ip6_flow = htonl(0x60000000UL);
ip6.ip6_plen = htons(icmp6_size + pseudo.length);
ip6.ip6_nxt = IPPROTO_ICMPV6;
@ -501,18 +495,18 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
ip6.ip6_dst = pseudo.ip6_dst;
/* Fill in ICMP header */
icmp6.icmp6_type = type;
icmp6.icmp6_code = code;
icmp6.icmp6_cksum = 0;
/* Create pseudo header */
pseudo.length = htonl(icmp6_size + pseudo.length);
pseudo.next = htonl(IPPROTO_ICMPV6);
/* Generate checksum */
checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
checksum = inet_checksum(&icmp6, icmp6_size, checksum);
checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
@ -523,9 +517,9 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
memcpy(packet->data + ether_size, &ip6, ip6_size);
memcpy(packet->data + ether_size + ip6_size, &icmp6, icmp6_size);
packet->len = ether_size + ip6_size + ntohl(pseudo.length);
send_packet(source, packet);
}
@ -538,7 +532,7 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
subnet = lookup_subnet_ipv6(&dest);
if(!subnet) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
source->name, source->hostname,
ntohs(dest.x[0]),
ntohs(dest.x[1]),
@ -554,7 +548,7 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
}
if(subnet->owner == source) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
}
@ -565,19 +559,24 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
if(via == source) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
return;
}
if(directonly && subnet->owner != via)
return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
if(via && packet->len > MAX(via->mtu, 1294) && via != myself) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
packet->len = MAX(via->mtu, 1294);
route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
return;
}
clamp_mss(source, via, packet);
send_packet(subnet->owner, packet);
}
@ -592,19 +591,19 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
bool has_opt;
struct {
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
struct in6_addr ip6_src;
struct in6_addr ip6_dst;
uint32_t length;
uint32_t next;
} pseudo;
if(!checklength(source, packet, ether_size + ip6_size + ns_size))
return;
has_opt = packet->len >= ether_size + ip6_size + ns_size + opt_size + ETH_ALEN;
if(source != myself) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Got neighbor solicitation request from %s (%s) while in router mode!", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Got neighbor solicitation request from %s (%s) while in router mode!", source->name, source->hostname);
return;
}
@ -624,7 +623,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
if(ns.nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
(has_opt && opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR)) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type neighbor solicitation request");
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: received unknown type neighbor solicitation request");
return;
}
@ -648,7 +647,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
}
if(checksum) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: checksum error for neighbor solicitation request");
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: checksum error for neighbor solicitation request");
return;
}
@ -657,7 +656,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
subnet = lookup_subnet_ipv6((ipv6_t *) &ns.nd_ns_target);
if(!subnet) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
logger(DEBUG_TRAFFIC, 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]),
@ -673,22 +672,22 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
/* Check if it is for our own subnet */
if(subnet->owner == myself)
return; /* silently ignore */
return; /* silently ignore */
/* Create neighbor advertation reply */
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */
ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */
ip6.ip6_src = ns.nd_ns_target;
if(has_opt)
memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
ns.nd_ns_cksum = 0;
ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
ns.nd_ns_reserved = htonl(0x40000000UL); /* Set solicited flag */
ns.nd_ns_reserved = htonl(0x40000000UL); /* Set solicited flag */
opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
/* Create pseudo header */
@ -731,7 +730,7 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) {
return;
}
if(packet->data[38] == 255)
if(broadcast_mode && packet->data[38] == 255)
broadcast_packet(source, packet);
else
route_ipv6_unicast(source, packet);
@ -748,7 +747,7 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
return;
if(source != myself) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Got ARP request from %s (%s) while in router mode!", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Got ARP request from %s (%s) while in router mode!", source->name, source->hostname);
return;
}
@ -765,7 +764,7 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type ARP request");
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: received unknown type ARP request");
return;
}
@ -774,7 +773,7 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
subnet = lookup_subnet_ipv4((ipv4_t *) &arp.arp_tpa);
if(!subnet) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: ARP request for unknown address %d.%d.%d.%d",
logger(DEBUG_TRAFFIC, 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;
@ -783,17 +782,17 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
/* Check if it is for our own subnet */
if(subnet->owner == myself)
return; /* silently ignore */
return; /* silently ignore */
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */
memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */
memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */
memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */
memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */
memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */
memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
arp.arp_op = htons(ARPOP_REPLY);
/* Copy structs on stack back to packet */
@ -826,7 +825,7 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
}
if(subnet->owner == source) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
}
@ -839,9 +838,9 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
if(directonly && subnet->owner != via)
return;
if(via && packet->len > via->mtu && via != myself) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
uint16_t type = packet->data[12] << 8 | packet->data[13];
if(type == ETH_P_IP && packet->len > 590) {
if(packet->data[20] & 0x40) {
@ -859,20 +858,70 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
}
clamp_mss(source, via, packet);
send_packet(subnet->owner, packet);
}
static void send_pcap(vpn_packet_t *packet) {
pcap = false;
for(splay_node_t *node = connection_tree->head; node; node = node->next) {
connection_t *c = node->data;
for list_each(connection_t, c, connection_list) {
if(!c->status.pcap)
continue;
else
pcap = true;
if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, packet->len))
send_meta(c, (char *)packet->data, packet->len);
pcap = true;
int len = packet->len;
if(c->outmaclength && c->outmaclength < len)
len = c->outmaclength;
if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, len))
send_meta(c, (char *)packet->data, len);
}
}
static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
uint16_t type = packet->data[12] << 8 | packet->data[13];
switch (type) {
case ETH_P_IP:
if(!checklength(source, packet, 14 + 32))
return false;
if(packet->data[22] < 1) {
if(packet->data[25] != IPPROTO_ICMP || packet->data[46] != ICMP_TIME_EXCEEDED)
route_ipv4_unreachable(source, packet, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
return false;
}
uint16_t old = packet->data[22] << 8 | packet->data[23];
packet->data[22]--;
uint16_t new = packet->data[22] << 8 | packet->data[23];
uint32_t checksum = packet->data[24] << 8 | packet->data[25];
checksum += old + (~new & 0xFFFF);
while(checksum >> 16)
checksum = (checksum & 0xFFFF) + (checksum >> 16);
packet->data[24] = checksum >> 8;
packet->data[25] = checksum & 0xff;
return true;
case ETH_P_IPV6:
if(!checklength(source, packet, 14 + 40))
return false;
if(packet->data[21] < 1) {
if(packet->data[20] != IPPROTO_ICMPV6 || packet->data[54] != ICMP6_TIME_EXCEEDED)
route_ipv6_unreachable(source, packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
return false;
}
packet->data[21]--;
return true;
default:
return true;
}
}
@ -888,28 +937,30 @@ void route(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size))
return;
if(decrement_ttl && source != myself)
if(!do_decrement_ttl(source, packet))
return;
uint16_t type = packet->data[12] << 8 | packet->data[13];
switch (routing_mode) {
case RMODE_ROUTER:
{
uint16_t type = packet->data[12] << 8 | packet->data[13];
switch (type) {
case ETH_P_ARP:
route_arp(source, packet);
break;
switch (type) {
case ETH_P_ARP:
route_arp(source, packet);
break;
case ETH_P_IP:
route_ipv4(source, packet);
break;
case ETH_P_IP:
route_ipv4(source, packet);
break;
case ETH_P_IPV6:
route_ipv6(source, packet);
break;
case ETH_P_IPV6:
route_ipv6(source, packet);
break;
default:
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet from %s (%s): unknown type %hx", source->name, source->hostname, type);
break;
}
default:
logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown type %hx", source->name, source->hostname, type);
break;
}
break;

View file

@ -1,7 +1,7 @@
/*
route.h -- header file for route.c
Copyright (C) 2000-2005 Ivo Timmermans
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -36,8 +36,16 @@ typedef enum fmode_t {
FMODE_KERNEL,
} fmode_t;
typedef enum bmode_t {
BMODE_NONE = 0,
BMODE_MST,
BMODE_DIRECT,
} bmode_t;
extern rmode_t routing_mode;
extern fmode_t forwarding_mode;
extern bmode_t broadcast_mode;
extern bool decrement_ttl;
extern bool directonly;
extern bool overwrite_mac;
extern bool priorityinheritance;
@ -48,4 +56,4 @@ extern mac_t mymac;
extern void route(struct node_t *, struct vpn_packet_t *);
#endif /* __TINC_ROUTE_H__ */
#endif /* __TINC_ROUTE_H__ */

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Solaris tun device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2011 Guus Sliepen <guus@tinc-vpn.org>
2001-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -35,7 +35,7 @@
#define DEFAULT_DEVICE "/dev/tun"
int device_fd = -1;
int ip_fd = -1, if_fd = -1;
static int ip_fd = -1, if_fd = -1;
char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
@ -43,7 +43,7 @@ static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
bool setup_device(void) {
static bool setup_device(void) {
int ppa;
char *ptr;
@ -51,10 +51,14 @@ bool setup_device(void) {
device = xstrdup(DEFAULT_DEVICE);
if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) {
logger(LOG_ERR, "Could not open %s: %s", device, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
ppa = 0;
ptr = device;
@ -63,35 +67,43 @@ bool setup_device(void) {
ppa = atoi(ptr);
if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
logger(LOG_ERR, "Could not open /dev/ip: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open /dev/ip: %s", strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(ip_fd, F_SETFD, FD_CLOEXEC);
#endif
/* Assign a new PPA and get its unit number. */
if((ppa = ioctl(device_fd, TUNNEWPPA, ppa)) < 0) {
logger(LOG_ERR, "Can't assign new interface: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't assign new interface: %s", strerror(errno));
return false;
}
if((if_fd = open(device, O_RDWR, 0)) < 0) {
logger(LOG_ERR, "Could not open %s twice: %s", device,
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s twice: %s", device,
strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(if_fd, F_SETFD, FD_CLOEXEC);
#endif
if(ioctl(if_fd, I_PUSH, "ip") < 0) {
logger(LOG_ERR, "Can't push IP module: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't push IP module: %s", strerror(errno));
return false;
}
/* Assign ppa according to the unit number returned by tun device */
if(ioctl(if_fd, IF_UNITSEL, (char *) &ppa) < 0) {
logger(LOG_ERR, "Can't set PPA %d: %s", ppa, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't set PPA %d: %s", ppa, strerror(errno));
return false;
}
if(ioctl(ip_fd, I_LINK, if_fd) < 0) {
logger(LOG_ERR, "Can't link TUN device to IP: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Can't link TUN device to IP: %s", strerror(errno));
return false;
}
@ -100,12 +112,12 @@ bool setup_device(void) {
device_info = "Solaris tun device";
logger(LOG_INFO, "%s is a %s", device, device_info);
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
}
void close_device(void) {
static void close_device(void) {
close(if_fd);
close(ip_fd);
close(device_fd);
@ -114,11 +126,11 @@ void close_device(void) {
free(iface);
}
bool read_packet(vpn_packet_t *packet) {
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -133,28 +145,29 @@ bool read_packet(vpn_packet_t *packet) {
packet->data[13] = 0xDD;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
memset(packet->data, 0, 12);
packet->len = inlen + 14;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
static bool write_packet(vpn_packet_t *packet) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info,
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
@ -164,8 +177,16 @@ bool write_packet(vpn_packet_t *packet) {
return true;
}
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
static void dump_device_stats(void) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,6 +1,6 @@
/*
splay_tree.c -- splay tree and linked list convenience
Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2004-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -44,10 +44,10 @@ static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *r
rightbottom->left = child;
child->parent = rightbottom;
rightbottom = child;
if((root->left = child->right))
child->right->parent = root;
child->right = root;
root->parent = child;
@ -74,7 +74,7 @@ static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *r
rightbottom->left = root;
root->parent = rightbottom;
rightbottom = root;
root->left = NULL;
child->parent = NULL;
@ -88,10 +88,10 @@ static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *r
leftbottom->right = child;
child->parent = leftbottom;
leftbottom = child;
if((root->right = child->left))
child->left->parent = root;
child->left = root;
root->parent = child;
@ -158,14 +158,14 @@ static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *r
return tree->root;
}
static void splay_bottom_up(splay_tree_t *tree, splay_node_t *node) {
splay_node_t *parent, *grandparent, *greatgrandparent;
while((parent = node->parent)) {
if(!(grandparent = parent->parent)) { /* zig */
if(node == parent->left) {
if((parent->left = node->right))
if((parent->left = node->right))
parent->left->parent = parent;
node->right = parent;
} else {
@ -326,7 +326,7 @@ splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *tree, const
} else if(c > 0) {
if(node->right)
node = node->right;
else
else
break;
} else {
break;
@ -384,12 +384,12 @@ splay_node_t *splay_insert(splay_tree_t *tree, void *data) {
new = splay_alloc_node();
new->data = data;
if(result < 0)
splay_insert_before(tree, closest, new);
else
splay_insert_after(tree, closest, new);
}
}
return new;
}
@ -402,7 +402,7 @@ splay_node_t *splay_insert_node(splay_tree_t *tree, splay_node_t *node) {
splay_insert_top(tree, node);
else {
closest = splay_search_closest_node(tree, node->data, &result);
if(!result)
return NULL;
@ -530,9 +530,7 @@ void splay_delete(splay_tree_t *tree, void *data) {
/* Fast tree cleanup */
void splay_delete_tree(splay_tree_t *tree) {
splay_node_t *node, *next;
for(node = tree->head; node; node = next) {
for(splay_node_t *node = tree->head, *next; node; node = next) {
next = node->next;
splay_free_node(tree, node);
}
@ -543,18 +541,14 @@ void splay_delete_tree(splay_tree_t *tree) {
/* Tree walking */
void splay_foreach(const splay_tree_t *tree, splay_action_t action) {
splay_node_t *node, *next;
for(node = tree->head; node; node = next) {
for(splay_node_t *node = tree->head, *next; node; node = next) {
next = node->next;
action(node->data);
}
}
void splay_foreach_node(const splay_tree_t *tree, splay_action_t action) {
splay_node_t *node, *next;
for(node = tree->head; node; node = next) {
for(splay_node_t *node = tree->head, *next; node; node = next) {
next = node->next;
action(node);
}

View file

@ -1,6 +1,6 @@
/*
splay_tree.h -- header file for splay_tree.c
Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2004-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -104,4 +104,6 @@ extern splay_node_t *splay_search_closest_greater_node(splay_tree_t *, const voi
extern void splay_foreach(const splay_tree_t *, splay_action_t);
extern void splay_foreach_node(const splay_tree_t *, splay_action_t);
#define splay_each(type, item, tree) (type *item = (type *)1; item; item = NULL) for(splay_node_t *node = (tree)->head, *next; item = node ? node->data : NULL, next = node ? node->next : NULL, node; node = next)
#endif

662
src/sptps.c Normal file
View file

@ -0,0 +1,662 @@
/*
sptps.c -- Simple Peer-to-Peer Security
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>,
2010 Brandon L. Black <blblack@gmail.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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "cipher.h"
#include "crypto.h"
#include "digest.h"
#include "ecdh.h"
#include "ecdsa.h"
#include "logger.h"
#include "prf.h"
#include "sptps.h"
unsigned int sptps_replaywin = 16;
/*
Nonce MUST be exchanged first (done)
Signatures MUST be done over both nonces, to guarantee the signature is fresh
Otherwise: if ECDHE key of one side is compromised, it can be reused!
Add explicit tag to beginning of structure to distinguish the client and server when signing. (done)
Sign all handshake messages up to ECDHE kex with long-term public keys. (done)
HMACed KEX finished message to prevent downgrade attacks and prove you have the right key material (done by virtue of ECDSA over the whole ECDHE exchange?)
Explicit close message needs to be added.
Maybe do add some alert messages to give helpful error messages? Not more than TLS sends.
Use counter mode instead of OFB. (done)
Make sure ECC operations are fixed time (aka prevent side-channel attacks).
*/
void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap) {
}
void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap) {
vfprintf(stderr, format, ap);
fputc('\n', stderr);
}
void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap) = sptps_log_stderr;
// Log an error message.
static bool error(sptps_t *s, int s_errno, const char *format, ...) {
if(format) {
va_list ap;
va_start(ap, format);
sptps_log(s, s_errno, format, ap);
va_end(ap);
}
errno = s_errno;
return false;
}
static void warning(sptps_t *s, const char *format, ...) {
va_list ap;
va_start(ap, format);
sptps_log(s, 0, format, ap);
va_end(ap);
}
// Send a record (datagram version, accepts all record types, handles encryption and authentication).
static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
char buffer[len + 23UL];
// Create header with sequence number, length and record type
uint32_t seqno = htonl(s->outseqno++);
uint16_t netlen = htons(len);
memcpy(buffer, &netlen, 2);
memcpy(buffer + 2, &seqno, 4);
buffer[6] = type;
// Add plaintext (TODO: avoid unnecessary copy)
memcpy(buffer + 7, data, len);
if(s->outstate) {
// If first handshake has finished, encrypt and HMAC
cipher_set_counter(&s->outcipher, &seqno, sizeof seqno);
if(!cipher_counter_xor(&s->outcipher, buffer + 6, len + 1UL, buffer + 6))
return false;
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
return false;
return s->send_data(s->handle, type, buffer + 2, len + 21UL);
} else {
// Otherwise send as plaintext
return s->send_data(s->handle, type, buffer + 2, len + 5UL);
}
}
// Send a record (private version, accepts all record types, handles encryption and authentication).
static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
if(s->datagram)
return send_record_priv_datagram(s, type, data, len);
char buffer[len + 23UL];
// Create header with sequence number, length and record type
uint32_t seqno = htonl(s->outseqno++);
uint16_t netlen = htons(len);
memcpy(buffer, &seqno, 4);
memcpy(buffer + 4, &netlen, 2);
buffer[6] = type;
// Add plaintext (TODO: avoid unnecessary copy)
memcpy(buffer + 7, data, len);
if(s->outstate) {
// If first handshake has finished, encrypt and HMAC
if(!cipher_counter_xor(&s->outcipher, buffer + 4, len + 3UL, buffer + 4))
return false;
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
return false;
return s->send_data(s->handle, type, buffer + 4, len + 19UL);
} else {
// Otherwise send as plaintext
return s->send_data(s->handle, type, buffer + 4, len + 3UL);
}
}
// Send an application record.
bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
// Sanity checks: application cannot send data before handshake is finished,
// and only record types 0..127 are allowed.
if(!s->outstate)
return error(s, EINVAL, "Handshake phase not finished yet");
if(type >= SPTPS_HANDSHAKE)
return error(s, EINVAL, "Invalid application record type");
return send_record_priv(s, type, data, len);
}
// Send a Key EXchange record, containing a random nonce and an ECDHE public key.
static bool send_kex(sptps_t *s) {
size_t keylen = ECDH_SIZE;
// Make room for our KEX message, which we will keep around since send_sig() needs it.
if(s->mykex)
abort();
s->mykex = realloc(s->mykex, 1 + 32 + keylen);
if(!s->mykex)
return error(s, errno, strerror(errno));
// Set version byte to zero.
s->mykex[0] = SPTPS_VERSION;
// Create a random nonce.
randomize(s->mykex + 1, 32);
// Create a new ECDH public key.
if(!ecdh_generate_public(&s->ecdh, s->mykex + 1 + 32))
return false;
return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
}
// Send a SIGnature record, containing an ECDSA signature over both KEX records.
static bool send_sig(sptps_t *s) {
size_t keylen = ECDH_SIZE;
size_t siglen = ecdsa_size(&s->mykey);
// Concatenate both KEX messages, plus tag indicating if it is from the connection originator, plus label
char msg[(1 + 32 + keylen) * 2 + 1 + s->labellen];
char sig[siglen];
msg[0] = s->initiator;
memcpy(msg + 1, s->mykex, 1 + 32 + keylen);
memcpy(msg + 1 + 33 + keylen, s->hiskex, 1 + 32 + keylen);
memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
// Sign the result.
if(!ecdsa_sign(&s->mykey, msg, sizeof msg, sig))
return false;
// Send the SIG exchange record.
return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof sig);
}
// Generate key material from the shared secret created from the ECDHE key exchange.
static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
// Initialise cipher and digest structures if necessary
if(!s->outstate) {
bool result
= cipher_open_by_name(&s->incipher, "aes-256-ecb")
&& cipher_open_by_name(&s->outcipher, "aes-256-ecb")
&& digest_open_by_name(&s->indigest, "sha256", 16)
&& digest_open_by_name(&s->outdigest, "sha256", 16);
if(!result)
return false;
}
// Allocate memory for key material
size_t keylen = digest_keylength(&s->indigest) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher) + cipher_keylength(&s->outcipher);
s->key = realloc(s->key, keylen);
if(!s->key)
return error(s, errno, strerror(errno));
// Create the HMAC seed, which is "key expansion" + session label + server nonce + client nonce
char seed[s->labellen + 64 + 13];
strcpy(seed, "key expansion");
if(s->initiator) {
memcpy(seed + 13, s->mykex + 1, 32);
memcpy(seed + 45, s->hiskex + 1, 32);
} else {
memcpy(seed + 13, s->hiskex + 1, 32);
memcpy(seed + 45, s->mykex + 1, 32);
}
memcpy(seed + 77, s->label, s->labellen);
// Use PRF to generate the key material
if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen))
return false;
return true;
}
// Send an ACKnowledgement record.
static bool send_ack(sptps_t *s) {
return send_record_priv(s, SPTPS_HANDSHAKE, "", 0);
}
// Receive an ACKnowledgement record.
static bool receive_ack(sptps_t *s, const char *data, uint16_t len) {
if(len)
return error(s, EIO, "Invalid ACK record length");
if(s->initiator) {
bool result
= cipher_set_counter_key(&s->incipher, s->key)
&& digest_set_key(&s->indigest, s->key + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
if(!result)
return false;
} else {
bool result
= cipher_set_counter_key(&s->incipher, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest))
&& digest_set_key(&s->indigest, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
if(!result)
return false;
}
free(s->key);
s->key = NULL;
s->instate = true;
return true;
}
// Receive a Key EXchange record, respond by sending a SIG record.
static bool receive_kex(sptps_t *s, const char *data, uint16_t len) {
// Verify length of the HELLO record
if(len != 1 + 32 + ECDH_SIZE)
return error(s, EIO, "Invalid KEX record length");
// Ignore version number for now.
// Make a copy of the KEX message, send_sig() and receive_sig() need it
if(s->hiskex)
abort();
s->hiskex = realloc(s->hiskex, len);
if(!s->hiskex)
return error(s, errno, strerror(errno));
memcpy(s->hiskex, data, len);
return send_sig(s);
}
// Receive a SIGnature record, verify it, if it passed, compute the shared secret and calculate the session keys.
static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
size_t keylen = ECDH_SIZE;
size_t siglen = ecdsa_size(&s->hiskey);
// Verify length of KEX record.
if(len != siglen)
return error(s, EIO, "Invalid KEX record length");
// Concatenate both KEX messages, plus tag indicating if it is from the connection originator
char msg[(1 + 32 + keylen) * 2 + 1 + s->labellen];
msg[0] = !s->initiator;
memcpy(msg + 1, s->hiskex, 1 + 32 + keylen);
memcpy(msg + 1 + 33 + keylen, s->mykex, 1 + 32 + keylen);
memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
// Verify signature.
if(!ecdsa_verify(&s->hiskey, msg, sizeof msg, data))
return false;
// Compute shared secret.
char shared[ECDH_SHARED_SIZE];
if(!ecdh_compute_shared(&s->ecdh, s->hiskex + 1 + 32, shared))
return false;
// Generate key material from shared secret.
if(!generate_key_material(s, shared, sizeof shared))
return false;
free(s->mykex);
free(s->hiskex);
s->mykex = NULL;
s->hiskex = NULL;
// Send cipher change record
if(s->outstate && !send_ack(s))
return false;
// TODO: only set new keys after ACK has been set/received
if(s->initiator) {
bool result
= cipher_set_counter_key(&s->outcipher, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest))
&& digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest) + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
if(!result)
return false;
} else {
bool result
= cipher_set_counter_key(&s->outcipher, s->key)
&& digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
if(!result)
return false;
}
return true;
}
// Force another Key EXchange (for testing purposes).
bool sptps_force_kex(sptps_t *s) {
if(!s->outstate || s->state != SPTPS_SECONDARY_KEX)
return error(s, EINVAL, "Cannot force KEX in current state");
s->state = SPTPS_KEX;
return send_kex(s);
}
// Receive a handshake record.
static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
// Only a few states to deal with handshaking.
switch(s->state) {
case SPTPS_SECONDARY_KEX:
// We receive a secondary KEX request, first respond by sending our own.
if(!send_kex(s))
return false;
case SPTPS_KEX:
// We have sent our KEX request, we expect our peer to sent one as well.
if(!receive_kex(s, data, len))
return false;
s->state = SPTPS_SIG;
return true;
case SPTPS_SIG:
// If we already sent our secondary public ECDH key, we expect the peer to send his.
if(!receive_sig(s, data, len))
return false;
if(s->outstate)
s->state = SPTPS_ACK;
else {
s->outstate = true;
if(!receive_ack(s, NULL, 0))
return false;
s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
s->state = SPTPS_SECONDARY_KEX;
}
return true;
case SPTPS_ACK:
// We expect a handshake message to indicate transition to the new keys.
if(!receive_ack(s, data, len))
return false;
s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
s->state = SPTPS_SECONDARY_KEX;
return true;
// TODO: split ACK into a VERify and ACK?
default:
return error(s, EIO, "Invalid session state");
}
}
// Check datagram for valid HMAC
bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) {
if(!s->instate || len < 21)
return false;
char buffer[len + 23];
uint16_t netlen = htons(len - 21);
memcpy(buffer, &netlen, 2);
memcpy(buffer + 2, data, len);
return digest_verify(&s->indigest, buffer, len - 14, buffer + len - 14);
}
// Receive incoming data, datagram version.
static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len) {
if(len < (s->instate ? 21 : 5))
return error(s, EIO, "Received short packet");
uint32_t seqno;
memcpy(&seqno, data, 4);
seqno = ntohl(seqno);
if(!s->instate) {
if(seqno != s->inseqno)
return error(s, EIO, "Invalid packet seqno: %d != %d", seqno, s->inseqno);
s->inseqno = seqno + 1;
uint8_t type = data[4];
if(type != SPTPS_HANDSHAKE)
return error(s, EIO, "Application record received before handshake finished");
return receive_handshake(s, data + 5, len - 5);
}
// Replay protection using a sliding window of configurable size.
// s->inseqno is expected sequence number
// seqno is received sequence number
// s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
// The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
if(s->replaywin) {
if(seqno != s->inseqno) {
if(seqno >= s->inseqno + s->replaywin * 8) {
// Prevent packets that jump far ahead of the queue from causing many others to be dropped.
if(s->farfuture++ < s->replaywin >> 2)
return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture);
// Unless we have seen lots of them, in which case we consider the others lost.
warning(s, "Lost %d packets\n", seqno - s->inseqno);
memset(s->late, 0, s->replaywin);
} else if (seqno < s->inseqno) {
// If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
} else {
// We missed some packets. Mark them in the bitmap as being late.
for(int i = s->inseqno; i < seqno; i++)
s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
}
}
// Mark the current packet as not being late.
s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
s->farfuture = 0;
}
if(seqno > s->inseqno)
s->inseqno = seqno + 1;
uint16_t netlen = htons(len - 21);
char buffer[len + 23];
memcpy(buffer, &netlen, 2);
memcpy(buffer + 2, data, len);
memcpy(&seqno, buffer + 2, 4);
// Check HMAC and decrypt.
if(!digest_verify(&s->indigest, buffer, len - 14, buffer + len - 14))
return error(s, EIO, "Invalid HMAC");
cipher_set_counter(&s->incipher, &seqno, sizeof seqno);
if(!cipher_counter_xor(&s->incipher, buffer + 6, len - 4, buffer + 6))
return false;
// Append a NULL byte for safety.
buffer[len - 14] = 0;
uint8_t type = buffer[6];
if(type < SPTPS_HANDSHAKE) {
if(!s->instate)
return error(s, EIO, "Application record received before handshake finished");
if(!s->receive_record(s->handle, type, buffer + 7, len - 21))
return false;
} else if(type == SPTPS_HANDSHAKE) {
if(!receive_handshake(s, buffer + 7, len - 21))
return false;
} else {
return error(s, EIO, "Invalid record type");
}
return true;
}
// Receive incoming data. Check if it contains a complete record, if so, handle it.
bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
if(s->datagram)
return sptps_receive_data_datagram(s, data, len);
while(len) {
// First read the 2 length bytes.
if(s->buflen < 6) {
size_t toread = 6 - s->buflen;
if(toread > len)
toread = len;
memcpy(s->inbuf + s->buflen, data, toread);
s->buflen += toread;
len -= toread;
data += toread;
// Exit early if we don't have the full length.
if(s->buflen < 6)
return true;
// Decrypt the length bytes
if(s->instate) {
if(!cipher_counter_xor(&s->incipher, s->inbuf + 4, 2, &s->reclen))
return false;
} else {
memcpy(&s->reclen, s->inbuf + 4, 2);
}
s->reclen = ntohs(s->reclen);
// If we have the length bytes, ensure our buffer can hold the whole request.
s->inbuf = realloc(s->inbuf, s->reclen + 23UL);
if(!s->inbuf)
return error(s, errno, strerror(errno));
// Add sequence number.
uint32_t seqno = htonl(s->inseqno++);
memcpy(s->inbuf, &seqno, 4);
// Exit early if we have no more data to process.
if(!len)
return true;
}
// Read up to the end of the record.
size_t toread = s->reclen + (s->instate ? 23UL : 7UL) - s->buflen;
if(toread > len)
toread = len;
memcpy(s->inbuf + s->buflen, data, toread);
s->buflen += toread;
len -= toread;
data += toread;
// If we don't have a whole record, exit.
if(s->buflen < s->reclen + (s->instate ? 23UL : 7UL))
return true;
// Check HMAC and decrypt.
if(s->instate) {
if(!digest_verify(&s->indigest, s->inbuf, s->reclen + 7UL, s->inbuf + s->reclen + 7UL))
return error(s, EIO, "Invalid HMAC");
if(!cipher_counter_xor(&s->incipher, s->inbuf + 6UL, s->reclen + 1UL, s->inbuf + 6UL))
return false;
}
// Append a NULL byte for safety.
s->inbuf[s->reclen + 7UL] = 0;
uint8_t type = s->inbuf[6];
if(type < SPTPS_HANDSHAKE) {
if(!s->instate)
return error(s, EIO, "Application record received before handshake finished");
if(!s->receive_record(s->handle, type, s->inbuf + 7, s->reclen))
return false;
} else if(type == SPTPS_HANDSHAKE) {
if(!receive_handshake(s, s->inbuf + 7, s->reclen))
return false;
} else {
return error(s, EIO, "Invalid record type");
}
s->buflen = 4;
}
return true;
}
// Start a SPTPS session.
bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t mykey, ecdsa_t hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
// Initialise struct sptps
memset(s, 0, sizeof *s);
s->handle = handle;
s->initiator = initiator;
s->datagram = datagram;
s->mykey = mykey;
s->hiskey = hiskey;
s->replaywin = sptps_replaywin;
if(s->replaywin) {
s->late = malloc(s->replaywin);
if(!s->late)
return error(s, errno, strerror(errno));
}
s->label = malloc(labellen);
if(!s->label)
return error(s, errno, strerror(errno));
if(!datagram) {
s->inbuf = malloc(7);
if(!s->inbuf)
return error(s, errno, strerror(errno));
s->buflen = 4;
memset(s->inbuf, 0, 4);
}
memcpy(s->label, label, labellen);
s->labellen = labellen;
s->send_data = send_data;
s->receive_record = receive_record;
// Do first KEX immediately
s->state = SPTPS_KEX;
return send_kex(s);
}
// Stop a SPTPS session.
bool sptps_stop(sptps_t *s) {
// Clean up any resources.
cipher_close(&s->incipher);
cipher_close(&s->outcipher);
digest_close(&s->indigest);
digest_close(&s->outdigest);
ecdh_free(&s->ecdh);
free(s->inbuf);
free(s->mykex);
free(s->hiskex);
free(s->key);
free(s->label);
free(s->late);
memset(s, 0, sizeof *s);
return true;
}

94
src/sptps.h Normal file
View file

@ -0,0 +1,94 @@
/*
sptps.h -- Simple Peer-to-Peer Security
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>,
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __SPTPS_H__
#define __SPTPS_H__
#include "system.h"
#include "cipher.h"
#include "digest.h"
#include "ecdh.h"
#include "ecdsa.h"
#define SPTPS_VERSION 0
// Record types
#define SPTPS_HANDSHAKE 128 // Key exchange and authentication
#define SPTPS_ALERT 129 // Warning or error messages
#define SPTPS_CLOSE 130 // Application closed the connection
// Key exchange states
#define SPTPS_KEX 0 // Waiting for the first Key EXchange record
#define SPTPS_SECONDARY_KEX 1 // Ready to receive a secondary Key EXchange record
#define SPTPS_SIG 2 // Waiting for a SIGnature record
#define SPTPS_ACK 3 // Waiting for an ACKnowledgement record
typedef bool (*send_data_t)(void *handle, uint8_t type, const char *data, size_t len);
typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
typedef struct sptps {
bool initiator;
bool datagram;
int state;
char *inbuf;
size_t buflen;
uint16_t reclen;
bool instate;
cipher_t incipher;
digest_t indigest;
uint32_t inseqno;
unsigned int replaywin;
unsigned int farfuture;
char *late;
bool outstate;
cipher_t outcipher;
digest_t outdigest;
uint32_t outseqno;
ecdsa_t mykey;
ecdsa_t hiskey;
ecdh_t ecdh;
char *mykex;
char *hiskex;
char *key;
char *label;
size_t labellen;
void *handle;
send_data_t send_data;
receive_record_t receive_record;
} sptps_t;
extern unsigned int sptps_replaywin;
extern void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap);
extern void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap);
extern void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap);
extern bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t mykey, ecdsa_t hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record);
extern bool sptps_stop(sptps_t *s);
extern bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len);
extern bool sptps_receive_data(sptps_t *s, const char *data, size_t len);
extern bool sptps_force_kex(sptps_t *s);
extern bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len);
#endif

194
src/sptps_test.c Normal file
View file

@ -0,0 +1,194 @@
/*
sptps_test.c -- Simple Peer-to-Peer Security test program
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>,
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "crypto.h"
#include "ecdsa.h"
#include "sptps.h"
#include "utils.h"
// Symbols necessary to link with logger.o
bool send_request(void *c, const char *msg, ...) { return false; }
struct list_t *connection_list = NULL;
bool send_meta(void *c, const char *msg , int len) { return false; }
char *logfilename = NULL;
ecdsa_t mykey, hiskey;
static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
char hex[len * 2 + 1];
bin2hex(data, hex, len);
fprintf(stderr, "Sending %d bytes of data:\n%s\n", (int)len, hex);
const int *sock = handle;
if(send(*sock, data, len, 0) != len)
return false;
return true;
}
static bool receive_record(void *handle, uint8_t type, const char *data, uint16_t len) {
fprintf(stderr, "Received type %d record of %hu bytes:\n", type, len);
fwrite(data, len, 1, stdout);
return true;
}
int main(int argc, char *argv[]) {
bool initiator = false;
bool datagram = false;
if(argc > 1 && !strcmp(argv[1], "-d")) {
datagram = true;
argc--;
argv++;
}
if(argc < 4) {
fprintf(stderr, "Usage: %s [-d] my_ecdsa_key_file his_ecdsa_key_file [host] port\n", argv[0]);
return 1;
}
if(argc > 4)
initiator = true;
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state))
return 1;
#endif
struct addrinfo *ai, hint;
memset(&hint, 0, sizeof hint);
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = initiator ? 0 : AI_PASSIVE;
if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
fprintf(stderr, "getaddrinfo() failed: %s\n", strerror(errno));
return 1;
}
int sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
if(sock < 0) {
fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
return 1;
}
int one = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
if(initiator) {
if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
fprintf(stderr, "Could not connect to peer: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Connected\n");
} else {
if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
return 1;
}
if(listen(sock, 1)) {
fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Listening...\n");
sock = accept(sock, NULL, NULL);
if(sock < 0) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Connected\n");
}
crypto_init();
FILE *fp = fopen(argv[1], "r");
if(!ecdsa_read_pem_private_key(&mykey, fp))
return 1;
fclose(fp);
fp = fopen(argv[2], "r");
if(!ecdsa_read_pem_public_key(&hiskey, fp))
return 1;
fclose(fp);
fprintf(stderr, "Keys loaded\n");
sptps_t s;
if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record))
return 1;
while(true) {
char buf[65535] = "";
fd_set fds;
FD_ZERO(&fds);
#ifndef HAVE_MINGW
FD_SET(0, &fds);
#endif
FD_SET(sock, &fds);
if(select(sock + 1, &fds, NULL, NULL, NULL) <= 0)
return 1;
if(FD_ISSET(0, &fds)) {
ssize_t len = read(0, buf, sizeof buf);
if(len < 0) {
fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
return 1;
}
if(len == 0)
break;
if(buf[0] == '^')
sptps_send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
else if(buf[0] == '$') {
sptps_force_kex(&s);
if(len > 1)
sptps_send_record(&s, 0, buf, len);
} else
if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, buf[0] == '\n' ? 0 : buf[0] == '*' ? sizeof buf : len))
return 1;
}
if(FD_ISSET(sock, &fds)) {
ssize_t len = recv(sock, buf, sizeof buf, 0);
if(len < 0) {
fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
return 1;
}
if(len == 0) {
fprintf(stderr, "Connection terminated by peer.\n");
break;
}
char hex[len * 2 + 1];
bin2hex(buf, hex, len);
fprintf(stderr, "Received %d bytes of data:\n%s\n", (int)len, hex);
if(!sptps_receive_data(&s, buf, len))
return 1;
}
}
if(!sptps_stop(&s))
return 1;
return 0;
}

View file

@ -1,6 +1,6 @@
/*
subnet.c -- handle subnet lookups and lists
Copyright (C) 2000-2010 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -23,6 +23,7 @@
#include "splay_tree.h"
#include "control_common.h"
#include "device.h"
#include "hash.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
@ -38,109 +39,14 @@ splay_tree_t *subnet_tree;
/* Subnet lookup cache */
static ipv4_t cache_ipv4_address[2];
static subnet_t *cache_ipv4_subnet[2];
static bool cache_ipv4_valid[2];
static int cache_ipv4_slot;
static ipv6_t cache_ipv6_address[2];
static subnet_t *cache_ipv6_subnet[2];
static bool cache_ipv6_valid[2];
static int cache_ipv6_slot;
static mac_t cache_mac_address[2];
static subnet_t *cache_mac_subnet[2];
static bool cache_mac_valid[2];
static int cache_mac_slot;
hash_t *ipv4_cache;
hash_t *ipv6_cache;
hash_t *mac_cache;
void subnet_cache_flush(void) {
cache_ipv4_valid[0] = cache_ipv4_valid[1] = false;
cache_ipv6_valid[0] = cache_ipv6_valid[1] = false;
cache_mac_valid[0] = cache_mac_valid[1] = false;
}
/* Subnet comparison */
static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
int result;
result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
int result;
result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength;
if(result)
return result;
result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
int result;
result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength;
if(result)
return result;
result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
int subnet_compare(const subnet_t *a, const subnet_t *b) {
int result;
result = a->type - b->type;
if(result)
return result;
switch (a->type) {
case SUBNET_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:
logger(LOG_ERR, "subnet_compare() was called with unknown subnet type %d, exitting!",
a->type);
exit(0);
}
return 0;
hash_clear(ipv4_cache);
hash_clear(ipv6_cache);
hash_clear(mac_cache);
}
/* Initialising trees */
@ -148,11 +54,17 @@ int subnet_compare(const subnet_t *a, const subnet_t *b) {
void init_subnets(void) {
subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet);
subnet_cache_flush();
ipv4_cache = hash_alloc(0x100, sizeof(ipv4_t));
ipv6_cache = hash_alloc(0x100, sizeof(ipv6_t));
mac_cache = hash_alloc(0x100, sizeof(mac_t));
}
void exit_subnets(void) {
splay_delete_tree(subnet_tree);
hash_free(ipv4_cache);
hash_free(ipv6_cache);
hash_free(mac_cache);
}
splay_tree_t *new_subnet_tree(void) {
@ -191,139 +103,6 @@ void subnet_del(node_t *n, subnet_t *subnet) {
subnet_cache_flush();
}
/* Ascii representation of subnets */
bool str2net(subnet_t *subnet, const char *subnetstr) {
int i, l;
uint16_t x[8];
int weight = 10;
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d",
&x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) {
if(l < 0 || l > 32)
return false;
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = l;
subnet->weight = weight;
for(i = 0; i < 4; i++) {
if(x[i] > 255)
return false;
subnet->net.ipv4.address.x[i] = x[i];
}
return true;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&l, &weight) >= 9) {
if(l < 0 || l > 128)
return false;
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = l;
subnet->weight = weight;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) {
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = 32;
subnet->weight = weight;
for(i = 0; i < 4; i++) {
if(x[i] > 255)
return false;
subnet->net.ipv4.address.x[i] = x[i];
}
return true;
}
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], &weight) >= 8) {
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = 128;
subnet->weight = weight;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) {
subnet->type = SUBNET_MAC;
subnet->weight = weight;
for(i = 0; i < 6; i++)
subnet->net.mac.address.x[i] = x[i];
return true;
}
return false;
}
bool net2str(char *netstr, int len, const subnet_t *subnet) {
if(!netstr || !subnet) {
logger(LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!", netstr, subnet);
return false;
}
switch (subnet->type) {
case SUBNET_MAC:
snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
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],
subnet->weight);
break;
case SUBNET_IPV4:
snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%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,
subnet->weight);
break;
case SUBNET_IPV6:
snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%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,
subnet->weight);
break;
default:
logger(LOG_ERR,
"net2str() was called with unknown subnet type %d, exiting!",
subnet->type);
exit(0);
}
return true;
}
/* Subnet lookup routines */
subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
@ -331,26 +110,16 @@ subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
}
subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
subnet_t *p, *r = NULL;
splay_node_t *n;
int i;
subnet_t *r = NULL;
// Check if this address is cached
for(i = 0; i < 2; i++) {
if(!cache_mac_valid[i])
continue;
if(owner && cache_mac_subnet[i] && cache_mac_subnet[i]->owner != owner)
continue;
if(!memcmp(address, &cache_mac_address[i], sizeof *address))
return cache_mac_subnet[i];
}
if((r = hash_search(mac_cache, address)))
return r;
// Search all subnets for a matching one
for(n = owner ? owner->subnet_tree->head : subnet_tree->head; n; n = n->next) {
p = n->data;
for splay_each(subnet_t, p, owner ? owner->subnet_tree : subnet_tree) {
if(!p || p->type != SUBNET_MAC)
continue;
@ -363,33 +132,23 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
// Cache the result
cache_mac_slot = !cache_mac_slot;
memcpy(&cache_mac_address[cache_mac_slot], address, sizeof *address);
cache_mac_subnet[cache_mac_slot] = r;
cache_mac_valid[cache_mac_slot] = true;
if(r)
hash_insert(mac_cache, address, r);
return r;
}
subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
subnet_t *p, *r = NULL;
splay_node_t *n;
int i;
subnet_t *r = NULL;
// Check if this address is cached
for(i = 0; i < 2; i++) {
if(!cache_ipv4_valid[i])
continue;
if(!memcmp(address, &cache_ipv4_address[i], sizeof *address))
return cache_ipv4_subnet[i];
}
if((r = hash_search(ipv4_cache, address)))
return r;
// Search all subnets for a matching one
for(n = subnet_tree->head; n; n = n->next) {
p = n->data;
for splay_each(subnet_t, p, subnet_tree) {
if(!p || p->type != SUBNET_IPV4)
continue;
@ -402,33 +161,23 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
// Cache the result
cache_ipv4_slot = !cache_ipv4_slot;
memcpy(&cache_ipv4_address[cache_ipv4_slot], address, sizeof *address);
cache_ipv4_subnet[cache_ipv4_slot] = r;
cache_ipv4_valid[cache_ipv4_slot] = true;
if(r)
hash_insert(ipv4_cache, address, r);
return r;
}
subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
subnet_t *p, *r = NULL;
splay_node_t *n;
int i;
subnet_t *r = NULL;
// Check if this address is cached
for(i = 0; i < 2; i++) {
if(!cache_ipv6_valid[i])
continue;
if(!memcmp(address, &cache_ipv6_address[i], sizeof *address))
return cache_ipv6_subnet[i];
}
if((r = hash_search(ipv6_cache, address)))
return r;
// Search all subnets for a matching one
for(n = subnet_tree->head; n; n = n->next) {
p = n->data;
for splay_each(subnet_t, p, subnet_tree) {
if(!p || p->type != SUBNET_IPV6)
continue;
@ -441,24 +190,20 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
// Cache the result
cache_ipv6_slot = !cache_ipv6_slot;
memcpy(&cache_ipv6_address[cache_ipv6_slot], address, sizeof *address);
cache_ipv6_subnet[cache_ipv6_slot] = r;
cache_ipv6_valid[cache_ipv6_slot] = true;
if(r)
hash_insert(ipv6_cache, address, r);
return r;
}
void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
splay_node_t *node;
int i;
char *envp[9] = {NULL};
char netstr[MAXNETSTR];
char *name, *address, *port;
char empty[] = "";
// Prepare environment variables to be passed to the script
char *envp[9] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
@ -469,15 +214,17 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
// 4 and 5 are reserved for SUBNET and WEIGHT
xasprintf(&envp[6], "REMOTEADDRESS=%s", address);
xasprintf(&envp[7], "REMOTEPORT=%s", port);
free(port);
free(address);
}
name = up ? "subnet-up" : "subnet-down";
if(!subnet) {
for(node = owner->subnet_tree->head; node; node = node->next) {
subnet = node->data;
for splay_each(subnet_t, subnet, owner->subnet_tree) {
if(!net2str(netstr, sizeof netstr, subnet))
continue;
// Strip the weight from the subnet, and put it in its own environment variable
char *weight = strchr(netstr, '#');
if(weight)
@ -512,20 +259,18 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
}
}
for(i = 0; envp[i] && i < 8; i++)
for(int i = 0; envp[i] && i < 8; i++)
free(envp[i]);
}
bool dump_subnets(connection_t *c) {
char netstr[MAXNETSTR];
subnet_t *subnet;
splay_node_t *node;
for splay_each(subnet_t, subnet, subnet_tree) {
char netstr[MAXNETSTR];
for(node = subnet_tree->head; node; node = node->next) {
subnet = node->data;
if(!net2str(netstr, sizeof netstr, subnet))
continue;
send_request(c, "%d %d %s owner %s",
send_request(c, "%d %d %s %s",
CONTROL, REQ_DUMP_SUBNETS,
netstr, subnet->owner->name);
}

View file

@ -1,6 +1,6 @@
/*
subnet.h -- header for subnet.c
Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -27,7 +27,7 @@ typedef enum subnet_type_t {
SUBNET_MAC = 0,
SUBNET_IPV4,
SUBNET_IPV6,
SUBNET_TYPES /* Guardian */
SUBNET_TYPES /* Guardian */
} subnet_type_t;
typedef struct subnet_mac_t {
@ -47,11 +47,11 @@ typedef struct subnet_ipv6_t {
#include "node.h"
typedef struct subnet_t {
struct node_t *owner; /* the owner of this subnet */
struct node_t *owner; /* the owner of this subnet */
subnet_type_t type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
time_t expires; /* expiry time */
int weight; /* weight (higher value is higher priority) */
subnet_type_t type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
time_t expires; /* expiry time */
int weight; /* weight (higher value is higher priority) */
/* And now for the actual subnet: */
@ -76,6 +76,10 @@ extern void free_subnet_tree(splay_tree_t *);
extern void subnet_add(struct node_t *, subnet_t *);
extern void subnet_del(struct node_t *, subnet_t *);
extern void subnet_update(struct node_t *, subnet_t *, bool);
extern int maskcmp(const void *, const void *, int);
extern void maskcpy(void *, const void *, int, int);
extern void mask(void *, int, int);
extern bool maskcheck(const void *, int, int);
extern bool net2str(char *, int, const subnet_t *);
extern bool str2net(subnet_t *, const char *);
extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
@ -85,4 +89,4 @@ extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
extern bool dump_subnets(struct connection_t *);
extern void subnet_cache_flush(void);
#endif /* __TINC_SUBNET_H__ */
#endif /* __TINC_SUBNET_H__ */

382
src/subnet_parse.c Normal file
View file

@ -0,0 +1,382 @@
/*
subnet_parse.c -- handle subnet parsing
Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
/* Subnet mask handling */
int maskcmp(const void *va, const void *vb, int masklen) {
int i, m, result;
const char *a = va;
const char *b = vb;
for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
result = a[i] - b[i];
if(result)
return result;
}
if(m)
return (a[i] & (0x100 - (1 << (8 - m)))) -
(b[i] & (0x100 - (1 << (8 - m))));
return 0;
}
void mask(void *va, int masklen, int len) {
int i;
char *a = va;
i = masklen / 8;
masklen %= 8;
if(masklen)
a[i++] &= (0x100 - (1 << (8 - masklen)));
for(; i < len; i++)
a[i] = 0;
}
void maskcpy(void *va, const void *vb, int masklen, int len) {
int i, m;
char *a = va;
const char *b = vb;
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
a[i] = b[i];
if(m) {
a[i] = b[i] & (0x100 - (1 << (8 - m)));
i++;
}
for(; i < len; i++)
a[i] = 0;
}
bool maskcheck(const void *va, int masklen, int len) {
int i;
const char *a = va;
i = masklen / 8;
masklen %= 8;
if(masklen && a[i++] & (0xff >> masklen))
return false;
for(; i < len; i++)
if(a[i] != 0)
return false;
return true;
}
/* Subnet comparison */
static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
int result;
result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
int result;
result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength;
if(result)
return result;
result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
int result;
result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength;
if(result)
return result;
result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
if(result)
return result;
result = a->weight - b->weight;
if(result || !a->owner || !b->owner)
return result;
return strcmp(a->owner->name, b->owner->name);
}
int subnet_compare(const subnet_t *a, const subnet_t *b) {
int result;
result = a->type - b->type;
if(result)
return result;
switch (a->type) {
case SUBNET_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:
logger(DEBUG_ALWAYS, LOG_ERR, "subnet_compare() was called with unknown subnet type %d, exitting!", a->type);
exit(1);
}
return 0;
}
/* Ascii representation of subnets */
bool str2net(subnet_t *subnet, const char *subnetstr) {
int i, l;
uint16_t x[8];
int weight = 10;
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d",
&x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) {
if(l < 0 || l > 32)
return false;
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = l;
subnet->weight = weight;
for(int i = 0; i < 4; i++) {
if(x[i] > 255)
return false;
subnet->net.ipv4.address.x[i] = x[i];
}
return true;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&l, &weight) >= 9) {
if(l < 0 || l > 128)
return false;
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = l;
subnet->weight = weight;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) {
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = 32;
subnet->weight = weight;
for(i = 0; i < 4; i++) {
if(x[i] > 255)
return false;
subnet->net.ipv4.address.x[i] = x[i];
}
return true;
}
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], &weight) >= 8) {
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = 128;
subnet->weight = weight;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) {
subnet->type = SUBNET_MAC;
subnet->weight = weight;
for(i = 0; i < 6; i++)
subnet->net.mac.address.x[i] = x[i];
return true;
}
// IPv6 short form
if(strstr(subnetstr, "::")) {
const char *p;
char *q;
int colons = 0;
// Count number of colons
for(p = subnetstr; *p; p++)
if(*p == ':')
colons++;
if(colons > 7)
return false;
// Scan numbers before the double colon
p = subnetstr;
for(i = 0; i < colons; i++) {
if(*p == ':')
break;
x[i] = strtoul(p, &q, 0x10);
if(!q || p == q || *q != ':')
return false;
p = ++q;
}
p++;
colons -= i;
if(!i) {
p++;
colons--;
}
if(!*p || *p == '/' || *p == '#')
colons--;
// Fill in the blanks
for(; i < 8 - colons; i++)
x[i] = 0;
// Scan the remaining numbers
for(; i < 8; i++) {
x[i] = strtoul(p, &q, 0x10);
if(!q || p == q)
return false;
if(i == 7) {
p = q;
break;
}
if(*q != ':')
return false;
p = ++q;
}
l = 128;
if(*p == '/')
sscanf(p, "/%d#%d", &l, &weight);
else if(*p == '#')
sscanf(p, "#%d", &weight);
if(l < 0 || l > 128)
return false;
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = l;
subnet->weight = weight;
for(i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
return false;
}
bool net2str(char *netstr, int len, const subnet_t *subnet) {
if(!netstr || !subnet) {
logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!", netstr, subnet);
return false;
}
switch (subnet->type) {
case SUBNET_MAC:
snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
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],
subnet->weight);
break;
case SUBNET_IPV4:
snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%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,
subnet->weight);
break;
case SUBNET_IPV6:
snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%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,
subnet->weight);
break;
default:
logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with unknown subnet type %d, exiting!", subnet->type);
exit(1);
}
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998-2005 Ivo Timmermans
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2008 Max Rijevski <maksuf@gmail.com>
2009 Michael Tokarev <mjt@tls.msk.ru>
2010 Julien Muchembled <jm@jmuchemb.eu>
@ -87,10 +87,10 @@ static const char *switchuser = NULL;
/* If nonzero, write log entries to a separate file. */
bool use_logfile = false;
char *identname = NULL; /* program name for syslog */
char *logfilename = NULL; /* log file location */
char *identname = NULL; /* program name for syslog */
char *logfilename = NULL; /* log file location */
char *pidfilename = NULL;
char **g_argv; /* a copy of the cmdline arguments */
char **g_argv; /* a copy of the cmdline arguments */
static int status = 1;
@ -107,6 +107,7 @@ static struct option const long_options[] = {
{"user", required_argument, NULL, 'U'},
{"logfile", optional_argument, NULL, 4},
{"pidfile", required_argument, NULL, 5},
{"option", required_argument, NULL, 'o'},
{NULL, 0, NULL, 0}
};
@ -122,18 +123,18 @@ static void usage(bool status) {
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"
" -n, --net=NETNAME Connect to net NETNAME.\n"
" -L, --mlock Lock tinc into main memory.\n"
" --logfile[=FILENAME] Write log entries to a logfile.\n"
" --pidfile=FILENAME Write PID and control socket cookie to FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\n"
" -o [HOST.]KEY=VALUE Set global/host configuration value.\n"
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n"
" --version Output version information and exit.\n\n");
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"
" -n, --net=NETNAME Connect to net NETNAME.\n"
" -L, --mlock Lock tinc into main memory.\n"
" --logfile[=FILENAME] Write log entries to a logfile.\n"
" --pidfile=FILENAME Write PID and control socket cookie to FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\n"
" -o, --option[HOST.]KEY=VALUE Set global/host configuration value.\n"
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n"
" --version Output version information and exit.\n\n");
printf("Report bugs to tinc@tinc-vpn.org.\n");
}
}
@ -148,77 +149,75 @@ static bool parse_options(int argc, char **argv) {
while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
case 0: /* long option */
break;
case 'c': /* config file */
case 'c': /* config file */
confbase = xstrdup(optarg);
break;
case 'D': /* no detach */
case 'D': /* no detach */
do_detach = false;
break;
case 'L': /* no detach */
case 'L': /* no detach */
#ifndef HAVE_MLOCKALL
logger(LOG_ERR, "%s not supported on this platform", "mlockall()");
logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "mlockall()");
return false;
#else
do_mlock = true;
break;
#endif
case 'd': /* inc debug level */
case 'd': /* inc debug level */
if(optarg)
debug_level = atoi(optarg);
else
debug_level++;
break;
case 'n': /* net name given */
/* netname "." is special: a "top-level name" */
netname = strcmp(optarg, ".") != 0 ?
xstrdup(optarg) : NULL;
case 'n': /* net name given */
netname = xstrdup(optarg);
break;
case 'o': /* option */
case 'o': /* option */
cfg = parse_config_line(optarg, NULL, ++lineno);
if (!cfg)
return false;
list_insert_tail(cmdline_conf, cfg);
break;
case 'R': /* chroot to NETNAME dir */
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;
case 'U': /* setuid to USER */
case 'U': /* setuid to USER */
switchuser = optarg;
break;
case 1: /* show help */
case 1: /* show help */
show_help = true;
break;
case 2: /* show version */
case 2: /* show version */
show_version = true;
break;
case 3: /* bypass security */
case 3: /* bypass security */
bypass_security = true;
break;
case 4: /* write log entries to a file */
case 4: /* write log entries to a file */
use_logfile = true;
if(optarg)
logfilename = xstrdup(optarg);
break;
case 5: /* open control socket here */
case 5: /* open control socket here */
pidfilename = xstrdup(optarg);
break;
case '?':
case '?': /* wrong options */
usage(true);
return false;
@ -227,6 +226,21 @@ static bool parse_options(int argc, char **argv) {
}
}
if(!netname && (netname = getenv("NETNAME")))
netname = xstrdup(netname);
/* netname "." is special: a "top-level name" */
if(netname && (!*netname || !strcmp(netname, "."))) {
free(netname);
netname = NULL;
}
if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
fprintf(stderr, "Invalid character in netname!\n");
return false;
}
return true;
}
@ -249,15 +263,15 @@ static void make_names(void) {
if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
if(!logfilename)
xasprintf(&logfilename, "%s/log/%s.log", identname);
xasprintf(&logfilename, "%s" SLASH "log" SLASH "%s.log", identname);
if(!confbase) {
if(netname)
xasprintf(&confbase, "%s/%s", installdir, netname);
xasprintf(&confbase, "%s" SLASH "%s", installdir, netname);
else
xasprintf(&confbase, "%s", installdir);
}
if(!pidfilename)
xasprintf(&pidfilename, "%s/pid", confbase);
xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
}
RegCloseKey(key);
if(*installdir)
@ -266,19 +280,19 @@ static void make_names(void) {
#endif
if(!logfilename)
xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
xasprintf(&logfilename, LOCALSTATEDIR SLASH "log" SLASH "%s.log", identname);
if(!pidfilename)
xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
xasprintf(&pidfilename, LOCALSTATEDIR SLASH "run" SLASH "%s.pid", identname);
if(netname) {
if(!confbase)
xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
else
logger(LOG_INFO, "Both netname and configuration directory given, using the latter...");
logger(DEBUG_ALWAYS, LOG_INFO, "Both netname and configuration directory given, using the latter...");
} else {
if(!confbase)
xasprintf(&confbase, CONFDIR "/tinc");
xasprintf(&confbase, CONFDIR SLASH "tinc");
}
}
@ -293,11 +307,11 @@ static void free_names(void) {
static bool drop_privs(void) {
#ifdef HAVE_MINGW
if (switchuser) {
logger(LOG_ERR, "%s not supported on this platform", "-U");
logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "-U");
return false;
}
if (do_chroot) {
logger(LOG_ERR, "%s not supported on this platform", "-R");
logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "-R");
return false;
}
#else
@ -305,23 +319,26 @@ static bool drop_privs(void) {
if (switchuser) {
struct passwd *pw = getpwnam(switchuser);
if (!pw) {
logger(LOG_ERR, "unknown user `%s'", switchuser);
logger(DEBUG_ALWAYS, LOG_ERR, "unknown user `%s'", switchuser);
return false;
}
uid = pw->pw_uid;
if (initgroups(switchuser, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
"initgroups", strerror(errno));
return false;
}
#ifndef __ANDROID__
// Not supported in android NDK
endgrent();
endpwent();
#endif
}
if (do_chroot) {
tzset(); /* for proper timestamps in logs */
tzset(); /* for proper timestamps in logs */
if (chroot(confbase) != 0 || chdir("/") != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
"chroot", strerror(errno));
return false;
}
@ -330,7 +347,7 @@ static bool drop_privs(void) {
}
if (switchuser)
if (setuid(uid) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
"setuid", strerror(errno));
return false;
}
@ -352,13 +369,13 @@ int main(int argc, char **argv) {
if(!parse_options(argc, argv))
return 1;
make_names();
if(show_version) {
printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
printf("Copyright (C) 1998-2011 Ivo Timmermans, Guus Sliepen and others.\n"
printf("Copyright (C) 1998-2012 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"
@ -374,20 +391,21 @@ int main(int argc, char **argv) {
#ifdef HAVE_MINGW
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
return 1;
}
#endif
openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
if(!event_init()) {
logger(LOG_ERR, "Error initializing libevent!");
return 1;
}
g_argv = argv;
if(getenv("LISTEN_PID") && atoi(getenv("LISTEN_PID")) == getpid())
do_detach = false;
#ifdef HAVE_UNSETENV
unsetenv("LISTEN_PID");
#endif
init_configuration(&config_tree);
/* Slllluuuuuuurrrrp! */
@ -400,7 +418,7 @@ int main(int argc, char **argv) {
#ifdef HAVE_LZO
if(lzo_init() != LZO_E_OK) {
logger(LOG_ERR, "Error initializing LZO compressor!");
logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing LZO compressor!");
return 1;
}
#endif
@ -416,6 +434,7 @@ int main2(int argc, char **argv) {
InitializeCriticalSection(&mutex);
EnterCriticalSection(&mutex);
#endif
char *priority = NULL;
if(!detach())
return 1;
@ -425,12 +444,17 @@ int main2(int argc, char **argv) {
* This has to be done after daemon()/fork() so it works for child.
* No need to do that in parent as it's very short-lived. */
if(do_mlock && mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s", "mlockall",
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "mlockall",
strerror(errno));
return 1;
}
#endif
if(!event_init()) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing libevent!");
return 1;
}
/* Setup sockets and open device. */
if(!setup_network())
@ -445,32 +469,27 @@ int main2(int argc, char **argv) {
/* Change process priority */
char *priority = NULL;
if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
if(!strcasecmp(priority, "Normal")) {
if (setpriority(NORMAL_PRIORITY_CLASS) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
"setpriority", strerror(errno));
goto end;
}
} else if(!strcasecmp(priority, "Low")) {
if (setpriority(BELOW_NORMAL_PRIORITY_CLASS) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
"setpriority", strerror(errno));
goto end;
}
} else if(!strcasecmp(priority, "High")) {
if (setpriority(HIGH_PRIORITY_CLASS) != 0) {
logger(LOG_ERR, "System call `%s' failed: %s",
"setpriority", strerror(errno));
goto end;
}
} else {
logger(LOG_ERR, "Invalid priority `%s`!", priority);
goto end;
}
}
if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
if(!strcasecmp(priority, "Normal")) {
if (setpriority(NORMAL_PRIORITY_CLASS) != 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
goto end;
}
} else if(!strcasecmp(priority, "Low")) {
if (setpriority(BELOW_NORMAL_PRIORITY_CLASS) != 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
goto end;
}
} else if(!strcasecmp(priority, "High")) {
if (setpriority(HIGH_PRIORITY_CLASS) != 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
goto end;
}
} else {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid priority `%s`!", priority);
goto end;
}
}
/* drop privileges */
if (!drop_privs())
@ -482,8 +501,8 @@ int main2(int argc, char **argv) {
/* Shutdown properly. */
ifdebug(CONNECTIONS)
dump_device_stats();
if(debug_level >= DEBUG_CONNECTIONS)
devops.dump_stats();
close_network_connections();
@ -491,11 +510,14 @@ end:
exit_control();
end_nonet:
logger(LOG_NOTICE, "Terminating");
logger(DEBUG_ALWAYS, LOG_NOTICE, "Terminating");
free(priority);
crypto_exit();
exit_configuration(&config_tree);
free(cmdline_conf);
free_names();
return status;

View file

@ -1,6 +1,6 @@
/*
top.c -- Show real-time statistics from a running tincd
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2011-2012 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -59,7 +59,6 @@ static bool cumulative = false;
static list_t node_list;
static struct timeval now, prev, diff;
static int delay = 1000;
static bool running = true;
static bool changed = true;
static const char *unit = "bytes";
static float scale = 1;
@ -85,10 +84,8 @@ static void update(int fd) {
uint64_t out_packets;
uint64_t out_bytes;
for(list_node_t *i = node_list.head; i; i = i->next) {
nodestats_t *node = i->data;
node->known = false;
}
for list_each(nodestats_t, ns, &node_list)
ns->known = false;
while(recvline(fd, line, sizeof line)) {
int n = sscanf(line, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, &code, &req, name, &in_packets, &in_bytes, &out_packets, &out_bytes);
@ -104,18 +101,17 @@ static void update(int fd) {
nodestats_t *found = NULL;
for(list_node_t *i = node_list.head; i; i = i->next) {
nodestats_t *node = i->data;
int result = strcmp(name, node->name);
for list_each(nodestats_t, ns, &node_list) {
int result = strcmp(name, ns->name);
if(result > 0) {
continue;
} if(result == 0) {
found = node;
found = ns;
break;
} else {
found = xmalloc_and_zero(sizeof *found);
found->name = xstrdup(name);
list_insert_before(&node_list, i, found);
list_insert_before(&node_list, node, found);
changed = true;
break;
}
@ -153,14 +149,14 @@ static void redraw(void) {
if(changed) {
n = 0;
sorted = xrealloc(sorted, node_list.count * sizeof *sorted);
for(list_node_t *i = node_list.head; i; i = i->next)
sorted[n++] = i->data;
for list_each(nodestats_t, ns, &node_list)
sorted[n++] = ns;
changed = false;
}
for(int i = 0; i < n; i++)
sorted[i]->i = i;
int cmpfloat(float a, float b) {
if(a < b)
return -1;
@ -247,6 +243,7 @@ static void redraw(void) {
void top(int fd) {
initscr();
timeout(delay);
bool running = true;
while(running) {
update(fd);

Some files were not shown because too many files have changed in this diff Show more