Import Upstream version 1.1~pre11

This commit is contained in:
Guus Sliepen 2019-08-26 13:44:51 +02:00
parent 60cff3039b
commit 1813f3157e
128 changed files with 10991 additions and 3132 deletions

View file

@ -1,9 +1,36 @@
## Produce this file with automake to get Makefile.in
sbin_PROGRAMS = tincd tinc sptps_test
sbin_PROGRAMS = tincd tinc sptps_test sptps_keypair
## Make sure version.c is always rebuilt
.PHONY: version.c
version.c:
if LINUX
sbin_PROGRAMS += sptps_speed
endif
DEFAULT_INCLUDES =
ed25519_SOURCES = \
ed25519/add_scalar.c \
ed25519/ed25519.h \
ed25519/fe.c ed25519/fe.h \
ed25519/fixedint.h \
ed25519/ge.c ed25519/ge.h \
ed25519/key_exchange.c \
ed25519/keypair.c \
ed25519/precomp_data.h \
ed25519/sc.c ed25519/sc.h \
ed25519/sha512.c ed25519/sha512.h \
ed25519/sign.c \
ed25519/verify.c
chacha_poly1305_SOURCES = \
chacha-poly1305/chacha.c chacha-poly1305/chacha.h \
chacha-poly1305/chacha-poly1305.c chacha-poly1305/chacha-poly1305.h \
chacha-poly1305/poly1305.c chacha-poly1305/poly1305.h
tincd_SOURCES = \
buffer.c buffer.h \
cipher.h \
@ -63,7 +90,10 @@ tincd_SOURCES = \
system.h \
tincd.c \
utils.c utils.h \
xalloc.h
xalloc.h \
version.c version.h \
$(ed25519_SOURCES) \
$(chacha_poly1305_SOURCES)
tinc_SOURCES = \
dropin.c dropin.h \
@ -79,13 +109,31 @@ tinc_SOURCES = \
subnet_parse.c subnet.h \
tincctl.c tincctl.h \
top.c top.h \
utils.c utils.h
utils.c utils.h \
version.c version.h \
$(ed25519_SOURCES) \
$(chacha_poly1305_SOURCES)
sptps_test_SOURCES = \
logger.c logger.h \
sptps.c sptps.h \
sptps_test.c \
utils.c utils.h
utils.c utils.h \
$(ed25519_SOURCES) \
$(chacha_poly1305_SOURCES)
sptps_keypair_SOURCES = \
sptps_keypair.c \
utils.c utils.h \
$(ed25519_SOURCES)
sptps_speed_SOURCES = \
logger.c logger.h \
sptps.c sptps.h \
sptps_speed.c \
utils.c utils.h \
$(ed25519_SOURCES) \
$(chacha_poly1305_SOURCES)
## Conditionally compile device drivers
@ -125,26 +173,35 @@ tincd_SOURCES += \
openssl/cipher.c \
openssl/crypto.c \
openssl/digest.c openssl/digest.h \
openssl/ecdh.c \
openssl/ecdsa.c \
ed25519/ecdh.c \
ed25519/ecdsa.c \
openssl/prf.c \
openssl/rsa.c
tinc_SOURCES += \
openssl/cipher.c \
openssl/crypto.c \
openssl/digest.c openssl/digest.h \
openssl/ecdh.c \
openssl/ecdsa.c \
openssl/ecdsagen.c \
ed25519/ecdh.c \
ed25519/ecdsa.c \
ed25519/ecdsagen.c \
openssl/prf.c \
openssl/rsa.c \
openssl/rsagen.c
sptps_test_SOURCES += \
openssl/cipher.c \
openssl/crypto.c \
openssl/digest.c openssl/digest.h \
openssl/ecdh.c \
openssl/ecdsa.c \
ed25519/ecdh.c \
ed25519/ecdsa.c \
openssl/prf.c
sptps_keypair_SOURCES += \
openssl/crypto.c \
ed25519/ecdsagen.c
sptps_speed_SOURCES += \
openssl/crypto.c \
openssl/digest.c openssl/digest.h \
ed25519/ecdh.c \
ed25519/ecdsa.c \
ed25519/ecdsagen.c \
openssl/prf.c
endif
@ -177,8 +234,9 @@ sptps_test_SOURCES += \
endif
tinc_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
sptps_speed_LDADD = -lrt
LIBS = @LIBS@ @LIBGCRYPT_LIBS@
LIBS = @LIBS@
if TUNEMU
LIBS += -lpcap

View file

@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.14 from Makefile.am.
# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@ -78,44 +78,57 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT)
@LINUX_TRUE@am__append_1 = linux/device.c
@BSD_TRUE@am__append_2 = bsd/device.c
@BSD_TRUE@@TUNEMU_TRUE@am__append_3 = bsd/tunemu.c bsd/tunemu.h
@SOLARIS_TRUE@am__append_4 = solaris/device.c
@MINGW_TRUE@am__append_5 = mingw/device.c mingw/common.h
@CYGWIN_TRUE@am__append_6 = cygwin/device.c
@UML_TRUE@am__append_7 = uml_device.c
@VDE_TRUE@am__append_8 = vde_device.c
@OPENSSL_TRUE@am__append_9 = \
@OPENSSL_TRUE@ openssl/cipher.c \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ openssl/digest.c openssl/digest.h \
@OPENSSL_TRUE@ openssl/ecdh.c \
@OPENSSL_TRUE@ openssl/ecdsa.c \
@OPENSSL_TRUE@ openssl/prf.c \
@OPENSSL_TRUE@ openssl/rsa.c
sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT) \
sptps_keypair$(EXEEXT) $(am__EXEEXT_1)
@LINUX_TRUE@am__append_1 = sptps_speed
@LINUX_TRUE@am__append_2 = linux/device.c
@BSD_TRUE@am__append_3 = bsd/device.c
@BSD_TRUE@@TUNEMU_TRUE@am__append_4 = bsd/tunemu.c bsd/tunemu.h
@SOLARIS_TRUE@am__append_5 = solaris/device.c
@MINGW_TRUE@am__append_6 = mingw/device.c mingw/common.h
@CYGWIN_TRUE@am__append_7 = cygwin/device.c
@UML_TRUE@am__append_8 = uml_device.c
@VDE_TRUE@am__append_9 = vde_device.c
@OPENSSL_TRUE@am__append_10 = \
@OPENSSL_TRUE@ openssl/cipher.c \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ openssl/digest.c openssl/digest.h \
@OPENSSL_TRUE@ openssl/ecdh.c \
@OPENSSL_TRUE@ openssl/ecdsa.c \
@OPENSSL_TRUE@ openssl/ecdsagen.c \
@OPENSSL_TRUE@ ed25519/ecdh.c \
@OPENSSL_TRUE@ ed25519/ecdsa.c \
@OPENSSL_TRUE@ openssl/prf.c \
@OPENSSL_TRUE@ openssl/rsa.c \
@OPENSSL_TRUE@ openssl/rsagen.c
@OPENSSL_TRUE@ openssl/rsa.c
@OPENSSL_TRUE@am__append_11 = \
@OPENSSL_TRUE@ openssl/cipher.c \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ openssl/digest.c openssl/digest.h \
@OPENSSL_TRUE@ openssl/ecdh.c \
@OPENSSL_TRUE@ openssl/ecdsa.c \
@OPENSSL_TRUE@ ed25519/ecdh.c \
@OPENSSL_TRUE@ ed25519/ecdsa.c \
@OPENSSL_TRUE@ ed25519/ecdsagen.c \
@OPENSSL_TRUE@ openssl/prf.c \
@OPENSSL_TRUE@ openssl/rsa.c \
@OPENSSL_TRUE@ openssl/rsagen.c
@OPENSSL_TRUE@am__append_12 = \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ openssl/digest.c openssl/digest.h \
@OPENSSL_TRUE@ ed25519/ecdh.c \
@OPENSSL_TRUE@ ed25519/ecdsa.c \
@OPENSSL_TRUE@ openssl/prf.c
@GCRYPT_TRUE@am__append_12 = \
@OPENSSL_TRUE@am__append_13 = \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ ed25519/ecdsagen.c
@OPENSSL_TRUE@am__append_14 = \
@OPENSSL_TRUE@ openssl/crypto.c \
@OPENSSL_TRUE@ openssl/digest.c openssl/digest.h \
@OPENSSL_TRUE@ ed25519/ecdh.c \
@OPENSSL_TRUE@ ed25519/ecdsa.c \
@OPENSSL_TRUE@ ed25519/ecdsagen.c \
@OPENSSL_TRUE@ openssl/prf.c
@GCRYPT_TRUE@am__append_15 = \
@GCRYPT_TRUE@ gcrypt/cipher.c \
@GCRYPT_TRUE@ gcrypt/crypto.c \
@GCRYPT_TRUE@ gcrypt/digest.c gcrypt/digest.h \
@ -124,7 +137,7 @@ sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT)
@GCRYPT_TRUE@ gcrypt/prf.c \
@GCRYPT_TRUE@ gcrypt/rsa.c
@GCRYPT_TRUE@am__append_13 = \
@GCRYPT_TRUE@am__append_16 = \
@GCRYPT_TRUE@ gcrypt/cipher.c \
@GCRYPT_TRUE@ gcrypt/crypto.c \
@GCRYPT_TRUE@ gcrypt/digest.c gcrypt/digest.h \
@ -135,7 +148,7 @@ sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT)
@GCRYPT_TRUE@ gcrypt/rsa.c \
@GCRYPT_TRUE@ gcrypt/rsagen.c
@GCRYPT_TRUE@am__append_14 = \
@GCRYPT_TRUE@am__append_17 = \
@GCRYPT_TRUE@ gcrypt/cipher.c \
@GCRYPT_TRUE@ gcrypt/crypto.c \
@GCRYPT_TRUE@ gcrypt/digest.c gcrypt/digest.h \
@ -143,60 +156,120 @@ sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT)
@GCRYPT_TRUE@ gcrypt/ecdsa.c \
@GCRYPT_TRUE@ gcrypt/prf.c
@TUNEMU_TRUE@am__append_15 = -lpcap
@TUNEMU_TRUE@am__append_18 = -lpcap
subdir = src
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(top_srcdir)/depcomp
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/lzo.m4 \
$(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/readline.m4 \
$(top_srcdir)/m4/zlib.m4 $(top_srcdir)/configure.ac
$(top_srcdir)/m4/ax_check_compile_flag.m4 \
$(top_srcdir)/m4/ax_check_link_flag.m4 \
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/libgcrypt.m4 \
$(top_srcdir)/m4/lzo.m4 $(top_srcdir)/m4/openssl.m4 \
$(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/zlib.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
@LINUX_TRUE@am__EXEEXT_1 = sptps_speed$(EXEEXT)
am__installdirs = "$(DESTDIR)$(sbindir)"
PROGRAMS = $(sbin_PROGRAMS)
am__sptps_test_SOURCES_DIST = logger.c logger.h sptps.c sptps.h \
sptps_test.c utils.c utils.h openssl/cipher.c openssl/crypto.c \
openssl/digest.c openssl/digest.h openssl/ecdh.c \
openssl/ecdsa.c openssl/prf.c gcrypt/cipher.c gcrypt/crypto.c \
gcrypt/digest.c gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdsa.c \
gcrypt/prf.c
am__sptps_keypair_SOURCES_DIST = sptps_keypair.c utils.c utils.h \
ed25519/add_scalar.c ed25519/ed25519.h ed25519/fe.c \
ed25519/fe.h ed25519/fixedint.h ed25519/ge.c ed25519/ge.h \
ed25519/key_exchange.c ed25519/keypair.c \
ed25519/precomp_data.h ed25519/sc.c ed25519/sc.h \
ed25519/sha512.c ed25519/sha512.h ed25519/sign.c \
ed25519/verify.c openssl/crypto.c ed25519/ecdsagen.c
am__dirstamp = $(am__leading_dot)dirstamp
@OPENSSL_TRUE@am__objects_1 = openssl/cipher.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) openssl/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/ecdsa.$(OBJEXT) openssl/prf.$(OBJEXT)
@GCRYPT_TRUE@am__objects_2 = gcrypt/cipher.$(OBJEXT) \
am__objects_1 = ed25519/add_scalar.$(OBJEXT) ed25519/fe.$(OBJEXT) \
ed25519/ge.$(OBJEXT) ed25519/key_exchange.$(OBJEXT) \
ed25519/keypair.$(OBJEXT) ed25519/sc.$(OBJEXT) \
ed25519/sha512.$(OBJEXT) ed25519/sign.$(OBJEXT) \
ed25519/verify.$(OBJEXT)
@OPENSSL_TRUE@am__objects_2 = openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsagen.$(OBJEXT)
am_sptps_keypair_OBJECTS = sptps_keypair.$(OBJEXT) utils.$(OBJEXT) \
$(am__objects_1) $(am__objects_2)
sptps_keypair_OBJECTS = $(am_sptps_keypair_OBJECTS)
sptps_keypair_LDADD = $(LDADD)
am__sptps_speed_SOURCES_DIST = logger.c logger.h sptps.c sptps.h \
sptps_speed.c utils.c utils.h ed25519/add_scalar.c \
ed25519/ed25519.h ed25519/fe.c ed25519/fe.h ed25519/fixedint.h \
ed25519/ge.c ed25519/ge.h ed25519/key_exchange.c \
ed25519/keypair.c ed25519/precomp_data.h ed25519/sc.c \
ed25519/sc.h ed25519/sha512.c ed25519/sha512.h ed25519/sign.c \
ed25519/verify.c chacha-poly1305/chacha.c \
chacha-poly1305/chacha.h chacha-poly1305/chacha-poly1305.c \
chacha-poly1305/chacha-poly1305.h chacha-poly1305/poly1305.c \
chacha-poly1305/poly1305.h openssl/crypto.c openssl/digest.c \
openssl/digest.h ed25519/ecdh.c ed25519/ecdsa.c \
ed25519/ecdsagen.c openssl/prf.c
am__objects_3 = chacha-poly1305/chacha.$(OBJEXT) \
chacha-poly1305/chacha-poly1305.$(OBJEXT) \
chacha-poly1305/poly1305.$(OBJEXT)
@OPENSSL_TRUE@am__objects_4 = openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) ed25519/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsa.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsagen.$(OBJEXT) openssl/prf.$(OBJEXT)
am_sptps_speed_OBJECTS = logger.$(OBJEXT) sptps.$(OBJEXT) \
sptps_speed.$(OBJEXT) utils.$(OBJEXT) $(am__objects_1) \
$(am__objects_3) $(am__objects_4)
sptps_speed_OBJECTS = $(am_sptps_speed_OBJECTS)
sptps_speed_DEPENDENCIES =
am__sptps_test_SOURCES_DIST = logger.c logger.h sptps.c sptps.h \
sptps_test.c utils.c utils.h ed25519/add_scalar.c \
ed25519/ed25519.h ed25519/fe.c ed25519/fe.h ed25519/fixedint.h \
ed25519/ge.c ed25519/ge.h ed25519/key_exchange.c \
ed25519/keypair.c ed25519/precomp_data.h ed25519/sc.c \
ed25519/sc.h ed25519/sha512.c ed25519/sha512.h ed25519/sign.c \
ed25519/verify.c chacha-poly1305/chacha.c \
chacha-poly1305/chacha.h chacha-poly1305/chacha-poly1305.c \
chacha-poly1305/chacha-poly1305.h chacha-poly1305/poly1305.c \
chacha-poly1305/poly1305.h openssl/crypto.c openssl/digest.c \
openssl/digest.h ed25519/ecdh.c ed25519/ecdsa.c openssl/prf.c \
gcrypt/cipher.c gcrypt/crypto.c gcrypt/digest.c \
gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdsa.c gcrypt/prf.c
@OPENSSL_TRUE@am__objects_5 = openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) ed25519/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsa.$(OBJEXT) openssl/prf.$(OBJEXT)
@GCRYPT_TRUE@am__objects_6 = gcrypt/cipher.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/crypto.$(OBJEXT) gcrypt/digest.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/ecdh.$(OBJEXT) gcrypt/ecdsa.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/prf.$(OBJEXT)
am_sptps_test_OBJECTS = logger.$(OBJEXT) sptps.$(OBJEXT) \
sptps_test.$(OBJEXT) utils.$(OBJEXT) $(am__objects_1) \
$(am__objects_2)
$(am__objects_3) $(am__objects_5) $(am__objects_6)
sptps_test_OBJECTS = $(am_sptps_test_OBJECTS)
sptps_test_LDADD = $(LDADD)
am__tinc_SOURCES_DIST = dropin.c dropin.h getopt.c getopt.h getopt1.c \
info.c info.h invitation.c invitation.h list.c list.h names.c \
names.h netutl.c netutl.h script.c script.h sptps.c sptps.h \
subnet_parse.c subnet.h tincctl.c tincctl.h top.c top.h \
utils.c utils.h openssl/cipher.c openssl/crypto.c \
openssl/digest.c openssl/digest.h openssl/ecdh.c \
openssl/ecdsa.c openssl/ecdsagen.c openssl/prf.c openssl/rsa.c \
utils.c utils.h version.c version.h ed25519/add_scalar.c \
ed25519/ed25519.h ed25519/fe.c ed25519/fe.h ed25519/fixedint.h \
ed25519/ge.c ed25519/ge.h ed25519/key_exchange.c \
ed25519/keypair.c ed25519/precomp_data.h ed25519/sc.c \
ed25519/sc.h ed25519/sha512.c ed25519/sha512.h ed25519/sign.c \
ed25519/verify.c chacha-poly1305/chacha.c \
chacha-poly1305/chacha.h chacha-poly1305/chacha-poly1305.c \
chacha-poly1305/chacha-poly1305.h chacha-poly1305/poly1305.c \
chacha-poly1305/poly1305.h openssl/cipher.c openssl/crypto.c \
openssl/digest.c openssl/digest.h ed25519/ecdh.c \
ed25519/ecdsa.c ed25519/ecdsagen.c openssl/prf.c openssl/rsa.c \
openssl/rsagen.c gcrypt/cipher.c gcrypt/crypto.c \
gcrypt/digest.c gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdsa.c \
gcrypt/ecdsagen.c gcrypt/prf.c gcrypt/rsa.c gcrypt/rsagen.c
@OPENSSL_TRUE@am__objects_3 = openssl/cipher.$(OBJEXT) \
@OPENSSL_TRUE@am__objects_7 = openssl/cipher.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) openssl/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/ecdsa.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/ecdsagen.$(OBJEXT) openssl/prf.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) ed25519/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsa.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsagen.$(OBJEXT) openssl/prf.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/rsa.$(OBJEXT) openssl/rsagen.$(OBJEXT)
@GCRYPT_TRUE@am__objects_4 = gcrypt/cipher.$(OBJEXT) \
@GCRYPT_TRUE@am__objects_8 = gcrypt/cipher.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/crypto.$(OBJEXT) gcrypt/digest.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/ecdh.$(OBJEXT) gcrypt/ecdsa.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/ecdsagen.$(OBJEXT) gcrypt/prf.$(OBJEXT) \
@ -205,8 +278,9 @@ am_tinc_OBJECTS = dropin.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
info.$(OBJEXT) invitation.$(OBJEXT) list.$(OBJEXT) \
names.$(OBJEXT) netutl.$(OBJEXT) script.$(OBJEXT) \
sptps.$(OBJEXT) subnet_parse.$(OBJEXT) tincctl.$(OBJEXT) \
top.$(OBJEXT) utils.$(OBJEXT) $(am__objects_3) \
$(am__objects_4)
top.$(OBJEXT) utils.$(OBJEXT) version.$(OBJEXT) \
$(am__objects_1) $(am__objects_3) $(am__objects_7) \
$(am__objects_8)
tinc_OBJECTS = $(am_tinc_OBJECTS)
am__DEPENDENCIES_1 =
tinc_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
@ -225,28 +299,37 @@ am__tincd_SOURCES_DIST = buffer.c buffer.h cipher.h conf.c conf.h \
protocol_subnet.c raw_socket_device.c route.c route.h rsa.h \
rsagen.h script.c script.h splay_tree.c splay_tree.h sptps.c \
sptps.h subnet.c subnet.h subnet_parse.c system.h tincd.c \
utils.c utils.h xalloc.h linux/device.c bsd/device.c \
utils.c utils.h xalloc.h version.c version.h \
ed25519/add_scalar.c ed25519/ed25519.h ed25519/fe.c \
ed25519/fe.h ed25519/fixedint.h ed25519/ge.c ed25519/ge.h \
ed25519/key_exchange.c ed25519/keypair.c \
ed25519/precomp_data.h ed25519/sc.c ed25519/sc.h \
ed25519/sha512.c ed25519/sha512.h ed25519/sign.c \
ed25519/verify.c chacha-poly1305/chacha.c \
chacha-poly1305/chacha.h chacha-poly1305/chacha-poly1305.c \
chacha-poly1305/chacha-poly1305.h chacha-poly1305/poly1305.c \
chacha-poly1305/poly1305.h linux/device.c bsd/device.c \
bsd/tunemu.c bsd/tunemu.h solaris/device.c mingw/device.c \
mingw/common.h cygwin/device.c uml_device.c vde_device.c \
openssl/cipher.c openssl/crypto.c openssl/digest.c \
openssl/digest.h openssl/ecdh.c openssl/ecdsa.c openssl/prf.c \
openssl/digest.h ed25519/ecdh.c ed25519/ecdsa.c openssl/prf.c \
openssl/rsa.c gcrypt/cipher.c gcrypt/crypto.c gcrypt/digest.c \
gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdsa.c gcrypt/prf.c \
gcrypt/rsa.c
@LINUX_TRUE@am__objects_5 = linux/device.$(OBJEXT)
@BSD_TRUE@am__objects_6 = bsd/device.$(OBJEXT)
@BSD_TRUE@@TUNEMU_TRUE@am__objects_7 = bsd/tunemu.$(OBJEXT)
@SOLARIS_TRUE@am__objects_8 = solaris/device.$(OBJEXT)
@MINGW_TRUE@am__objects_9 = mingw/device.$(OBJEXT)
@CYGWIN_TRUE@am__objects_10 = cygwin/device.$(OBJEXT)
@UML_TRUE@am__objects_11 = uml_device.$(OBJEXT)
@VDE_TRUE@am__objects_12 = vde_device.$(OBJEXT)
@OPENSSL_TRUE@am__objects_13 = openssl/cipher.$(OBJEXT) \
@LINUX_TRUE@am__objects_9 = linux/device.$(OBJEXT)
@BSD_TRUE@am__objects_10 = bsd/device.$(OBJEXT)
@BSD_TRUE@@TUNEMU_TRUE@am__objects_11 = bsd/tunemu.$(OBJEXT)
@SOLARIS_TRUE@am__objects_12 = solaris/device.$(OBJEXT)
@MINGW_TRUE@am__objects_13 = mingw/device.$(OBJEXT)
@CYGWIN_TRUE@am__objects_14 = cygwin/device.$(OBJEXT)
@UML_TRUE@am__objects_15 = uml_device.$(OBJEXT)
@VDE_TRUE@am__objects_16 = vde_device.$(OBJEXT)
@OPENSSL_TRUE@am__objects_17 = openssl/cipher.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/crypto.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) openssl/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/ecdsa.$(OBJEXT) openssl/prf.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/digest.$(OBJEXT) ed25519/ecdh.$(OBJEXT) \
@OPENSSL_TRUE@ ed25519/ecdsa.$(OBJEXT) openssl/prf.$(OBJEXT) \
@OPENSSL_TRUE@ openssl/rsa.$(OBJEXT)
@GCRYPT_TRUE@am__objects_14 = gcrypt/cipher.$(OBJEXT) \
@GCRYPT_TRUE@am__objects_18 = gcrypt/cipher.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/crypto.$(OBJEXT) gcrypt/digest.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/ecdh.$(OBJEXT) gcrypt/ecdsa.$(OBJEXT) \
@GCRYPT_TRUE@ gcrypt/prf.$(OBJEXT) gcrypt/rsa.$(OBJEXT)
@ -265,10 +348,11 @@ am_tincd_OBJECTS = buffer.$(OBJEXT) conf.$(OBJEXT) \
raw_socket_device.$(OBJEXT) route.$(OBJEXT) script.$(OBJEXT) \
splay_tree.$(OBJEXT) sptps.$(OBJEXT) subnet.$(OBJEXT) \
subnet_parse.$(OBJEXT) tincd.$(OBJEXT) utils.$(OBJEXT) \
$(am__objects_5) $(am__objects_6) $(am__objects_7) \
$(am__objects_8) $(am__objects_9) $(am__objects_10) \
$(am__objects_11) $(am__objects_12) $(am__objects_13) \
$(am__objects_14)
version.$(OBJEXT) $(am__objects_1) $(am__objects_3) \
$(am__objects_9) $(am__objects_10) $(am__objects_11) \
$(am__objects_12) $(am__objects_13) $(am__objects_14) \
$(am__objects_15) $(am__objects_16) $(am__objects_17) \
$(am__objects_18)
tincd_OBJECTS = $(am_tincd_OBJECTS)
tincd_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
@ -298,9 +382,11 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(sptps_test_SOURCES) $(tinc_SOURCES) $(tincd_SOURCES)
DIST_SOURCES = $(am__sptps_test_SOURCES_DIST) $(am__tinc_SOURCES_DIST) \
$(am__tincd_SOURCES_DIST)
SOURCES = $(sptps_keypair_SOURCES) $(sptps_speed_SOURCES) \
$(sptps_test_SOURCES) $(tinc_SOURCES) $(tincd_SOURCES)
DIST_SOURCES = $(am__sptps_keypair_SOURCES_DIST) \
$(am__sptps_speed_SOURCES_DIST) $(am__sptps_test_SOURCES_DIST) \
$(am__tinc_SOURCES_DIST) $(am__tincd_SOURCES_DIST)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@ -354,11 +440,8 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@ @LIBGCRYPT_LIBS@ $(am__append_15)
LIBS = @LIBS@ $(am__append_18)
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
@ -372,7 +455,6 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
READLINE_LIBS = @READLINE_LIBS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
@ -429,6 +511,25 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
DEFAULT_INCLUDES =
ed25519_SOURCES = \
ed25519/add_scalar.c \
ed25519/ed25519.h \
ed25519/fe.c ed25519/fe.h \
ed25519/fixedint.h \
ed25519/ge.c ed25519/ge.h \
ed25519/key_exchange.c \
ed25519/keypair.c \
ed25519/precomp_data.h \
ed25519/sc.c ed25519/sc.h \
ed25519/sha512.c ed25519/sha512.h \
ed25519/sign.c \
ed25519/verify.c
chacha_poly1305_SOURCES = \
chacha-poly1305/chacha.c chacha-poly1305/chacha.h \
chacha-poly1305/chacha-poly1305.c chacha-poly1305/chacha-poly1305.h \
chacha-poly1305/poly1305.c chacha-poly1305/poly1305.h
tincd_SOURCES = buffer.c buffer.h cipher.h conf.c conf.h connection.c \
connection.h control.c control.h control_common.h crypto.h \
device.h digest.h dropin.c dropin.h dummy_device.c ecdh.h \
@ -444,18 +545,27 @@ tincd_SOURCES = buffer.c buffer.h cipher.h conf.c conf.h connection.c \
protocol_subnet.c raw_socket_device.c route.c route.h rsa.h \
rsagen.h script.c script.h splay_tree.c splay_tree.h sptps.c \
sptps.h subnet.c subnet.h subnet_parse.c system.h tincd.c \
utils.c utils.h xalloc.h $(am__append_1) $(am__append_2) \
utils.c utils.h xalloc.h version.c version.h \
$(ed25519_SOURCES) $(chacha_poly1305_SOURCES) $(am__append_2) \
$(am__append_3) $(am__append_4) $(am__append_5) \
$(am__append_6) $(am__append_7) $(am__append_8) \
$(am__append_9) $(am__append_12)
$(am__append_9) $(am__append_10) $(am__append_15)
tinc_SOURCES = dropin.c dropin.h getopt.c getopt.h getopt1.c info.c \
info.h invitation.c invitation.h list.c list.h names.c names.h \
netutl.c netutl.h script.c script.h sptps.c sptps.h \
subnet_parse.c subnet.h tincctl.c tincctl.h top.c top.h \
utils.c utils.h $(am__append_10) $(am__append_13)
utils.c utils.h version.c version.h $(ed25519_SOURCES) \
$(chacha_poly1305_SOURCES) $(am__append_11) $(am__append_16)
sptps_test_SOURCES = logger.c logger.h sptps.c sptps.h sptps_test.c \
utils.c utils.h $(am__append_11) $(am__append_14)
utils.c utils.h $(ed25519_SOURCES) $(chacha_poly1305_SOURCES) \
$(am__append_12) $(am__append_17)
sptps_keypair_SOURCES = sptps_keypair.c utils.c utils.h \
$(ed25519_SOURCES) $(am__append_13)
sptps_speed_SOURCES = logger.c logger.h sptps.c sptps.h sptps_speed.c \
utils.c utils.h $(ed25519_SOURCES) $(chacha_poly1305_SOURCES) \
$(am__append_14)
tinc_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
sptps_speed_LDADD = -lrt
AM_CFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
all: all-am
@ -549,24 +659,69 @@ installcheck-sbinPROGRAMS: $(sbin_PROGRAMS)
else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
done; \
done; rm -f c$${pid}_.???; exit $$bad
ed25519/$(am__dirstamp):
@$(MKDIR_P) ed25519
@: > ed25519/$(am__dirstamp)
ed25519/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) ed25519/$(DEPDIR)
@: > ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/add_scalar.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/fe.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/ge.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/key_exchange.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/keypair.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/sc.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/sha512.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/sign.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/verify.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
openssl/$(am__dirstamp):
@$(MKDIR_P) openssl
@: > openssl/$(am__dirstamp)
openssl/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) openssl/$(DEPDIR)
@: > openssl/$(DEPDIR)/$(am__dirstamp)
openssl/cipher.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
openssl/crypto.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
ed25519/ecdsagen.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
sptps_keypair$(EXEEXT): $(sptps_keypair_OBJECTS) $(sptps_keypair_DEPENDENCIES) $(EXTRA_sptps_keypair_DEPENDENCIES)
@rm -f sptps_keypair$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sptps_keypair_OBJECTS) $(sptps_keypair_LDADD) $(LIBS)
chacha-poly1305/$(am__dirstamp):
@$(MKDIR_P) chacha-poly1305
@: > chacha-poly1305/$(am__dirstamp)
chacha-poly1305/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) chacha-poly1305/$(DEPDIR)
@: > chacha-poly1305/$(DEPDIR)/$(am__dirstamp)
chacha-poly1305/chacha.$(OBJEXT): chacha-poly1305/$(am__dirstamp) \
chacha-poly1305/$(DEPDIR)/$(am__dirstamp)
chacha-poly1305/chacha-poly1305.$(OBJEXT): \
chacha-poly1305/$(am__dirstamp) \
chacha-poly1305/$(DEPDIR)/$(am__dirstamp)
chacha-poly1305/poly1305.$(OBJEXT): chacha-poly1305/$(am__dirstamp) \
chacha-poly1305/$(DEPDIR)/$(am__dirstamp)
openssl/digest.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
openssl/ecdh.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
openssl/ecdsa.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
ed25519/ecdh.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
ed25519/ecdsa.$(OBJEXT): ed25519/$(am__dirstamp) \
ed25519/$(DEPDIR)/$(am__dirstamp)
openssl/prf.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
sptps_speed$(EXEEXT): $(sptps_speed_OBJECTS) $(sptps_speed_DEPENDENCIES) $(EXTRA_sptps_speed_DEPENDENCIES)
@rm -f sptps_speed$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sptps_speed_OBJECTS) $(sptps_speed_LDADD) $(LIBS)
gcrypt/$(am__dirstamp):
@$(MKDIR_P) gcrypt
@: > gcrypt/$(am__dirstamp)
@ -589,7 +744,7 @@ gcrypt/prf.$(OBJEXT): gcrypt/$(am__dirstamp) \
sptps_test$(EXEEXT): $(sptps_test_OBJECTS) $(sptps_test_DEPENDENCIES) $(EXTRA_sptps_test_DEPENDENCIES)
@rm -f sptps_test$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sptps_test_OBJECTS) $(sptps_test_LDADD) $(LIBS)
openssl/ecdsagen.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/cipher.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
openssl/rsa.$(OBJEXT): openssl/$(am__dirstamp) \
openssl/$(DEPDIR)/$(am__dirstamp)
@ -655,7 +810,9 @@ tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES) $(EXTRA_tincd_DEPENDENCIE
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f bsd/*.$(OBJEXT)
-rm -f chacha-poly1305/*.$(OBJEXT)
-rm -f cygwin/*.$(OBJEXT)
-rm -f ed25519/*.$(OBJEXT)
-rm -f gcrypt/*.$(OBJEXT)
-rm -f linux/*.$(OBJEXT)
-rm -f mingw/*.$(OBJEXT)
@ -704,6 +861,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script.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_keypair.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sptps_speed.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@
@ -713,9 +872,25 @@ distclean-compile:
@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@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@bsd/$(DEPDIR)/device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@bsd/$(DEPDIR)/tunemu.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@chacha-poly1305/$(DEPDIR)/chacha-poly1305.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@chacha-poly1305/$(DEPDIR)/chacha.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@chacha-poly1305/$(DEPDIR)/poly1305.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@cygwin/$(DEPDIR)/device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/add_scalar.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/ecdh.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/ecdsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/ecdsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/fe.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/ge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/key_exchange.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/keypair.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/sc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/sha512.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/sign.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ed25519/$(DEPDIR)/verify.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@gcrypt/$(DEPDIR)/cipher.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@gcrypt/$(DEPDIR)/crypto.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@gcrypt/$(DEPDIR)/digest.Po@am__quote@
@ -730,9 +905,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/cipher.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/crypto.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/digest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/ecdh.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/ecdsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/ecdsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/prf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/rsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@openssl/$(DEPDIR)/rsagen.Po@am__quote@
@ -871,8 +1043,12 @@ distclean-generic:
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-rm -f bsd/$(DEPDIR)/$(am__dirstamp)
-rm -f bsd/$(am__dirstamp)
-rm -f chacha-poly1305/$(DEPDIR)/$(am__dirstamp)
-rm -f chacha-poly1305/$(am__dirstamp)
-rm -f cygwin/$(DEPDIR)/$(am__dirstamp)
-rm -f cygwin/$(am__dirstamp)
-rm -f ed25519/$(DEPDIR)/$(am__dirstamp)
-rm -f ed25519/$(am__dirstamp)
-rm -f gcrypt/$(DEPDIR)/$(am__dirstamp)
-rm -f gcrypt/$(am__dirstamp)
-rm -f linux/$(DEPDIR)/$(am__dirstamp)
@ -892,7 +1068,7 @@ clean: clean-am
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR) bsd/$(DEPDIR) cygwin/$(DEPDIR) gcrypt/$(DEPDIR) linux/$(DEPDIR) mingw/$(DEPDIR) openssl/$(DEPDIR) solaris/$(DEPDIR)
-rm -rf ./$(DEPDIR) bsd/$(DEPDIR) chacha-poly1305/$(DEPDIR) cygwin/$(DEPDIR) ed25519/$(DEPDIR) gcrypt/$(DEPDIR) linux/$(DEPDIR) mingw/$(DEPDIR) openssl/$(DEPDIR) solaris/$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@ -938,7 +1114,7 @@ install-ps-am:
installcheck-am: installcheck-sbinPROGRAMS
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR) bsd/$(DEPDIR) cygwin/$(DEPDIR) gcrypt/$(DEPDIR) linux/$(DEPDIR) mingw/$(DEPDIR) openssl/$(DEPDIR) solaris/$(DEPDIR)
-rm -rf ./$(DEPDIR) bsd/$(DEPDIR) chacha-poly1305/$(DEPDIR) cygwin/$(DEPDIR) ed25519/$(DEPDIR) gcrypt/$(DEPDIR) linux/$(DEPDIR) mingw/$(DEPDIR) openssl/$(DEPDIR) solaris/$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@ -973,6 +1149,9 @@ uninstall-am: uninstall-sbinPROGRAMS
uninstall-am uninstall-sbinPROGRAMS
.PHONY: version.c
version.c:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction BSD tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2013 Guus Sliepen <guus@tinc-vpn.org>
2001-2014 Guus Sliepen <guus@tinc-vpn.org>
2009 Grzegorz Dymarek <gregd72002@googlemail.com>
This program is free software; you can redistribute it and/or modify
@ -35,7 +35,7 @@
#endif
#define DEFAULT_TUN_DEVICE "/dev/tun0"
#if defined(HAVE_FREEBSD) || defined(HAVE_NETBSD)
#if defined(HAVE_DARWIN) || defined(HAVE_FREEBSD) || defined(HAVE_NETBSD)
#define DEFAULT_TAP_DEVICE "/dev/tap0"
#else
#define DEFAULT_TAP_DEVICE "/dev/tun0"
@ -54,8 +54,6 @@ int device_fd = -1;
char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
#if defined(ENABLE_TUNEMU)
static device_type_t device_type = DEVICE_TYPE_TUNEMU;
#elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) || defined(HAVE_DRAGONFLY)
@ -65,18 +63,9 @@ static device_type_t device_type = DEVICE_TYPE_TUN;
#endif
static bool setup_device(void) {
get_config_string(lookup_config(config_tree, "Device"), &device);
char *type;
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 */;
@ -95,10 +84,29 @@ static bool setup_device(void) {
return false;
}
} else {
if(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
if((device && strstr(device, "tap")) || routing_mode != RMODE_ROUTER)
device_type = DEVICE_TYPE_TAP;
}
if(!device) {
if(device_type == DEVICE_TYPE_TAP)
device = xstrdup(DEFAULT_TAP_DEVICE);
else
device = xstrdup(DEFAULT_TUN_DEVICE);
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = NULL;
#ifndef TAPGIFNAME
if (iface) {
logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring specified interface name '%s' as device rename is not supported on this platform", iface);
free(iface);
iface = NULL;
}
#endif
if (!iface)
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
switch(device_type) {
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU: {
@ -199,9 +207,11 @@ static void close_device(void) {
default:
close(device_fd);
}
device_fd = -1;
free(device);
free(iface);
free(device); device = NULL;
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
@ -212,10 +222,10 @@ static bool read_packet(vpn_packet_t *packet) {
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(device_type == DEVICE_TYPE_TUNEMU)
inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
inlen = tunemu_read(device_fd, DATA(packet) + 14, MTU - 14);
else
#endif
inlen = read(device_fd, packet->data + 14, MTU - 14);
inlen = read(device_fd, DATA(packet) + 14, MTU - 14);
if(inlen <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
@ -223,29 +233,29 @@ static bool read_packet(vpn_packet_t *packet) {
return false;
}
switch(packet->data[14] >> 4) {
switch(DATA(packet)[14] >> 4) {
case 4:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
DATA(packet)[12] = 0x08;
DATA(packet)[13] = 0x00;
break;
case 6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
DATA(packet)[12] = 0x86;
DATA(packet)[13] = 0xDD;
break;
default:
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
DATA(packet)[14] >> 4, device_info, device);
return false;
}
memset(packet->data, 0, 12);
memset(DATA(packet), 0, 12);
packet->len = inlen + 14;
break;
case DEVICE_TYPE_TUNIFHEAD: {
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}};
struct iovec vector[2] = {{&type, sizeof type}, {DATA(packet) + 14, MTU - 14}};
if((inlen = readv(device_fd, vector, 2)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
@ -255,13 +265,13 @@ static bool read_packet(vpn_packet_t *packet) {
switch (ntohl(type)) {
case AF_INET:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
DATA(packet)[12] = 0x08;
DATA(packet)[13] = 0x00;
break;
case AF_INET6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
DATA(packet)[12] = 0x86;
DATA(packet)[13] = 0xDD;
break;
default:
@ -271,13 +281,13 @@ static bool read_packet(vpn_packet_t *packet) {
return false;
}
memset(packet->data, 0, 12);
memset(DATA(packet), 0, 12);
packet->len = inlen + 10;
break;
}
case DEVICE_TYPE_TAP:
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -290,8 +300,6 @@ static bool read_packet(vpn_packet_t *packet) {
return false;
}
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s",
packet->len, device_info);
@ -304,7 +312,7 @@ static bool write_packet(vpn_packet_t *packet) {
switch(device_type) {
case DEVICE_TYPE_TUN:
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -313,10 +321,10 @@ static bool write_packet(vpn_packet_t *packet) {
case DEVICE_TYPE_TUNIFHEAD: {
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}};
struct iovec vector[2] = {{&type, sizeof type}, {DATA(packet) + 14, packet->len - 14}};
int af;
af = (packet->data[12] << 8) + packet->data[13];
af = (DATA(packet)[12] << 8) + DATA(packet)[13];
switch (af) {
case 0x0800:
@ -341,7 +349,7 @@ static bool write_packet(vpn_packet_t *packet) {
}
case DEVICE_TYPE_TAP:
if(write(device_fd, packet->data, packet->len) < 0) {
if(write(device_fd, DATA(packet), packet->len) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -350,7 +358,7 @@ static bool write_packet(vpn_packet_t *packet) {
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
if(tunemu_write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -362,21 +370,12 @@ static bool write_packet(vpn_packet_t *packet) {
return false;
}
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 os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -0,0 +1,103 @@
#include "../system.h"
#include "../cipher.h"
#include "../xalloc.h"
#include "chacha.h"
#include "chacha-poly1305.h"
#include "poly1305.h"
struct chacha_poly1305_ctx {
struct chacha_ctx main_ctx, header_ctx;
};
chacha_poly1305_ctx_t *chacha_poly1305_init(void)
{
chacha_poly1305_ctx_t *ctx = xzalloc(sizeof *ctx);
return ctx;
}
void chacha_poly1305_exit(chacha_poly1305_ctx_t *ctx)
{
free(ctx);
}
bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key)
{
chacha_keysetup(&ctx->main_ctx, key, 256);
chacha_keysetup(&ctx->header_ctx, key + 32, 256);
return true;
}
static void put_u64(void *vp, uint64_t v)
{
uint8_t *p = (uint8_t *) vp;
p[0] = (uint8_t) (v >> 56) & 0xff;
p[1] = (uint8_t) (v >> 48) & 0xff;
p[2] = (uint8_t) (v >> 40) & 0xff;
p[3] = (uint8_t) (v >> 32) & 0xff;
p[4] = (uint8_t) (v >> 24) & 0xff;
p[5] = (uint8_t) (v >> 16) & 0xff;
p[6] = (uint8_t) (v >> 8) & 0xff;
p[7] = (uint8_t) v & 0xff;
}
bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
uint8_t seqbuf[8];
const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
uint8_t poly_key[POLY1305_KEYLEN];
/*
* Run ChaCha20 once to generate the Poly1305 key. The IV is the
* packet sequence number.
*/
memset(poly_key, 0, sizeof(poly_key));
put_u64(seqbuf, seqnr);
chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
/* Set Chacha's block counter to 1 */
chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
poly1305_auth(outdata + inlen, outdata, inlen, poly_key);
if (outlen)
*outlen = inlen + POLY1305_TAGLEN;
return true;
}
bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
uint8_t seqbuf[8];
const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
uint8_t expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
/*
* Run ChaCha20 once to generate the Poly1305 key. The IV is the
* packet sequence number.
*/
memset(poly_key, 0, sizeof(poly_key));
put_u64(seqbuf, seqnr);
chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
/* Set Chacha's block counter to 1 */
chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
/* Check tag before anything else */
inlen -= POLY1305_TAGLEN;
const uint8_t *tag = indata + inlen;
poly1305_auth(expected_tag, indata, inlen, poly_key);
if (memcmp(expected_tag, tag, POLY1305_TAGLEN))
return false;
chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
if (outlen)
*outlen = inlen;
return true;
}

View file

@ -0,0 +1,15 @@
#ifndef CHACHA_POLY1305_H
#define CHACHA_POLY1305_H
#define CHACHA_POLY1305_KEYLEN 64
typedef struct chacha_poly1305_ctx chacha_poly1305_ctx_t;
extern chacha_poly1305_ctx_t *chacha_poly1305_init(void);
extern void chacha_poly1305_exit(chacha_poly1305_ctx_t *);
extern bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key);
extern bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen);
extern bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen);
#endif //CHACHA_POLY1305_H

View file

@ -0,0 +1,215 @@
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
#include "../system.h"
#include "chacha.h"
typedef struct chacha_ctx chacha_ctx;
#define U8C(v) (v##U)
#define U32C(v) (v##U)
#define U8V(v) ((uint8_t)(v) & U8C(0xFF))
#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF))
#define ROTL32(v, n) \
(U32V((v) << (n)) | ((v) >> (32 - (n))))
#define U8TO32_LITTLE(p) \
(((uint32_t)((p)[0]) ) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U32TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
} while (0)
#define ROTATE(v,c) (ROTL32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) (U32V((v) + (w)))
#define PLUSONE(v) (PLUS((v),1))
#define QUARTERROUND(a,b,c,d) \
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
static const char sigma[16] = "expand 32-byte k";
static const char tau[16] = "expand 16-byte k";
void chacha_keysetup(chacha_ctx *x, const uint8_t *k, uint32_t kbits)
{
const char *constants;
x->input[4] = U8TO32_LITTLE(k + 0);
x->input[5] = U8TO32_LITTLE(k + 4);
x->input[6] = U8TO32_LITTLE(k + 8);
x->input[7] = U8TO32_LITTLE(k + 12);
if (kbits == 256) { /* recommended */
k += 16;
constants = sigma;
} else { /* kbits == 128 */
constants = tau;
}
x->input[8] = U8TO32_LITTLE(k + 0);
x->input[9] = U8TO32_LITTLE(k + 4);
x->input[10] = U8TO32_LITTLE(k + 8);
x->input[11] = U8TO32_LITTLE(k + 12);
x->input[0] = U8TO32_LITTLE(constants + 0);
x->input[1] = U8TO32_LITTLE(constants + 4);
x->input[2] = U8TO32_LITTLE(constants + 8);
x->input[3] = U8TO32_LITTLE(constants + 12);
}
void chacha_ivsetup(chacha_ctx *x, const uint8_t *iv, const uint8_t *counter)
{
x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0);
x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4);
x->input[14] = U8TO32_LITTLE(iv + 0);
x->input[15] = U8TO32_LITTLE(iv + 4);
}
void
chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes)
{
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
uint8_t *ctarget = NULL;
uint8_t tmp[64];
uint32_t i;
if (!bytes)
return;
j0 = x->input[0];
j1 = x->input[1];
j2 = x->input[2];
j3 = x->input[3];
j4 = x->input[4];
j5 = x->input[5];
j6 = x->input[6];
j7 = x->input[7];
j8 = x->input[8];
j9 = x->input[9];
j10 = x->input[10];
j11 = x->input[11];
j12 = x->input[12];
j13 = x->input[13];
j14 = x->input[14];
j15 = x->input[15];
for (;;) {
if (bytes < 64) {
for (i = 0; i < bytes; ++i)
tmp[i] = m[i];
m = tmp;
ctarget = c;
c = tmp;
}
x0 = j0;
x1 = j1;
x2 = j2;
x3 = j3;
x4 = j4;
x5 = j5;
x6 = j6;
x7 = j7;
x8 = j8;
x9 = j9;
x10 = j10;
x11 = j11;
x12 = j12;
x13 = j13;
x14 = j14;
x15 = j15;
for (i = 20; i > 0; i -= 2) {
QUARTERROUND(x0, x4, x8, x12)
QUARTERROUND(x1, x5, x9, x13)
QUARTERROUND(x2, x6, x10, x14)
QUARTERROUND(x3, x7, x11, x15)
QUARTERROUND(x0, x5, x10, x15)
QUARTERROUND(x1, x6, x11, x12)
QUARTERROUND(x2, x7, x8, x13)
QUARTERROUND(x3, x4, x9, x14)
}
x0 = PLUS(x0, j0);
x1 = PLUS(x1, j1);
x2 = PLUS(x2, j2);
x3 = PLUS(x3, j3);
x4 = PLUS(x4, j4);
x5 = PLUS(x5, j5);
x6 = PLUS(x6, j6);
x7 = PLUS(x7, j7);
x8 = PLUS(x8, j8);
x9 = PLUS(x9, j9);
x10 = PLUS(x10, j10);
x11 = PLUS(x11, j11);
x12 = PLUS(x12, j12);
x13 = PLUS(x13, j13);
x14 = PLUS(x14, j14);
x15 = PLUS(x15, j15);
x0 = XOR(x0, U8TO32_LITTLE(m + 0));
x1 = XOR(x1, U8TO32_LITTLE(m + 4));
x2 = XOR(x2, U8TO32_LITTLE(m + 8));
x3 = XOR(x3, U8TO32_LITTLE(m + 12));
x4 = XOR(x4, U8TO32_LITTLE(m + 16));
x5 = XOR(x5, U8TO32_LITTLE(m + 20));
x6 = XOR(x6, U8TO32_LITTLE(m + 24));
x7 = XOR(x7, U8TO32_LITTLE(m + 28));
x8 = XOR(x8, U8TO32_LITTLE(m + 32));
x9 = XOR(x9, U8TO32_LITTLE(m + 36));
x10 = XOR(x10, U8TO32_LITTLE(m + 40));
x11 = XOR(x11, U8TO32_LITTLE(m + 44));
x12 = XOR(x12, U8TO32_LITTLE(m + 48));
x13 = XOR(x13, U8TO32_LITTLE(m + 52));
x14 = XOR(x14, U8TO32_LITTLE(m + 56));
x15 = XOR(x15, U8TO32_LITTLE(m + 60));
j12 = PLUSONE(j12);
if (!j12) {
j13 = PLUSONE(j13);
/* stopping at 2^70 bytes per nonce is user's responsibility */
}
U32TO8_LITTLE(c + 0, x0);
U32TO8_LITTLE(c + 4, x1);
U32TO8_LITTLE(c + 8, x2);
U32TO8_LITTLE(c + 12, x3);
U32TO8_LITTLE(c + 16, x4);
U32TO8_LITTLE(c + 20, x5);
U32TO8_LITTLE(c + 24, x6);
U32TO8_LITTLE(c + 28, x7);
U32TO8_LITTLE(c + 32, x8);
U32TO8_LITTLE(c + 36, x9);
U32TO8_LITTLE(c + 40, x10);
U32TO8_LITTLE(c + 44, x11);
U32TO8_LITTLE(c + 48, x12);
U32TO8_LITTLE(c + 52, x13);
U32TO8_LITTLE(c + 56, x14);
U32TO8_LITTLE(c + 60, x15);
if (bytes <= 64) {
if (bytes < 64) {
for (i = 0; i < bytes; ++i)
ctarget[i] = c[i];
}
x->input[12] = j12;
x->input[13] = j13;
return;
}
bytes -= 64;
c += 64;
m += 64;
}
}

View file

@ -0,0 +1,24 @@
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
#ifndef CHACHA_H
#define CHACHA_H
struct chacha_ctx {
uint32_t input[16];
};
#define CHACHA_MINKEYLEN 16
#define CHACHA_NONCELEN 8
#define CHACHA_CTRLEN 8
#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN)
#define CHACHA_BLOCKLEN 64
void chacha_keysetup(struct chacha_ctx *x, const uint8_t *k, uint32_t kbits);
void chacha_ivsetup(struct chacha_ctx *x, const uint8_t *iv, const uint8_t *ctr);
void chacha_encrypt_bytes(struct chacha_ctx *x, const uint8_t *m, uint8_t * c, uint32_t bytes);
#endif /* CHACHA_H */

View file

@ -0,0 +1,197 @@
/*
* Public Domain poly1305 from Andrew Moon
* poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
*/
#include "../system.h"
#include "poly1305.h"
#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
#define U8TO32_LE(p) \
(((uint32_t)((p)[0])) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U32TO8_LE(p, v) \
do { \
(p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); \
} while (0)
void
poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN])
{
uint32_t t0, t1, t2, t3;
uint32_t h0, h1, h2, h3, h4;
uint32_t r0, r1, r2, r3, r4;
uint32_t s1, s2, s3, s4;
uint32_t b, nb;
size_t j;
uint64_t t[5];
uint64_t f0, f1, f2, f3;
uint32_t g0, g1, g2, g3, g4;
uint64_t c;
unsigned char mp[16];
/* clamp key */
t0 = U8TO32_LE(key + 0);
t1 = U8TO32_LE(key + 4);
t2 = U8TO32_LE(key + 8);
t3 = U8TO32_LE(key + 12);
/* precompute multipliers */
r0 = t0 & 0x3ffffff;
t0 >>= 26;
t0 |= t1 << 6;
r1 = t0 & 0x3ffff03;
t1 >>= 20;
t1 |= t2 << 12;
r2 = t1 & 0x3ffc0ff;
t2 >>= 14;
t2 |= t3 << 18;
r3 = t2 & 0x3f03fff;
t3 >>= 8;
r4 = t3 & 0x00fffff;
s1 = r1 * 5;
s2 = r2 * 5;
s3 = r3 * 5;
s4 = r4 * 5;
/* init state */
h0 = 0;
h1 = 0;
h2 = 0;
h3 = 0;
h4 = 0;
/* full blocks */
if (inlen < 16)
goto poly1305_donna_atmost15bytes;
poly1305_donna_16bytes:
m += 16;
inlen -= 16;
t0 = U8TO32_LE(m - 16);
t1 = U8TO32_LE(m - 12);
t2 = U8TO32_LE(m - 8);
t3 = U8TO32_LE(m - 4);
h0 += t0 & 0x3ffffff;
h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff;
h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff;
h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff;
h4 += (t3 >> 8) | (1 << 24);
poly1305_donna_mul:
t[0] = mul32x32_64(h0, r0) + mul32x32_64(h1, s4) + mul32x32_64(h2, s3) + mul32x32_64(h3, s2) + mul32x32_64(h4, s1);
t[1] = mul32x32_64(h0, r1) + mul32x32_64(h1, r0) + mul32x32_64(h2, s4) + mul32x32_64(h3, s3) + mul32x32_64(h4, s2);
t[2] = mul32x32_64(h0, r2) + mul32x32_64(h1, r1) + mul32x32_64(h2, r0) + mul32x32_64(h3, s4) + mul32x32_64(h4, s3);
t[3] = mul32x32_64(h0, r3) + mul32x32_64(h1, r2) + mul32x32_64(h2, r1) + mul32x32_64(h3, r0) + mul32x32_64(h4, s4);
t[4] = mul32x32_64(h0, r4) + mul32x32_64(h1, r3) + mul32x32_64(h2, r2) + mul32x32_64(h3, r1) + mul32x32_64(h4, r0);
h0 = (uint32_t) t[0] & 0x3ffffff;
c = (t[0] >> 26);
t[1] += c;
h1 = (uint32_t) t[1] & 0x3ffffff;
b = (uint32_t) (t[1] >> 26);
t[2] += b;
h2 = (uint32_t) t[2] & 0x3ffffff;
b = (uint32_t) (t[2] >> 26);
t[3] += b;
h3 = (uint32_t) t[3] & 0x3ffffff;
b = (uint32_t) (t[3] >> 26);
t[4] += b;
h4 = (uint32_t) t[4] & 0x3ffffff;
b = (uint32_t) (t[4] >> 26);
h0 += b * 5;
if (inlen >= 16)
goto poly1305_donna_16bytes;
/* final bytes */
poly1305_donna_atmost15bytes:
if (!inlen)
goto poly1305_donna_finish;
for (j = 0; j < inlen; j++)
mp[j] = m[j];
mp[j++] = 1;
for (; j < 16; j++)
mp[j] = 0;
inlen = 0;
t0 = U8TO32_LE(mp + 0);
t1 = U8TO32_LE(mp + 4);
t2 = U8TO32_LE(mp + 8);
t3 = U8TO32_LE(mp + 12);
h0 += t0 & 0x3ffffff;
h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff;
h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff;
h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff;
h4 += (t3 >> 8);
goto poly1305_donna_mul;
poly1305_donna_finish:
b = h0 >> 26;
h0 = h0 & 0x3ffffff;
h1 += b;
b = h1 >> 26;
h1 = h1 & 0x3ffffff;
h2 += b;
b = h2 >> 26;
h2 = h2 & 0x3ffffff;
h3 += b;
b = h3 >> 26;
h3 = h3 & 0x3ffffff;
h4 += b;
b = h4 >> 26;
h4 = h4 & 0x3ffffff;
h0 += b * 5;
b = h0 >> 26;
h0 = h0 & 0x3ffffff;
h1 += b;
g0 = h0 + 5;
b = g0 >> 26;
g0 &= 0x3ffffff;
g1 = h1 + b;
b = g1 >> 26;
g1 &= 0x3ffffff;
g2 = h2 + b;
b = g2 >> 26;
g2 &= 0x3ffffff;
g3 = h3 + b;
b = g3 >> 26;
g3 &= 0x3ffffff;
g4 = h4 + b - (1 << 26);
b = (g4 >> 31) - 1;
nb = ~b;
h0 = (h0 & nb) | (g0 & b);
h1 = (h1 & nb) | (g1 & b);
h2 = (h2 & nb) | (g2 & b);
h3 = (h3 & nb) | (g3 & b);
h4 = (h4 & nb) | (g4 & b);
f0 = ((h0) | (h1 << 26)) + (uint64_t) U8TO32_LE(&key[16]);
f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t) U8TO32_LE(&key[20]);
f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t) U8TO32_LE(&key[24]);
f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t) U8TO32_LE(&key[28]);
U32TO8_LE(&out[0], f0);
f1 += (f0 >> 32);
U32TO8_LE(&out[4], f1);
f2 += (f1 >> 32);
U32TO8_LE(&out[8], f2);
f3 += (f2 >> 32);
U32TO8_LE(&out[12], f3);
}

View file

@ -0,0 +1,16 @@
/* $OpenBSD: poly1305.h,v 1.2 2013/12/19 22:57:13 djm Exp $ */
/*
* Public Domain poly1305 from Andrew Moon
* poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
*/
#ifndef POLY1305_H
#define POLY1305_H
#define POLY1305_KEYLEN 32
#define POLY1305_TAGLEN 16
void poly1305_auth(uint8_t out[POLY1305_TAGLEN], const uint8_t *m, size_t inlen, const uint8_t key[POLY1305_KEYLEN]);
#endif /* POLY1305_H */

View file

@ -34,11 +34,8 @@ extern size_t cipher_keylength(const cipher_t *);
extern void cipher_get_key(const cipher_t *, void *);
extern bool cipher_set_key(cipher_t *, void *, bool) __attribute__ ((__warn_unused_result__));
extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool) __attribute__ ((__warn_unused_result__));
extern bool cipher_set_counter(cipher_t *, const void *, size_t) __attribute__ ((__warn_unused_result__));
extern bool cipher_set_counter_key(cipher_t *, void *) __attribute__ ((__warn_unused_result__));
extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__));
extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__));
extern bool cipher_counter_xor(cipher_t *, const void *indata, size_t inlen, void *outdata) __attribute__ ((__warn_unused_result__));
extern int cipher_get_nid(const cipher_t *);
extern bool cipher_active(const cipher_t *);

View file

@ -1,10 +1,11 @@
/*
conf.c -- configuration code
Copyright (C) 1998 Robert van der Meulen
Copyright (C) 1998 Robert van der Meulen
1998-2005 Ivo Timmermans
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000 Cris van Pelt
2010-2011 Julien Muchembled <jm@jmuchemb.eu>
2000 Cris van Pelt
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2013 Florent Clairambault <florent@clairambault.fr>
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
@ -376,6 +377,29 @@ bool read_server_config(void) {
errno = 0;
x = read_config_file(config_tree, fname);
// We will try to read the conf files in the "conf.d" dir
if (x) {
char * dname;
xasprintf(&dname, "%s" SLASH "conf.d", confbase);
DIR *dir = opendir (dname);
// If we can find this dir
if (dir) {
struct dirent *ep;
// We list all the files in it
while (x && (ep = readdir (dir))) {
size_t l = strlen(ep->d_name);
// And we try to read the ones that end with ".conf"
if (l > 5 && !strcmp(".conf", & ep->d_name[ l - 5 ])) {
free(fname);
xasprintf(&fname, "%s" SLASH "%s", dname, ep->d_name);
x = read_config_file(config_tree, fname);
}
}
closedir (dir);
}
free(dname);
}
if(!x && errno)
logger(DEBUG_ALWAYS, LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));

View file

@ -36,7 +36,7 @@
typedef struct connection_status_t {
unsigned int pinged:1; /* sent ping */
unsigned int active:1; /* 1 if active.. */
unsigned int unused_active:1;
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 */
@ -49,7 +49,7 @@ typedef struct connection_status_t {
unsigned int log:1; /* 1 if this is a control connection requesting log dump */
unsigned int invitation:1; /* 1 if this is an invitation */
unsigned int invitation_used:1; /* 1 if the invitation has been consumed */
unsigned int unused:19;
unsigned int unused:18;
} connection_status_t;
#include "ecdsa.h"

View file

@ -106,7 +106,7 @@ bool control_h(connection_t *c, const char *request) {
for list_each(connection_t, other, connection_list) {
if(strcmp(other->name, name))
continue;
terminate_connection(other, other->status.active);
terminate_connection(other, other->edge);
found = true;
}
@ -178,15 +178,15 @@ bool init_control(void) {
#ifndef HAVE_MINGW
int unix_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(unix_fd < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create UNIX socket: %s", sockstrerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create UNIX socket: %s", sockstrerror(sockerrno));
return false;
}
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strncpy(sun.sun_path, unixsocketname, sizeof sun.sun_path);
struct sockaddr_un sa_un;
sa_un.sun_family = AF_UNIX;
strncpy(sa_un.sun_path, unixsocketname, sizeof sa_un.sun_path);
if(connect(unix_fd, (struct sockaddr *)&sun, sizeof sun) >= 0) {
if(connect(unix_fd, (struct sockaddr *)&sa_un, sizeof sa_un) >= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "UNIX socket %s is still in use!", unixsocketname);
return false;
}
@ -194,16 +194,16 @@ bool init_control(void) {
unlink(unixsocketname);
umask(mask | 077);
int result = bind(unix_fd, (struct sockaddr *)&sun, sizeof sun);
int result = bind(unix_fd, (struct sockaddr *)&sa_un, sizeof sa_un);
umask(mask);
if(result < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind UNIX socket to %s: %s", unixsocketname, sockstrerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind UNIX socket to %s: %s", unixsocketname, sockstrerror(sockerrno));
return false;
}
if(listen(unix_fd, 3) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not listen on UNIX socket %s: %s", unixsocketname, sockstrerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not listen on UNIX socket %s: %s", unixsocketname, sockstrerror(sockerrno));
return false;
}

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a Cygwin environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2013 Guus Sliepen <guus@tinc-vpn.org>
2002-2014 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,9 +40,6 @@ char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static pid_t reader_pid;
static int sp[2];
@ -218,18 +215,19 @@ static bool setup_device(void) {
static void close_device(void) {
close(sp[0]);
close(sp[1]);
CloseHandle(device_handle);
CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
kill(reader_pid, SIGKILL);
free(device);
free(iface);
free(device); device = NULL;
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
if((inlen = read(sp[0], DATA(packet), MTU)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -237,8 +235,6 @@ static bool read_packet(vpn_packet_t *packet) {
packet->len = inlen;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
@ -251,26 +247,17 @@ 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(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
if(!WriteFile (device_handle, DATA(packet), packet->len, &outlen, NULL)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
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 os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -27,17 +27,13 @@ extern int device_fd;
extern char *device;
extern char *iface;
extern uint64_t device_in_packets;
extern uint64_t device_in_bytes;
extern uint64_t device_out_packets;
extern uint64_t device_out_bytes;
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);
void (*enable)(void); /* optional */
void (*disable)(void); /* optional */
} devops_t;
extern const devops_t os_devops;

View file

@ -25,9 +25,6 @@
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";
@ -43,20 +40,12 @@ static bool read_packet(vpn_packet_t *packet) {
}
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,
};

View file

@ -20,8 +20,8 @@
#ifndef __TINC_ECDH_H__
#define __TINC_ECDH_H__
#define ECDH_SIZE 67
#define ECDH_SHARED_SIZE 66
#define ECDH_SIZE 32
#define ECDH_SHARED_SIZE 32
#ifndef __TINC_ECDH_INTERNAL__
typedef struct ecdh ecdh_t;

56
src/ed25519/add_scalar.c Normal file
View file

@ -0,0 +1,56 @@
#include "ed25519.h"
#include "ge.h"
#include "sc.h"
/* see http://crypto.stackexchange.com/a/6215/4697 */
void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) {
const unsigned char SC_1[32] = {1}; /* scalar with value 1 */
unsigned char n[32];
ge_p3 nB;
ge_p1p1 A_p1p1;
ge_p3 A;
ge_p3 public_key_unpacked;
ge_cached T;
int i;
/* copy the scalar and clear highest bit */
for (i = 0; i < 31; ++i) {
n[i] = scalar[i];
}
n[31] = scalar[31] & 127;
/* private key: a = n + t */
if (private_key) {
sc_muladd(private_key, SC_1, n, private_key);
}
/* public key: A = nB + T */
if (public_key) {
/* if we know the private key we don't need a point addition, which is faster */
/* using a "timing attack" you could find out wether or not we know the private
key, but this information seems rather useless - if this is important pass
public_key and private_key seperately in 2 function calls */
if (private_key) {
ge_scalarmult_base(&A, private_key);
} else {
/* unpack public key into T */
ge_frombytes_negate_vartime(&public_key_unpacked, public_key);
fe_neg(public_key_unpacked.X, public_key_unpacked.X); // undo negate
fe_neg(public_key_unpacked.T, public_key_unpacked.T); // undo negate
ge_p3_to_cached(&T, &public_key_unpacked);
/* calculate n*B */
ge_scalarmult_base(&nB, n);
/* A = n*B + T */
ge_add(&A_p1p1, &nB, &T);
ge_p1p1_to_p3(&A, &A_p1p1);
}
/* pack public key */
ge_p3_tobytes(public_key, &A);
}
}

51
src/ed25519/ecdh.c Normal file
View file

@ -0,0 +1,51 @@
/*
ecdh.c -- Diffie-Hellman key exchange handling
Copyright (C) 2011-2013 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 "ed25519.h"
#define __TINC_ECDH_INTERNAL__
typedef struct ecdh_t {
uint8_t private[64];
} ecdh_t;
#include "../crypto.h"
#include "../ecdh.h"
#include "../xalloc.h"
ecdh_t *ecdh_generate_public(void *pubkey) {
ecdh_t *ecdh = xzalloc(sizeof *ecdh);
uint8_t seed[32];
randomize(seed, sizeof seed);
ed25519_create_keypair(pubkey, ecdh->private, seed);
return ecdh;
}
bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
ed25519_key_exchange(shared, pubkey, ecdh->private);
free(ecdh);
return true;
}
void ecdh_free(ecdh_t *ecdh) {
free(ecdh);
}

145
src/ed25519/ecdsa.c Normal file
View file

@ -0,0 +1,145 @@
/*
ecdsa.c -- ECDSA key handling
Copyright (C) 2011-2013 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 "ed25519.h"
#define __TINC_ECDSA_INTERNAL__
typedef struct {
uint8_t private[64];
uint8_t public[32];
} ecdsa_t;
#include "../logger.h"
#include "../ecdsa.h"
#include "../utils.h"
#include "../xalloc.h"
// Get and set ECDSA keys
//
ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
int len = strlen(p);
if(len != 43) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid size %d for public key!", len);
return 0;
}
ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
len = b64decode(p, ecdsa->public, len);
if(len != 32) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid format of public key! len = %d", len);
free(ecdsa);
return 0;
}
return ecdsa;
}
char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
char *base64 = xmalloc(44);
b64encode(ecdsa->public, base64, sizeof ecdsa->public);
return base64;
}
// Read PEM ECDSA keys
static bool read_pem(FILE *fp, const char *type, void *buf, size_t size) {
char line[1024];
bool data = false;
size_t typelen = strlen(type);
while(fgets(line, sizeof line, fp)) {
if(!data) {
if(strncmp(line, "-----BEGIN ", 11))
continue;
if(strncmp(line + 11, type, typelen))
continue;
data = true;
continue;
}
if(!strncmp(line, "-----END ", 9))
break;
size_t linelen = strcspn(line, "\r\n");
size_t len = b64decode(line, line, linelen);
if(!len) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid base64 data in PEM file\n");
return false;
}
if(len > size) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too much base64 data in PEM file\n");
return false;
}
memcpy(buf, line, len);
buf += len;
size -= len;
}
if(size) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too little base64 data in PEM file\n");
return false;
}
return true;
}
ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
if(read_pem(fp, "ED25519 PUBLIC KEY", ecdsa->public, sizeof ecdsa->public))
return ecdsa;
free(ecdsa);
return 0;
}
ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) {
ecdsa_t *ecdsa = xmalloc(sizeof *ecdsa);
if(read_pem(fp, "ED25519 PRIVATE KEY", ecdsa->private, sizeof *ecdsa))
return ecdsa;
free(ecdsa);
return 0;
}
size_t ecdsa_size(ecdsa_t *ecdsa) {
return 64;
}
// TODO: standardise output format?
bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t len, void *sig) {
ed25519_sign(sig, in, len, ecdsa->public, ecdsa->private);
return true;
}
bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t len, const void *sig) {
return ed25519_verify(sig, in, len, ecdsa->public);
}
bool ecdsa_active(ecdsa_t *ecdsa) {
return ecdsa;
}
void ecdsa_free(ecdsa_t *ecdsa) {
free(ecdsa);
}

View file

@ -19,13 +19,15 @@
#include "../system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/obj_mac.h>
#include "ed25519.h"
#define __TINC_ECDSA_INTERNAL__
typedef EC_KEY ecdsa_t;
typedef struct {
uint8_t private[64];
uint8_t public[32];
} ecdsa_t;
#include "../crypto.h"
#include "../ecdsagen.h"
#include "../utils.h"
#include "../xalloc.h"
@ -33,38 +35,37 @@ typedef EC_KEY ecdsa_t;
// Generate ECDSA key
ecdsa_t *ecdsa_generate(void) {
ecdsa_t *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
if(!ecdsa || !EC_KEY_generate_key(ecdsa)) {
fprintf(stderr, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
ecdsa_free(ecdsa);
return false;
}
EC_KEY_set_asn1_flag(ecdsa, OPENSSL_EC_NAMED_CURVE);
EC_KEY_set_conv_form(ecdsa, POINT_CONVERSION_COMPRESSED);
uint8_t seed[32];
randomize(seed, sizeof seed);
ed25519_create_keypair(ecdsa->public, ecdsa->private, seed);
return ecdsa;
}
// Write PEM ECDSA keys
static bool write_pem(FILE *fp, const char *type, void *buf, size_t size) {
fprintf(fp, "-----BEGIN %s-----\n", type);
char base64[65];
while(size) {
size_t todo = size > 48 ? 48 : size;
b64encode(buf, base64, todo);
fprintf(fp, "%s\n", base64);
buf += todo;
size -= todo;
}
fprintf(fp, "-----END %s-----\n", type);
return !ferror(fp);
}
bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
BIO *out = BIO_new(BIO_s_file());
if(!out)
return false;
BIO_set_fp(out, fp, BIO_NOCLOSE);
bool result = PEM_write_bio_EC_PUBKEY(out, ecdsa);
BIO_free(out);
return result;
return write_pem(fp, "ED25519 PUBLIC KEY", ecdsa->public, sizeof ecdsa->public);
}
bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
BIO *out = BIO_new(BIO_s_file());
if(!out)
return false;
BIO_set_fp(out, fp, BIO_NOCLOSE);
bool result = PEM_write_bio_ECPrivateKey(out, ecdsa, NULL, NULL, 0, NULL, NULL);
BIO_free(out);
return result;
return write_pem(fp, "ED25519 PRIVATE KEY", ecdsa->private, sizeof *ecdsa);
}

38
src/ed25519/ed25519.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef ED25519_H
#define ED25519_H
#include <stddef.h>
#if defined(_WIN32)
#if defined(ED25519_BUILD_DLL)
#define ED25519_DECLSPEC __declspec(dllexport)
#elif defined(ED25519_DLL)
#define ED25519_DECLSPEC __declspec(dllimport)
#else
#define ED25519_DECLSPEC
#endif
#else
#define ED25519_DECLSPEC
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ED25519_NO_SEED
int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed);
#endif
void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key);
void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
#ifdef __cplusplus
}
#endif
#endif

1491
src/ed25519/fe.c Normal file

File diff suppressed because it is too large Load diff

41
src/ed25519/fe.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef FE_H
#define FE_H
#include "fixedint.h"
/*
fe means field element.
Here the field is \Z/(2^255-19).
An element t, entries t[0]...t[9], represents the integer
t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
Bounds on each t[i] vary depending on context.
*/
typedef int32_t fe[10];
void fe_0(fe h);
void fe_1(fe h);
void fe_frombytes(fe h, const unsigned char *s);
void fe_tobytes(unsigned char *s, const fe h);
void fe_copy(fe h, const fe f);
int fe_isnegative(const fe f);
int fe_isnonzero(const fe f);
void fe_cmov(fe f, const fe g, unsigned int b);
void fe_cswap(fe f, fe g, unsigned int b);
void fe_neg(fe h, const fe f);
void fe_add(fe h, const fe f, const fe g);
void fe_invert(fe out, const fe z);
void fe_sq(fe h, const fe f);
void fe_sq2(fe h, const fe f);
void fe_mul(fe h, const fe f, const fe g);
void fe_mul121666(fe h, fe f);
void fe_pow22523(fe out, const fe z);
void fe_sub(fe h, const fe f, const fe g);
#endif

70
src/ed25519/fixedint.h Normal file
View file

@ -0,0 +1,70 @@
/*
Portable header to provide the 32 and 64 bits type.
Not a compatible replacement for <stdint.h>, do not blindly use it as such.
*/
#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED)
#include <stdint.h>
#define FIXEDINT_H_INCLUDED
#if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C)
#include <limits.h>
#define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
#endif
#endif
#ifndef FIXEDINT_H_INCLUDED
#define FIXEDINT_H_INCLUDED
/* (u)int32_t */
#ifndef uint32_t
#if (ULONG_MAX == 0xffffffffUL)
typedef unsigned long uint32_t;
#elif (UINT_MAX == 0xffffffffUL)
typedef unsigned int uint32_t;
#elif (USHRT_MAX == 0xffffffffUL)
typedef unsigned short uint32_t;
#endif
#endif
#ifndef int32_t
#if (LONG_MAX == 0x7fffffffL)
typedef signed long int32_t;
#elif (INT_MAX == 0x7fffffffL)
typedef signed int int32_t;
#elif (SHRT_MAX == 0x7fffffffL)
typedef signed short int32_t;
#endif
#endif
/* (u)int64_t */
#if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L)
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define UINT64_C(v) v ##ULL
#define INT64_C(v) v ##LL
#elif defined(__GNUC__)
__extension__ typedef long long int64_t;
__extension__ typedef unsigned long long uint64_t;
#define UINT64_C(v) v ##ULL
#define INT64_C(v) v ##LL
#elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC)
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define UINT64_C(v) v ##ULL
#define INT64_C(v) v ##LL
#elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC)
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define UINT64_C(v) v ##UI64
#define INT64_C(v) v ##I64
#endif
#endif

467
src/ed25519/ge.c Normal file
View file

@ -0,0 +1,467 @@
#include "ge.h"
#include "precomp_data.h"
/*
r = p + q
*/
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->YplusX);
fe_mul(r->Y, r->Y, q->YminusX);
fe_mul(r->T, q->T2d, p->T);
fe_mul(r->X, p->Z, q->Z);
fe_add(t0, r->X, r->X);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_add(r->Z, t0, r->T);
fe_sub(r->T, t0, r->T);
}
static void slide(signed char *r, const unsigned char *a) {
int i;
int b;
int k;
for (i = 0; i < 256; ++i) {
r[i] = 1 & (a[i >> 3] >> (i & 7));
}
for (i = 0; i < 256; ++i)
if (r[i]) {
for (b = 1; b <= 6 && i + b < 256; ++b) {
if (r[i + b]) {
if (r[i] + (r[i + b] << b) <= 15) {
r[i] += r[i + b] << b;
r[i + b] = 0;
} else if (r[i] - (r[i + b] << b) >= -15) {
r[i] -= r[i + b] << b;
for (k = i + b; k < 256; ++k) {
if (!r[k]) {
r[k] = 1;
break;
}
r[k] = 0;
}
} else {
break;
}
}
}
}
}
/*
r = a * A + b * B
where a = a[0]+256*a[1]+...+256^31 a[31].
and b = b[0]+256*b[1]+...+256^31 b[31].
B is the Ed25519 base point (x,4/5) with x positive.
*/
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
signed char aslide[256];
signed char bslide[256];
ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
ge_p1p1 t;
ge_p3 u;
ge_p3 A2;
int i;
slide(aslide, a);
slide(bslide, b);
ge_p3_to_cached(&Ai[0], A);
ge_p3_dbl(&t, A);
ge_p1p1_to_p3(&A2, &t);
ge_add(&t, &A2, &Ai[0]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[1], &u);
ge_add(&t, &A2, &Ai[1]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[2], &u);
ge_add(&t, &A2, &Ai[2]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[3], &u);
ge_add(&t, &A2, &Ai[3]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[4], &u);
ge_add(&t, &A2, &Ai[4]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[5], &u);
ge_add(&t, &A2, &Ai[5]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[6], &u);
ge_add(&t, &A2, &Ai[6]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[7], &u);
ge_p2_0(r);
for (i = 255; i >= 0; --i) {
if (aslide[i] || bslide[i]) {
break;
}
}
for (; i >= 0; --i) {
ge_p2_dbl(&t, r);
if (aslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_add(&t, &u, &Ai[aslide[i] / 2]);
} else if (aslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
}
if (bslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_madd(&t, &u, &Bi[bslide[i] / 2]);
} else if (bslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
}
ge_p1p1_to_p2(r, &t);
}
}
static const fe d = {
-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
};
static const fe sqrtm1 = {
-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
};
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) {
fe u;
fe v;
fe v3;
fe vxx;
fe check;
fe_frombytes(h->Y, s);
fe_1(h->Z);
fe_sq(u, h->Y);
fe_mul(v, u, d);
fe_sub(u, u, h->Z); /* u = y^2-1 */
fe_add(v, v, h->Z); /* v = dy^2+1 */
fe_sq(v3, v);
fe_mul(v3, v3, v); /* v3 = v^3 */
fe_sq(h->X, v3);
fe_mul(h->X, h->X, v);
fe_mul(h->X, h->X, u); /* x = uv^7 */
fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
fe_mul(h->X, h->X, v3);
fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */
fe_sq(vxx, h->X);
fe_mul(vxx, vxx, v);
fe_sub(check, vxx, u); /* vx^2-u */
if (fe_isnonzero(check)) {
fe_add(check, vxx, u); /* vx^2+u */
if (fe_isnonzero(check)) {
return -1;
}
fe_mul(h->X, h->X, sqrtm1);
}
if (fe_isnegative(h->X) == (s[31] >> 7)) {
fe_neg(h->X, h->X);
}
fe_mul(h->T, h->X, h->Y);
return 0;
}
/*
r = p + q
*/
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->yplusx);
fe_mul(r->Y, r->Y, q->yminusx);
fe_mul(r->T, q->xy2d, p->T);
fe_add(t0, p->Z, p->Z);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_add(r->Z, t0, r->T);
fe_sub(r->T, t0, r->T);
}
/*
r = p - q
*/
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->yminusx);
fe_mul(r->Y, r->Y, q->yplusx);
fe_mul(r->T, q->xy2d, p->T);
fe_add(t0, p->Z, p->Z);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_sub(r->Z, t0, r->T);
fe_add(r->T, t0, r->T);
}
/*
r = p
*/
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
fe_mul(r->X, p->X, p->T);
fe_mul(r->Y, p->Y, p->Z);
fe_mul(r->Z, p->Z, p->T);
}
/*
r = p
*/
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) {
fe_mul(r->X, p->X, p->T);
fe_mul(r->Y, p->Y, p->Z);
fe_mul(r->Z, p->Z, p->T);
fe_mul(r->T, p->X, p->Y);
}
void ge_p2_0(ge_p2 *h) {
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
}
/*
r = 2 * p
*/
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
fe t0;
fe_sq(r->X, p->X);
fe_sq(r->Z, p->Y);
fe_sq2(r->T, p->Z);
fe_add(r->Y, p->X, p->Y);
fe_sq(t0, r->Y);
fe_add(r->Y, r->Z, r->X);
fe_sub(r->Z, r->Z, r->X);
fe_sub(r->X, t0, r->Y);
fe_sub(r->T, r->T, r->Z);
}
void ge_p3_0(ge_p3 *h) {
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
fe_0(h->T);
}
/*
r = 2 * p
*/
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
ge_p2 q;
ge_p3_to_p2(&q, p);
ge_p2_dbl(r, &q);
}
/*
r = p
*/
static const fe d2 = {
-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
};
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
fe_add(r->YplusX, p->Y, p->X);
fe_sub(r->YminusX, p->Y, p->X);
fe_copy(r->Z, p->Z);
fe_mul(r->T2d, p->T, d2);
}
/*
r = p
*/
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
fe_copy(r->X, p->X);
fe_copy(r->Y, p->Y);
fe_copy(r->Z, p->Z);
}
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) {
fe recip;
fe x;
fe y;
fe_invert(recip, h->Z);
fe_mul(x, h->X, recip);
fe_mul(y, h->Y, recip);
fe_tobytes(s, y);
s[31] ^= fe_isnegative(x) << 7;
}
static unsigned char equal(signed char b, signed char c) {
unsigned char ub = b;
unsigned char uc = c;
unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
uint64_t y = x; /* 0: yes; 1..255: no */
y -= 1; /* large: yes; 0..254: no */
y >>= 63; /* 1: yes; 0: no */
return (unsigned char) y;
}
static unsigned char negative(signed char b) {
uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
x >>= 63; /* 1: yes; 0: no */
return (unsigned char) x;
}
static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) {
fe_cmov(t->yplusx, u->yplusx, b);
fe_cmov(t->yminusx, u->yminusx, b);
fe_cmov(t->xy2d, u->xy2d, b);
}
static void select(ge_precomp *t, int pos, signed char b) {
ge_precomp minust;
unsigned char bnegative = negative(b);
unsigned char babs = b - (((-bnegative) & b) << 1);
fe_1(t->yplusx);
fe_1(t->yminusx);
fe_0(t->xy2d);
cmov(t, &base[pos][0], equal(babs, 1));
cmov(t, &base[pos][1], equal(babs, 2));
cmov(t, &base[pos][2], equal(babs, 3));
cmov(t, &base[pos][3], equal(babs, 4));
cmov(t, &base[pos][4], equal(babs, 5));
cmov(t, &base[pos][5], equal(babs, 6));
cmov(t, &base[pos][6], equal(babs, 7));
cmov(t, &base[pos][7], equal(babs, 8));
fe_copy(minust.yplusx, t->yminusx);
fe_copy(minust.yminusx, t->yplusx);
fe_neg(minust.xy2d, t->xy2d);
cmov(t, &minust, bnegative);
}
/*
h = a * B
where a = a[0]+256*a[1]+...+256^31 a[31]
B is the Ed25519 base point (x,4/5) with x positive.
Preconditions:
a[31] <= 127
*/
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
signed char e[64];
signed char carry;
ge_p1p1 r;
ge_p2 s;
ge_precomp t;
int i;
for (i = 0; i < 32; ++i) {
e[2 * i + 0] = (a[i] >> 0) & 15;
e[2 * i + 1] = (a[i] >> 4) & 15;
}
/* each e[i] is between 0 and 15 */
/* e[63] is between 0 and 7 */
carry = 0;
for (i = 0; i < 63; ++i) {
e[i] += carry;
carry = e[i] + 8;
carry >>= 4;
e[i] -= carry << 4;
}
e[63] += carry;
/* each e[i] is between -8 and 8 */
ge_p3_0(h);
for (i = 1; i < 64; i += 2) {
select(&t, i / 2, e[i]);
ge_madd(&r, h, &t);
ge_p1p1_to_p3(h, &r);
}
ge_p3_dbl(&r, h);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p3(h, &r);
for (i = 0; i < 64; i += 2) {
select(&t, i / 2, e[i]);
ge_madd(&r, h, &t);
ge_p1p1_to_p3(h, &r);
}
}
/*
r = p - q
*/
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->YminusX);
fe_mul(r->Y, r->Y, q->YplusX);
fe_mul(r->T, q->T2d, p->T);
fe_mul(r->X, p->Z, q->Z);
fe_add(t0, r->X, r->X);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_sub(r->Z, t0, r->T);
fe_add(r->T, t0, r->T);
}
void ge_tobytes(unsigned char *s, const ge_p2 *h) {
fe recip;
fe x;
fe y;
fe_invert(recip, h->Z);
fe_mul(x, h->X, recip);
fe_mul(y, h->Y, recip);
fe_tobytes(s, y);
s[31] ^= fe_isnegative(x) << 7;
}

74
src/ed25519/ge.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef GE_H
#define GE_H
#include "fe.h"
/*
ge means group element.
Here the group is the set of pairs (x,y) of field elements (see fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2
where d = -121665/121666.
Representations:
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
ge_precomp (Duif): (y+x,y-x,2dxy)
*/
typedef struct {
fe X;
fe Y;
fe Z;
} ge_p2;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p3;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p1p1;
typedef struct {
fe yplusx;
fe yminusx;
fe xy2d;
} ge_precomp;
typedef struct {
fe YplusX;
fe YminusX;
fe Z;
fe T2d;
} ge_cached;
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h);
void ge_tobytes(unsigned char *s, const ge_p2 *h);
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s);
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b);
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a);
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p);
void ge_p2_0(ge_p2 *h);
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p);
void ge_p3_0(ge_p3 *h);
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p);
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p);
#endif

View file

@ -0,0 +1,79 @@
#include "ed25519.h"
#include "fe.h"
void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) {
unsigned char e[32];
unsigned int i;
fe x1;
fe x2;
fe z2;
fe x3;
fe z3;
fe tmp0;
fe tmp1;
int pos;
unsigned int swap;
unsigned int b;
/* copy the private key and make sure it's valid */
for (i = 0; i < 32; ++i) {
e[i] = private_key[i];
}
e[0] &= 248;
e[31] &= 63;
e[31] |= 64;
/* unpack the public key and convert edwards to montgomery */
/* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */
fe_frombytes(x1, public_key);
fe_1(tmp1);
fe_add(tmp0, x1, tmp1);
fe_sub(tmp1, tmp1, x1);
fe_invert(tmp1, tmp1);
fe_mul(x1, tmp0, tmp1);
fe_1(x2);
fe_0(z2);
fe_copy(x3, x1);
fe_1(z3);
swap = 0;
for (pos = 254; pos >= 0; --pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
swap ^= b;
fe_cswap(x2, x3, swap);
fe_cswap(z2, z3, swap);
swap = b;
/* from montgomery.h */
fe_sub(tmp0, x3, z3);
fe_sub(tmp1, x2, z2);
fe_add(x2, x2, z2);
fe_add(z2, x3, z3);
fe_mul(z3, tmp0, x2);
fe_mul(z2, z2, tmp1);
fe_sq(tmp0, tmp1);
fe_sq(tmp1, x2);
fe_add(x3, z3, z2);
fe_sub(z2, z3, z2);
fe_mul(x2, tmp1, tmp0);
fe_sub(tmp1, tmp1, tmp0);
fe_sq(z2, z2);
fe_mul121666(z3, tmp1);
fe_sq(x3, x3);
fe_add(tmp0, tmp0, z3);
fe_mul(z3, x1, z2);
fe_mul(z2, tmp1, tmp0);
}
fe_cswap(x2, x3, swap);
fe_cswap(z2, z3, swap);
fe_invert(z2, z2);
fe_mul(x2, x2, z2);
fe_tobytes(shared_secret, x2);
}

16
src/ed25519/keypair.c Normal file
View file

@ -0,0 +1,16 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) {
ge_p3 A;
sha512(seed, 32, private_key);
private_key[0] &= 248;
private_key[31] &= 63;
private_key[31] |= 64;
ge_scalarmult_base(&A, private_key);
ge_p3_tobytes(public_key, &A);
}

1391
src/ed25519/precomp_data.h Normal file

File diff suppressed because it is too large Load diff

809
src/ed25519/sc.c Normal file
View file

@ -0,0 +1,809 @@
#include "fixedint.h"
#include "sc.h"
static uint64_t load_3(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
return result;
}
static uint64_t load_4(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
result |= ((uint64_t) in[3]) << 24;
return result;
}
/*
Input:
s[0]+256*s[1]+...+256^63*s[63] = s
Output:
s[0]+256*s[1]+...+256^31*s[31] = s mod l
where l = 2^252 + 27742317777372353535851937790883648493.
Overwrites s in place.
*/
void sc_reduce(unsigned char *s) {
int64_t s0 = 2097151 & load_3(s);
int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
int64_t s8 = 2097151 & load_3(s + 21);
int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
int64_t s11 = 2097151 & (load_4(s + 28) >> 7);
int64_t s12 = 2097151 & (load_4(s + 31) >> 4);
int64_t s13 = 2097151 & (load_3(s + 34) >> 1);
int64_t s14 = 2097151 & (load_4(s + 36) >> 6);
int64_t s15 = 2097151 & (load_3(s + 39) >> 3);
int64_t s16 = 2097151 & load_3(s + 42);
int64_t s17 = 2097151 & (load_4(s + 44) >> 5);
int64_t s18 = 2097151 & (load_3(s + 47) >> 2);
int64_t s19 = 2097151 & (load_4(s + 49) >> 7);
int64_t s20 = 2097151 & (load_4(s + 52) >> 4);
int64_t s21 = 2097151 & (load_3(s + 55) >> 1);
int64_t s22 = 2097151 & (load_4(s + 57) >> 6);
int64_t s23 = (load_4(s + 60) >> 3);
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry11 = s11 >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
s[0] = (unsigned char) (s0 >> 0);
s[1] = (unsigned char) (s0 >> 8);
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
s[3] = (unsigned char) (s1 >> 3);
s[4] = (unsigned char) (s1 >> 11);
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
s[6] = (unsigned char) (s2 >> 6);
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
s[8] = (unsigned char) (s3 >> 1);
s[9] = (unsigned char) (s3 >> 9);
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
s[11] = (unsigned char) (s4 >> 4);
s[12] = (unsigned char) (s4 >> 12);
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
s[14] = (unsigned char) (s5 >> 7);
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
s[16] = (unsigned char) (s6 >> 2);
s[17] = (unsigned char) (s6 >> 10);
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
s[19] = (unsigned char) (s7 >> 5);
s[20] = (unsigned char) (s7 >> 13);
s[21] = (unsigned char) (s8 >> 0);
s[22] = (unsigned char) (s8 >> 8);
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
s[24] = (unsigned char) (s9 >> 3);
s[25] = (unsigned char) (s9 >> 11);
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
s[27] = (unsigned char) (s10 >> 6);
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
s[29] = (unsigned char) (s11 >> 1);
s[30] = (unsigned char) (s11 >> 9);
s[31] = (unsigned char) (s11 >> 17);
}
/*
Input:
a[0]+256*a[1]+...+256^31*a[31] = a
b[0]+256*b[1]+...+256^31*b[31] = b
c[0]+256*c[1]+...+256^31*c[31] = c
Output:
s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
int64_t a0 = 2097151 & load_3(a);
int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
int64_t a8 = 2097151 & load_3(a + 21);
int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
int64_t a11 = (load_4(a + 28) >> 7);
int64_t b0 = 2097151 & load_3(b);
int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
int64_t b8 = 2097151 & load_3(b + 21);
int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
int64_t b11 = (load_4(b + 28) >> 7);
int64_t c0 = 2097151 & load_3(c);
int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
int64_t c8 = 2097151 & load_3(c + 21);
int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
int64_t c11 = (load_4(c + 28) >> 7);
int64_t s0;
int64_t s1;
int64_t s2;
int64_t s3;
int64_t s4;
int64_t s5;
int64_t s6;
int64_t s7;
int64_t s8;
int64_t s9;
int64_t s10;
int64_t s11;
int64_t s12;
int64_t s13;
int64_t s14;
int64_t s15;
int64_t s16;
int64_t s17;
int64_t s18;
int64_t s19;
int64_t s20;
int64_t s21;
int64_t s22;
int64_t s23;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
int64_t carry17;
int64_t carry18;
int64_t carry19;
int64_t carry20;
int64_t carry21;
int64_t carry22;
s0 = c0 + a0 * b0;
s1 = c1 + a0 * b1 + a1 * b0;
s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;
s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0;
s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3;
s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;
s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
s20 = a9 * b11 + a10 * b10 + a11 * b9;
s21 = a10 * b11 + a11 * b10;
s22 = a11 * b11;
s23 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry18 = (s18 + (1 << 20)) >> 21;
s19 += carry18;
s18 -= carry18 << 21;
carry20 = (s20 + (1 << 20)) >> 21;
s21 += carry20;
s20 -= carry20 << 21;
carry22 = (s22 + (1 << 20)) >> 21;
s23 += carry22;
s22 -= carry22 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
carry17 = (s17 + (1 << 20)) >> 21;
s18 += carry17;
s17 -= carry17 << 21;
carry19 = (s19 + (1 << 20)) >> 21;
s20 += carry19;
s19 -= carry19 << 21;
carry21 = (s21 + (1 << 20)) >> 21;
s22 += carry21;
s21 -= carry21 << 21;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry11 = s11 >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
s[0] = (unsigned char) (s0 >> 0);
s[1] = (unsigned char) (s0 >> 8);
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
s[3] = (unsigned char) (s1 >> 3);
s[4] = (unsigned char) (s1 >> 11);
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
s[6] = (unsigned char) (s2 >> 6);
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
s[8] = (unsigned char) (s3 >> 1);
s[9] = (unsigned char) (s3 >> 9);
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
s[11] = (unsigned char) (s4 >> 4);
s[12] = (unsigned char) (s4 >> 12);
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
s[14] = (unsigned char) (s5 >> 7);
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
s[16] = (unsigned char) (s6 >> 2);
s[17] = (unsigned char) (s6 >> 10);
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
s[19] = (unsigned char) (s7 >> 5);
s[20] = (unsigned char) (s7 >> 13);
s[21] = (unsigned char) (s8 >> 0);
s[22] = (unsigned char) (s8 >> 8);
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
s[24] = (unsigned char) (s9 >> 3);
s[25] = (unsigned char) (s9 >> 11);
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
s[27] = (unsigned char) (s10 >> 6);
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
s[29] = (unsigned char) (s11 >> 1);
s[30] = (unsigned char) (s11 >> 9);
s[31] = (unsigned char) (s11 >> 17);
}

12
src/ed25519/sc.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef SC_H
#define SC_H
/*
The set of scalars is \Z/l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_reduce(unsigned char *s);
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
#endif

275
src/ed25519/sha512.c Normal file
View file

@ -0,0 +1,275 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
*/
#include "fixedint.h"
#include "sha512.h"
/* the K array */
static const uint64_t K[80] = {
UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
};
/* Various logical functions */
#define ROR64c(x, y) \
( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \
((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF))
#define STORE64H(x, y) \
{ (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
(y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
(y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
(y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
#define LOAD64H(x, y) \
{ x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \
(((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \
(((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \
(((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); }
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
#define Maj(x,y,z) (((x | y) & z) | (x & y))
#define S(x, n) ROR64c(x, n)
#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n))
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
#ifndef MIN
#define MIN(x, y) ( ((x)<(y))?(x):(y) )
#endif
/* compress 1024-bits */
static int sha512_compress(sha512_context *md, unsigned char *buf)
{
uint64_t S[8], W[80], t0, t1;
int i;
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = md->state[i];
}
/* copy the state into 1024-bits into W[0..15] */
for (i = 0; i < 16; i++) {
LOAD64H(W[i], buf + (8*i));
}
/* fill W[16..79] */
for (i = 16; i < 80; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
}
/* Compress */
#define RND(a,b,c,d,e,f,g,h,i) \
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c);\
d += t0; \
h = t0 + t1;
for (i = 0; i < 80; i += 8) {
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
}
#undef RND
/* feedback */
for (i = 0; i < 8; i++) {
md->state[i] = md->state[i] + S[i];
}
return 0;
}
/**
Initialize the hash state
@param md The hash state you wish to initialize
@return 0 if successful
*/
int sha512_init(sha512_context * md) {
if (md == NULL) return 1;
md->curlen = 0;
md->length = 0;
md->state[0] = UINT64_C(0x6a09e667f3bcc908);
md->state[1] = UINT64_C(0xbb67ae8584caa73b);
md->state[2] = UINT64_C(0x3c6ef372fe94f82b);
md->state[3] = UINT64_C(0xa54ff53a5f1d36f1);
md->state[4] = UINT64_C(0x510e527fade682d1);
md->state[5] = UINT64_C(0x9b05688c2b3e6c1f);
md->state[6] = UINT64_C(0x1f83d9abfb41bd6b);
md->state[7] = UINT64_C(0x5be0cd19137e2179);
return 0;
}
/**
Process a block of memory though the hash
@param md The hash state
@param in The data to hash
@param inlen The length of the data (octets)
@return 0 if successful
*/
int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
{
size_t n;
size_t i;
int err;
if (md == NULL) return 1;
if (in == NULL) return 1;
if (md->curlen > sizeof(md->buf)) {
return 1;
}
while (inlen > 0) {
if (md->curlen == 0 && inlen >= 128) {
if ((err = sha512_compress (md, (unsigned char *)in)) != 0) {
return err;
}
md->length += 128 * 8;
in += 128;
inlen -= 128;
} else {
n = MIN(inlen, (128 - md->curlen));
for (i = 0; i < n; i++) {
md->buf[i + md->curlen] = in[i];
}
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == 128) {
if ((err = sha512_compress (md, md->buf)) != 0) {
return err;
}
md->length += 8*128;
md->curlen = 0;
}
}
}
return 0;
}
/**
Terminate the hash to get the digest
@param md The hash state
@param out [out] The destination of the hash (64 bytes)
@return 0 if successful
*/
int sha512_final(sha512_context * md, unsigned char *out)
{
int i;
if (md == NULL) return 1;
if (out == NULL) return 1;
if (md->curlen >= sizeof(md->buf)) {
return 1;
}
/* increase the length of the message */
md->length += md->curlen * UINT64_C(8);
/* append the '1' bit */
md->buf[md->curlen++] = (unsigned char)0x80;
/* if the length is currently above 112 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (md->curlen > 112) {
while (md->curlen < 128) {
md->buf[md->curlen++] = (unsigned char)0;
}
sha512_compress(md, md->buf);
md->curlen = 0;
}
/* pad upto 120 bytes of zeroes
* note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash
* > 2^64 bits of data... :-)
*/
while (md->curlen < 120) {
md->buf[md->curlen++] = (unsigned char)0;
}
/* store length */
STORE64H(md->length, md->buf+120);
sha512_compress(md, md->buf);
/* copy output */
for (i = 0; i < 8; i++) {
STORE64H(md->state[i], out+(8*i));
}
return 0;
}
int sha512(const unsigned char *message, size_t message_len, unsigned char *out)
{
sha512_context ctx;
int ret;
if ((ret = sha512_init(&ctx))) return ret;
if ((ret = sha512_update(&ctx, message, message_len))) return ret;
if ((ret = sha512_final(&ctx, out))) return ret;
return 0;
}

21
src/ed25519/sha512.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef SHA512_H
#define SHA512_H
#include <stddef.h>
#include "fixedint.h"
/* state */
typedef struct sha512_context_ {
uint64_t length, state[8];
size_t curlen;
unsigned char buf[128];
} sha512_context;
int sha512_init(sha512_context * md);
int sha512_final(sha512_context * md, unsigned char *out);
int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen);
int sha512(const unsigned char *message, size_t message_len, unsigned char *out);
#endif

31
src/ed25519/sign.c Normal file
View file

@ -0,0 +1,31 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
#include "sc.h"
void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) {
sha512_context hash;
unsigned char hram[64];
unsigned char r[64];
ge_p3 R;
sha512_init(&hash);
sha512_update(&hash, private_key + 32, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, r);
sc_reduce(r);
ge_scalarmult_base(&R, r);
ge_p3_tobytes(signature, &R);
sha512_init(&hash);
sha512_update(&hash, signature, 32);
sha512_update(&hash, public_key, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, hram);
sc_reduce(hram);
sc_muladd(signature + 32, hram, private_key, r);
}

77
src/ed25519/verify.c Normal file
View file

@ -0,0 +1,77 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
#include "sc.h"
static int consttime_equal(const unsigned char *x, const unsigned char *y) {
unsigned char r = 0;
r = x[0] ^ y[0];
#define F(i) r |= x[i] ^ y[i]
F(1);
F(2);
F(3);
F(4);
F(5);
F(6);
F(7);
F(8);
F(9);
F(10);
F(11);
F(12);
F(13);
F(14);
F(15);
F(16);
F(17);
F(18);
F(19);
F(20);
F(21);
F(22);
F(23);
F(24);
F(25);
F(26);
F(27);
F(28);
F(29);
F(30);
F(31);
#undef F
return !r;
}
int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) {
unsigned char h[64];
unsigned char checker[32];
sha512_context hash;
ge_p3 A;
ge_p2 R;
if (signature[63] & 224) {
return 0;
}
if (ge_frombytes_negate_vartime(&A, public_key) != 0) {
return 0;
}
sha512_init(&hash);
sha512_update(&hash, signature, 32);
sha512_update(&hash, public_key, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, h);
sc_reduce(h);
ge_double_scalarmult_vartime(&R, h, &A, signature + 32);
ge_tobytes(checker, &R);
if (!consttime_equal(checker, signature)) {
return 0;
}
return 1;
}

View file

@ -110,11 +110,13 @@ bool dump_edges(connection_t *c) {
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",
char* local_address = sockaddr2hostname(&e->local_address);
send_request(c, "%d %d %s %s %s %s %x %d",
CONTROL, REQ_DUMP_EDGES,
e->from->name, e->to->name, address,
e->options, e->weight);
local_address, e->options, e->weight);
free(address);
free(local_address);
}
}

View file

@ -30,6 +30,7 @@ typedef struct edge_t {
struct node_t *from;
struct node_t *to;
sockaddr_t address;
sockaddr_t local_address;
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */

View file

@ -23,15 +23,26 @@
#include "event.h"
#include "net.h"
#include "utils.h"
#include "xalloc.h"
struct timeval now;
#ifndef HAVE_MINGW
static fd_set readfds;
static fd_set writefds;
static volatile bool running;
#else
static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
static DWORD event_count = 0;
#endif
static bool running;
static int io_compare(const io_t *a, const io_t *b) {
#ifndef HAVE_MINGW
return a->fd - b->fd;
#else
return a->event - b->event;
#endif
}
static int timeout_compare(const timeout_t *a, const timeout_t *b) {
@ -60,6 +71,14 @@ void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
return;
io->fd = fd;
#ifdef HAVE_MINGW
if (io->fd != -1) {
io->event = WSACreateEvent();
if (io->event == WSA_INVALID_EVENT)
abort();
}
event_count++;
#endif
io->cb = cb;
io->data = data;
io->node.data = io;
@ -70,9 +89,21 @@ void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
abort();
}
void io_set(io_t *io, int flags) {
io->flags = flags;
#ifdef HAVE_MINGW
void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
io->event = event;
io_add(io, cb, data, -1, 0);
}
#endif
void io_set(io_t *io, int flags) {
if (flags == io->flags)
return;
io->flags = flags;
if (io->fd == -1)
return;
#ifndef HAVE_MINGW
if(flags & IO_READ)
FD_SET(io->fd, &readfds);
else
@ -82,6 +113,15 @@ void io_set(io_t *io, int flags) {
FD_SET(io->fd, &writefds);
else
FD_CLR(io->fd, &writefds);
#else
long events = 0;
if (flags & IO_WRITE)
events |= WRITE_EVENTS;
if (flags & IO_READ)
events |= READ_EVENTS;
if (WSAEventSelect(io->fd, io->event, events) != 0)
abort();
#endif
}
void io_del(io_t *io) {
@ -89,6 +129,11 @@ void io_del(io_t *io) {
return;
io_set(io, 0);
#ifdef HAVE_MINGW
if (io->fd != -1 && WSACloseEvent(io->event) == FALSE)
abort();
event_count--;
#endif
splay_unlink_node(&io_tree, &io->node);
io->cb = NULL;
@ -182,30 +227,37 @@ void signal_del(signal_t *sig) {
}
#endif
static struct timeval * get_time_remaining(struct timeval *diff) {
gettimeofday(&now, NULL);
struct timeval *tv = NULL;
while(timeout_tree.head) {
timeout_t *timeout = timeout_tree.head->data;
timersub(&timeout->tv, &now, diff);
if(diff->tv_sec < 0) {
timeout->cb(timeout->data);
if(timercmp(&timeout->tv, &now, <))
timeout_del(timeout);
} else {
tv = diff;
break;
}
}
return tv;
}
bool event_loop(void) {
running = true;
#ifndef HAVE_MINGW
fd_set readable;
fd_set writable;
while(running) {
gettimeofday(&now, NULL);
struct timeval diff, *tv = NULL;
while(timeout_tree.head) {
timeout_t *timeout = timeout_tree.head->data;
timersub(&timeout->tv, &now, &diff);
if(diff.tv_sec < 0) {
timeout->cb(timeout->data);
if(timercmp(&timeout->tv, &now, <))
timeout_del(timeout);
} else {
tv = &diff;
break;
}
}
struct timeval diff;
struct timeval *tv = get_time_remaining(&diff);
memcpy(&readable, &readfds, sizeof readable);
memcpy(&writable, &writefds, sizeof writable);
@ -216,16 +268,10 @@ bool event_loop(void) {
fds = last->fd + 1;
}
#ifdef HAVE_MINGW
LeaveCriticalSection(&mutex);
#endif
int n = select(fds, &readable, &writable, NULL, tv);
#ifdef HAVE_MINGW
EnterCriticalSection(&mutex);
#endif
if(n < 0) {
if(sockwouldblock(errno))
if(sockwouldblock(sockerrno))
continue;
else
return false;
@ -241,16 +287,77 @@ bool event_loop(void) {
io->cb(io->data, IO_READ);
}
}
#else
while (running) {
struct timeval diff;
struct timeval *tv = get_time_remaining(&diff);
DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
if (!event_count) {
Sleep(timeout_ms);
continue;
}
/*
For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
this event being fired again if ignored.
*/
io_t* writeable_io = NULL;
for splay_each(io_t, io, &io_tree)
if (io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
writeable_io = io;
break;
}
if (writeable_io) {
writeable_io->cb(writeable_io->data, IO_WRITE);
continue;
}
WSAEVENT* events = xmalloc(event_count * sizeof(*events));
DWORD event_index = 0;
for splay_each(io_t, io, &io_tree) {
events[event_index] = io->event;
event_index++;
}
DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
WSAEVENT event;
if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count)
event = events[result - WSA_WAIT_EVENT_0];
free(events);
if (result == WSA_WAIT_TIMEOUT)
continue;
if (result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count)
return false;
io_t *io = splay_search(&io_tree, &((io_t){.event = event}));
if (!io)
abort();
if (io->fd == -1) {
io->cb(io->data, 0);
} else {
WSANETWORKEVENTS network_events;
if (WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0)
return false;
if (network_events.lNetworkEvents & WRITE_EVENTS)
io->cb(io->data, IO_WRITE);
if (network_events.lNetworkEvents & READ_EVENTS)
io->cb(io->data, IO_READ);
}
}
#endif
return true;
}
void event_flush_output(void) {
for splay_each(io_t, io, &io_tree)
if(FD_ISSET(io->fd, &writefds))
io->cb(io->data, IO_WRITE);
}
void event_exit(void) {
running = false;
}

View file

@ -32,6 +32,9 @@ typedef void (*signal_cb_t)(void *data);
typedef struct io_t {
int fd;
int flags;
#ifdef HAVE_MINGW
WSAEVENT event;
#endif
io_cb_t cb;
void *data;
splay_node_t node;
@ -54,6 +57,9 @@ typedef struct signal_t {
extern struct timeval now;
extern void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags);
#ifdef HAVE_MINGW
extern void io_add_event(io_t *io, io_cb_t cb, void* data, WSAEVENT event);
#endif
extern void io_del(io_t *io);
extern void io_set(io_t *io, int flags);
@ -65,7 +71,6 @@ extern void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum);
extern void signal_del(signal_t *sig);
extern bool event_loop(void);
extern void event_flush_output(void);
extern void event_exit(void);
#endif

View file

@ -176,9 +176,13 @@ static void sssp_bfs(void) {
&& (e->to->distance != n->distance + 1 || e->weight >= e->to->prevedge->weight))
continue;
// Only update nexthop if it doesn't increase the path length
if(!e->to->status.visited || (e->to->distance == n->distance + 1 && e->weight >= e->to->prevedge->weight))
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
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;
@ -200,6 +204,9 @@ static void sssp_bfs(void) {
static void check_reachability(void) {
/* Check reachability status. */
int reachable_count = 0;
int became_reachable_count = 0;
int became_unreachable_count = 0;
for splay_each(node_t, n, node_tree) {
if(n->status.visited != n->status.reachable) {
n->status.reachable = !n->status.reachable;
@ -208,9 +215,13 @@ static void check_reachability(void) {
if(n->status.reachable) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable",
n->name, n->hostname);
if (n != myself)
became_reachable_count++;
} else {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became unreachable",
n->name, n->hostname);
if (n != myself)
became_unreachable_count++;
}
if(experimental && OPTION_VERSION(n->options) >= 2)
@ -264,15 +275,18 @@ static void check_reachability(void) {
update_node_udp(n, NULL);
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);
}
}
}
if(n->status.reachable && n != myself)
reachable_count++;
}
if (device_standby) {
if (reachable_count == 0 && became_unreachable_count > 0)
device_disable();
else if (reachable_count > 0 && reachable_count == became_reachable_count)
device_enable();
}
}

View file

@ -91,6 +91,13 @@ void *hash_search_or_insert(hash_t *hash, const void *key, const void *value) {
return NULL;
}
/* Deleting */
void hash_delete(hash_t *hash, const void *key) {
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
hash->values[i] = NULL;
}
/* Utility functions */
void hash_clear(hash_t *hash) {

View file

@ -31,6 +31,7 @@ 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_delete(hash_t *, const void *key);
extern void *hash_search(const hash_t *, const void *key);
extern void *hash_search_or_insert(hash_t *, const void *key, const void *value);

View file

@ -39,6 +39,7 @@
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#ifdef HAVE_MINGW
#include <w32api.h>

View file

@ -24,6 +24,7 @@
#include "subnet.h"
#include "tincctl.h"
#include "info.h"
#include "utils.h"
#include "xalloc.h"
void logger(int level, int priority, const char *format, ...) {
@ -49,6 +50,7 @@ static int info_node(int fd, const char *item) {
char line[4096];
char node[4096];
char id[4096];
char from[4096];
char to[4096];
char subnet[4096];
@ -67,12 +69,12 @@ static int info_node(int fd, const char *item) {
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 %"PRIx32" %s %s %d %hd %hd %hd %ld", &code, &req, node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
int n = sscanf(line, "%d %d %s %s %s port %s %d %d %d %d %x %"PRIx32" %s %s %d %hd %hd %hd %ld", &code, &req, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
if(n == 2)
break;
if(n != 18) {
if(n != 19) {
fprintf(stderr, "Unable to parse node dump from tincd.\n");
return 1;
}
@ -94,6 +96,7 @@ static int info_node(int fd, const char *item) {
}
printf("Node: %s\n", item);
printf("Node ID: %s\n", id);
printf("Address: %s port %s\n", host, port);
char timestr[32] = "never";

View file

@ -1,6 +1,6 @@
/*
invitation.c -- Create and accept invitations
Copyright (C) 2013 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2013-2014 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
@ -142,12 +142,19 @@ char *get_my_hostname() {
}
}
if(!tty) {
if(!hostname) {
fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n");
return NULL;
}
goto save;
}
again:
printf("Please enter your host's external address or hostname");
fprintf(stderr, "Please enter your host's external address or hostname");
if(hostname)
printf(" [%s]", hostname);
printf(": ");
fflush(stdout);
fprintf(stderr, " [%s]", hostname);
fprintf(stderr, ": ");
if(!fgets(line, sizeof line, stdin)) {
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
@ -190,8 +197,10 @@ done:
else
xasprintf(&hostport, "%s:%s", hostname, port);
} else {
hostport = hostname;
hostname = NULL;
if(strchr(hostname, ':'))
xasprintf(&hostport, "[%s]", hostname);
else
hostport = xstrdup(hostname);
}
free(hostname);
@ -241,7 +250,7 @@ int cmd_invite(int argc, char *argv[]) {
}
free(filename);
// If a daemon is running, ensure no other nodes now about this name
// If a daemon is running, ensure no other nodes know about this name
bool found = false;
if(connect_tincd(false)) {
sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
@ -312,7 +321,7 @@ int cmd_invite(int argc, char *argv[]) {
free(filename);
ecdsa_t *key;
xasprintf(&filename, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", confbase);
xasprintf(&filename, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
// Remove the key if there are no outstanding invitations.
if(!count)
@ -404,8 +413,12 @@ int cmd_invite(int argc, char *argv[]) {
char buf[1024];
while(fgets(buf, sizeof buf, tc)) {
if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
|| (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9])))
|| (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
fputs(buf, f);
// Make sure there is a newline character.
if(!strchr(buf, '\n'))
fputc('\n', f);
}
}
fclose(tc);
}
@ -567,7 +580,7 @@ make_names:
if(!access(tinc_conf, F_OK)) {
fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
if(!tty || confbasegiven)
if(confbasegiven)
return false;
// Generate a random netname, ask for a better one later.
@ -600,6 +613,7 @@ make_names:
FILE *fh = fopen(filename, "w");
if(!fh) {
fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
fclose(f);
return false;
}
@ -709,7 +723,7 @@ make_names:
if(!b64key)
return false;
xasprintf(&filename, "%s" SLASH "ecdsa_key.priv", confbase);
xasprintf(&filename, "%s" SLASH "ed25519_key.priv", confbase);
f = fopenmask(filename, "w", 0600);
if(!ecdsa_write_pem_private_key(key, f)) {
@ -721,7 +735,7 @@ make_names:
fclose(f);
fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
fprintf(fh, "Ed25519PublicKey = %s\n", b64key);
sptps_send_record(&sptps, 1, b64key, strlen(b64key));
free(b64key);
@ -743,7 +757,7 @@ make_names:
check_port(name);
ask_netname:
if(ask_netname) {
if(ask_netname && tty) {
fprintf(stderr, "Enter a new netname: ");
if(!fgets(line, sizeof line, stdin)) {
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
@ -767,11 +781,13 @@ ask_netname:
make_names();
}
fprintf(stderr, "Configuration stored in: %s\n", confbase);
return true;
}
static bool invitation_send(void *handle, uint8_t type, const char *data, size_t len) {
static bool invitation_send(void *handle, uint8_t type, const void *data, size_t len) {
while(len) {
int result = send(sock, data, len, 0);
if(result == -1 && errno == EINTR)
@ -784,7 +800,7 @@ static bool invitation_send(void *handle, uint8_t type, const char *data, size_t
return true;
}
static bool invitation_receive(void *handle, uint8_t type, const char *msg, uint16_t len) {
static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
switch(type) {
case SPTPS_HANDSHAKE:
return sptps_send_record(&sptps, 0, cookie, sizeof cookie);
@ -850,10 +866,8 @@ int cmd_join(int argc, char *argv[]) {
if(argc > 1) {
invitation = argv[1];
} else {
if(tty) {
printf("Enter invitation URL: ");
fflush(stdout);
}
if(tty)
fprintf(stderr, "Enter invitation URL: ");
errno = EPIPE;
if(!fgets(line, sizeof line, stdin)) {
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
@ -893,7 +907,7 @@ int cmd_join(int argc, char *argv[]) {
if(!port || !*port)
port = "655";
if(!b64decode(slash, hash, 18) || !b64decode(slash + 24, cookie, 18))
if(!b64decode(slash, hash, 24) || !b64decode(slash + 24, cookie, 24))
goto invalid;
// Generate a throw-away key for the invitation.

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Linux ethertap and tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2013 Guus Sliepen <guus@tinc-vpn.org>
2001-2014 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
@ -46,11 +46,6 @@ static char *type = NULL;
static char ifrname[IFNAMSIZ];
static char *device_info;
uint64_t device_in_packets = 0;
uint64_t device_in_bytes = 0;
uint64_t device_out_packets = 0;
uint64_t device_out_bytes = 0;
static bool setup_device(void) {
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
@ -110,15 +105,25 @@ static bool setup_device(void) {
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
if(ifr.ifr_flags & IFF_TAP) {
struct ifreq ifr_mac = {};
if(!ioctl(device_fd, SIOCGIFHWADDR, &ifr_mac))
memcpy(mymac.x, ifr_mac.ifr_hwaddr.sa_data, ETH_ALEN);
else
logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get MAC address of %s: %s", device, strerror(errno));
}
return true;
}
static void close_device(void) {
close(device_fd);
device_fd = -1;
free(type);
free(device);
free(iface);
free(type); type = NULL;
free(device); device = NULL;
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
@ -126,7 +131,7 @@ static bool read_packet(vpn_packet_t *packet) {
switch(device_type) {
case DEVICE_TYPE_TUN:
inlen = read(device_fd, packet->data + 10, MTU - 10);
inlen = read(device_fd, DATA(packet) + 10, MTU - 10);
if(inlen <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
@ -134,11 +139,11 @@ static bool read_packet(vpn_packet_t *packet) {
return false;
}
memset(packet->data, 0, 12);
memset(DATA(packet), 0, 12);
packet->len = inlen + 10;
break;
case DEVICE_TYPE_TAP:
inlen = read(device_fd, packet->data, MTU);
inlen = read(device_fd, DATA(packet), MTU);
if(inlen <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
@ -152,9 +157,6 @@ static bool read_packet(vpn_packet_t *packet) {
abort();
}
device_in_packets++;
device_in_bytes += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
@ -167,15 +169,15 @@ static bool write_packet(vpn_packet_t *packet) {
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) {
DATA(packet)[10] = DATA(packet)[11] = 0;
if(write(device_fd, DATA(packet) + 10, packet->len - 10) < 0) {
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) {
if(write(device_fd, DATA(packet), packet->len) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
@ -185,22 +187,12 @@ static bool write_packet(vpn_packet_t *packet) {
abort();
}
device_out_packets++;
device_out_bytes += 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_in_bytes);
logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -65,6 +65,8 @@ enum {
#endif
#endif
#include <stdbool.h>
extern debug_t debug_level;
extern bool logcontrol;
extern void openlogger(const char *, logmode_t);

View file

@ -1,6 +1,6 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2013 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2014 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
2006 Scott Lamb <slamb@slamb.org>
@ -30,7 +30,7 @@
#include "utils.h"
#include "xalloc.h"
bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t length) {
bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t length) {
connection_t *c = handle;
if(!c) {
@ -76,11 +76,12 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
void broadcast_meta(connection_t *from, const char *buffer, int length) {
for list_each(connection_t, c, connection_list)
if(c != from && c->status.active)
if(c != from && c->edge)
send_meta(c, buffer, length);
}
bool receive_meta_sptps(void *handle, uint8_t type, const char *data, uint16_t length) {
bool receive_meta_sptps(void *handle, uint8_t type, const void *vdata, uint16_t length) {
const char *data = vdata;
connection_t *c = handle;
if(!c) {
@ -142,7 +143,7 @@ bool receive_meta(connection_t *c) {
inlen = recv(c->socket, inbuf, sizeof inbuf - c->inbuf.len, 0);
if(inlen <= 0) {
if(!inlen || !errno) {
if(!inlen || !sockerrno) {
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)",
c->name, c->hostname);
} else if(sockwouldblock(sockerrno))

View file

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

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a MinGW environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2013 Guus Sliepen <guus@tinc-vpn.org>
2002-2014 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,55 +36,52 @@
int device_fd = -1;
static HANDLE device_handle = INVALID_HANDLE_VALUE;
static io_t device_read_io;
static OVERLAPPED device_read_overlapped;
static vpn_packet_t device_read_packet;
char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
extern char *myport;
static DWORD WINAPI tapreader(void *bla) {
static void device_issue_read() {
device_read_overlapped.Offset = 0;
device_read_overlapped.OffsetHigh = 0;
int status;
DWORD len;
OVERLAPPED overlapped;
vpn_packet_t packet;
logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader running");
/* Read from tap device and send to parent */
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
overlapped.Offset = 0;
overlapped.OffsetHigh = 0;
ResetEvent(overlapped.hEvent);
status = ReadFile(device_handle, (void *)packet.data, MTU, &len, &overlapped);
if(!status) {
if(GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(overlapped.hEvent, INFINITE);
if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
continue;
} else {
for (;;) {
DWORD len;
status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
if (!status) {
if (GetLastError() != ERROR_IO_PENDING)
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return -1;
}
break;
}
EnterCriticalSection(&mutex);
packet.len = len;
packet.priority = 0;
route(myself, &packet);
event_flush_output();
LeaveCriticalSection(&mutex);
device_read_packet.len = len;
device_read_packet.priority = 0;
route(myself, &device_read_packet);
}
}
static void device_handle_read(void *data, int flags) {
ResetEvent(device_read_overlapped.hEvent);
DWORD len;
if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
device, strerror(errno));
return;
}
device_read_packet.len = len;
device_read_packet.priority = 0;
route(myself, &device_read_packet);
device_issue_read();
}
static bool setup_device(void) {
HKEY key, key2;
int i;
@ -94,12 +91,10 @@ static bool setup_device(void) {
char adaptername[1024];
char tapname[1024];
DWORD len;
unsigned long status;
bool found = false;
int err;
HANDLE thread;
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
@ -191,20 +186,6 @@ static bool setup_device(void) {
overwrite_mac = 1;
}
/* Start the tap reader */
thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
if(!thread) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
return false;
}
/* Set media status for newer TAP-Win32 devices */
status = true;
DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
device_info = "Windows tap device";
logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
@ -212,11 +193,36 @@ static bool setup_device(void) {
return true;
}
static void close_device(void) {
CloseHandle(device_handle);
static void enable_device(void) {
logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
free(device);
free(iface);
ULONG status = 1;
DWORD len;
DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
device_read_overlapped.hEvent = device_read_io.event;
device_issue_read();
}
static void disable_device(void) {
logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
io_del(&device_read_io);
CancelIo(device_handle);
CloseHandle(device_read_overlapped.hEvent);
ULONG status = 0;
DWORD len;
DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
}
static void close_device(void) {
CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
free(device); device = NULL;
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
@ -230,26 +236,19 @@ 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(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
if(!WriteFile(device_handle, DATA(packet), packet->len, &outlen, &overlapped)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
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 os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
.enable = enable_device,
.disable = disable_device,
};

View file

@ -31,9 +31,6 @@
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}};
@ -132,7 +129,7 @@ static bool setup_device(void) {
#endif
default:
logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %hx unsupported", ai->ai_family);
logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %x unsupported", ai->ai_family);
goto error;
}
@ -151,33 +148,33 @@ error:
}
static void close_device(void) {
close(device_fd);
close(device_fd); device_fd = -1;
free(device);
free(iface);
free(device); device = NULL;
free(iface); iface = NULL;
if(ai)
freeaddrinfo(ai);
if(ai) {
freeaddrinfo(ai); ai = NULL;
}
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
if((lenin = recv(device_fd, (void *)packet->data, MTU, 0)) <= 0) {
if((lenin = recv(device_fd, DATA(packet), MTU, 0)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
device, sockstrerror(sockerrno));
return false;
}
if(!memcmp(&ignore_src, packet->data + 6, sizeof ignore_src)) {
if(!memcmp(&ignore_src, DATA(packet) + 6, sizeof ignore_src)) {
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
return false;
}
packet->len = lenin;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
@ -188,45 +185,20 @@ 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, (void *)packet->data, packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
if(sendto(device_fd, DATA(packet), 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));
sockstrerror(sockerrno));
return false;
}
device_total_out += packet->len;
memcpy(&ignore_src, packet->data + 6, sizeof ignore_src);
memcpy(&ignore_src, DATA(packet) + 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

View file

@ -64,8 +64,6 @@ void make_names(void) {
else
xasprintf(&confbase, "%s", installdir);
}
if(!pidfilename)
xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
}
RegCloseKey(key);
}
@ -73,11 +71,26 @@ void make_names(void) {
if(!confdir)
confdir = xstrdup(CONFDIR SLASH "tinc");
if(!confbase) {
if(netname)
xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
else
xasprintf(&confbase, CONFDIR SLASH "tinc");
}
#ifdef HAVE_MINGW
if(!logfilename)
xasprintf(&logfilename, "%s" SLASH "log", confbase);
if(!pidfilename)
xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
#else
if(!logfilename)
xasprintf(&logfilename, LOCALSTATEDIR SLASH "log" SLASH "%s.log", identname);
if(!pidfilename)
xasprintf(&pidfilename, LOCALSTATEDIR SLASH "run" SLASH "%s.pid", identname);
#endif
if(!unixsocketname) {
int len = strlen(pidfilename);
@ -88,13 +101,6 @@ void make_names(void) {
else
strcpy(unixsocketname + len, ".socket");
}
if(!confbase) {
if(netname)
xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
else
xasprintf(&confbase, CONFDIR SLASH "tinc");
}
}
void free_names(void) {

View file

@ -36,6 +36,10 @@
#include "subnet.h"
#include "xalloc.h"
#ifdef HAVE_RESOLV_H
#include <resolv.h>
#endif
int contradicting_add_edge = 0;
int contradicting_del_edge = 0;
static int sleeptime = 10;
@ -93,8 +97,6 @@ void purge(void) {
void terminate_connection(connection_t *c, bool report) {
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
c->status.active = false;
if(c->node && c->node->connection == c)
c->node->connection = NULL;
@ -129,6 +131,12 @@ void terminate_connection(connection_t *c, bool report) {
if(outgoing)
do_outgoing_connection(outgoing);
#ifndef HAVE_MINGW
/* Clean up dead proxy processes */
while(waitpid(-1, NULL, WNOHANG) > 0);
#endif
}
/*
@ -145,7 +153,7 @@ static void timeout_handler(void *data) {
continue;
if(c->last_ping_time + pingtimeout <= now.tv_sec) {
if(c->status.active) {
if(c->edge) {
if(c->status.pinged) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now.tv_sec) {
@ -160,7 +168,7 @@ static void timeout_handler(void *data) {
else
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
}
terminate_connection(c, c->status.active);
terminate_connection(c, c->edge);
}
}
@ -194,11 +202,11 @@ static void periodic_handler(void *data) {
/* Count number of active connections */
int nc = 0;
for list_each(connection_t, c, connection_list) {
if(c->status.active && !c->status.control)
if(c->edge)
nc++;
}
if(nc < autoconnect) {
if(nc < 3) {
/* Not enough active connections, try to add one.
Choose a random node, if we don't have a connection to it,
and we are not already trying to make one, create an
@ -232,7 +240,7 @@ static void periodic_handler(void *data) {
}
break;
}
} else if(nc > autoconnect) {
} else if(nc > 3) {
/* Too many active connections, try to remove one.
Choose a random outgoing connection to a node
that has at least one other connection.
@ -241,7 +249,7 @@ static void periodic_handler(void *data) {
int i = 0;
for list_each(connection_t, c, connection_list) {
if(!c->status.active || c->status.control)
if(!c->edge)
continue;
if(i++ != r)
@ -253,12 +261,12 @@ static void periodic_handler(void *data) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
list_delete(outgoing_list, c->outgoing);
c->outgoing = NULL;
terminate_connection(c, c->status.active);
terminate_connection(c, c->edge);
break;
}
}
if(nc >= autoconnect) {
if(nc >= 3) {
/* If we have enough active connections,
remove any pending outgoing connections.
*/
@ -283,7 +291,7 @@ static void periodic_handler(void *data) {
void handle_meta_connection_data(connection_t *c) {
if (!receive_meta(c)) {
terminate_connection(c, c->status.active);
terminate_connection(c, c->edge);
return;
}
}
@ -303,6 +311,9 @@ static void sighup_handler(void *data) {
static void sigalrm_handler(void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
#ifdef HAVE_DECL_RES_INIT
res_init();
#endif
retry();
}
#endif
@ -334,11 +345,14 @@ int reload_configuration(void) {
if(strictsubnets) {
for splay_each(subnet_t, subnet, subnet_tree)
subnet->expires = 1;
if (subnet->owner)
subnet->expires = 1;
load_all_subnets();
for splay_each(subnet_t, subnet, subnet_tree) {
if (!subnet->owner)
continue;
if(subnet->expires == 1) {
send_del_subnet(everyone, subnet);
if(subnet->owner->status.reachable)
@ -402,7 +416,7 @@ int reload_configuration(void) {
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);
terminate_connection(c, c->edge);
}
free(fname);
}
@ -452,7 +466,7 @@ int main_loop(void) {
#endif
if(!event_loop()) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
return 1;
}

View file

@ -1,7 +1,7 @@
/*
net.h -- header for net.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 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,8 +32,8 @@
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#endif
/* 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)
/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + srcid + dstid + padding + HMAC + compressor overhead */
#define MAXSIZE (MTU + 4 + sizeof(node_id_t) + sizeof(node_id_t) + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)
/* 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)
@ -52,7 +52,12 @@ typedef struct ipv6_t {
uint16_t x[8];
} ipv6_t;
typedef struct node_id_t {
uint8_t x[6];
} node_id_t;
typedef short length_t;
typedef uint32_t seqno_t;
#define AF_UNKNOWN 255
@ -80,10 +85,16 @@ typedef union sockaddr_t {
#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
#endif
#define SEQNO(x) ((x)->data + (x)->offset - 4)
#define SRCID(x) ((node_id_t *)((x)->data + (x)->offset - 6))
#define DSTID(x) ((node_id_t *)((x)->data + (x)->offset - 12))
#define DATA(x) ((x)->data + (x)->offset)
#define DEFAULT_PACKET_OFFSET 12
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
length_t len; /* The actual number of valid bytes in the `data' field (including seqno or dstid/srcid) */
length_t offset; /* Offset in the buffer where the packet data starts (righter after seqno or dstid/srcid) */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
uint8_t data[MAXSIZE];
} vpn_packet_t;
@ -103,6 +114,7 @@ typedef struct listen_socket_t {
io_t tcp;
io_t udp;
sockaddr_t sa;
bool bindto;
} listen_socket_t;
#include "conf.h"
@ -125,7 +137,6 @@ extern int seconds_till_retry;
extern int addressfamily;
extern unsigned replaywin;
extern bool localdiscovery;
extern sockaddr_t localdiscovery_address;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
@ -136,7 +147,8 @@ extern int udp_sndbuf;
extern int max_connection_burst;
extern bool do_prune;
extern char *myport;
extern int autoconnect;
extern bool device_standby;
extern bool autoconnect;
extern bool disablebuggypeers;
extern int contradicting_add_edge;
extern int contradicting_del_edge;
@ -171,12 +183,14 @@ extern void handle_new_meta_connection(void *, int);
extern void handle_new_unix_connection(void *, int);
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 bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
extern bool receive_sptps_record(void *handle, uint8_t type, const void *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 void device_enable(void);
extern void device_disable(void);
extern bool setup_myself_reloadable(void);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *);
@ -199,8 +213,6 @@ extern void load_all_nodes(void);
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
#else
extern CRITICAL_SECTION mutex;
#endif
#endif /* __TINC_NET_H__ */

View file

@ -1,7 +1,7 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 Guus Sliepen <guus@tinc-vpn.org>
2010 Timothy Redaelli <timothy@redaelli.eu>
2010 Brandon Black <blblack@gmail.com>
@ -54,8 +54,7 @@ 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;
sockaddr_t localdiscovery_address;
bool localdiscovery = true;
#define MAX_SEQNO 1073741824
@ -140,19 +139,19 @@ static void send_mtu_probe_handler(void *data) {
len = 64;
vpn_packet_t packet;
memset(packet.data, 0, 14);
randomize(packet.data + 14, len - 14);
packet.offset = DEFAULT_PACKET_OFFSET;
memset(DATA(&packet), 0, 14);
randomize(DATA(&packet) + 14, len - 14);
packet.len = len;
if(i >= 4 && n->mtuprobes <= 10)
packet.priority = -1;
else
packet.priority = 0;
packet.priority = 0;
n->status.send_locally = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
send_udppacket(n, &packet);
}
n->status.send_locally = false;
n->probe_counter = 0;
gettimeofday(&n->probe_time, NULL);
@ -178,24 +177,24 @@ void send_mtu_probe(node_t *n) {
}
static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
if(!packet->data[0]) {
if(!DATA(packet)[0]) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe request %d from %s (%s)", packet->len, n->name, n->hostname);
/* It's a probe request, send back a reply */
/* Type 2 probe replies were introduced in protocol 17.3 */
if ((n->options >> 24) == 3) {
uint8_t* data = packet->data;
if ((n->options >> 24) >= 3) {
uint8_t *data = DATA(packet);
*data++ = 2;
uint16_t len16 = htons(len); memcpy(data, &len16, 2); data += 2;
struct timeval now;
gettimeofday(&now, NULL);
uint32_t sec = htonl(now.tv_sec); memcpy(data, &sec, 4); data += 4;
uint32_t usec = htonl(now.tv_usec); memcpy(data, &usec, 4); data += 4;
packet->len = data - packet->data;
packet->len -= 10;
} else {
/* Legacy protocol: n won't understand type 2 probe replies. */
packet->data[0] = 1;
DATA(packet)[0] = 1;
}
/* Temporarily set udp_confirmed, so that the reply is sent
@ -207,14 +206,14 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
n->status.udp_confirmed = udp_confirmed;
} else {
length_t probelen = len;
if (packet->data[0] == 2) {
if (DATA(packet)[0] == 2) {
if (len < 3)
logger(DEBUG_TRAFFIC, LOG_WARNING, "Received invalid (too short) MTU probe reply from %s (%s)", n->name, n->hostname);
else {
uint16_t probelen16; memcpy(&probelen16, packet->data + 1, 2); probelen = ntohs(probelen16);
uint16_t probelen16; memcpy(&probelen16, DATA(packet) + 1, 2); probelen = ntohs(probelen16);
}
}
logger(DEBUG_TRAFFIC, LOG_INFO, "Got type %d MTU probe reply %d from %s (%s)", packet->data[0], probelen, n->name, n->hostname);
logger(DEBUG_TRAFFIC, LOG_INFO, "Got type %d MTU probe reply %d from %s (%s)", DATA(packet)[0], probelen, n->name, n->hostname);
/* It's a valid reply: now we know bidirectional communication
is possible using the address and socket that the reply
@ -256,9 +255,9 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
timersub(&now, &n->probe_time, &diff);
struct timeval probe_timestamp = now;
if (packet->data[0] == 2 && packet->len >= 11) {
uint32_t sec; memcpy(&sec, packet->data + 3, 4);
uint32_t usec; memcpy(&usec, packet->data + 7, 4);
if (DATA(packet)[0] == 2 && packet->len >= 11) {
uint32_t sec; memcpy(&sec, DATA(packet) + 3, 4);
uint32_t usec; memcpy(&usec, DATA(packet) + 7, 4);
probe_timestamp.tv_sec = ntohl(sec);
probe_timestamp.tv_usec = ntohl(usec);
}
@ -350,20 +349,21 @@ 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);
return sptps_verify_datagram(&n->sptps, DATA(inpkt), inpkt->len);
if(!digest_active(n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest))
if(!digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
return false;
return digest_verify(n->indigest, &inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest));
return digest_verify(n->indigest, SEQNO(inpkt), inpkt->len - digest_length(n->indigest), DATA(inpkt) + inpkt->len - digest_length(n->indigest));
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
size_t outlen;
pkt1.offset = DEFAULT_PACKET_OFFSET;
pkt2.offset = DEFAULT_PACKET_OFFSET;
if(n->status.sptps) {
if(!n->sptps.state) {
@ -373,43 +373,51 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
} else {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
}
return;
return false;
}
sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
return;
inpkt->offset += 2 * sizeof(node_id_t);
if(!sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len - 2 * sizeof(node_id_t))) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Got bad packet from %s (%s)", n->name, n->hostname);
return false;
}
return true;
}
if(!cipher_active(n->incipher)) {
if(!n->status.validkey) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
return;
return false;
}
/* Check packet length */
if(inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest)) {
if(inpkt->len < sizeof(seqno_t) + digest_length(n->indigest)) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got too short packet from %s (%s)",
n->name, n->hostname);
return;
return false;
}
/* It's a legacy UDP packet, the data starts after the seqno */
inpkt->offset += sizeof(seqno_t);
/* Check the message authentication code */
if(digest_active(n->indigest)) {
inpkt->len -= digest_length(n->indigest);
if(!digest_verify(n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
if(!digest_verify(n->indigest, SEQNO(inpkt), inpkt->len, SEQNO(inpkt) + inpkt->len)) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
return;
return false;
}
}
/* Decrypt the packet */
if(cipher_active(n->incipher)) {
outpkt = pkt[nextpkt++];
vpn_packet_t *outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_decrypt(n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_decrypt(n->incipher, SEQNO(inpkt), inpkt->len, SEQNO(outpkt), &outlen, true)) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
return;
return false;
}
outpkt->len = outlen;
@ -418,38 +426,40 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Check the sequence number */
inpkt->len -= sizeof inpkt->seqno;
inpkt->seqno = ntohl(inpkt->seqno);
seqno_t seqno;
memcpy(&seqno, SEQNO(inpkt), sizeof seqno);
seqno = ntohl(seqno);
inpkt->len -= sizeof seqno;
if(replaywin) {
if(inpkt->seqno != n->received_seqno + 1) {
if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
if(seqno != n->received_seqno + 1) {
if(seqno >= n->received_seqno + replaywin * 8) {
if(n->farfuture++ < replaywin >> 2) {
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;
n->name, n->hostname, seqno - n->received_seqno - 1, n->farfuture);
return false;
}
logger(DEBUG_ALWAYS, LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
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))) {
} else if (seqno <= n->received_seqno) {
if((n->received_seqno >= replaywin * 8 && seqno <= n->received_seqno - replaywin * 8) || !(n->late[(seqno / 8) % replaywin] & (1 << seqno % 8))) {
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;
n->name, n->hostname, seqno, n->received_seqno);
return false;
}
} else {
for(int i = n->received_seqno + 1; i < inpkt->seqno; i++)
for(int i = n->received_seqno + 1; i < seqno; i++)
n->late[(i / 8) % replaywin] |= 1 << i % 8;
}
}
n->farfuture = 0;
n->late[(inpkt->seqno / 8) % replaywin] &= ~(1 << inpkt->seqno % 8);
n->late[(seqno / 8) % replaywin] &= ~(1 << seqno % 8);
}
if(inpkt->seqno > n->received_seqno)
n->received_seqno = inpkt->seqno;
if(seqno > n->received_seqno)
n->received_seqno = seqno;
n->received++;
@ -461,12 +471,12 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
length_t origlen = inpkt->len;
if(n->incompression) {
outpkt = pkt[nextpkt++];
vpn_packet_t *outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
if((outpkt->len = uncompress_packet(DATA(outpkt), DATA(inpkt), inpkt->len, n->incompression)) < 0) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)",
n->name, n->hostname);
return;
return false;
}
inpkt = outpkt;
@ -476,16 +486,18 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
inpkt->priority = 0;
if(!inpkt->data[12] && !inpkt->data[13])
if(!DATA(inpkt)[12] && !DATA(inpkt)[13])
mtu_probe_h(n, inpkt, origlen);
else
receive_packet(n, inpkt);
return true;
}
void receive_tcppacket(connection_t *c, const char *buffer, int len) {
vpn_packet_t outpkt;
outpkt.offset = DEFAULT_PACKET_OFFSET;
if(len > sizeof outpkt.data)
if(len > sizeof outpkt.data - outpkt.offset)
return;
outpkt.len = len;
@ -493,30 +505,46 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
outpkt.priority = 0;
else
outpkt.priority = -1;
memcpy(outpkt.data, buffer, len);
memcpy(DATA(&outpkt), buffer, 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 < now.tv_sec) {
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;
static bool try_sptps(node_t *n) {
if(n->status.validkey)
return true;
/* If n is a TCP-only neighbor, we'll only use "cleartext" PACKET
messages anyway, so there's no need for SPTPS at all. */
if(n->connection && ((myself->options | n->options) & OPTION_TCPONLY))
return false;
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 < now.tv_sec) {
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 false;
}
static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
/* Note: condition order is as intended - even if we have a direct
metaconnection, we want to try SPTPS anyway as it's the only way to
get UDP going */
if(!try_sptps(n) && !n->connection)
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);
if(!(DATA(origpkt)[12] | DATA(origpkt)[13])) {
sptps_send_record(&n->sptps, PKT_PROBE, (char *)DATA(origpkt), origpkt->len);
return;
}
@ -531,7 +559,8 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
vpn_packet_t outpkt;
if(n->outcompression) {
int len = compress_packet(outpkt.data + offset, origpkt->data + offset, origpkt->len - offset, n->outcompression);
outpkt.offset = 0;
int len = compress_packet(DATA(&outpkt) + offset, DATA(origpkt) + 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) {
@ -541,10 +570,29 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
}
}
sptps_send_record(&n->sptps, type, (char *)origpkt->data + offset, origpkt->len - offset);
/* If we have a direct metaconnection to n, and we can't use UDP, then
don't bother with SPTPS and just use a "plaintext" PACKET message.
We don't really care about end-to-end security since we're not
sending the message through any intermediate nodes. */
if(n->connection && origpkt->len > n->minmtu)
send_tcppacket(n->connection, origpkt);
else
sptps_send_record(&n->sptps, type, DATA(origpkt) + offset, origpkt->len - offset);
return;
}
static void adapt_socket(const sockaddr_t *sa, int *sock) {
/* Make sure we have a suitable socket for the chosen address */
if(listen_socket[*sock].sa.sa.sa_family != sa->sa.sa_family) {
for(int i = 0; i < listen_sockets; i++) {
if(listen_socket[i].sa.sa.sa_family == sa->sa.sa_family) {
*sock = i;
break;
}
}
}
}
static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock) {
/* Latest guess */
*sa = &n->address;
@ -583,54 +631,30 @@ static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock
*sock = rand() % listen_sockets;
}
/* Make sure we have a suitable socket for the chosen address */
if(listen_socket[*sock].sa.sa.sa_family != (*sa)->sa.sa_family) {
for(int i = 0; i < listen_sockets; i++) {
if(listen_socket[i].sa.sa.sa_family == (*sa)->sa.sa_family) {
*sock = i;
break;
}
}
}
adapt_socket(*sa, sock);
}
static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int *sock) {
static sockaddr_t broadcast_ipv4 = {
.in = {
.sin_family = AF_INET,
.sin_addr.s_addr = -1,
}
};
static void choose_local_address(const node_t *n, const sockaddr_t **sa, int *sock) {
*sa = NULL;
static sockaddr_t broadcast_ipv6 = {
.in6 = {
.sin6_family = AF_INET6,
.sin6_addr.s6_addr[0x0] = 0xff,
.sin6_addr.s6_addr[0x1] = 0x02,
.sin6_addr.s6_addr[0xf] = 0x01,
}
};
/* Pick one of the edges from this node at random, then use its local address. */
*sock = rand() % listen_sockets;
int i = 0;
int j = rand() % n->edge_tree->count;
edge_t *candidate = NULL;
if(listen_socket[*sock].sa.sa.sa_family == AF_INET6) {
if(localdiscovery_address.sa.sa_family == AF_INET6) {
localdiscovery_address.in6.sin6_port = n->prevedge->address.in.sin_port;
*sa = &localdiscovery_address;
} else {
broadcast_ipv6.in6.sin6_port = n->prevedge->address.in.sin_port;
broadcast_ipv6.in6.sin6_scope_id = listen_socket[*sock].sa.in6.sin6_scope_id;
*sa = &broadcast_ipv6;
}
} else {
if(localdiscovery_address.sa.sa_family == AF_INET) {
localdiscovery_address.in.sin_port = n->prevedge->address.in.sin_port;
*sa = &localdiscovery_address;
} else {
broadcast_ipv4.in.sin_port = n->prevedge->address.in.sin_port;
*sa = &broadcast_ipv4;
for splay_each(edge_t, e, n->edge_tree) {
if(i++ == j) {
candidate = e;
break;
}
}
if (candidate && candidate->local_address.sa.sa_family) {
*sa = &candidate->local_address;
*sock = rand() % listen_sockets;
adapt_socket(*sa, sock);
}
}
static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
@ -643,8 +667,11 @@ 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;
#endif
int origpriority = origpkt->priority;
#endif
pkt1.offset = DEFAULT_PACKET_OFFSET;
pkt2.offset = DEFAULT_PACKET_OFFSET;
if(!n->status.reachable) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
@ -671,7 +698,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
return;
}
if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (DATA(inpkt)[12] | DATA(inpkt)[13])) {
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");
@ -689,7 +716,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
if(n->outcompression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
if((outpkt->len = compress_packet(DATA(outpkt), DATA(inpkt), inpkt->len, n->outcompression)) < 0) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)",
n->name, n->hostname);
return;
@ -700,8 +727,9 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add sequence number */
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof inpkt->seqno;
seqno_t seqno = htonl(++(n->sent_seqno));
memcpy(SEQNO(inpkt), &seqno, sizeof seqno);
inpkt->len += sizeof seqno;
/* Encrypt the packet */
@ -709,7 +737,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_encrypt(n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_encrypt(n->outcipher, SEQNO(inpkt), inpkt->len, SEQNO(outpkt), &outlen, true)) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
goto end;
}
@ -721,7 +749,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add the message authentication code */
if(digest_active(n->outdigest)) {
if(!digest_create(n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len)) {
if(!digest_create(n->outdigest, SEQNO(inpkt), inpkt->len, SEQNO(inpkt) + inpkt->len)) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
goto end;
}
@ -731,14 +759,12 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Send the packet */
const sockaddr_t *sa;
const sockaddr_t *sa = NULL;
int sock;
/* Overloaded use of priority field: -1 means local broadcast */
if(origpriority == -1 && n->prevedge)
choose_broadcast_address(n, &sa, &sock);
else
if(n->status.send_locally)
choose_local_address(n, &sa, &sock);
if(!sa)
choose_udp_address(n, &sa, &sock);
#if defined(SOL_IP) && defined(IP_TOS)
@ -747,11 +773,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
priority = origpriority;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
if(setsockopt(listen_socket[n->sock].udp.fd, 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));
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
}
#endif
if(sendto(listen_socket[sock].udp.fd, (char *) &inpkt->seqno, inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sendto(listen_socket[sock].udp.fd, SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
@ -765,39 +791,67 @@ end:
origpkt->len = origlen;
}
bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
node_t *to = handle;
static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void *data, size_t len) {
node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop;
bool direct = from == myself && to == relay;
bool relay_supported = (relay->options >> 24) >= 4;
bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
/* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */
/* We don't really need the relay's key, but we need to establish a UDP tunnel with it and discover its MTU. */
if (!direct && relay_supported && !tcponly)
try_sptps(relay);
if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && len > to->minmtu)) {
/* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU.
TODO: When relaying, the original sender does not know the end-to-end PMTU (it only knows the PMTU of the first hop).
This can lead to scenarios where large packets are sent over UDP to relay, but then relay has no choice but fall back to TCP. */
if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->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) {
if(from == myself && !to->status.validkey) {
to->incompression = myself->incompression;
return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, to->incompression);
return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, from->name, to->name, buf, to->incompression);
} else {
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, REQ_SPTPS, buf);
}
}
/* Otherwise, send the packet via UDP */
const sockaddr_t *sa;
int sock;
choose_udp_address(to, &sa, &sock);
if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(to->maxmtu >= len)
to->maxmtu = len - 1;
if(to->mtu >= len)
to->mtu = len - 1;
size_t overhead = 0;
if(relay_supported) overhead += sizeof to->id + sizeof from->id;
char buf[len + overhead]; char* buf_ptr = buf;
if(relay_supported) {
if(direct) {
/* Inform the recipient that this packet was sent directly. */
node_id_t nullid = {};
memcpy(buf_ptr, &nullid, sizeof nullid); buf_ptr += sizeof nullid;
} else {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
}
memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id;
}
/* TODO: if this copy turns out to be a performance concern, change sptps_send_record() to add some "pre-padding" to the buffer and use that instead */
memcpy(buf_ptr, data, len); buf_ptr += len;
const sockaddr_t *sa = NULL;
int sock;
if(relay->status.send_locally)
choose_local_address(relay, &sa, &sock);
if(!sa)
choose_udp_address(relay, &sa, &sock);
logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname);
if(sendto(listen_socket[sock].udp.fd, buf, buf_ptr - buf, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
// Compensate for SPTPS overhead
len -= SPTPS_DATAGRAM_OVERHEAD;
if(relay->maxmtu >= len)
relay->maxmtu = len - 1;
if(relay->mtu >= len)
relay->mtu = len - 1;
} else {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", relay->name, relay->hostname, sockstrerror(sockerrno));
return false;
}
}
@ -805,7 +859,11 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
return true;
}
bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) {
bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
return send_sptps_data_priv(handle, myself, type, data, len);
}
bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
node_t *from = handle;
if(type == SPTPS_HANDSHAKE) {
@ -823,10 +881,11 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
}
vpn_packet_t inpkt;
inpkt.offset = DEFAULT_PACKET_OFFSET;
if(type == PKT_PROBE) {
inpkt.len = len;
memcpy(inpkt.data, data, len);
memcpy(DATA(&inpkt), data, len);
mtu_probe_h(from, &inpkt, len);
return true;
}
@ -846,7 +905,7 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
int offset = (type & PKT_MAC) ? 0 : 14;
if(type & PKT_COMPRESSED) {
length_t ulen = uncompress_packet(inpkt.data + offset, (const uint8_t *)data, len, from->incompression);
length_t ulen = uncompress_packet(DATA(&inpkt) + offset, (const uint8_t *)data, len, from->incompression);
if(ulen < 0) {
return false;
} else {
@ -855,25 +914,25 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
if(inpkt.len > MAXSIZE)
abort();
} else {
memcpy(inpkt.data + offset, data, len);
memcpy(DATA(&inpkt) + offset, data, len);
inpkt.len = len + offset;
}
/* Generate the Ethernet packet type if necessary */
if(offset) {
switch(inpkt.data[14] >> 4) {
switch(DATA(&inpkt)[14] >> 4) {
case 4:
inpkt.data[12] = 0x08;
inpkt.data[13] = 0x00;
DATA(&inpkt)[12] = 0x08;
DATA(&inpkt)[13] = 0x00;
break;
case 6:
inpkt.data[12] = 0x86;
inpkt.data[13] = 0xDD;
DATA(&inpkt)[12] = 0x86;
DATA(&inpkt)[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);
DATA(&inpkt)[14] >> 4, from->name, from->hostname);
return false;
}
}
@ -890,7 +949,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
if(n == myself) {
if(overwrite_mac)
memcpy(packet->data, mymac.x, ETH_ALEN);
memcpy(DATA(packet), mymac.x, ETH_ALEN);
n->out_packets++;
n->out_bytes += packet->len;
devops.write(packet);
@ -948,7 +1007,7 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
// 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)
if(c->edge && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
break;
@ -1002,12 +1061,14 @@ void handle_incoming_vpn_data(void *data, int flags) {
listen_socket_t *ls = data;
vpn_packet_t pkt;
char *hostname;
sockaddr_t from = {{0}};
socklen_t fromlen = sizeof from;
node_t *n;
int len;
node_id_t nullid = {};
sockaddr_t addr = {};
socklen_t addrlen = sizeof addr;
node_t *from, *to;
bool direct = false;
len = recvfrom(ls->udp.fd, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
pkt.offset = 0;
int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
if(len <= 0 || len > MAXSIZE) {
if(!sockwouldblock(sockerrno))
@ -1017,32 +1078,76 @@ void handle_incoming_vpn_data(void *data, int flags) {
pkt.len = len;
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
// Try to figure out who sent this packet.
node_t *n = lookup_node_udp(&addr);
if(!n) {
n = try_harder(&from, &pkt);
if(n)
update_node_udp(n, &from);
else if(debug_level >= DEBUG_PROTOCOL) {
hostname = sockaddr2hostname(&from);
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
free(hostname);
return;
// It might be from a 1.1 node, which might have a source ID in the packet.
pkt.offset = 2 * sizeof(node_id_t);
from = lookup_node_id(SRCID(&pkt));
if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) {
if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)))
n = from;
else
goto skip_harder;
}
else
return;
}
n->sock = ls - listen_socket;
if(!n) {
pkt.offset = 0;
n = try_harder(&addr, &pkt);
}
receive_udppacket(n, &pkt);
skip_harder:
if(!n) {
if(debug_level >= DEBUG_PROTOCOL) {
hostname = sockaddr2hostname(&addr);
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
free(hostname);
}
return;
}
if(n->status.sptps) {
pkt.offset = 2 * sizeof(node_id_t);
if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) {
direct = true;
from = n;
to = myself;
} else {
from = lookup_node_id(SRCID(&pkt));
to = lookup_node_id(DSTID(&pkt));
}
if(!from || !to) {
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
return;
}
if(to != myself) {
send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t));
return;
}
} else {
direct = true;
from = n;
}
pkt.offset = 0;
if(!receive_udppacket(from, &pkt))
return;
n->sock = ls - listen_socket;
if(direct && sockaddrcmp(&addr, &n->address))
update_node_udp(n, &addr);
}
void handle_device_data(void *data, int flags) {
vpn_packet_t packet;
packet.offset = DEFAULT_PACKET_OFFSET;
packet.priority = 0;
if(devops.read(&packet)) {

View file

@ -1,7 +1,7 @@
/*
net_setup.c -- Setup.
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2010 Brandon Black <blblack@gmail.com>
@ -44,15 +44,17 @@
#include "xalloc.h"
char *myport;
static char *myname;
static io_t device_io;
devops_t devops;
bool device_standby = false;
char *proxyhost;
char *proxyport;
char *proxyuser;
char *proxypass;
proxytype_t proxytype;
int autoconnect;
bool autoconnect;
bool disablebuggypeers;
char *scriptinterpreter;
@ -71,25 +73,23 @@ bool node_read_ecdsa_public_key(node_t *n) {
if(!read_host_config(config_tree, n->name))
goto exit;
/* First, check for simple ECDSAPublicKey statement */
/* First, check for simple Ed25519PublicKey statement */
if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
if(get_config_string(lookup_config(config_tree, "Ed25519PublicKey"), &p)) {
n->ecdsa = ecdsa_set_base64_public_key(p);
free(p);
goto exit;
}
/* Else, check for ECDSAPublicKeyFile statement and read it */
/* Else, check for Ed25519PublicKeyFile statement and read it */
if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &pubname))
if(!get_config_string(lookup_config(config_tree, "Ed25519PublicKeyFile"), &pubname))
xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
fp = fopen(pubname, "r");
if(!fp) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", pubname, strerror(errno));
if(!fp)
goto exit;
}
n->ecdsa = ecdsa_read_pem_public_key(fp);
fclose(fp);
@ -114,23 +114,23 @@ bool read_ecdsa_public_key(connection_t *c) {
return false;
}
/* First, check for simple ECDSAPublicKey statement */
/* First, check for simple Ed25519PublicKey statement */
if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
if(get_config_string(lookup_config(c->config_tree, "Ed25519PublicKey"), &p)) {
c->ecdsa = ecdsa_set_base64_public_key(p);
free(p);
return c->ecdsa;
}
/* Else, check for ECDSAPublicKeyFile statement and read it */
/* Else, check for Ed25519PublicKeyFile statement and read it */
if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
if(!get_config_string(lookup_config(c->config_tree, "Ed25519PublicKeyFile"), &fname))
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
fp = fopen(fname, "r");
if(!fp) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s",
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 public key file `%s': %s",
fname, strerror(errno));
free(fname);
return false;
@ -140,7 +140,7 @@ bool read_ecdsa_public_key(connection_t *c) {
fclose(fp);
if(!c->ecdsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Parsing ECDSA public key file `%s' failed.", fname);
logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
free(fname);
return c->ecdsa;
}
@ -189,15 +189,15 @@ static bool read_ecdsa_private_key(void) {
/* Check for PrivateKeyFile statement and read it */
if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
xasprintf(&fname, "%s" SLASH "ecdsa_key.priv", confbase);
if(!get_config_string(lookup_config(config_tree, "Ed25519PrivateKeyFile"), &fname))
xasprintf(&fname, "%s" SLASH "ed25519_key.priv", confbase);
fp = fopen(fname, "r");
if(!fp) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 private key file `%s': %s", fname, strerror(errno));
if(errno == ENOENT)
logger(DEBUG_ALWAYS, LOG_INFO, "Create an ECDSA keypair with `tinc -n %s generate-ecdsa-keys'.", netname ?: ".");
logger(DEBUG_ALWAYS, LOG_INFO, "Create an Ed25519 keypair with `tinc -n %s generate-ed25519-keys'.", netname ?: ".");
free(fname);
return false;
}
@ -206,20 +206,20 @@ static bool read_ecdsa_private_key(void) {
struct stat s;
if(fstat(fileno(fp), &s)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat Ed25519 private key file `%s': %s'", fname, strerror(errno));
free(fname);
return false;
}
if(s.st_mode & ~0100700)
logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname);
logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for Ed25519 private key file `%s'!", fname);
#endif
myself->connection->ecdsa = ecdsa_read_pem_private_key(fp);
fclose(fp);
if(!myself->connection->ecdsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
free(fname);
return myself->connection->ecdsa;
}
@ -233,7 +233,7 @@ static bool read_invitation_key(void) {
invitation_key = NULL;
}
xasprintf(&fname, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", confbase);
xasprintf(&fname, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
fp = fopen(fname, "r");
@ -241,7 +241,7 @@ static bool read_invitation_key(void) {
invitation_key = ecdsa_read_pem_private_key(fp);
fclose(fp);
if(!invitation_key)
logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
}
free(fname);
@ -277,6 +277,8 @@ static bool read_rsa_private_key(void) {
if(!fp) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
fname, strerror(errno));
if(errno == ENOENT)
logger(DEBUG_ALWAYS, LOG_INFO, "Create an RSA keypair with `tinc -n %s generate-rsa-keys'.", netname ?: ".");
free(fname);
return false;
}
@ -403,40 +405,16 @@ void load_all_nodes(void) {
char *get_name(void) {
char *name = NULL;
char *returned_name;
get_config_string(lookup_config(config_tree, "Name"), &name);
if(!name)
return NULL;
if(*name == '$') {
char *envname = getenv(name + 1);
if(!envname) {
if(strcmp(name + 1, "HOST")) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
return false;
}
char envname[32];
if(gethostname(envname, 32)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", strerror(errno));
return false;
}
envname[31] = 0;
}
free(name);
name = xstrdup(envname);
for(char *c = name; *c; c++)
if(!isalnum(*c))
*c = '_';
}
if(!check_id(name)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
free(name);
return false;
}
return name;
returned_name = replace_name(name);
free(name);
return returned_name;
}
bool setup_myself_reloadable(void) {
@ -445,7 +423,6 @@ bool setup_myself_reloadable(void) {
char *fmode = NULL;
char *bmode = NULL;
char *afname = NULL;
char *address = NULL;
char *space;
bool choice;
@ -532,16 +509,6 @@ bool setup_myself_reloadable(void) {
get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
memset(&localdiscovery_address, 0, sizeof localdiscovery_address);
if(get_config_string(lookup_config(config_tree, "LocalDiscoveryAddress"), &address)) {
struct addrinfo *ai = str2addrinfo(address, myport, SOCK_DGRAM);
free(address);
if(!ai)
return false;
memcpy(&localdiscovery_address, ai->ai_addr, ai->ai_addrlen);
}
if(get_config_string(lookup_config(config_tree, "Mode"), &rmode)) {
if(!strcasecmp(rmode, "router"))
routing_mode = RMODE_ROUTER;
@ -596,6 +563,20 @@ bool setup_myself_reloadable(void) {
free(bmode);
}
const char* const DEFAULT_BROADCAST_SUBNETS[] = { "ff:ff:ff:ff:ff:ff", "255.255.255.255", "224.0.0.0/4", "ff00::/8" };
for (size_t i = 0; i < sizeof(DEFAULT_BROADCAST_SUBNETS) / sizeof(*DEFAULT_BROADCAST_SUBNETS); i++) {
subnet_t *s = new_subnet();
if (!str2net(s, DEFAULT_BROADCAST_SUBNETS[i]))
abort();
subnet_add(NULL, s);
}
for (config_t* cfg = lookup_config(config_tree, "BroadcastSubnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
subnet_t *s;
if (!get_config_subnet(cfg, &s))
continue;
subnet_add(NULL, s);
}
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
@ -631,7 +612,15 @@ bool setup_myself_reloadable(void) {
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect);
config_t *cfg = lookup_config(config_tree, "AutoConnect");
if(cfg) {
if(!get_config_bool(cfg, &autoconnect)) {
// Some backwards compatibility with when this option was an int
int val = 0;
get_config_int(cfg, &val);
autoconnect = val;
}
}
get_config_bool(lookup_config(config_tree, "DisableBuggyPeers"), &disablebuggypeers);
@ -640,18 +629,133 @@ bool setup_myself_reloadable(void) {
return true;
}
/*
Add listening sockets.
*/
static bool add_listen_address(char *address, bool bindto) {
char *port = myport;
if(address) {
char *space = strchr(address, ' ');
if(space) {
*space++ = 0;
port = space;
}
if(!strcmp(address, "*"))
*address = 0;
}
struct addrinfo *ai, hint = {0};
hint.ai_family = addressfamily;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE;
int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
free(address);
if(err || !ai) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
return false;
}
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
// Ignore duplicate addresses
bool found = false;
for(int i = 0; i < listen_sockets; i++)
if(!memcmp(&listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
found = true;
break;
}
if(found)
continue;
if(listen_sockets >= MAXSOCKETS) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
return false;
}
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0)
continue;
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0) {
close(tcp_fd);
continue;
}
io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
free(hostname);
}
listen_socket[listen_sockets].bindto = bindto;
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
}
freeaddrinfo(ai);
return true;
}
void device_enable(void) {
if (devops.enable)
devops.enable();
/* Run tinc-up script to further initialize the tap interface */
char *envp[5] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NAME=%s", myname);
execute_script("tinc-up", envp);
for(int i = 0; i < 4; i++)
free(envp[i]);
}
void device_disable(void) {
char *envp[5] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NAME=%s", myname);
execute_script("tinc-down", envp);
for(int i = 0; i < 4; i++)
free(envp[i]);
if (devops.disable)
devops.disable();
}
/*
Configure node_t myself and set up the local sockets (listen only)
*/
static bool setup_myself(void) {
char *name, *hostname, *cipher, *digest, *type;
char *address = NULL;
bool port_specified = false;
if(!(name = get_name())) {
logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
return false;
}
myname = xstrdup(name);
myself = new_node();
myself->connection = new_connection();
myself->name = name;
@ -660,9 +764,8 @@ static bool setup_myself(void) {
if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
myport = xstrdup("655");
xasprintf(&myself->hostname, "MYSELF port %s", myport);
myself->connection->hostname = xstrdup(myself->hostname);
else
port_specified = true;
myself->connection->options = 0;
myself->connection->protocol_major = PROT_MAJOR;
@ -670,13 +773,25 @@ static bool setup_myself(void) {
myself->options |= PROT_MINOR << 24;
get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental);
if(!get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental)) {
experimental = read_ecdsa_private_key();
if(!experimental)
logger(DEBUG_ALWAYS, LOG_WARNING, "Support for SPTPS disabled.");
} else {
if(experimental && !read_ecdsa_private_key())
return false;
}
if(experimental && !read_ecdsa_private_key())
return false;
if(!read_rsa_private_key()) {
if(experimental) {
logger(DEBUG_ALWAYS, LOG_WARNING, "Support for legacy protocol disabled.");
} else {
logger(DEBUG_ALWAYS, LOG_ERR, "No private keys available, cannot start tinc!");
return false;
}
}
if(!read_rsa_private_key())
return false;
/* Ensure myport is numeric */
if(!atoi(myport)) {
struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
@ -744,7 +859,9 @@ static bool setup_myself(void) {
if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
cipher = xstrdup("blowfish");
if(!(myself->incipher = cipher_open_by_name(cipher))) {
if(!strcasecmp(cipher, "none")) {
myself->incipher = NULL;
} else if(!(myself->incipher = cipher_open_by_name(cipher))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
return false;
}
@ -766,7 +883,9 @@ static bool setup_myself(void) {
if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
digest = xstrdup("sha1");
if(!(myself->indigest = digest_open_by_name(digest, maclength))) {
if(!strcasecmp(digest, "none")) {
myself->indigest = NULL;
} else if(!(myself->indigest = digest_open_by_name(digest, maclength))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
return false;
}
@ -822,6 +941,8 @@ static bool setup_myself(void) {
#endif
}
get_config_bool(lookup_config(config_tree, "DeviceStandby"), &device_standby);
if(!devops.setup())
return false;
@ -847,7 +968,7 @@ static bool setup_myself(void) {
for(int i = 0; i < listen_sockets; i++) {
salen = sizeof sa;
if(getsockname(i + 3, &sa.sa, &salen) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(sockerrno));
return false;
}
@ -872,73 +993,25 @@ static bool setup_myself(void) {
}
} else {
listen_sockets = 0;
config_t *cfg = lookup_config(config_tree, "BindToAddress");
int cfgs = 0;
do {
for(config_t *cfg = lookup_config(config_tree, "BindToAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
cfgs++;
get_config_string(cfg, &address);
if(cfg)
cfg = lookup_config_next(config_tree, cfg);
char *port = myport;
if(address) {
char *space = strchr(address, ' ');
if(space) {
*space++ = 0;
port = space;
}
if(!strcmp(address, "*"))
*address = 0;
}
struct addrinfo *ai, hint = {0};
hint.ai_family = addressfamily;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE;
int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
free(address);
if(err || !ai) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
if(!add_listen_address(address, true))
return false;
}
}
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
if(listen_sockets >= MAXSOCKETS) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
return false;
}
for(config_t *cfg = lookup_config(config_tree, "ListenAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
cfgs++;
get_config_string(cfg, &address);
if(!add_listen_address(address, false))
return false;
}
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0)
continue;
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0) {
close(tcp_fd);
continue;
}
io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
free(hostname);
}
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
}
freeaddrinfo(ai);
} while(cfg);
if(!cfgs)
if(!add_listen_address(address, NULL))
return false;
}
if(!listen_sockets) {
@ -946,6 +1019,24 @@ static bool setup_myself(void) {
return false;
}
/* If no Port option was specified, set myport to the port used by the first listening socket. */
if(!port_specified || atoi(myport) == 0) {
sockaddr_t sa;
socklen_t salen = sizeof sa;
if(!getsockname(listen_socket[0].udp.fd, &sa.sa, &salen)) {
free(myport);
sockaddr2str(&sa, NULL, &myport);
if(!myport)
myport = xstrdup("655");
}
}
xasprintf(&myself->hostname, "MYSELF port %s", myport);
myself->connection->hostname = xstrdup(myself->hostname);
/* Done. */
last_config_check = now.tv_sec;
return true;
@ -982,18 +1073,8 @@ bool setup_network(void) {
if(!init_control())
return false;
/* Run tinc-up script to further initialize the tap interface */
char *envp[5] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NAME=%s", myself->name);
execute_script("tinc-up", envp);
for(int i = 0; i < 4; i++)
free(envp[i]);
if (!device_standby)
device_enable();
/* Run subnet-up scripts for our own subnets */
@ -1016,7 +1097,8 @@ void close_network_connections(void) {
terminate_connection(c, false);
}
list_delete_list(outgoing_list);
if(outgoing_list)
list_delete_list(outgoing_list);
if(myself && myself->connection) {
subnet_update(myself, NULL, false);
@ -1031,28 +1113,27 @@ void close_network_connections(void) {
close(listen_socket[i].udp.fd);
}
char *envp[5] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NAME=%s", myself->name);
exit_requests();
exit_edges();
exit_subnets();
exit_nodes();
exit_connections();
execute_script("tinc-down", envp);
if (!device_standby)
device_disable();
if(myport) free(myport);
free(myport);
for(int i = 0; i < 4; i++)
free(envp[i]);
devops.close();
if (device_fd >= 0)
io_del(&device_io);
if (devops.close)
devops.close();
exit_control();
free(myname);
free(scriptextension);
free(scriptinterpreter);
return;
}

View file

@ -1,7 +1,7 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2009 Florian Forster <octo@verplant.org>
@ -103,7 +103,7 @@ static bool bind_to_interface(int sd) {
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
if(status) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(errno));
sockstrerror(sockerrno));
return false;
}
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
@ -116,7 +116,7 @@ static bool bind_to_interface(int sd) {
static bool bind_to_address(connection_t *c) {
int s = -1;
for(int i = 0; i < listen_sockets; i++) {
for(int i = 0; i < listen_sockets && listen_socket[i].bindto; i++) {
if(listen_socket[i].sa.sa.sa_family != c->address.sa.sa_family)
continue;
if(s >= 0)
@ -134,7 +134,7 @@ static bool bind_to_address(connection_t *c) {
sa.in6.sin6_port = 0;
if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Can't bind outgoing socket: %s", sockstrerror(sockerrno));
return false;
}
@ -179,7 +179,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
closesocket(nfd);
logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(sockerrno));
sockstrerror(sockerrno));
return -1;
}
#else
@ -247,10 +247,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
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(DEBUG_ALWAYS, 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, sockstrerror(sockerrno));
if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
logger(DEBUG_ALWAYS, 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, sockstrerror(sockerrno));
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
@ -271,8 +271,6 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
option = 1;
setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
}
#else
#warning No way to disable IPv4 fragmentation
#endif
#if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
@ -285,8 +283,6 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
option = 1;
setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
}
#else
#warning No way to disable IPv6 fragmentation
#endif
if (!bind_to_interface(nfd)) {
@ -334,7 +330,7 @@ static void do_outgoing_pipe(connection_t *c, char *command) {
int fd[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", strerror(errno));
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", sockstrerror(sockerrno));
return;
}
@ -383,16 +379,16 @@ static void handle_meta_write(connection_t *c) {
ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
if(outlen <= 0) {
if(!errno || errno == EPIPE) {
if(!sockerrno || sockerrno == 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));
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, sockstrerror(sockerrno));
}
terminate_connection(c, c->status.active);
terminate_connection(c, c->edge);
return;
}
@ -405,19 +401,38 @@ static void handle_meta_io(void *data, int flags) {
connection_t *c = data;
if(c->status.connecting) {
c->status.connecting = false;
/*
The event loop does not protect against spurious events. Verify that we are actually connected
by issuing an empty send() call.
int result;
socklen_t len = sizeof result;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
if(!result)
finish_connecting(c);
else {
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(result));
terminate_connection(c, false);
Note that the behavior of send() on potentially unconnected sockets differ between platforms:
+------------+-----------+-------------+-----------+
| Event | POSIX | Linux | Windows |
+------------+-----------+-------------+-----------+
| Spurious | ENOTCONN | EWOULDBLOCK | ENOTCONN |
| Failed | ENOTCONN | (cause) | ENOTCONN |
| Successful | (success) | (success) | (success) |
+------------+-----------+-------------+-----------+
*/
if (send(c->socket, NULL, 0, 0) != 0) {
if (sockwouldblock(sockerrno))
return;
int socket_error;
if (!socknotconn(sockerrno))
socket_error = sockerrno;
else {
socklen_t len = sizeof socket_error;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&socket_error, &len);
}
if (socket_error) {
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(socket_error));
terminate_connection(c, false);
}
return;
}
c->status.connecting = false;
finish_connecting(c);
}
if(flags & IO_WRITE)
@ -547,6 +562,39 @@ begin:
return true;
}
// Find edges pointing to this node, and use them to build a list of unique, known addresses.
static struct addrinfo *get_known_addresses(node_t *n) {
struct addrinfo *ai = NULL;
for splay_each(edge_t, e, n->edge_tree) {
if(!e->reverse)
continue;
bool found = false;
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
if(!sockaddrcmp(&e->reverse->address, (sockaddr_t *)aip->ai_addr)) {
found = true;
break;
}
}
if(found)
continue;
struct addrinfo *nai = xzalloc(sizeof *nai);
if(ai)
ai->ai_next = nai;
ai = nai;
ai->ai_family = e->reverse->address.sa.sa_family;
ai->ai_socktype = SOCK_STREAM;
ai->ai_protocol = IPPROTO_TCP;
ai->ai_addrlen = SALEN(e->reverse->address.sa);
ai->ai_addr = xmalloc(ai->ai_addrlen);
memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen);
}
return ai;
}
void setup_outgoing_connection(outgoing_t *outgoing) {
timeout_del(&outgoing->ev);
@ -564,8 +612,12 @@ void setup_outgoing_connection(outgoing_t *outgoing) {
outgoing->cfg = lookup_config(outgoing->config_tree, "Address");
if(!outgoing->cfg) {
logger(DEBUG_ALWAYS, LOG_ERR, "No address specified for %s", outgoing->name);
return;
if(n)
outgoing->aip = outgoing->ai = get_known_addresses(n);
if(!outgoing->ai) {
logger(DEBUG_ALWAYS, LOG_ERR, "No address known for %s", outgoing->name);
return;
}
}
do_outgoing_connection(outgoing);
@ -594,7 +646,6 @@ void handle_new_meta_connection(void *data, int flags) {
// Check if we get many connections from the same host
static sockaddr_t prev_sa;
static time_t prev_time;
static int tarpit = -1;
if(tarpit >= 0) {
@ -621,7 +672,6 @@ void handle_new_meta_connection(void *data, int flags) {
}
memcpy(&prev_sa, &sa, sizeof sa);
prev_time = now.tv_sec;
// Check if we get many connections from different hosts
@ -770,7 +820,7 @@ void try_outgoing_connections(void) {
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);
terminate_connection(c, c->edge);
}
}

View file

@ -30,8 +30,12 @@
#include "utils.h"
#include "xalloc.h"
static digest_t *sha256;
splay_tree_t *node_tree;
static splay_tree_t *node_id_tree;
static hash_t *node_udp_cache;
static hash_t *node_id_cache;
node_t *myself;
@ -39,14 +43,26 @@ static int node_compare(const node_t *a, const node_t *b) {
return strcmp(a->name, b->name);
}
static int node_id_compare(const node_t *a, const node_t *b) {
return memcmp(&a->id, &b->id, sizeof(node_id_t));
}
void init_nodes(void) {
sha256 = digest_open_by_name("sha256", sizeof(node_id_t));
node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
node_id_tree = splay_alloc_tree((splay_compare_t) node_id_compare, NULL);
node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t));
node_id_cache = hash_alloc(0x100, sizeof(node_id_t));
}
void exit_nodes(void) {
hash_free(node_id_cache);
hash_free(node_udp_cache);
splay_delete_tree(node_id_tree);
splay_delete_tree(node_tree);
digest_close(sha256);
}
node_t *new_node(void) {
@ -93,16 +109,23 @@ void free_node(node_t *n) {
}
void node_add(node_t *n) {
digest_create(sha256, n->name, strlen(n->name), &n->id);
splay_insert(node_tree, n);
splay_insert(node_id_tree, n);
}
void node_del(node_t *n) {
hash_delete(node_udp_cache, &n->address);
hash_delete(node_id_cache, &n->id);
for splay_each(subnet_t, s, n->subnet_tree)
subnet_del(n, s);
for splay_each(edge_t, e, n->edge_tree)
edge_del(e);
splay_delete(node_id_tree, n);
splay_delete(node_tree, n);
}
@ -114,6 +137,18 @@ node_t *lookup_node(char *name) {
return splay_search(node_tree, &n);
}
node_t *lookup_node_id(const node_id_t *id) {
node_t *n = hash_search(node_id_cache, id);
if(!n) {
node_t tmp = {.id = *id};
n = splay_search(node_id_tree, &tmp);
if(n)
hash_insert(node_id_cache, id, n);
}
return n;
}
node_t *lookup_node_udp(const sockaddr_t *sa) {
return hash_search(node_udp_cache, sa);
}
@ -124,7 +159,7 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
return;
}
hash_insert(node_udp_cache, &n->address, NULL);
hash_delete(node_udp_cache, &n->address);
if(sa) {
n->address = *sa;
@ -140,15 +175,27 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
n->hostname = sockaddr2hostname(&n->address);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
}
/* invalidate UDP information - note that this is a security feature as well to make sure
we can't be tricked into flooding any random address with UDP packets */
n->status.udp_confirmed = false;
n->mtuprobes = 0;
n->minmtu = 0;
n->maxmtu = MTU;
}
bool dump_nodes(connection_t *c) {
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),
for splay_each(node_t, n, node_tree) {
char id[2 * sizeof n->id + 1];
for (size_t c = 0; c < sizeof n->id; ++c)
sprintf(id + 2 * c, "%02hhx", n->id.x[c]);
id[sizeof id - 1] = 0;
send_request(c, "%d %d %s %s %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", CONTROL, REQ_DUMP_NODES,
n->name, id, 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, (long)n->last_state_change);
}
return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
}

View file

@ -37,11 +37,13 @@ typedef struct node_status_t {
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;
unsigned int send_locally:1; /* 1 if the next UDP packet should be sent on the local network */
unsigned int unused:23;
} node_status_t;
typedef struct node_t {
char *name; /* name of this node */
node_id_t id; /* unique node ID (name hash) */
uint32_t options; /* options turned on for this node */
int sock; /* Socket to use for outgoing UDP packets */
@ -110,6 +112,7 @@ extern void free_node(node_t *);
extern void node_add(node_t *);
extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_id(const node_id_t *);
extern node_t *lookup_node_udp(const sockaddr_t *);
extern bool dump_nodes(struct connection_t *);
extern bool dump_traffic(struct connection_t *);

View file

@ -30,15 +30,8 @@
struct cipher {
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher;
struct cipher_counter *counter;
};
typedef struct cipher_counter {
unsigned char counter[CIPHER_MAX_IV_SIZE];
unsigned char block[CIPHER_MAX_IV_SIZE];
int n;
} cipher_counter_t;
static cipher_t *cipher_open(const EVP_CIPHER *evp_cipher) {
cipher_t *cipher = xzalloc(sizeof *cipher);
cipher->cipher = evp_cipher;
@ -76,7 +69,6 @@ void cipher_close(cipher_t *cipher) {
return;
EVP_CIPHER_CTX_cleanup(&cipher->ctx);
free(cipher->counter);
free(cipher);
}
@ -84,7 +76,7 @@ size_t cipher_keylength(const cipher_t *cipher) {
if(!cipher || !cipher->cipher)
return 0;
return cipher->cipher->key_len + cipher->cipher->block_size;
return cipher->cipher->key_len + cipher->cipher->iv_len;
}
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
@ -117,70 +109,6 @@ bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encry
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 = xzalloc(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->block[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;

View file

@ -1,6 +1,6 @@
/*
crypto.c -- Cryptographic miscellaneous functions and initialisation
Copyright (C) 2007-2013 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2014 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
@ -25,8 +25,65 @@
#include "../crypto.h"
#ifndef HAVE_MINGW
static int random_fd = -1;
static void random_init(void) {
random_fd = open("/dev/urandom", O_RDONLY);
if(random_fd < 0)
random_fd = open("/dev/random", O_RDONLY);
if(random_fd < 0) {
fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno));
abort();
}
}
static void random_exit(void) {
close(random_fd);
}
void randomize(void *out, size_t outlen) {
while(outlen) {
size_t len = read(random_fd, out, outlen);
if(len <= 0) {
if(errno == EAGAIN || errno == EINTR)
continue;
fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno));
abort();
}
out += len;
outlen -= len;
}
}
#else
#include <wincrypt.h>
HCRYPTPROV prov;
void random_init(void) {
if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
fprintf(stderr, "CryptAcquireContext() failed!\n");
abort();
}
}
void random_exit(void) {
CryptReleaseContext(prov, 0);
}
void randomize(void *out, size_t outlen) {
if(!CryptGenRandom(prov, outlen, out)) {
fprintf(stderr, "CryptGenRandom() failed\n");
abort();
}
}
#endif
void crypto_init(void) {
RAND_load_file("/dev/urandom", 1024);
random_init();
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
@ -42,8 +99,7 @@ void crypto_init(void) {
void crypto_exit(void) {
EVP_cleanup();
}
void randomize(void *out, size_t outlen) {
RAND_pseudo_bytes(out, outlen);
ERR_free_strings();
ENGINE_cleanup();
random_exit();
}

View file

@ -1,96 +0,0 @@
/*
ecdh.c -- Diffie-Hellman key exchange handling
Copyright (C) 2011-2013 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/err.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
#define __TINC_ECDH_INTERNAL__
typedef EC_KEY ecdh_t;
#include "../ecdh.h"
#include "../logger.h"
#include "../utils.h"
#include "../xalloc.h"
ecdh_t *ecdh_generate_public(void *pubkey) {
ecdh_t *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);
logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
const EC_POINT *point = EC_KEY_get0_public_key(ecdh);
if(!point) {
EC_KEY_free(ecdh);
logger(DEBUG_ALWAYS, LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
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);
logger(DEBUG_ALWAYS, LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
return ecdh;
}
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));
EC_KEY_free(ecdh);
return false;
}
int result = EC_POINT_oct2point(EC_KEY_get0_group(ecdh), point, pubkey, ECDH_SIZE, NULL);
if(!result) {
EC_POINT_free(point);
EC_KEY_free(ecdh);
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);
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);
}

View file

@ -1,131 +0,0 @@
/*
ecdsa.c -- ECDSA key handling
Copyright (C) 2011-2013 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>
#define __TINC_ECDSA_INTERNAL__
typedef EC_KEY ecdsa_t;
#include "../logger.h"
#include "../ecdsa.h"
#include "../utils.h"
#include "../xalloc.h"
// Get and set ECDSA keys
//
ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
ecdsa_t *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 NULL;
}
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));
EC_KEY_free(ecdsa);
return NULL;
}
return ecdsa;
}
char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
unsigned char *pubkey = NULL;
int len = i2o_ECPublicKey(ecdsa, &pubkey);
char *base64 = xmalloc(len * 4 / 3 + 5);
b64encode((char *)pubkey, base64, len);
free(pubkey);
return base64;
}
// Read PEM ECDSA keys
ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
ecdsa_t *ecdsa = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL);
if(!ecdsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return ecdsa;
}
ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) {
ecdsa_t *ecdsa = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);
if(!ecdsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return ecdsa;
}
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);
}

View file

@ -61,8 +61,10 @@ rsa_t *rsa_set_hex_private_key(char *n, char *e, char *d) {
rsa_t *rsa_read_pem_public_key(FILE *fp) {
rsa_t *rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
if(!rsa)
if(!rsa) {
rewind(fp);
rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
}
if(!rsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));

View file

@ -34,6 +34,7 @@
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
#include "version.h"
/* If zero, don't detach from the terminal. */
bool do_detach = true;
@ -108,6 +109,8 @@ static bool install_service(void) {
return true;
}
io_t stop_io;
DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
switch(request) {
case SERVICE_CONTROL_INTERROGATE:
@ -124,10 +127,11 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
return ERROR_CALL_NOT_IMPLEMENTED;
}
event_exit();
status.dwWaitHint = 30000;
status.dwWaitHint = 1000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
if (WSASetEvent(stop_io.event) == FALSE)
abort();
return NO_ERROR;
}
@ -209,7 +213,7 @@ bool detach(void) {
openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
logger(DEBUG_ALWAYS, LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
VERSION, __DATE__, __TIME__, debug_level);
VERSION, BUILD_DATE, BUILD_TIME, debug_level);
return true;
}

View file

@ -29,6 +29,7 @@ extern bool detach(void);
extern bool kill_other(int);
#ifdef HAVE_MINGW
extern io_t stop_io;
extern bool init_service(void);
#endif

View file

@ -55,17 +55,6 @@ 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;
return true;
}
/* Generic request routines - takes care of logging and error
detection as well */

View file

@ -26,7 +26,7 @@
/* Protocol version. Different major versions are incompatible. */
#define PROT_MAJOR 17
#define PROT_MINOR 3 /* Should not exceed 255! */
#define PROT_MINOR 4 /* Should not exceed 255! */
/* Silly Windows */
@ -81,7 +81,6 @@ extern ecdsa_t *invitation_key;
extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
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);

View file

@ -1,7 +1,7 @@
/*
protocol_auth.c -- handle the meta-protocol, authentication
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 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
@ -172,7 +172,7 @@ static bool finalize_invitation(connection_t *c, const char *data, uint16_t len)
return false;
}
fprintf(f, "ECDSAPublicKey = %s\n", data);
fprintf(f, "Ed25519PublicKey = %s\n", data);
fclose(f);
logger(DEBUG_CONNECTIONS, LOG_INFO, "Key succesfully received from %s (%s)", c->name, c->hostname);
@ -198,7 +198,7 @@ static bool finalize_invitation(connection_t *c, const char *data, uint16_t len)
return true;
}
static bool receive_invitation_sptps(void *handle, uint8_t type, const char *data, uint16_t len) {
static bool receive_invitation_sptps(void *handle, uint8_t type, const void *data, uint16_t len) {
connection_t *c = handle;
if(type == 128)
@ -380,12 +380,13 @@ bool id_h(connection_t *c, const char *request) {
if(experimental)
read_ecdsa_public_key(c);
} else {
if(c->protocol_minor && !ecdsa_active(c->ecdsa))
c->protocol_minor = 1;
/* Ignore failures if no key known yet */
}
/* Forbid version rollback for nodes whose ECDSA key we know */
if(c->protocol_minor && !ecdsa_active(c->ecdsa))
c->protocol_minor = 1;
/* Forbid version rollback for nodes whose Ed25519 key we know */
if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) {
logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) tries to roll back protocol version to %d.%d",
@ -411,6 +412,11 @@ bool id_h(connection_t *c, const char *request) {
}
bool send_metakey(connection_t *c) {
if(!myself->connection->rsa) {
logger(DEBUG_CONNECTIONS, LOG_ERR, "Peer %s (%s) uses legacy protocol which we don't support", c->name, c->hostname);
return false;
}
if(!read_rsa_public_key(c))
return false;
@ -420,7 +426,7 @@ bool send_metakey(connection_t *c) {
if(!(c->outdigest = digest_open_sha1(-1)))
return false;
size_t len = rsa_size(c->rsa);
const size_t len = rsa_size(c->rsa);
char key[len];
char enckey[len];
char hexkey[2 * len + 1];
@ -477,9 +483,12 @@ bool send_metakey(connection_t *c) {
}
bool metakey_h(connection_t *c, const char *request) {
if(!myself->connection->rsa)
return false;
char hexkey[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
size_t len = rsa_size(myself->connection->rsa);
const size_t len = rsa_size(myself->connection->rsa);
char enckey[len];
char key[len];
@ -513,14 +522,22 @@ bool metakey_h(connection_t *c, const char *request) {
/* Check and lookup cipher and digest algorithms */
if(!(c->incipher = cipher_open_by_nid(cipher)) || !cipher_set_key_from_rsa(c->incipher, key, len, false)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
return false;
if(cipher) {
if(!(c->incipher = cipher_open_by_nid(cipher)) || !cipher_set_key_from_rsa(c->incipher, key, len, false)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
return false;
}
} else {
c->incipher = NULL;
}
if(!(c->indigest = digest_open_by_nid(digest, -1))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
return false;
if(digest) {
if(!(c->indigest = digest_open_by_nid(digest, -1))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
return false;
}
} else {
c->indigest = NULL;
}
c->status.decryptin = true;
@ -531,7 +548,7 @@ bool metakey_h(connection_t *c, const char *request) {
}
bool send_challenge(connection_t *c) {
size_t len = rsa_size(c->rsa);
const size_t len = rsa_size(c->rsa);
char buffer[len * 2 + 1];
if(!c->hischallenge)
@ -551,8 +568,11 @@ bool send_challenge(connection_t *c) {
}
bool challenge_h(connection_t *c, const char *request) {
if(!myself->connection->rsa)
return false;
char buffer[MAX_STRING_SIZE];
size_t len = rsa_size(myself->connection->rsa);
const size_t len = rsa_size(myself->connection->rsa);
size_t digestlen = digest_length(c->indigest);
char digest[digestlen];
@ -628,7 +648,7 @@ bool chal_reply_h(connection_t *c, const char *request) {
}
static bool send_upgrade(connection_t *c) {
/* Special case when protocol_minor is 1: the other end is ECDSA capable,
/* Special case when protocol_minor is 1: the other end is Ed25519 capable,
* but doesn't know our key yet. So send it now. */
char *pubkey = ecdsa_get_base64_public_key(myself->connection->ecdsa);
@ -672,7 +692,8 @@ bool send_ack(connection_t *c) {
if(choice)
c->options |= OPTION_CLAMP_MSS;
get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight);
if(!get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight))
get_config_int(lookup_config(config_tree, "Weight"), &c->estimated_weight);
return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, (c->options & 0xffffff) | (experimental ? (PROT_MINOR << 24) : 0));
}
@ -716,12 +737,26 @@ static bool upgrade_h(connection_t *c, const char *request) {
}
if(ecdsa_active(c->ecdsa) || read_ecdsa_public_key(c)) {
logger(DEBUG_ALWAYS, LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
char *knownkey = ecdsa_get_base64_public_key(c->ecdsa);
bool different = strcmp(knownkey, pubkey);
free(knownkey);
if(different) {
logger(DEBUG_ALWAYS, LOG_ERR, "Already have an Ed25519 public key from %s (%s) which is different from the one presented now!", c->name, c->hostname);
return false;
}
logger(DEBUG_ALWAYS, LOG_INFO, "Already have Ed25519 public key from %s (%s), ignoring.", c->name, c->hostname);
c->allow_request = TERMREQ;
return send_termreq(c);
}
c->ecdsa = ecdsa_set_base64_public_key(pubkey);
if(!c->ecdsa) {
logger(DEBUG_ALWAYS, LOG_INFO, "Got bad Ed25519 public key from %s (%s), not upgrading.", c->name, c->hostname);
return false;
}
logger(DEBUG_ALWAYS, LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
append_config_file(c->name, "ECDSAPublicKey", pubkey);
logger(DEBUG_ALWAYS, LOG_INFO, "Got Ed25519 public key from %s (%s), upgrading!", c->name, c->hostname);
append_config_file(c->name, "Ed25519PublicKey", pubkey);
c->allow_request = TERMREQ;
return send_termreq(c);
}
@ -795,7 +830,6 @@ bool ack_h(connection_t *c, const char *request) {
/* Activate this connection */
c->allow_request = ALL;
c->status.active = true;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
c->hostname);
@ -812,6 +846,16 @@ bool ack_h(connection_t *c, const char *request) {
sockaddr2str(&c->address, &hisaddress, NULL);
c->edge->address = str2sockaddr(hisaddress, hisport);
free(hisaddress);
sockaddr_t local_sa;
socklen_t local_salen = sizeof local_sa;
if (getsockname(c->socket, &local_sa.sa, &local_salen) < 0)
logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get local socket address for connection with %s", c->name);
else {
char *local_address;
sockaddr2str(&local_sa, &local_address, NULL);
c->edge->local_address = str2sockaddr(local_address, myport);
free(local_address);
}
c->edge->weight = (weight + c->estimated_weight) / 2;
c->edge->connection = c;
c->edge->options = c->options;

View file

@ -37,14 +37,19 @@
bool send_add_edge(connection_t *c, const edge_t *e) {
bool x;
char *address, *port;
char *local_address, *local_port;
sockaddr2str(&e->address, &address, &port);
sockaddr2str(&e->local_address, &local_address, &local_port);
x = send_request(c, "%d %x %s %s %s %s %x %d", ADD_EDGE, rand(),
x = send_request(c, "%d %x %s %s %s %s %x %d %s %s", ADD_EDGE, rand(),
e->from->name, e->to->name, address, port,
e->options, e->weight);
e->options, e->weight, local_address, local_port);
free(address);
free(port);
free(local_address);
free(local_port);
return x;
}
@ -56,12 +61,15 @@ bool add_edge_h(connection_t *c, const char *request) {
char to_name[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
char to_port[MAX_STRING_SIZE];
sockaddr_t address;
char address_local[MAX_STRING_SIZE] = "unknown";
char port_local[MAX_STRING_SIZE] = "unknown";
sockaddr_t address, local_address;
uint32_t options;
int weight;
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) {
int parameter_count = sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d "MAX_STRING" "MAX_STRING,
from_name, to_name, to_address, to_port, &options, &weight, address_local, port_local);
if (parameter_count != 6 && parameter_count != 8) {
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
c->hostname);
return false;
@ -109,13 +117,14 @@ bool add_edge_h(connection_t *c, const char *request) {
/* Convert addresses */
address = str2sockaddr(to_address, to_port);
local_address = str2sockaddr(address_local, port_local);
/* Check if edge already exists */
e = lookup_edge(from, to);
if(e) {
if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address) || sockaddrcmp(&e->local_address, &local_address)) {
if(from == myself) {
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
"ADD_EDGE", c->name, c->hostname);
@ -145,6 +154,7 @@ bool add_edge_h(connection_t *c, const char *request) {
e->from = from;
e->to = to;
e->address = address;
e->local_address = local_address;
e->options = options;
e->weight = weight;
edge_add(e);

View file

@ -1,7 +1,7 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 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 @@ void send_key_changed(void) {
/* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
for list_each(connection_t, c, connection_list)
if(c->status.active && c->node && c->node->status.reachable && !c->node->status.sptps)
if(c->edge && c->node && c->node->status.reachable && !c->node->status.sptps)
send_ans_key(c->node);
/* Force key exchange for connections using SPTPS */
@ -87,7 +87,7 @@ bool key_changed_h(connection_t *c, const char *request) {
return true;
}
static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
node_t *to = handle;
to->sptps.send_data = send_sptps_data;
char buf[len * 4 / 3 + 5];
@ -98,7 +98,7 @@ static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data
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);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No Ed25519 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;
}
@ -124,6 +124,11 @@ bool send_req_key(node_t *to) {
static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, int reqno) {
switch(reqno) {
case REQ_PUBKEY: {
if(!node_read_ecdsa_public_key(from)) {
/* Request their key *before* we send our key back. Otherwise the first SPTPS packet from them will get dropped. */
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Preemptively requesting Ed25519 key for %s (%s)", from->name, from->hostname);
send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, 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);
@ -142,14 +147,14 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
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);
logger(DEBUG_PROTOCOL, LOG_INFO, "Learned Ed25519 public key from %s (%s)", from->name, from->hostname);
append_config_file(from->name, "Ed25519PublicKey", 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);
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No Ed25519 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;
}
@ -250,6 +255,7 @@ bool req_key_h(connection_t *c, const char *request) {
return true;
}
/* TODO: forwarding SPTPS packets in this way is inefficient because we send them over TCP without checking for UDP connectivity */
send_request(to->nexthop->connection, "%s", request);
}
@ -260,25 +266,32 @@ bool send_ans_key(node_t *to) {
if(to->status.sptps)
abort();
size_t keylen = cipher_keylength(myself->incipher);
size_t keylen = myself->incipher ? cipher_keylength(myself->incipher) : 1;
char key[keylen * 2 + 1];
randomize(key, keylen);
cipher_close(to->incipher);
digest_close(to->indigest);
to->incipher = cipher_open_by_nid(cipher_get_nid(myself->incipher));
to->indigest = digest_open_by_nid(digest_get_nid(myself->indigest), digest_length(myself->indigest));
if(myself->incipher) {
to->incipher = cipher_open_by_nid(cipher_get_nid(myself->incipher));
if(!to->incipher)
abort();
if(!cipher_set_key(to->incipher, key, false))
abort();
}
if(myself->indigest) {
to->indigest = digest_open_by_nid(digest_get_nid(myself->indigest), digest_length(myself->indigest));
if(!to->indigest)
abort();
if(!digest_set_key(to->indigest, key, keylen))
abort();
}
to->incompression = myself->incompression;
if(!to->incipher || !to->indigest)
abort();
randomize(key, keylen);
if(!cipher_set_key(to->incipher, key, false))
abort();
if(!digest_set_key(to->indigest, key, keylen))
abort();
bin2hex(key, key, keylen);
// Reset sequence number and late packet window
@ -386,7 +399,9 @@ bool ans_key_h(connection_t *c, const char *request) {
update_node_udp(from, &sa);
}
if(from->options & OPTION_PMTU_DISCOVERY && !(from->options & OPTION_TCPONLY))
/* Don't send probes if we can't send UDP packets directly to that node.
TODO: the indirect (via) condition can change at any time as edges are added and removed, so this should probably be moved to graph.c. */
if((from->via == myself || from->via == from) && from->options & OPTION_PMTU_DISCOVERY && !(from->options & OPTION_TCPONLY))
send_mtu_probe(from);
}
@ -395,14 +410,22 @@ bool ans_key_h(connection_t *c, const char *request) {
/* Check and lookup cipher and digest algorithms */
if(!(from->outcipher = cipher_open_by_nid(cipher))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
return false;
if(cipher) {
if(!(from->outcipher = cipher_open_by_nid(cipher))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
return false;
}
} else {
from->outcipher = NULL;
}
if(!(from->outdigest = digest_open_by_nid(digest, maclength))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
return false;
if(digest) {
if(!(from->outdigest = digest_open_by_nid(digest, maclength))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
return false;
}
} else {
from->outdigest = NULL;
}
if(maclength != digest_length(from->outdigest)) {
@ -414,16 +437,16 @@ bool ans_key_h(connection_t *c, const char *request) {
keylen = hex2bin(key, key, sizeof key);
if(keylen != cipher_keylength(from->outcipher)) {
if(keylen != (from->outcipher ? cipher_keylength(from->outcipher) : 1)) {
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 */
if(!cipher_set_key(from->outcipher, key, true))
if(from->outcipher && !cipher_set_key(from->outcipher, key, true))
return false;
if(!digest_set_key(from->outdigest, key, keylen))
if(from->outdigest && !digest_set_key(from->outdigest, key, keylen))
return false;
from->status.validkey = true;

View file

@ -131,7 +131,7 @@ bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
if(!send_request(c, "%d %hd", PACKET, packet->len))
return false;
return send_meta(c, (char *)packet->data, packet->len);
return send_meta(c, (char *)DATA(packet), packet->len);
}
bool tcppacket_h(connection_t *c, const char *request) {

View file

@ -32,12 +32,9 @@
#include "route.h"
#include "xalloc.h"
#if defined(PF_PACKET) && defined(ETH_P_ALL) && defined(AF_PACKET)
#if defined(PF_PACKET) && defined(ETH_P_ALL) && defined(AF_PACKET) && defined(SIOCGIFINDEX)
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
struct ifreq ifr;
struct sockaddr_ll sa;
@ -86,16 +83,17 @@ static bool setup_device(void) {
}
static void close_device(void) {
close(device_fd);
close(device_fd); device_fd = -1;
free(device);
free(iface);
free(device); device = NULL;
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
@ -103,8 +101,6 @@ static bool read_packet(vpn_packet_t *packet) {
packet->len = inlen;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
@ -115,29 +111,20 @@ 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) {
if(write(device_fd, DATA(packet), packet->len) < 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;
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 raw_socket_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};
#else
@ -152,6 +139,5 @@ const devops_t raw_socket_devops = {
.close = NULL,
.read = NULL,
.write = NULL,
.dump_stats = NULL,
};
#endif

View file

@ -115,16 +115,16 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
/* Find TCP header */
int start = ether_size;
uint16_t type = packet->data[12] << 8 | packet->data[13];
uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
if(type == ETH_P_8021Q) {
start += 4;
type = packet->data[16] << 8 | packet->data[17];
type = DATA(packet)[16] << 8 | DATA(packet)[17];
}
if(type == ETH_P_IP && packet->data[start + 9] == 6)
start += (packet->data[start] & 0xf) * 4;
else if(type == ETH_P_IPV6 && packet->data[start + 6] == 6)
if(type == ETH_P_IP && DATA(packet)[start + 9] == 6)
start += (DATA(packet)[start] & 0xf) * 4;
else if(type == ETH_P_IPV6 && DATA(packet)[start + 6] == 6)
start += 40;
else
return;
@ -133,38 +133,38 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
return;
/* Use data offset field to calculate length of options field */
int len = ((packet->data[start + 12] >> 4) - 5) * 4;
int len = ((DATA(packet)[start + 12] >> 4) - 5) * 4;
if(packet->len < start + 20 + len)
return;
/* Search for MSS option header */
for(int i = 0; i < len;) {
if(packet->data[start + 20 + i] == 0)
if(DATA(packet)[start + 20 + i] == 0)
break;
if(packet->data[start + 20 + i] == 1) {
if(DATA(packet)[start + 20 + i] == 1) {
i++;
continue;
}
if(i > len - 2 || i > len - packet->data[start + 21 + i])
if(i > len - 2 || i > len - DATA(packet)[start + 21 + i])
break;
if(packet->data[start + 20 + i] != 2) {
if(packet->data[start + 21 + i] < 2)
if(DATA(packet)[start + 20 + i] != 2) {
if(DATA(packet)[start + 21 + i] < 2)
break;
i += packet->data[start + 21 + i];
i += DATA(packet)[start + 21 + i];
continue;
}
if(packet->data[start + 21] != 4)
if(DATA(packet)[start + 21] != 4)
break;
/* Found it */
uint16_t oldmss = packet->data[start + 22 + i] << 8 | packet->data[start + 23 + i];
uint16_t oldmss = DATA(packet)[start + 22 + i] << 8 | DATA(packet)[start + 23 + i];
uint16_t newmss = mtu - start - 20;
uint16_t csum = packet->data[start + 16] << 8 | packet->data[start + 17];
uint16_t csum = DATA(packet)[start + 16] << 8 | DATA(packet)[start + 17];
if(oldmss <= newmss)
break;
@ -172,23 +172,23 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
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;
packet->data[start + 23 + i] = newmss & 0xff;
DATA(packet)[start + 22 + i] = newmss >> 8;
DATA(packet)[start + 23 + i] = newmss & 0xff;
csum ^= 0xffff;
csum -= oldmss;
csum += newmss;
csum ^= 0xffff;
packet->data[start + 16] = csum >> 8;
packet->data[start + 17] = csum & 0xff;
DATA(packet)[start + 16] = csum >> 8;
DATA(packet)[start + 17] = csum & 0xff;
break;
}
}
static void swap_mac_addresses(vpn_packet_t *packet) {
mac_t tmp;
memcpy(&tmp, &packet->data[0], sizeof tmp);
memcpy(&packet->data[0], &packet->data[6], sizeof tmp);
memcpy(&packet->data[6], &tmp, sizeof tmp);
memcpy(&tmp, &DATA(packet)[0], sizeof tmp);
memcpy(&DATA(packet)[0], &DATA(packet)[6], sizeof tmp);
memcpy(&DATA(packet)[6], &tmp, sizeof tmp);
}
static void age_subnets(void *data) {
@ -203,7 +203,7 @@ static void age_subnets(void *data) {
}
for list_each(connection_t, c, connection_list)
if(c->status.active)
if(c->edge)
send_del_subnet(c, s);
subnet_del(myself, s);
@ -223,7 +223,7 @@ static void learn_mac(mac_t *address) {
/* If we don't know this MAC address yet, store it */
if(!subnet) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx",
logger(DEBUG_TRAFFIC, LOG_INFO, "Learned new MAC address %x:%x:%x:%x:%x:%x",
address->x[0], address->x[1], address->x[2], address->x[3],
address->x[4], address->x[5]);
@ -238,7 +238,7 @@ static void learn_mac(mac_t *address) {
/* And tell all other tinc daemons it's our MAC */
for list_each(connection_t, c, connection_list)
if(c->status.active)
if(c->edge)
send_add_subnet(c, subnet);
timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
@ -267,7 +267,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
/* Copy headers from packet into properly aligned structs on the stack */
memcpy(&ip, packet->data + ether_size, ip_size);
memcpy(&ip, DATA(packet) + ether_size, ip_size);
/* Remember original source and destination */
@ -284,7 +284,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
/* Copy first part of original contents to ICMP message */
memmove(packet->data + ether_size + ip_size + icmp_size, packet->data + ether_size, oldlen);
memmove(DATA(packet) + ether_size + ip_size + icmp_size, DATA(packet) + ether_size, oldlen);
/* Fill in IPv4 header */
@ -309,12 +309,12 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
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);
icmp.icmp_cksum = inet_checksum(DATA(packet) + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
/* Copy structs on stack back to packet */
memcpy(packet->data + ether_size, &ip, ip_size);
memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size);
memcpy(DATA(packet) + ether_size, &ip, ip_size);
memcpy(DATA(packet) + ether_size + ip_size, &icmp, icmp_size);
packet->len = ether_size + ip_size + icmp_size + oldlen;
@ -330,8 +330,9 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
uint8_t *offset;
uint16_t ip_off, origf;
memcpy(&ip, packet->data + ether_size, ip_size);
memcpy(&ip, DATA(packet) + ether_size, ip_size);
fragment.priority = packet->priority;
fragment.offset = DEFAULT_PACKET_OFFSET;
if(ip.ip_hl != ip_size / 4)
return;
@ -345,7 +346,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
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;
offset = DATA(packet) + ether_size + ip_size;
maxlen = (dest->mtu - ether_size - ip_size) & ~0x7;
ip_off = ntohs(ip.ip_off);
origf = ip_off & ~IP_OFFMASK;
@ -353,7 +354,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
while(todo) {
len = todo > maxlen ? maxlen : todo;
memcpy(fragment.data + ether_size + ip_size, offset, len);
memcpy(DATA(&fragment) + ether_size + ip_size, offset, len);
todo -= len;
offset += len;
@ -361,8 +362,8 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
ip.ip_off = htons(ip_off | origf | (todo ? IP_MF : 0));
ip.ip_sum = 0;
ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
memcpy(fragment.data, packet->data, ether_size);
memcpy(fragment.data + ether_size, &ip, ip_size);
memcpy(DATA(&fragment), DATA(packet), ether_size);
memcpy(DATA(&fragment) + ether_size, &ip, ip_size);
fragment.len = ether_size + ip_size + len;
send_packet(dest, &fragment);
@ -371,12 +372,15 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
}
}
static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
static void route_ipv4(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size + ip_size))
return;
subnet_t *subnet;
node_t *via;
ipv4_t dest;
memcpy(&dest, &packet->data[30], sizeof dest);
memcpy(&dest, &DATA(packet)[30], sizeof dest);
subnet = lookup_subnet_ipv4(&dest);
if(!subnet) {
@ -391,6 +395,11 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
return;
}
if (!subnet->owner) {
broadcast_packet(source, packet);
return;
}
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
@ -403,7 +412,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
if(priorityinheritance)
packet->priority = packet->data[15];
packet->priority = DATA(packet)[15];
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
@ -417,7 +426,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
if(via && packet->len > MAX(via->mtu, 590) && via != myself) {
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) {
if(DATA(packet)[20] & 0x40) {
packet->len = MAX(via->mtu, 590);
route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
} else {
@ -432,20 +441,6 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
send_packet(subnet->owner, packet);
}
static void route_ipv4(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size + ip_size))
return;
if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
packet->data[30] == 255 &&
packet->data[31] == 255 &&
packet->data[32] == 255 &&
packet->data[33] == 255)))
broadcast_packet(source, packet);
else
route_ipv4_unicast(source, packet);
}
/* RFC 2463 */
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
@ -469,7 +464,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
/* Copy headers from packet to structs on the stack */
memcpy(&ip6, packet->data + ether_size, ip6_size);
memcpy(&ip6, DATA(packet) + ether_size, ip6_size);
/* Remember original source and destination */
@ -486,7 +481,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
/* Copy first part of original contents to ICMP message */
memmove(packet->data + ether_size + ip6_size + icmp6_size, packet->data + ether_size, pseudo.length);
memmove(DATA(packet) + ether_size + ip6_size + icmp6_size, DATA(packet) + ether_size, pseudo.length);
/* Fill in IPv6 header */
@ -512,26 +507,36 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
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);
checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
icmp6.icmp6_cksum = checksum;
/* Copy structs on stack back to packet */
memcpy(packet->data + ether_size, &ip6, ip6_size);
memcpy(packet->data + ether_size + ip6_size, &icmp6, icmp6_size);
memcpy(DATA(packet) + ether_size, &ip6, ip6_size);
memcpy(DATA(packet) + ether_size + ip6_size, &icmp6, icmp6_size);
packet->len = ether_size + ip6_size + ntohl(pseudo.length);
send_packet(source, packet);
}
static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
static void route_neighborsol(node_t *source, vpn_packet_t *packet);
static void route_ipv6(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size + ip6_size))
return;
if(DATA(packet)[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && DATA(packet)[54] == ND_NEIGHBOR_SOLICIT) {
route_neighborsol(source, packet);
return;
}
subnet_t *subnet;
node_t *via;
ipv6_t dest;
memcpy(&dest, &packet->data[38], sizeof dest);
memcpy(&dest, &DATA(packet)[38], sizeof dest);
subnet = lookup_subnet_ipv6(&dest);
if(!subnet) {
@ -550,6 +555,11 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
return;
}
if (!subnet->owner) {
broadcast_packet(source, packet);
return;
}
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
@ -612,15 +622,15 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
/* Copy headers from packet to structs on the stack */
memcpy(&ip6, packet->data + ether_size, ip6_size);
memcpy(&ns, packet->data + ether_size + ip6_size, ns_size);
memcpy(&ip6, DATA(packet) + ether_size, ip6_size);
memcpy(&ns, DATA(packet) + ether_size + ip6_size, ns_size);
if(has_opt)
memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size);
memcpy(&opt, DATA(packet) + ether_size + ip6_size + ns_size, opt_size);
/* First, snatch the source address from the neighbor solicitation packet */
if(overwrite_mac)
memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
memcpy(mymac.x, DATA(packet) + ETH_ALEN, ETH_ALEN);
/* Check if this is a valid neighbor solicitation request */
@ -646,7 +656,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
checksum = inet_checksum(&ns, ns_size, checksum);
if(has_opt) {
checksum = inet_checksum(&opt, opt_size, checksum);
checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
}
if(checksum) {
@ -679,14 +689,14 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
/* 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(DATA(packet), DATA(packet) + ETH_ALEN, ETH_ALEN); /* copy destination address */
DATA(packet)[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */
ip6.ip6_src = ns.nd_ns_target;
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(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, DATA(packet) + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
ns.nd_ns_cksum = 0;
ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
@ -709,36 +719,21 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
checksum = inet_checksum(&ns, ns_size, checksum);
if(has_opt) {
checksum = inet_checksum(&opt, opt_size, checksum);
checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
}
ns.nd_ns_hdr.icmp6_cksum = checksum;
/* Copy structs on stack back to packet */
memcpy(packet->data + ether_size, &ip6, ip6_size);
memcpy(packet->data + ether_size + ip6_size, &ns, ns_size);
memcpy(DATA(packet) + ether_size, &ip6, ip6_size);
memcpy(DATA(packet) + ether_size + ip6_size, &ns, ns_size);
if(has_opt)
memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size);
memcpy(DATA(packet) + ether_size + ip6_size + ns_size, &opt, opt_size);
send_packet(source, packet);
}
static void route_ipv6(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ether_size + ip6_size))
return;
if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
route_neighborsol(source, packet);
return;
}
if(broadcast_mode && packet->data[38] == 255)
broadcast_packet(source, packet);
else
route_ipv6_unicast(source, packet);
}
/* RFC 826 */
static void route_arp(node_t *source, vpn_packet_t *packet) {
@ -757,11 +752,11 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
/* First, snatch the source address from the ARP packet */
if(overwrite_mac)
memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
memcpy(mymac.x, DATA(packet) + ETH_ALEN, ETH_ALEN);
/* Copy headers from packet to structs on the stack */
memcpy(&arp, packet->data + ether_size, arp_size);
memcpy(&arp, DATA(packet) + ether_size, arp_size);
/* Check if this is a valid ARP request */
@ -787,20 +782,20 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
if(subnet->owner == myself)
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(DATA(packet), DATA(packet) + ETH_ALEN, ETH_ALEN); /* copy destination address */
DATA(packet)[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */
memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */
memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */
memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
memcpy(arp.arp_sha, DATA(packet) + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
arp.arp_op = htons(ARPOP_REPLY);
/* Copy structs on stack back to packet */
memcpy(packet->data + ether_size, &arp, arp_size);
memcpy(DATA(packet) + ether_size, &arp, arp_size);
send_packet(source, packet);
}
@ -813,16 +808,16 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
if(source == myself) {
mac_t src;
memcpy(&src, &packet->data[6], sizeof src);
memcpy(&src, &DATA(packet)[6], sizeof src);
learn_mac(&src);
}
/* Lookup destination address */
memcpy(&dest, &packet->data[0], sizeof dest);
memcpy(&dest, &DATA(packet)[0], sizeof dest);
subnet = lookup_subnet_mac(NULL, &dest);
if(!subnet) {
if(!subnet || !subnet->owner) {
broadcast_packet(source, packet);
return;
}
@ -835,10 +830,10 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return;
uint16_t type = packet->data[12] << 8 | packet->data[13];
uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size)
packet->priority = packet->data[15];
packet->priority = DATA(packet)[15];
// Handle packets larger than PMTU
@ -852,12 +847,12 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
length_t ethlen = 14;
if(type == ETH_P_8021Q) {
type = packet->data[16] << 8 | packet->data[17];
type = DATA(packet)[16] << 8 | DATA(packet)[17];
ethlen += 4;
}
if(type == ETH_P_IP && packet->len > 576 + ethlen) {
if(packet->data[6 + ethlen] & 0x40) {
if(DATA(packet)[6 + ethlen] & 0x40) {
packet->len = via->mtu;
route_ipv4_unreachable(source, packet, ethlen, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
} else {
@ -889,16 +884,16 @@ static void send_pcap(vpn_packet_t *packet) {
len = c->outmaclength;
if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, len))
send_meta(c, (char *)packet->data, len);
send_meta(c, (char *)DATA(packet), len);
}
}
static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
uint16_t type = packet->data[12] << 8 | packet->data[13];
uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
length_t ethlen = ether_size;
if(type == ETH_P_8021Q) {
type = packet->data[16] << 8 | packet->data[17];
type = DATA(packet)[16] << 8 | DATA(packet)[17];
ethlen += 4;
}
@ -907,22 +902,22 @@ static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ethlen + ip_size))
return false;
if(packet->data[ethlen + 8] < 1) {
if(packet->data[ethlen + 11] != IPPROTO_ICMP || packet->data[ethlen + 32] != ICMP_TIME_EXCEEDED)
if(DATA(packet)[ethlen + 8] < 1) {
if(DATA(packet)[ethlen + 11] != IPPROTO_ICMP || DATA(packet)[ethlen + 32] != ICMP_TIME_EXCEEDED)
route_ipv4_unreachable(source, packet, ethlen, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
return false;
}
uint16_t old = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
packet->data[ethlen + 8]--;
uint16_t new = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
uint16_t old = DATA(packet)[ethlen + 8] << 8 | DATA(packet)[ethlen + 9];
DATA(packet)[ethlen + 8]--;
uint16_t new = DATA(packet)[ethlen + 8] << 8 | DATA(packet)[ethlen + 9];
uint32_t checksum = packet->data[ethlen + 10] << 8 | packet->data[ethlen + 11];
uint32_t checksum = DATA(packet)[ethlen + 10] << 8 | DATA(packet)[ethlen + 11];
checksum += old + (~new & 0xFFFF);
while(checksum >> 16)
checksum = (checksum & 0xFFFF) + (checksum >> 16);
packet->data[ethlen + 10] = checksum >> 8;
packet->data[ethlen + 11] = checksum & 0xff;
DATA(packet)[ethlen + 10] = checksum >> 8;
DATA(packet)[ethlen + 11] = checksum & 0xff;
return true;
@ -930,13 +925,13 @@ static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
if(!checklength(source, packet, ethlen + ip6_size))
return false;
if(packet->data[ethlen + 7] < 1) {
if(packet->data[ethlen + 6] != IPPROTO_ICMPV6 || packet->data[ethlen + 40] != ICMP6_TIME_EXCEEDED)
if(DATA(packet)[ethlen + 7] < 1) {
if(DATA(packet)[ethlen + 6] != IPPROTO_ICMPV6 || DATA(packet)[ethlen + 40] != ICMP6_TIME_EXCEEDED)
route_ipv6_unreachable(source, packet, ethlen, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
return false;
}
packet->data[ethlen + 7]--;
DATA(packet)[ethlen + 7]--;
return true;
@ -961,7 +956,7 @@ void route(node_t *source, vpn_packet_t *packet) {
if(!do_decrement_ttl(source, packet))
return;
uint16_t type = packet->data[12] << 8 | packet->data[13];
uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
switch (routing_mode) {
case RMODE_ROUTER:

View file

@ -49,7 +49,7 @@ bool execute_script(const char *name, char **envp) {
if(q) {
memcpy(ext, p, q - p);
ext[q - p] = 0;
*q++;
q++;
} else {
strcpy(ext, p);
}

View file

@ -1,7 +1,8 @@
/*
device.c -- Interaction with Solaris tun device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2013 Guus Sliepen <guus@tinc-vpn.org>
2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
2001-2014 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
@ -23,171 +24,350 @@
#include <sys/stropts.h>
#include <sys/sockio.h>
#include <net/if_tun.h>
#include "../conf.h"
#include "../device.h"
#include "../logger.h"
#include "../names.h"
#include "../net.h"
#include "../route.h"
#include "../utils.h"
#include "../xalloc.h"
#define DEFAULT_DEVICE "/dev/tun"
#ifndef TUNNEWPPA
#warning Missing net/if_tun.h, using hardcoded value for TUNNEWPPA
#define TUNNEWPPA (('T'<<16) | 0x0001)
#endif
#define DEFAULT_TUN_DEVICE "/dev/tun"
#define DEFAULT_TAP_DEVICE "/dev/tap"
static enum {
DEVICE_TYPE_TUN,
DEVICE_TYPE_TAP,
} device_type = DEVICE_TYPE_TUN;
int device_fd = -1;
static int ip_fd = -1, if_fd = -1;
static int ip_fd = -1;
char *device = NULL;
char *iface = NULL;
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
int ppa;
char *ptr;
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((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno));
if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
if(!strcasecmp(type, "tun"))
/* use default */;
else if(!strcasecmp(type, "tap"))
device_type = DEVICE_TYPE_TAP;
else {
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown device type %s!", type);
return false;
}
} else {
if(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
device_type = DEVICE_TYPE_TAP;
}
if(device_type == DEVICE_TYPE_TUN)
device_info = "Solaris tun device";
else
device_info = "Solaris tap device";
/* The following is black magic copied from OpenVPN. */
if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", "/dev/ip", strerror(errno));
return false;
}
if((device_fd = open(device, O_RDWR, 0)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
return false;
}
/* Get unit number. */
char *ptr = device;
get_config_string(lookup_config(config_tree, "Interface"), &ptr);
while(*ptr && !isdigit(*ptr))
ptr++;
int ppa = atoi(ptr);
/* Assign a new PPA and get its unit number. */
struct strioctl strioc_ppa = {
.ic_cmd = TUNNEWPPA,
.ic_len = sizeof ppa,
.ic_dp = (char *)&ppa,
};
if(!*ptr) { /* no number given, try dynamic */
bool found = false;
while(!found && ppa < 64) {
int new_ppa = ioctl(device_fd, I_STR, &strioc_ppa);
if(new_ppa >= 0) {
ppa = new_ppa;
found = true;
break;
}
ppa++;
}
if(!found) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not find free PPA for %s %s!", device_info, device);
return false;
}
} else { /* try this particular one */
if((ppa = ioctl(device_fd, I_STR, &strioc_ppa)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not assign PPA %d for %s %s!", ppa, device_info, device);
return false;
}
}
int if_fd;
if((if_fd = open(device, O_RDWR, 0)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
return false;
}
if(ioctl(if_fd, I_PUSH, "ip") < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not push IP module onto %s %s!", device_info, device);
return false;
}
xasprintf(&iface, "%s%d", device_type == DEVICE_TYPE_TUN ? "tun" : "tap", ppa);
{
/* Remove muxes just in case they are left over from a crashed tincd */
struct lifreq ifr = {};
strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name);
if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) {
int muxid = ifr.lifr_arp_muxid;
ioctl(ip_fd, I_PUNLINK, muxid);
muxid = ifr.lifr_ip_muxid;
ioctl(ip_fd, I_PUNLINK, muxid);
}
}
if(device_type == DEVICE_TYPE_TUN) {
/* Assign ppa according to the unit number returned by tun device */
if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device);
return false;
}
}
int arp_fd = -1;
if(device_type == DEVICE_TYPE_TAP) {
struct lifreq ifr = {};
if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set flags on %s %s!", device_info, device);
return false;
}
strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name));
ifr.lifr_ppa = ppa;
/* Assign ppa according to the unit number returned by tun device */
if(ioctl(if_fd, SIOCSLIFNAME, &ifr) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device);
return false;
}
if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set flags on %s %s!", device_info, device);
return false;
}
/* Push arp module to if_fd */
if(ioctl(if_fd, I_PUSH, "arp") < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device);
return false;
}
/* Pop any modules on the stream */
while(true) {
if(ioctl(ip_fd, I_POP, NULL) < 0)
break;
}
/* Push arp module to ip_fd */
if(ioctl(ip_fd, I_PUSH, "arp") < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s!", "/dev/ip");
return false;
}
/* Open arp_fd */
if((arp_fd = open(device, O_RDWR, 0)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno));
return false;
}
/* Push arp module to arp_fd */
if(ioctl(arp_fd, I_PUSH, "arp") < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device);
return false;
}
/* Set ifname to arp */
struct strioctl strioc_if = {
.ic_cmd = SIOCSLIFNAME,
.ic_len = sizeof ifr,
.ic_dp = (char *)&ifr,
};
if(ioctl(arp_fd, I_STR, &strioc_if) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set ifname to %s %s", device_info, device);
return false;
}
}
int ip_muxid, arp_muxid;
if((ip_muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not link %s %s to IP", device_info, device);
return false;
}
if(device_type == DEVICE_TYPE_TAP) {
if((arp_muxid = ioctl(ip_fd, I_PLINK, arp_fd)) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not link %s %s to ARP", device_info, device);
return false;
}
close(arp_fd);
}
struct lifreq ifr = {};
strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name));
ifr.lifr_ip_muxid = ip_muxid;
if(device_type == DEVICE_TYPE_TAP) {
ifr.lifr_arp_muxid = arp_muxid;
}
if(ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) {
if(device_type == DEVICE_TYPE_TAP) {
ioctl(ip_fd, I_PUNLINK, arp_muxid);
}
ioctl(ip_fd, I_PUNLINK, ip_muxid);
logger(DEBUG_ALWAYS, LOG_ERR, "Could not set multiplexor id for %s %s", device_info, device);
return false;
}
close(if_fd);
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
ppa = 0;
ptr = device;
while(*ptr && !isdigit((int) *ptr))
ptr++;
ppa = atoi(ptr);
if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
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(DEBUG_ALWAYS, LOG_ERR, "Can't assign new interface: %s", strerror(errno));
return false;
}
if((if_fd = open(device, O_RDWR, 0)) < 0) {
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(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(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(DEBUG_ALWAYS, LOG_ERR, "Can't link TUN device to IP: %s", strerror(errno));
return false;
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
xasprintf(&iface, "tun%d", ppa);
device_info = "Solaris tun device";
logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
return true;
}
static void close_device(void) {
close(if_fd);
close(ip_fd);
close(device_fd);
if(iface) {
struct lifreq ifr = {};
strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name);
if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) {
int muxid = ifr.lifr_arp_muxid;
ioctl(ip_fd, I_PUNLINK, muxid);
muxid = ifr.lifr_ip_muxid;
ioctl(ip_fd, I_PUNLINK, muxid);
}
}
free(device);
free(iface);
close(ip_fd); ip_fd = -1;
close(device_fd); device_fd = -1;
free(device); device = NULL;
free(iface); iface = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
int inlen;
if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
switch(device_type) {
case DEVICE_TYPE_TUN:
if((inlen = read(device_fd, DATA(packet) + 14, MTU - 14)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
return false;
}
switch(packet->data[14] >> 4) {
case 4:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
switch(DATA(packet)[14] >> 4) {
case 4:
DATA(packet)[12] = 0x08;
DATA(packet)[13] = 0x00;
break;
case 6:
DATA(packet)[12] = 0x86;
DATA(packet)[13] = 0xDD;
break;
default:
logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", DATA(packet)[14] >> 4, device_info, device);
return false;
}
memset(DATA(packet), 0, 12);
packet->len = inlen + 14;
break;
case 6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
case DEVICE_TYPE_TAP:
if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
return false;
}
packet->len = inlen + 14;
break;
default:
logger(DEBUG_TRAFFIC, LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
abort();
}
memset(packet->data, 0, 12);
packet->len = inlen + 14;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
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);
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(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info,
device, strerror(errno));
return false;
switch(device_type) {
case DEVICE_TYPE_TUN:
if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
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, DATA(packet), packet->len) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
return false;
}
break;
default:
abort();
}
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 os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,6 +1,6 @@
/*
sptps.c -- Simple Peer-to-Peer Security
Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2011-2014 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
@ -20,9 +20,8 @@
#include "system.h"
#include "cipher.h"
#include "chacha-poly1305/chacha-poly1305.h"
#include "crypto.h"
#include "digest.h"
#include "ecdh.h"
#include "ecdsa.h"
#include "logger.h"
@ -40,7 +39,7 @@ unsigned int sptps_replaywin = 16;
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?)
HMACed KEX finished message to prevent downgrade attacks and prove you have the right key material (done by virtue of Ed25519 over the whole ECDHE exchange?)
Explicit close message needs to be added.
@ -82,72 +81,53 @@ static void warning(sptps_t *s, const char *format, ...) {
}
// 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];
static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
char buffer[len + 21UL];
// Create header with sequence number, length and record type
uint32_t seqno = htonl(s->outseqno++);
uint16_t netlen = htons(len);
uint32_t seqno = s->outseqno++;
uint32_t netseqno = ntohl(seqno);
memcpy(buffer, &netlen, 2);
memcpy(buffer + 2, &seqno, 4);
buffer[6] = type;
// Add plaintext (TODO: avoid unnecessary copy)
memcpy(buffer + 7, data, len);
memcpy(buffer, &netseqno, 4);
buffer[4] = type;
memcpy(buffer + 5, data, len);
if(s->outstate) {
// If first handshake has finished, encrypt and HMAC
if(!cipher_set_counter(s->outcipher, &seqno, sizeof seqno))
return false;
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);
chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL);
return s->send_data(s->handle, type, buffer, len + 21UL);
} else {
// Otherwise send as plaintext
return s->send_data(s->handle, type, buffer + 2, len + 5UL);
return s->send_data(s->handle, type, buffer, 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) {
static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
if(s->datagram)
return send_record_priv_datagram(s, type, data, len);
char buffer[len + 23UL];
char buffer[len + 19UL];
// Create header with sequence number, length and record type
uint32_t seqno = htonl(s->outseqno++);
uint32_t seqno = 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);
memcpy(buffer, &netlen, 2);
buffer[2] = type;
memcpy(buffer + 3, 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);
chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL);
return s->send_data(s->handle, type, buffer, len + 19UL);
} else {
// Otherwise send as plaintext
return s->send_data(s->handle, type, buffer + 4, len + 3UL);
return s->send_data(s->handle, type, buffer, len + 3UL);
}
}
// Send an application record.
bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
bool sptps_send_record(sptps_t *s, uint8_t type, const void *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)
@ -165,7 +145,7 @@ static bool send_kex(sptps_t *s) {
// Make room for our KEX message, which we will keep around since send_sig() needs it.
if(s->mykex)
abort();
return false;
s->mykex = realloc(s->mykex, 1 + 32 + keylen);
if(!s->mykex)
return error(s, errno, strerror(errno));
@ -178,12 +158,12 @@ static bool send_kex(sptps_t *s) {
// Create a new ECDH public key.
if(!(s->ecdh = ecdh_generate_public(s->mykex + 1 + 32)))
return false;
return error(s, EINVAL, "Failed to generate ECDH public key");
return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
}
// Send a SIGnature record, containing an ECDSA signature over both KEX records.
// Send a SIGnature record, containing an Ed25519 signature over both KEX records.
static bool send_sig(sptps_t *s) {
size_t keylen = ECDH_SIZE;
size_t siglen = ecdsa_size(s->mykey);
@ -199,7 +179,7 @@ static bool send_sig(sptps_t *s) {
// Sign the result.
if(!ecdsa_sign(s->mykey, msg, sizeof msg, sig))
return false;
return error(s, EINVAL, "Failed to sign SIG record");
// Send the SIG exchange record.
return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof sig);
@ -209,16 +189,14 @@ static bool send_sig(sptps_t *s) {
static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
// Initialise cipher and digest structures if necessary
if(!s->outstate) {
s->incipher = cipher_open_by_name("aes-256-ecb");
s->outcipher = cipher_open_by_name("aes-256-ecb");
s->indigest = digest_open_by_name("sha256", 16);
s->outdigest = digest_open_by_name("sha256", 16);
if(!s->incipher || !s->outcipher || !s->indigest || !s->outdigest)
return false;
s->incipher = chacha_poly1305_init();
s->outcipher = chacha_poly1305_init();
if(!s->incipher || !s->outcipher)
return error(s, EINVAL, "Failed to open cipher");
}
// Allocate memory for key material
size_t keylen = digest_keylength(s->indigest) + digest_keylength(s->outdigest) + cipher_keylength(s->incipher) + cipher_keylength(s->outcipher);
size_t keylen = 2 * CHACHA_POLY1305_KEYLEN;
s->key = realloc(s->key, keylen);
if(!s->key)
@ -238,7 +216,7 @@ static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
// Use PRF to generate the key material
if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen))
return false;
return error(s, EINVAL, "Failed to generate key material");
return true;
}
@ -254,17 +232,11 @@ static bool receive_ack(sptps_t *s, const char *data, uint16_t 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;
if(!chacha_poly1305_set_key(s->incipher, s->key))
return error(s, EINVAL, "Failed to set counter");
} 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;
if(!chacha_poly1305_set_key(s->incipher, s->key + CHACHA_POLY1305_KEYLEN))
return error(s, EINVAL, "Failed to set counter");
}
free(s->key);
@ -284,7 +256,7 @@ static bool receive_kex(sptps_t *s, const char *data, uint16_t len) {
// Make a copy of the KEX message, send_sig() and receive_sig() need it
if(s->hiskex)
abort();
return error(s, EINVAL, "Received a second KEX message before first has been processed");
s->hiskex = realloc(s->hiskex, len);
if(!s->hiskex)
return error(s, errno, strerror(errno));
@ -313,12 +285,12 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
// Verify signature.
if(!ecdsa_verify(s->hiskey, msg, sizeof msg, data))
return false;
return error(s, EIO, "Failed to verify SIG record");
// Compute shared secret.
char shared[ECDH_SHARED_SIZE];
if(!ecdh_compute_shared(s->ecdh, s->hiskex + 1 + 32, shared))
return false;
return error(s, EINVAL, "Failed to compute ECDH shared secret");
s->ecdh = NULL;
// Generate key material from shared secret.
@ -337,17 +309,11 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
// 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;
if(!chacha_poly1305_set_key(s->outcipher, s->key + CHACHA_POLY1305_KEYLEN))
return error(s, EINVAL, "Failed to set key");
} 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;
if(!chacha_poly1305_set_key(s->outcipher, s->key))
return error(s, EINVAL, "Failed to set key");
}
return true;
@ -404,18 +370,73 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
}
}
static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) {
// 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.
bool farfuture = s->farfuture < s->replaywin >> 2;
if (update_state)
s->farfuture++;
if(farfuture)
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);
if (update_state) {
// Mark all packets in the replay window as being late.
memset(s->late, 255, 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 if (update_state) {
// 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;
}
}
if (update_state) {
// Mark the current packet as not being late.
s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
s->farfuture = 0;
}
}
if (update_state) {
if(seqno >= s->inseqno)
s->inseqno = seqno + 1;
if(!s->inseqno)
s->received = 0;
else
s->received++;
}
return true;
}
// Check datagram for valid HMAC
bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) {
bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) {
if(!s->instate || len < 21)
return error(s, EIO, "Received short packet");
uint32_t seqno;
memcpy(&seqno, data, 4);
seqno = ntohl(seqno);
if (!sptps_check_seqno(s, seqno, false))
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);
char buffer[len];
size_t outlen;
return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
}
// Receive incoming data, datagram version.
@ -441,77 +462,31 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
return receive_handshake(s, data + 5, len - 5);
}
// Check HMAC.
uint16_t netlen = htons(len - 21);
// Decrypt
char buffer[len + 23];
char buffer[len];
memcpy(buffer, &netlen, 2);
memcpy(buffer + 2, data, len);
size_t outlen;
if(!digest_verify(s->indigest, buffer, len - 14, buffer + len - 14))
return error(s, EIO, "Invalid HMAC");
if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
return error(s, EIO, "Failed to decrypt and verify packet");
// 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);
// Mark all packets in the replay window as being late.
memset(s->late, 255, 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;
if(!s->inseqno)
s->received = 0;
else
s->received++;
// Decrypt.
memcpy(&seqno, buffer + 2, 4);
if(!cipher_set_counter(s->incipher, &seqno, sizeof seqno))
return false;
if(!cipher_counter_xor(s->incipher, buffer + 6, len - 4, buffer + 6))
if(!sptps_check_seqno(s, seqno, true))
return false;
// Append a NULL byte for safety.
buffer[len - 14] = 0;
buffer[len - 20] = 0;
uint8_t type = buffer[6];
uint8_t type = buffer[0];
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;
if(!s->receive_record(s->handle, type, buffer + 1, len - 21))
abort();
} else if(type == SPTPS_HANDSHAKE) {
if(!receive_handshake(s, buffer + 7, len - 21))
return false;
if(!receive_handshake(s, buffer + 1, len - 21))
abort();
} else {
return error(s, EIO, "Invalid record type %d", type);
}
@ -520,7 +495,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
}
// 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) {
bool sptps_receive_data(sptps_t *s, const void *data, size_t len) {
if(!s->state)
return error(s, EIO, "Invalid session state zero");
@ -529,8 +504,8 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
while(len) {
// First read the 2 length bytes.
if(s->buflen < 6) {
size_t toread = 6 - s->buflen;
if(s->buflen < 2) {
size_t toread = 2 - s->buflen;
if(toread > len)
toread = len;
@ -541,36 +516,26 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
data += toread;
// Exit early if we don't have the full length.
if(s->buflen < 6)
if(s->buflen < 2)
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);
}
// Get the length bytes
memcpy(&s->reclen, s->inbuf, 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);
s->inbuf = realloc(s->inbuf, s->reclen + 19UL);
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;
size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen;
if(toread > len)
toread = len;
@ -580,43 +545,44 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
data += toread;
// If we don't have a whole record, exit.
if(s->buflen < s->reclen + (s->instate ? 23UL : 7UL))
if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL))
return true;
// Update sequence number.
uint32_t seqno = s->inseqno++;
// 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;
if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL))
return error(s, EINVAL, "Failed to decrypt and verify record");
}
// Append a NULL byte for safety.
s->inbuf[s->reclen + 7UL] = 0;
s->inbuf[s->reclen + 3UL] = 0;
uint8_t type = s->inbuf[6];
uint8_t type = s->inbuf[2];
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))
if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen))
return false;
} else if(type == SPTPS_HANDSHAKE) {
if(!receive_handshake(s, s->inbuf + 7, s->reclen))
if(!receive_handshake(s, s->inbuf + 3, s->reclen))
return false;
} else {
return error(s, EIO, "Invalid record type %d", type);
}
s->buflen = 4;
s->buflen = 0;
}
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) {
bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
// Initialise struct sptps
memset(s, 0, sizeof *s);
@ -641,8 +607,7 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_
s->inbuf = malloc(7);
if(!s->inbuf)
return error(s, errno, strerror(errno));
s->buflen = 4;
memset(s->inbuf, 0, 4);
s->buflen = 0;
}
memcpy(s->label, label, labellen);
@ -659,10 +624,8 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_
// 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);
chacha_poly1305_exit(s->incipher);
chacha_poly1305_exit(s->outcipher);
ecdh_free(s->ecdh);
free(s->inbuf);
free(s->mykex);

View file

@ -1,6 +1,6 @@
/*
sptps.h -- Simple Peer-to-Peer Security
Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2011-2014 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
@ -22,8 +22,7 @@
#include "system.h"
#include "cipher.h"
#include "digest.h"
#include "chacha-poly1305/chacha-poly1305.h"
#include "ecdh.h"
#include "ecdsa.h"
@ -40,8 +39,11 @@
#define SPTPS_SIG 3 // Waiting for a SIGnature record
#define SPTPS_ACK 4 // 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);
// Overhead for datagrams
#define SPTPS_DATAGRAM_OVERHEAD 21
typedef bool (*send_data_t)(void *handle, uint8_t type, const void *data, size_t len);
typedef bool (*receive_record_t)(void *handle, uint8_t type, const void *data, uint16_t len);
typedef struct sptps {
bool initiator;
@ -53,8 +55,7 @@ typedef struct sptps {
uint16_t reclen;
bool instate;
cipher_t *incipher;
digest_t *indigest;
chacha_poly1305_ctx_t *incipher;
uint32_t inseqno;
uint32_t received;
unsigned int replaywin;
@ -62,8 +63,7 @@ typedef struct sptps {
char *late;
bool outstate;
cipher_t *outcipher;
digest_t *outdigest;
chacha_poly1305_ctx_t *outcipher;
uint32_t outseqno;
ecdsa_t *mykey;
@ -85,11 +85,11 @@ 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_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *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_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len);
extern bool sptps_receive_data(sptps_t *s, const void *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);
extern bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len);
#endif

108
src/sptps_keypair.c Normal file
View file

@ -0,0 +1,108 @@
/*
sptps_test.c -- Simple Peer-to-Peer Security test program
Copyright (C) 2011-2013 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 <getopt.h>
#include "crypto.h"
#include "ecdsagen.h"
#include "utils.h"
static char *program_name;
void logger(int level, int priority, const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
}
static void usage() {
fprintf(stderr, "Usage: %s [options] private_key_file public_key_file\n\n", program_name);
fprintf(stderr, "Valid options are:\n"
" --help Display this help and exit.\n"
"\n");
fprintf(stderr, "Report bugs to tinc@tinc-vpn.org.\n");
}
static struct option const long_options[] = {
{"help", no_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
int main(int argc, char *argv[]) {
program_name = argv[0];
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
case '?': /* wrong options */
usage();
return 1;
case 1: /* help */
usage();
return 0;
default:
break;
}
}
argc -= optind - 1;
argv += optind - 1;
if(argc != 3) {
fprintf(stderr, "Wrong number of arguments.\n");
usage();
return 1;
}
crypto_init();
ecdsa_t *key = ecdsa_generate();
if(!key)
return 1;
FILE *fp = fopen(argv[1], "w");
if(fp) {
ecdsa_write_pem_private_key(key, fp);
fclose(fp);
} else {
fprintf(stderr, "Could not open '%s' for writing: %s\n", argv[1], strerror(errno));
return 1;
}
fp = fopen(argv[2], "w");
if(fp) {
ecdsa_write_pem_public_key(key, fp);
fclose(fp);
} else {
fprintf(stderr, "Could not open '%s' for writing: %s\n", argv[2], strerror(errno));
return 1;
}
return 0;
}

232
src/sptps_speed.c Normal file
View file

@ -0,0 +1,232 @@
/*
sptps_speed.c -- SPTPS benchmark
Copyright (C) 2013-2014 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 <poll.h>
#include "crypto.h"
#include "ecdh.h"
#include "ecdsa.h"
#include "ecdsagen.h"
#include "sptps.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;
struct timeval now;
static bool send_data(void *handle, uint8_t type, const void *data, size_t len) {
int fd = *(int *)handle;
send(fd, data, len, 0);
return true;
}
static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
return true;
}
static void receive_data(sptps_t *sptps) {
char buf[4096];
int fd = *(int *)sptps->handle;
size_t len = recv(fd, buf, sizeof buf, 0);
if(!sptps_receive_data(sptps, buf, len))
abort();
}
struct timespec start;
struct timespec end;
double elapsed;
double rate;
unsigned int count;
static void clock_start() {
count = 0;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
}
static bool clock_countto(double seconds) {
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
elapsed = end.tv_sec + end.tv_nsec * 1e-9 - start.tv_sec - start.tv_nsec * 1e-9;
if(elapsed < seconds)
return ++count;
rate = count / elapsed;
return false;
}
int main(int argc, char *argv[]) {
ecdsa_t *key1, *key2;
ecdh_t *ecdh1, *ecdh2;
sptps_t sptps1, sptps2;
char buf1[4096], buf2[4096], buf3[4096];
double duration = argc > 1 ? atof(argv[1]) : 10;
crypto_init();
randomize(buf1, sizeof buf1);
randomize(buf2, sizeof buf2);
randomize(buf3, sizeof buf3);
// Key generation
fprintf(stderr, "Generating keys for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);)
ecdsa_free(ecdsa_generate());
fprintf(stderr, "%17.2lf op/s\n", rate);
key1 = ecdsa_generate();
key2 = ecdsa_generate();
// Ed25519 signatures
fprintf(stderr, "Ed25519 sign for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);)
ecdsa_sign(key1, buf1, 256, buf2);
fprintf(stderr, "%22.2lf op/s\n", rate);
fprintf(stderr, "Ed25519 verify for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);)
ecdsa_verify(key1, buf1, 256, buf2);
fprintf(stderr, "%20.2lf op/s\n", rate);
ecdh1 = ecdh_generate_public(buf1);
fprintf(stderr, "ECDH for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);) {
ecdh2 = ecdh_generate_public(buf2);
ecdh_compute_shared(ecdh2, buf1, buf3);
}
fprintf(stderr, "%28.2lf op/s\n", rate);
ecdh_free(ecdh1);
// SPTPS authentication phase
int fd[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
return 1;
}
struct pollfd pfd[2] = {{fd[0], POLLIN}, {fd[1], POLLIN}};
fprintf(stderr, "SPTPS/TCP authenticate for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);) {
sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents)
receive_data(&sptps1);
if(pfd[1].revents)
receive_data(&sptps2);
}
sptps_stop(&sptps1);
sptps_stop(&sptps2);
}
fprintf(stderr, "%10.2lf op/s\n", rate * 2);
// SPTPS data
sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents)
receive_data(&sptps1);
if(pfd[1].revents)
receive_data(&sptps2);
}
fprintf(stderr, "SPTPS/TCP transmit for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);) {
if(!sptps_send_record(&sptps1, 0, buf1, 1451))
abort();
receive_data(&sptps2);
}
rate *= 2 * 1451 * 8;
if(rate > 1e9)
fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
else if(rate > 1e6)
fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
else if(rate > 1e3)
fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
sptps_stop(&sptps1);
sptps_stop(&sptps2);
// SPTPS datagram authentication phase
close(fd[0]);
close(fd[1]);
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
return 1;
}
fprintf(stderr, "SPTPS/UDP authenticate for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);) {
sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents)
receive_data(&sptps1);
if(pfd[1].revents)
receive_data(&sptps2);
}
sptps_stop(&sptps1);
sptps_stop(&sptps2);
}
fprintf(stderr, "%10.2lf op/s\n", rate * 2);
// SPTPS datagram data
sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents)
receive_data(&sptps1);
if(pfd[1].revents)
receive_data(&sptps2);
}
fprintf(stderr, "SPTPS/UDP transmit for %lg seconds: ", duration);
for(clock_start(); clock_countto(duration);) {
if(!sptps_send_record(&sptps1, 0, buf1, 1451))
abort();
receive_data(&sptps2);
}
rate *= 2 * 1451 * 8;
if(rate > 1e9)
fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
else if(rate > 1e6)
fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
else if(rate > 1e3)
fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
sptps_stop(&sptps1);
sptps_stop(&sptps2);
// Clean up
close(fd[0]);
close(fd[1]);
ecdsa_free(key1);
ecdsa_free(key2);
crypto_exit();
return 0;
}

View file

@ -1,6 +1,6 @@
/*
sptps_test.c -- Simple Peer-to-Peer Security test program
Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2011-2014 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,6 +19,10 @@
#include "system.h"
#ifdef HAVE_LINUX
#include <linux/if_tun.h>
#endif
#include <getopt.h>
#include "crypto.h"
@ -33,23 +37,28 @@ bool send_meta(void *c, const char *msg , int len) { return false; }
char *logfilename = NULL;
struct timeval now;
static bool verbose;
static bool readonly;
static bool writeonly;
static int in = 0;
static int out = 1;
static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
static bool send_data(void *handle, uint8_t type, const void *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);
if(verbose)
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);
static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
if(verbose)
fprintf(stderr, "Received type %d record of %hu bytes:\n", type, len);
if(!writeonly)
fwrite(data, len, 1, stdout);
write(out, data, len);
return true;
}
@ -60,6 +69,7 @@ static struct option const long_options[] = {
{"writeonly", no_argument, NULL, 'w'},
{"packet-loss", required_argument, NULL, 'L'},
{"replay-window", required_argument, NULL, 'W'},
{"verbose", required_argument, NULL, 'v'},
{"help", no_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
@ -67,14 +77,18 @@ static struct option const long_options[] = {
const char *program_name;
static void usage() {
fprintf(stderr, "Usage: %s [options] my_ecdsa_key_file his_ecdsa_key_file [host] port\n\n", program_name);
fprintf(stderr, "Usage: %s [options] my_ed25519_key_file his_ed25519_key_file [host] port\n\n", program_name);
fprintf(stderr, "Valid options are:\n"
" -d, --datagram Enable datagram mode.\n"
" -q, --quit Quit when EOF occurs on stdin.\n"
" -r, --readonly Only send data from the socket to stdout.\n"
#ifdef HAVE_LINUX
" -t, --tun Use a tun device instead of stdio.\n"
#endif
" -w, --writeonly Only send data from stdin to the socket.\n"
" -L, --packet-loss RATE Fake packet loss of RATE percent.\n"
" -R, --replay-window N Set replay window to N bytes.\n"
" -v, --verbose Display debug messages.\n"
"\n");
fprintf(stderr, "Report bugs to tinc@tinc-vpn.org.\n");
}
@ -83,13 +97,16 @@ int main(int argc, char *argv[]) {
program_name = argv[0];
bool initiator = false;
bool datagram = false;
#ifdef HAVE_LINUX
bool tun = false;
#endif
int packetloss = 0;
int r;
int option_index = 0;
ecdsa_t *mykey = NULL, *hiskey = NULL;
bool quit = false;
while((r = getopt_long(argc, argv, "dqrwL:W:", long_options, &option_index)) != EOF) {
while((r = getopt_long(argc, argv, "dqrtwL:W:v", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
@ -106,6 +123,16 @@ int main(int argc, char *argv[]) {
readonly = true;
break;
case 't': /* read only */
#ifdef HAVE_LINUX
tun = true;
#else
fprintf(stderr, "--tun is only supported on Linux.\n");
usage();
return 1;
#endif
break;
case 'w': /* write only */
writeonly = true;
break;
@ -118,6 +145,10 @@ int main(int argc, char *argv[]) {
sptps_replaywin = atoi(optarg);
break;
case 'v': /* be verbose */
verbose = true;
break;
case '?': /* wrong options */
usage();
return 1;
@ -145,6 +176,25 @@ int main(int argc, char *argv[]) {
srand(time(NULL));
#ifdef HAVE_LINUX
if(tun) {
in = out = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
if(in < 0) {
fprintf(stderr, "Could not open tun device: %s\n", strerror(errno));
return 1;
}
struct ifreq ifr = {
.ifr_flags = IFF_TUN
};
if(ioctl(in, TUNSETIFF, &ifr)) {
fprintf(stderr, "Could not configure tun interface: %s\n", strerror(errno));
return 1;
}
ifr.ifr_name[IFNAMSIZ - 1] = 0;
fprintf(stderr, "Using tun interface %s\n", ifr.ifr_name);
}
#endif
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state))
@ -160,13 +210,13 @@ int main(int argc, char *argv[]) {
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));
fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
return 1;
}
int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock < 0) {
fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
return 1;
}
@ -175,26 +225,26 @@ int main(int argc, char *argv[]) {
if(initiator) {
if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
fprintf(stderr, "Could not connect to peer: %s\n", strerror(errno));
fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
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));
fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
return 1;
}
if(!datagram) {
if(listen(sock, 1)) {
fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
return 1;
}
fprintf(stderr, "Listening...\n");
sock = accept(sock, NULL, NULL);
if(sock < 0) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
return 1;
}
} else {
@ -205,12 +255,12 @@ int main(int argc, char *argv[]) {
socklen_t addrlen = sizeof addr;
if(recvfrom(sock, buf, sizeof buf, MSG_PEEK, &addr, &addrlen) <= 0) {
fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
return 1;
}
if(connect(sock, &addr, addrlen)) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
return 1;
}
}
@ -230,7 +280,8 @@ int main(int argc, char *argv[]) {
return 1;
fclose(fp);
fprintf(stderr, "Keys loaded\n");
if(verbose)
fprintf(stderr, "Keys loaded\n");
sptps_t s;
if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record))
@ -246,15 +297,14 @@ int main(int argc, char *argv[]) {
FD_ZERO(&fds);
#ifndef HAVE_MINGW
if(!readonly && s.instate)
FD_SET(0, &fds);
FD_SET(in, &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);
fprintf(stderr, "%zd\n", len);
if(FD_ISSET(in, &fds)) {
ssize_t len = read(in, buf, sizeof buf);
if(len < 0) {
fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
return 1;
@ -274,25 +324,28 @@ int main(int argc, char *argv[]) {
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))
if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && 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));
fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
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((rand() % 100) < packetloss) {
fprintf(stderr, "Dropped.\n");
if(verbose) {
char hex[len * 2 + 1];
bin2hex(buf, hex, len);
fprintf(stderr, "Received %d bytes of data:\n%s\n", (int)len, hex);
}
if(packetloss && (rand() % 100) < packetloss) {
if(verbose)
fprintf(stderr, "Dropped.\n");
continue;
}
if(!sptps_receive_data(&s, buf, len) && !datagram)

View file

@ -92,13 +92,15 @@ void subnet_add(node_t *n, subnet_t *subnet) {
subnet->owner = n;
splay_insert(subnet_tree, subnet);
splay_insert(n->subnet_tree, subnet);
if (n)
splay_insert(n->subnet_tree, subnet);
subnet_cache_flush();
}
void subnet_del(node_t *n, subnet_t *subnet) {
splay_delete(n->subnet_tree, subnet);
if (n)
splay_delete(n->subnet_tree, subnet);
splay_delete(subnet_tree, subnet);
subnet_cache_flush();
@ -126,7 +128,7 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
r = p;
if(p->owner->status.reachable)
if(!p->owner || p->owner->status.reachable)
break;
}
}
@ -155,7 +157,7 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
r = p;
if(p->owner->status.reachable)
if(!p->owner || p->owner->status.reachable)
break;
}
}
@ -184,7 +186,7 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
r = p;
if(p->owner->status.reachable)
if(!p->owner || p->owner->status.reachable)
break;
}
}
@ -205,21 +207,21 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
// Prepare environment variables to be passed to the script
char *envp[10] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NODE=%s", owner->name);
int n = 0;
xasprintf(&envp[n++], "NETNAME=%s", netname ? : "");
xasprintf(&envp[n++], "DEVICE=%s", device ? : "");
xasprintf(&envp[n++], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[n++], "NODE=%s", owner->name);
if(owner != myself) {
sockaddr2str(&owner->address, &address, &port);
// 4 and 5 are reserved for SUBNET and WEIGHT
xasprintf(&envp[6], "REMOTEADDRESS=%s", address);
xasprintf(&envp[7], "REMOTEPORT=%s", port);
xasprintf(&envp[n++], "REMOTEADDRESS=%s", address);
xasprintf(&envp[n++], "REMOTEPORT=%s", port);
free(port);
free(address);
}
xasprintf(&envp[8], "NAME=%s", myself->name);
xasprintf(&envp[n++], "NAME=%s", myself->name);
name = up ? "subnet-up" : "subnet-down";
@ -236,12 +238,10 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
weight = empty;
// Prepare the SUBNET and WEIGHT variables
if(envp[4])
free(envp[4]);
if(envp[5])
free(envp[5]);
xasprintf(&envp[4], "SUBNET=%s", netstr);
xasprintf(&envp[5], "WEIGHT=%s", weight);
free(envp[n]);
free(envp[n + 1]);
xasprintf(&envp[n], "SUBNET=%s", netstr);
xasprintf(&envp[n + 1], "WEIGHT=%s", weight);
execute_script(name, envp);
}
@ -255,8 +255,8 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
weight = empty;
// Prepare the SUBNET and WEIGHT variables
xasprintf(&envp[4], "SUBNET=%s", netstr);
xasprintf(&envp[5], "WEIGHT=%s", weight);
xasprintf(&envp[n], "SUBNET=%s", netstr);
xasprintf(&envp[n + 1], "WEIGHT=%s", weight);
execute_script(name, envp);
}
@ -275,7 +275,7 @@ bool dump_subnets(connection_t *c) {
send_request(c, "%d %d %s %s",
CONTROL, REQ_DUMP_SUBNETS,
netstr, subnet->owner->name);
netstr, subnet->owner ? subnet->owner->name : "(broadcast)");
}
return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);

View file

@ -27,6 +27,9 @@
#include "utils.h"
#include "xalloc.h"
/* Changing this default will affect ADD_SUBNET messages - beware of inconsistencies between versions */
static const int DEFAULT_WEIGHT = 10;
/* Subnet mask handling */
int maskcmp(const void *va, const void *vb, int masklen) {
@ -181,150 +184,121 @@ int subnet_compare(const subnet_t *a, const subnet_t *b) {
/* Ascii representation of subnets */
bool str2net(subnet_t *subnet, const char *subnetstr) {
int i, l;
char str[1024];
strncpy(str, subnetstr, sizeof(str));
str[sizeof str - 1] = 0;
int consumed;
int weight = DEFAULT_WEIGHT;
char *weight_separator = strchr(str, '#');
if (weight_separator) {
char *weight_str = weight_separator + 1;
if (sscanf(weight_str, "%d%n", &weight, &consumed) < 1)
return false;
if (weight_str[consumed])
return false;
*weight_separator = 0;
}
int prefixlength = -1;
char *prefixlength_separator = strchr(str, '/');
if (prefixlength_separator) {
char* prefixlength_str = prefixlength_separator + 1;
if (sscanf(prefixlength_str, "%d%n", &prefixlength, &consumed) < 1)
return false;
if (prefixlength_str[consumed])
return false;
*prefixlength_separator = 0;
if (prefixlength < 0)
return false;
}
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)
if (sscanf(str, "%hx:%hx:%hx:%hx:%hx:%hx%n", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &consumed) >= 6 && !str[consumed]) {
/*
Normally we should check that each part has two digits to prevent ambiguities.
However, in old tinc versions net2str() will agressively return MAC addresses with one-digit parts,
so we have to accept them otherwise we would be unable to parse ADD_SUBNET messages.
*/
if (prefixlength >= 0)
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++)
for(int 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)
if (sscanf(str, "%hu.%hu.%hu.%hu%n", &x[0], &x[1], &x[2], &x[3], &consumed) >= 4 && !str[consumed]) {
if (prefixlength == -1)
prefixlength = 32;
if (prefixlength > 32)
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 != ':')
subnet->type = SUBNET_IPV4;
subnet->net.ipv4.prefixlength = prefixlength;
subnet->weight = weight;
for(int i = 0; i < 4; i++) {
if (x[i] > 255)
return false;
p = ++q;
subnet->net.ipv4.address.x[i] = x[i];
}
return true;
}
p++;
colons -= i;
if(!i) {
p++;
colons--;
}
/* IPv6 */
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)
char* last_colon = strrchr(str, ':');
if (last_colon && sscanf(last_colon, ":%hu.%hu.%hu.%hu%n", &x[0], &x[1], &x[2], &x[3], &consumed) >= 4 && !last_colon[consumed]) {
/* Dotted quad suffix notation, convert to standard IPv6 notation */
for (int i = 0; i < 4; i++)
if (x[i] > 255)
return false;
if(i == 7) {
p = q;
break;
snprintf(last_colon, sizeof str - (last_colon - str), ":%02x%02x:%02x%02x", x[0], x[1], x[2], x[3]);
}
char* double_colon = strstr(str, "::");
if (double_colon) {
/* Figure out how many zero groups we need to expand */
int zero_group_count = 8;
for (const char* cur = str; *cur; cur++)
if (*cur != ':') {
zero_group_count--;
while(cur[1] && cur[1] != ':')
cur++;
}
if(*q != ':')
return false;
p = ++q;
}
if (zero_group_count < 1)
return false;
l = 128;
if(*p == '/')
sscanf(p, "/%d#%d", &l, &weight);
else if(*p == '#')
sscanf(p, "#%d", &weight);
/* Split the double colon in the middle to make room for zero groups */
double_colon++;
memmove(double_colon + (zero_group_count * 2 - 1), double_colon, strlen(double_colon) + 1);
if(l < 0 || l > 128)
/* Write zero groups in the resulting gap, overwriting the second colon */
for (int i = 0; i < zero_group_count; i++)
memcpy(&double_colon[i * 2], "0:", 2);
/* Remove any leading or trailing colons */
if (str[0] == ':')
memmove(&str[0], &str[1], strlen(&str[1]) + 1);
if (str[strlen(str) - 1] == ':')
str[strlen(str) - 1] = 0;
}
if (sscanf(str, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx%n",
&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &consumed) >= 8 && !str[consumed]) {
if (prefixlength == -1)
prefixlength = 128;
if (prefixlength > 128)
return false;
subnet->type = SUBNET_IPV6;
subnet->net.ipv6.prefixlength = l;
subnet->net.ipv6.prefixlength = prefixlength;
subnet->weight = weight;
for(i = 0; i < 8; i++)
for(int i = 0; i < 8; i++)
subnet->net.ipv6.address.x[i] = htons(x[i]);
return true;
}
@ -337,46 +311,101 @@ bool net2str(char *netstr, int len, const subnet_t *subnet) {
return false;
}
int result;
int prefixlength = -1;
switch (subnet->type) {
case SUBNET_MAC:
snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
result = snprintf(netstr, len, "%02x:%02x:%02x:%02x:%02x:%02x",
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);
subnet->net.mac.address.x[5]);
netstr += result;
len -= result;
break;
case SUBNET_IPV4:
snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%d",
result = snprintf(netstr, len, "%u.%u.%u.%u",
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);
subnet->net.ipv4.address.x[3]);
netstr += result;
len -= result;
prefixlength = subnet->net.ipv4.prefixlength;
if (prefixlength == 32)
prefixlength = -1;
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);
case SUBNET_IPV6: {
/* Find the longest sequence of consecutive zeroes */
int max_zero_length = 0;
int max_zero_length_index = 0;
int current_zero_length = 0;
int current_zero_length_index = 0;
for (int i = 0; i < 8; i++) {
if (subnet->net.ipv6.address.x[i] != 0)
current_zero_length = 0;
else {
if (current_zero_length == 0)
current_zero_length_index = i;
current_zero_length++;
if (current_zero_length > max_zero_length) {
max_zero_length = current_zero_length;
max_zero_length_index = current_zero_length_index;
}
}
}
/* Print the address */
for (int i = 0; i < 8;) {
if (max_zero_length > 1 && max_zero_length_index == i) {
/* Shorten the representation as per RFC 5952 */
const char* const FORMATS[] = { "%.1s", "%.2s", "%.3s" };
const char* const* format = &FORMATS[0];
if (i == 0)
format++;
if (i + max_zero_length == 8)
format++;
result = snprintf(netstr, len, *format, ":::");
i += max_zero_length;
} else {
result = snprintf(netstr, len, "%hx:", ntohs(subnet->net.ipv6.address.x[i]));
i++;
}
netstr += result;
len -= result;
}
/* Remove the trailing colon */
netstr--;
len++;
*netstr = 0;
prefixlength = subnet->net.ipv6.prefixlength;
if (prefixlength == 128)
prefixlength = -1;
break;
}
default:
logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with unknown subnet type %d, exiting!", subnet->type);
exit(1);
}
if (prefixlength >= 0) {
result = snprintf(netstr, len, "/%d", prefixlength);
netstr += result;
len -= result;
}
if (subnet->weight != DEFAULT_WEIGHT) {
snprintf(netstr, len, "#%d", subnet->weight);
netstr += result;
len -= result;
}
return true;
}

View file

@ -1,6 +1,6 @@
/*
tincctl.c -- Controlling a running tincd
Copyright (C) 2007-2013 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2007-2014 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
@ -38,6 +38,11 @@
#include "utils.h"
#include "tincctl.h"
#include "top.h"
#include "version.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
static char **orig_argv;
static int orig_argc;
@ -67,8 +72,10 @@ bool confbasegiven = false;
bool netnamegiven = false;
char *scriptinterpreter = NULL;
char *scriptextension = "";
static char *prompt;
static struct option const long_options[] = {
{"batch", no_argument, NULL, 'b'},
{"config", required_argument, NULL, 'c'},
{"net", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 1},
@ -80,8 +87,8 @@ static struct option const long_options[] = {
static void version(void) {
printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
printf("Copyright (C) 1998-2014 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"
@ -94,6 +101,7 @@ static void usage(bool status) {
} else {
printf("Usage: %s [options] command\n\n", program_name);
printf("Valid options are:\n"
" -b, --batch Don't ask for anything (non-interactive mode).\n"
" -c, --config=DIR Read configuration options from DIR.\n"
" -n, --net=NETNAME Connect to net NETNAME.\n"
" --pidfile=FILENAME Read control cookie from FILENAME.\n"
@ -111,9 +119,9 @@ static void usage(bool status) {
" restart [tincd options] Restart tincd.\n"
" reload Partially reload configuration of running tincd.\n"
" pid Show PID of currently running tincd.\n"
" generate-keys [bits] Generate new RSA and ECDSA public/private keypairs.\n"
" generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
" generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
" generate-ecdsa-keys Generate a new ECDSA public/private keypair.\n"
" generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
" dump Dump a list of one of the following things:\n"
" [reachable] nodes - all known nodes in the VPN\n"
" edges - all known connections in the VPN\n"
@ -137,6 +145,7 @@ static void usage(bool status) {
" exchange-all [--force] Same as export-all followed by import\n"
" invite NODE [...] Generate an invitation for NODE\n"
" join INVITATION Join a VPN using an INVITIATION\n"
" network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
"\n");
printf("Report bugs to tinc@tinc-vpn.org.\n");
}
@ -151,6 +160,10 @@ static bool parse_options(int argc, char **argv) {
case 0: /* long option */
break;
case 'b':
tty = false;
break;
case 'c': /* config file */
confbase = xstrdup(optarg);
confbasegiven = true;
@ -240,19 +253,19 @@ static void disable_old_keys(const char *filename, const char *what) {
while(fgets(buf, sizeof buf, r)) {
if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
if((strstr(buf, " EC ") && strstr(what, "ECDSA")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
disabled = true;
block = true;
}
}
bool ecdsapubkey = !strncasecmp(buf, "ECDSAPublicKey", 14) && strchr(" \t=", buf[14]) && strstr(what, "ECDSA");
bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
if(ecdsapubkey)
if(ed25519pubkey)
disabled = true;
if(w) {
if(block || ecdsapubkey)
if(block || ed25519pubkey)
fputc('#', w);
if(fputs(buf, w) < 0) {
error = true;
@ -308,8 +321,7 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo
/* Check stdin and stdout */
if(ask && tty) {
/* Ask for a file and/or directory name. */
fprintf(stdout, "Please enter a file to save %s to [%s]: ", what, filename);
fflush(stdout);
fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
if(fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
@ -350,15 +362,15 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo
}
/*
Generate a public/private ECDSA keypair, and ask for a file to store
Generate a public/private Ed25519 keypair, and ask for a file to store
them in.
*/
static bool ecdsa_keygen(bool ask) {
static bool ed25519_keygen(bool ask) {
ecdsa_t *key;
FILE *f;
char *pubname, *privname;
fprintf(stderr, "Generating ECDSA keypair:\n");
fprintf(stderr, "Generating Ed25519 keypair:\n");
if(!(key = ecdsa_generate())) {
fprintf(stderr, "Error during key generation!\n");
@ -366,8 +378,8 @@ static bool ecdsa_keygen(bool ask) {
} else
fprintf(stderr, "Done.\n");
xasprintf(&privname, "%s" SLASH "ecdsa_key.priv", confbase);
f = ask_and_open(privname, "private ECDSA key", "a", ask, 0600);
xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
free(privname);
if(!f)
@ -385,16 +397,16 @@ static bool ecdsa_keygen(bool ask) {
if(name)
xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
else
xasprintf(&pubname, "%s" SLASH "ecdsa_key.pub", confbase);
xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
f = ask_and_open(pubname, "public ECDSA key", "a", ask, 0666);
f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
free(pubname);
if(!f)
return false;
char *pubkey = ecdsa_get_base64_public_key(key);
fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
free(pubkey);
fclose(f);
@ -412,6 +424,15 @@ static bool rsa_keygen(int bits, bool ask) {
FILE *f;
char *pubname, *privname;
// Make sure the key size is a multiple of 8 bits.
bits &= ~0x7;
// Force them to be between 1024 and 8192 bits long.
if(bits < 1024)
bits = 1024;
if(bits > 8192)
bits = 8192;
fprintf(stderr, "Generating %d bits keys:\n", bits);
if(!(key = rsa_generate(bits, 0x10001))) {
@ -471,7 +492,7 @@ bool recvline(int fd, char *line, size_t len) {
while(!(newline = memchr(buffer, '\n', blen))) {
int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
if(result == -1 && errno == EINTR)
if(result == -1 && sockerrno == EINTR)
continue;
else if(result <= 0)
return false;
@ -497,7 +518,7 @@ bool recvdata(int fd, char *data, size_t len) {
while(blen < len) {
int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
if(result == -1 && errno == EINTR)
if(result == -1 && sockerrno == EINTR)
continue;
else if(result <= 0)
return false;
@ -528,8 +549,8 @@ bool sendline(int fd, char *format, ...) {
blen++;
while(blen) {
int result = send(fd, p, blen, 0);
if(result == -1 && errno == EINTR)
int result = send(fd, p, blen, MSG_NOSIGNAL);
if(result == -1 && sockerrno == EINTR)
continue;
else if(result <= 0)
return false;
@ -709,7 +730,7 @@ bool connect_tincd(bool verbose) {
if(getaddrinfo(host, port, &hints, &res) || !res) {
if(verbose)
fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, strerror(errno));
fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
return false;
}
@ -740,6 +761,11 @@ bool connect_tincd(bool verbose) {
freeaddrinfo(res);
#endif
#ifdef SO_NOSIGPIPE
static const int one = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
#endif
char data[4096];
int version;
@ -790,16 +816,31 @@ static int cmd_start(int argc, char *argv[]) {
int nargc = 0;
char **nargv = xzalloc((optind + argc) * sizeof *nargv);
nargv[nargc++] = c;
char *arg0 = c;
#ifdef HAVE_MINGW
/*
Windows has no real concept of an "argv array". A command line is just one string.
The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
it uses quotes to handle spaces in arguments.
Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
*/
xasprintf(&arg0, "\"%s\"", arg0);
#endif
nargv[nargc++] = arg0;
for(int i = 1; i < optind; i++)
nargv[nargc++] = orig_argv[i];
for(int i = 1; i < argc; i++)
nargv[nargc++] = argv[i];
#ifdef HAVE_MINGW
execvp(c, nargv);
fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
return 1;
int status = spawnvp(_P_WAIT, c, nargv);
if (status == -1) {
fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
return 1;
}
return status;
#else
pid_t pid = fork();
if(pid == -1) {
@ -961,11 +1002,14 @@ static int cmd_dump(int argc, char *argv[]) {
break;
char node[4096];
char id[4096];
char from[4096];
char to[4096];
char subnet[4096];
char host[4096];
char port[4096];
char local_host[4096];
char local_port[4096];
char via[4096];
char nexthop[4096];
int cipher, digest, maclength, compression, distance, socket, weight;
@ -976,8 +1020,8 @@ static int cmd_dump(int argc, char *argv[]) {
switch(req) {
case REQ_DUMP_NODES: {
int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
if(n != 16) {
int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
if(n != 17) {
fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
return 1;
}
@ -1000,14 +1044,14 @@ static int cmd_dump(int argc, char *argv[]) {
} else {
if(only_reachable && !status.reachable)
continue;
printf("%s at %s port %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)\n",
node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
printf("%s id %s at %s port %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)\n",
node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
}
} break;
case REQ_DUMP_EDGES: {
int n = sscanf(line, "%*d %*d %s %s %s port %s %x %d", from, to, host, port, &options, &weight);
if(n != 6) {
int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
if(n != 8) {
fprintf(stderr, "Unable to parse edge dump from tincd.\n");
return 1;
}
@ -1019,7 +1063,7 @@ static int cmd_dump(int argc, char *argv[]) {
else if(do_graph == 2)
printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
} else {
printf("%s to %s at %s port %s options %x weight %d\n", from, to, host, port, options, weight);
printf("%s to %s at %s port %s local %s port %s options %x weight %d\n", from, to, host, port, local_host, local_port, options, weight);
}
} break;
@ -1262,7 +1306,7 @@ char *get_my_name(bool verbose) {
continue;
if(*value) {
fclose(f);
return strdup(value);
return replace_name(value);
}
}
@ -1279,12 +1323,14 @@ const var_t variables[] = {
{"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
{"BindToInterface", VAR_SERVER},
{"Broadcast", VAR_SERVER | VAR_SAFE},
{"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
{"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
{"DecrementTTL", VAR_SERVER},
{"Device", VAR_SERVER},
{"DeviceStandby", VAR_SERVER},
{"DeviceType", VAR_SERVER},
{"DirectOnly", VAR_SERVER},
{"ECDSAPrivateKeyFile", VAR_SERVER},
{"Ed25519PrivateKeyFile", VAR_SERVER},
{"ExperimentalProtocol", VAR_SERVER},
{"Forwarding", VAR_SERVER},
{"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
@ -1292,6 +1338,7 @@ const var_t variables[] = {
{"IffOneQueue", VAR_SERVER},
{"Interface", VAR_SERVER},
{"KeyExpire", VAR_SERVER},
{"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
{"LocalDiscovery", VAR_SERVER},
{"MACExpire", VAR_SERVER},
{"MaxConnectionBurst", VAR_SERVER},
@ -1321,8 +1368,8 @@ const var_t variables[] = {
{"ClampMSS", VAR_SERVER | VAR_HOST},
{"Compression", VAR_SERVER | VAR_HOST},
{"Digest", VAR_SERVER | VAR_HOST},
{"ECDSAPublicKey", VAR_HOST},
{"ECDSAPublicKeyFile", VAR_SERVER | VAR_HOST},
{"Ed25519PublicKey", VAR_HOST},
{"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
{"IndirectData", VAR_SERVER | VAR_HOST},
{"MACLength", VAR_SERVER | VAR_HOST},
{"PMTU", VAR_SERVER | VAR_HOST},
@ -1591,9 +1638,12 @@ static int cmd_config(int argc, char *argv[]) {
}
if(action < -1) {
if(!found)
if(found) {
return 0;
} else {
fprintf(stderr, "No matching configuration variables found.\n");
return 0;
return 1;
}
}
// Make sure we wrote everything...
@ -1606,7 +1656,7 @@ static int cmd_config(int argc, char *argv[]) {
if(action < 0 && !removed) {
remove(tmpfile);
fprintf(stderr, "No configuration variables deleted.\n");
return *value != 0;
return 1;
}
// Replace the configuration file with the new one
@ -1628,18 +1678,6 @@ static int cmd_config(int argc, char *argv[]) {
return 0;
}
bool check_id(const char *name) {
if(!name || !*name)
return false;
for(int i = 0; i < strlen(name); i++) {
if(!isalnum(name[i]) && name[i] != '_')
return false;
}
return true;
}
static bool try_bind(int port) {
struct addrinfo *ai = NULL;
struct addrinfo hint = {
@ -1710,8 +1748,7 @@ static int cmd_init(int argc, char *argv[]) {
} else if(argc < 2) {
if(tty) {
char buf[1024];
fprintf(stdout, "Enter the Name you want your tinc node to have: ");
fflush(stdout);
fprintf(stderr, "Enter the Name you want your tinc node to have: ");
if(!fgets(buf, sizeof buf, stdin)) {
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
return 1;
@ -1763,7 +1800,7 @@ static int cmd_init(int argc, char *argv[]) {
fprintf(f, "Name = %s\n", name);
fclose(f);
if(!rsa_keygen(2048, false) || !ecdsa_keygen(false))
if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
return 1;
check_port(name);
@ -1777,7 +1814,7 @@ static int cmd_init(int argc, char *argv[]) {
fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
return 1;
}
fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit '$0'!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
fclose(f);
}
#endif
@ -1795,7 +1832,7 @@ static int cmd_generate_keys(int argc, char *argv[]) {
if(!name)
name = get_my_name(false);
return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ecdsa_keygen(true));
return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
}
static int cmd_generate_rsa_keys(int argc, char *argv[]) {
@ -1810,7 +1847,7 @@ static int cmd_generate_rsa_keys(int argc, char *argv[]) {
return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
}
static int cmd_generate_ecdsa_keys(int argc, char *argv[]) {
static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
if(argc > 1) {
fprintf(stderr, "Too many arguments!\n");
return 1;
@ -1819,7 +1856,7 @@ static int cmd_generate_ecdsa_keys(int argc, char *argv[]) {
if(!name)
name = get_my_name(false);
return !ecdsa_keygen(true);
return !ed25519_keygen(true);
}
static int cmd_help(int argc, char *argv[]) {
@ -2067,6 +2104,72 @@ static int cmd_exchange_all(int argc, char *argv[]) {
return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
}
static int switch_network(char *name) {
if(fd >= 0) {
close(fd);
fd = -1;
}
free(confbase);
confbase = NULL;
free(pidfilename);
pidfilename = NULL;
free(logfilename);
logfilename = NULL;
free(unixsocketname);
unixsocketname = NULL;
free(tinc_conf);
free(hosts_dir);
free(prompt);
free(netname);
netname = strcmp(name, ".") ? xstrdup(name) : NULL;
make_names();
xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
xasprintf(&prompt, "%s> ", identname);
return 0;
}
static int cmd_network(int argc, char *argv[]) {
if(argc > 2) {
fprintf(stderr, "Too many arguments!\n");
return 1;
}
if(argc == 2)
return switch_network(argv[1]);
DIR *dir = opendir(confdir);
if(!dir) {
fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
return 1;
}
struct dirent *ent;
while((ent = readdir(dir))) {
if(*ent->d_name == '.')
continue;
if(!strcmp(ent->d_name, "tinc.conf")) {
printf(".\n");
continue;
}
char *fname;
xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
if(!access(fname, R_OK))
printf("%s\n", ent->d_name);
free(fname);
}
closedir(dir);
return 0;
}
static const struct {
const char *command;
int (*function)(int argc, char *argv[]);
@ -2094,7 +2197,7 @@ static const struct {
{"init", cmd_init},
{"generate-keys", cmd_generate_keys},
{"generate-rsa-keys", cmd_generate_rsa_keys},
{"generate-ecdsa-keys", cmd_generate_ecdsa_keys},
{"generate-ed25519-keys", cmd_generate_ed25519_keys},
{"help", cmd_help},
{"version", cmd_version},
{"info", cmd_info},
@ -2106,6 +2209,7 @@ static const struct {
{"exchange-all", cmd_exchange_all},
{"invite", cmd_invite},
{"join", cmd_join},
{"network", cmd_network},
{NULL, NULL},
};
@ -2232,7 +2336,6 @@ static char **completion (const char *text, int start, int end) {
#endif
static int cmd_shell(int argc, char *argv[]) {
char *prompt;
xasprintf(&prompt, "%s> ", identname);
int result = 0;
char buf[4096];
@ -2336,6 +2439,7 @@ int main(int argc, char *argv[]) {
program_name = argv[0];
orig_argv = argv;
orig_argc = argc;
tty = isatty(0) && isatty(1);
if(!parse_options(argc, argv))
return 1;
@ -2366,8 +2470,6 @@ int main(int argc, char *argv[]) {
srand(time(NULL));
crypto_init();
tty = isatty(0) && isatty(1);
if(optind >= argc)
return cmd_shell(argc, argv);

View file

@ -1,7 +1,7 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998-2005 Ivo Timmermans
2000-2013 Guus Sliepen <guus@tinc-vpn.org>
2000-2014 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>
@ -49,6 +49,7 @@
#include "control.h"
#include "crypto.h"
#include "device.h"
#include "event.h"
#include "logger.h"
#include "names.h"
#include "net.h"
@ -57,6 +58,7 @@
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
#include "version.h"
/* If nonzero, display usage information and exit. */
static bool show_help = false;
@ -106,7 +108,6 @@ static struct option const long_options[] = {
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
CRITICAL_SECTION mutex;
int main2(int argc, char **argv);
#endif
@ -303,6 +304,17 @@ static bool drop_privs(void) {
#ifdef HAVE_MINGW
# define setpriority(level) !SetPriorityClass(GetCurrentProcess(), (level))
static void stop_handler(void *data, int flags) {
event_exit();
}
static BOOL WINAPI console_ctrl_handler(DWORD type) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got console shutdown request");
if (WSASetEvent(stop_io.event) == FALSE)
abort();
return TRUE;
}
#else
# define NORMAL_PRIORITY_CLASS 0
# define BELOW_NORMAL_PRIORITY_CLASS 10
@ -320,8 +332,8 @@ int main(int argc, char **argv) {
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-2013 Ivo Timmermans, Guus Sliepen and others.\n"
VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
printf("Copyright (C) 1998-2014 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"
@ -371,15 +383,24 @@ int main(int argc, char **argv) {
#endif
#ifdef HAVE_MINGW
if(!do_detach || !init_service())
return main2(argc, argv);
else
return 1;
io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
if (stop_io.event == FALSE)
abort();
int result;
if(!do_detach || !init_service()) {
SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
result = main2(argc, argv);
} else
result = 1;
if (WSACloseEvent(stop_io.event) == FALSE)
abort();
io_del(&stop_io);
return result;
}
int main2(int argc, char **argv) {
InitializeCriticalSection(&mutex);
EnterCriticalSection(&mutex);
#endif
char *priority = NULL;
@ -440,9 +461,6 @@ int main2(int argc, char **argv) {
/* Shutdown properly. */
if(debug_level >= DEBUG_CONNECTIONS)
devops.dump_stats();
end:
close_network_connections();

View file

@ -21,6 +21,7 @@
#ifdef HAVE_CURSES
#undef KEY_EVENT /* There are conflicting declarations for KEY_EVENT in Windows wincon.h and curses.h. */
#include <curses.h>
#include "control_common.h"
@ -66,8 +67,10 @@ static float bscale = 1;
static const char *punit = "pkts";
static float pscale = 1;
static void update(int fd) {
sendline(fd, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
static bool update(int fd) {
if(!sendline(fd, "%d %d", CONTROL, REQ_DUMP_TRAFFIC))
return false;
gettimeofday(&cur, NULL);
timersub(&cur, &prev, &diff);
@ -90,13 +93,10 @@ static void update(int fd) {
int n = sscanf(line, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, &code, &req, name, &in_packets, &in_bytes, &out_packets, &out_bytes);
if(n == 2)
break;
return true;
if(n != 7) {
endwin();
fprintf(stderr, "Error receiving traffic information\n");
exit(1);
}
if(n != 7)
return false;
nodestats_t *found = NULL;
@ -133,6 +133,8 @@ static void update(int fd) {
found->out_packets = out_packets;
found->out_bytes = out_bytes;
}
return false;
}
static int cmpfloat(float a, float b) {
@ -213,7 +215,8 @@ static void redraw(void) {
for(int i = 0; i < n; i++)
sorted[i]->i = i;
qsort(sorted, n, sizeof *sorted, sortfunc);
if(sorted)
qsort(sorted, n, sizeof *sorted, sortfunc);
for(int i = 0, row = 3; i < n; i++, row++) {
nodestats_t *node = sorted[i];
@ -245,7 +248,9 @@ void top(int fd) {
bool running = true;
while(running) {
update(fd);
if(!update(fd))
break;
redraw();
switch(getch()) {

View file

@ -38,9 +38,6 @@ static int write_fd = -1;
static int state = 0;
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
enum request_type { REQ_NEW_CONTROL };
static struct request {
@ -159,22 +156,29 @@ static bool setup_device(void) {
}
void close_device(void) {
if(listen_fd >= 0)
close(listen_fd);
if(listen_fd >= 0) {
close(listen_fd); listen_fd = -1;
}
if(request_fd >= 0)
close(request_fd);
if(request_fd >= 0) {
close(request_fd); request_fd = -1;
}
if(data_fd >= 0)
close(data_fd);
if(data_fd >= 0) {
close(data_fd); data_fd = -1;
}
if(write_fd >= 0)
close(write_fd);
if(write_fd >= 0) {
close(write_fd); write_fd = -1;
}
unlink(device);
free(device);
if(iface) free(iface);
free(device); device = NULL;
if(iface) {
free(iface); iface = NULL;
}
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
@ -240,7 +244,7 @@ static bool read_packet(vpn_packet_t *packet) {
}
case 2: {
if((inlen = read(data_fd, packet->data, MTU)) <= 0) {
if((inlen = read(data_fd, DATA(packet), MTU)) <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
event_exit();
@ -249,8 +253,6 @@ static bool read_packet(vpn_packet_t *packet) {
packet->len = inlen;
device_total_in += packet->len;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
@ -273,7 +275,7 @@ 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(write_fd, packet->data, packet->len) < 0) {
if(write(write_fd, DATA(packet), packet->len) < 0) {
if(errno != EINTR && errno != EAGAIN) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
event_exit();
@ -282,21 +284,12 @@ static bool write_packet(vpn_packet_t *packet) {
return false;
}
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 uml_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -18,10 +18,10 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "logger.h"
#include "system.h"
#include "../src/logger.h"
#include "utils.h"
#include "xalloc.h"
static const char hexadecimals[] = "0123456789ABCDEF";
static const char base64_original[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@ -52,14 +52,16 @@ static int charhex2bin(char c) {
return toupper(c) - 'A' + 10;
}
int hex2bin(const char *src, char *dst, int length) {
int hex2bin(const char *src, void *vdst, int length) {
char *dst = vdst;
int i;
for(i = 0; i < length && isxdigit(src[i * 2]) && isxdigit(src[i * 2 + 1]); i++)
dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]);
return i;
}
int bin2hex(const char *src, char *dst, int length) {
int bin2hex(const void *vsrc, char *dst, int length) {
const char *src = vsrc;
for(int i = length - 1; i >= 0; i--) {
dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15];
dst[i * 2] = hexadecimals[(unsigned char) src[i] >> 4];
@ -68,12 +70,12 @@ int bin2hex(const char *src, char *dst, int length) {
return length * 2;
}
int b64decode(const char *src, char *dst, int length) {
int b64decode(const char *src, void *dst, int length) {
int i;
uint32_t triplet = 0;
unsigned char *udst = (unsigned char *)dst;
for(i = 0; i < length / 3 * 4 && src[i]; i++) {
for(i = 0; i < length && src[i]; i++) {
triplet |= base64_decode[src[i] & 0xff] << (6 * (i & 3));
if((i & 3) == 3) {
if(triplet & 0xff000000U)
@ -99,7 +101,7 @@ int b64decode(const char *src, char *dst, int length) {
}
}
static int b64encode_internal(const char *src, char *dst, int length, const char *alphabet) {
static int b64encode_internal(const void *src, char *dst, int length, const char *alphabet) {
uint32_t triplet;
const unsigned char *usrc = (unsigned char *)src;
int si = length / 3 * 3;
@ -112,14 +114,14 @@ static int b64encode_internal(const char *src, char *dst, int length, const char
dst[di + 1] = alphabet[triplet & 63]; triplet >>= 6;
dst[di + 2] = alphabet[triplet];
dst[di + 3] = 0;
length = di + 2;
length = di + 3;
break;
case 1:
triplet = usrc[si];
dst[di] = alphabet[triplet & 63]; triplet >>= 6;
dst[di + 1] = alphabet[triplet];
dst[di + 2] = 0;
length = di + 1;
length = di + 2;
break;
default:
dst[di] = 0;
@ -140,11 +142,11 @@ static int b64encode_internal(const char *src, char *dst, int length, const char
return length;
}
int b64encode(const char *src, char *dst, int length) {
int b64encode(const void *src, char *dst, int length) {
return b64encode_internal(src, dst, length, base64_original);
}
int b64encode_urlsafe(const char *src, char *dst, int length) {
int b64encode_urlsafe(const void *src, char *dst, int length) {
return b64encode_internal(src, dst, length, base64_urlsafe);
}
@ -177,3 +179,54 @@ unsigned int bitfield_to_int(const void *bitfield, size_t size) {
memcpy(&value, bitfield, size);
return value;
}
bool check_id(const char *id) {
if(!id || !*id)
return false;
for(; *id; id++)
if(!isalnum(*id) && *id != '_')
return false;
return true;
}
/* Windows doesn't define HOST_NAME_MAX. */
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
char *replace_name(const char *name) {
char *ret_name;
if (name[0] == '$') {
char *envname = getenv(name + 1);
char hostname[HOST_NAME_MAX+1];
if (!envname) {
if (strcmp(name + 1, "HOST")) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
return NULL;
}
if (gethostname(hostname, sizeof hostname) || !*hostname) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", sockstrerror(sockerrno));
return NULL;
}
hostname[HOST_NAME_MAX] = 0;
envname = hostname;
}
ret_name = xstrdup(envname);
for (char *c = ret_name; *c; c++)
if (!isalnum(*c))
*c = '_';
} else {
ret_name = xstrdup(name);
}
if (!check_id(ret_name)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
free(ret_name);
return NULL;
}
return ret_name;
}

View file

@ -21,12 +21,12 @@
#ifndef __TINC_UTILS_H__
#define __TINC_UTILS_H__
extern int hex2bin(const char *src, char *dst, int length);
extern int bin2hex(const char *src, char *dst, int length);
extern int hex2bin(const char *src, void *dst, int length);
extern int bin2hex(const void *src, char *dst, int length);
extern int b64encode(const char *src, char *dst, int length);
extern int b64encode_urlsafe(const char *src, char *dst, int length);
extern int b64decode(const char *src, char *dst, int length);
extern int b64encode(const void *src, char *dst, int length);
extern int b64encode_urlsafe(const void *src, char *dst, int length);
extern int b64decode(const char *src, void *dst, int length);
#ifdef HAVE_MINGW
extern const char *winerror(int);
@ -37,6 +37,7 @@ extern const char *winerror(int);
#define sockmsgsize(x) ((x) == WSAEMSGSIZE)
#define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
#define sockinuse(x) ((x) == WSAEADDRINUSE)
#define socknotconn(x) ((x) == WSAENOTCONN)
#else
#define sockerrno errno
#define sockstrerror(x) strerror(x)
@ -44,8 +45,12 @@ extern const char *winerror(int);
#define sockmsgsize(x) ((x) == EMSGSIZE)
#define sockinprogress(x) ((x) == EINPROGRESS)
#define sockinuse(x) ((x) == EADDRINUSE)
#define socknotconn(x) ((x) == ENOTCONN)
#endif
extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
extern bool check_id(const char *);
char *replace_name(const char *name);
#endif /* __TINC_UTILS_H__ */

View file

@ -36,9 +36,6 @@ static int port = 0;
static char *group = NULL;
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
libvdeplug_dynopen(plug);
@ -85,19 +82,22 @@ static bool setup_device(void) {
}
static void close_device(void) {
if(conn)
plug.vde_close(conn);
if(conn) {
plug.vde_close(conn); conn = NULL;
}
if(plug.dl_handle)
libvdeplug_dynclose(plug);
free(device);
free(device); device = NULL;
free(iface);
free(iface); iface = NULL;
device_info = NULL;
}
static bool read_packet(vpn_packet_t *packet) {
int lenin = (ssize_t)plug.vde_recv(conn, packet->data, MTU, 0);
int lenin = (ssize_t)plug.vde_recv(conn, DATA(packet), MTU, 0);
if(lenin <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
event_exit();
@ -105,14 +105,14 @@ static bool read_packet(vpn_packet_t *packet) {
}
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) {
if((ssize_t)plug.vde_send(conn, packet->data, packet->len, 0) < 0) {
if((ssize_t)plug.vde_send(conn, DATA(packet), packet->len, 0) < 0) {
if(errno != EINTR && errno != EAGAIN) {
logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
event_exit();
@ -121,21 +121,12 @@ static bool write_packet(vpn_packet_t *packet) {
return false;
}
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 vde_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

24
src/version.c Normal file
View file

@ -0,0 +1,24 @@
/*
version.c -- version information
Copyright (C) 2014 Etienne Dechamps <etienne@edechamps.fr>
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 "version.h"
/* This file is always rebuilt (even if there are no changes) so that the following is updated */
const char* const BUILD_DATE = __DATE__;
const char* const BUILD_TIME = __TIME__;

26
src/version.h Normal file
View file

@ -0,0 +1,26 @@
/*
version.h -- header for version.c
Copyright (C) 2014 Etienne Dechamps <etienne@edechamps.fr>
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_VERSION_H__
#define __TINC_VERSION_H__
extern const char* const BUILD_DATE;
extern const char* const BUILD_TIME;
#endif /* __TINC_VERSION_H__ */