Import Upstream version 1.1~pre3
This commit is contained in:
parent
02de1cd2f1
commit
34d5939212
136 changed files with 13943 additions and 4867 deletions
|
|
@ -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
|
||||
|
|
|
|||
150
src/Makefile.in
150
src/Makefile.in
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
218
src/cipher.c
Normal 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;
|
||||
}
|
||||
132
src/conf.c
132
src/conf.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
43
src/crypto.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
26
src/device.h
26
src/device.h
|
|
@ -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
127
src/digest.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
62
src/dummy_device.c
Normal 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
96
src/ecdh.c
Normal 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
130
src/ecdsa.c
Normal 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;
|
||||
}
|
||||
}
|
||||
21
src/edge.c
21
src/edge.c
|
|
@ -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);
|
||||
|
|
|
|||
14
src/edge.h
14
src/edge.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
223
src/graph.c
223
src/graph.c
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
105
src/hash.c
Normal 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
41
src/hash.h
Normal 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
266
src/info.c
Normal 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
27
src/info.h
Normal 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
|
||||
|
||||
12
src/ipv4.h
12
src/ipv4.h
|
|
@ -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
|
||||
|
|
|
|||
10
src/ipv6.h
10
src/ipv6.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
42
src/list.c
42
src/list.c
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
147
src/logger.c
147
src/logger.c
|
|
@ -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) {
|
||||
|
|
|
|||
43
src/logger.h
43
src/logger.h
|
|
@ -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__ */
|
||||
|
|
|
|||
94
src/meta.c
94
src/meta.c
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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
235
src/multicast_device.c
Normal 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
231
src/net.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
62
src/net.h
62
src/net.h
|
|
@ -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__ */
|
||||
|
|
|
|||
444
src/net_packet.c
444
src/net_packet.c
|
|
@ -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);
|
||||
|
|
|
|||
716
src/net_setup.c
716
src/net_setup.c
File diff suppressed because it is too large
Load diff
476
src/net_socket.c
476
src/net_socket.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
93
src/netutl.c
93
src/netutl.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
86
src/node.c
86
src/node.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
78
src/node.h
78
src/node.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
75
src/prf.c
Normal 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);
|
||||
}
|
||||
117
src/process.c
117
src/process.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
301
src/route.c
301
src/route.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
12
src/route.h
12
src/route.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
662
src/sptps.c
Normal 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
94
src/sptps.h
Normal 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
194
src/sptps_test.c
Normal 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;
|
||||
}
|
||||
341
src/subnet.c
341
src/subnet.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
18
src/subnet.h
18
src/subnet.h
|
|
@ -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
382
src/subnet_parse.c
Normal 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;
|
||||
}
|
||||
1938
src/tincctl.c
1938
src/tincctl.c
File diff suppressed because it is too large
Load diff
200
src/tincd.c
200
src/tincd.c
|
|
@ -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;
|
||||
|
|
|
|||
25
src/top.c
25
src/top.c
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue