diff --git a/Makefile.am b/Makefile.am index e10a212b..67dc0bc1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,248 +1,253 @@ # # Copyright (C) 2010-2017 Red Hat, Inc. All rights reserved. # # Author: Fabio M. Di Nitto # # This software licensed under GPL-2.0+, LGPL-2.0+ # MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ ltmain.sh compile config.h.in config.h.in~ \ autoscan.log configure.scan test-driver include $(top_srcdir)/build-aux/check.mk AUTOMAKE_OPTIONS = foreign AM_DISTCHECK_CONFIGURE_FLAGS = --enable-kronosnetd ACLOCAL_AMFLAGS = -I m4 SPEC = $(PACKAGE_NAME).spec DEBCHANGELOG = debian/changelog DEBPOSTINST = debian/kronosnetd.postinst TARGZFILE = $(PACKAGE_NAME)-$(VERSION).tar.gz EXTRA_DIST = build-aux autogen.sh .version \ debian $(DEBCHANGELOG) $(DEBPOSTINST) \ $(SPEC) $(SPEC).in SUBDIRS = init libtap libknet kronosnetd if BUILD_DOCS SUBDIRS += docs endif if BUILD_POC SUBDIRS += poc-code endif dist_doc_DATA = \ COPYING.applications \ COPYING.libraries \ COPYRIGHT \ README.licence \ README \ TODO all-local: $(SPEC) $(DEBCHANGELOG) $(DEBPOSTINST) clean-local: rm -f $(SPEC) rm -f $(DEBPOSTINST) distclean-local: rm -f $(PACKAGE_NAME)-*.tar.* $(PACKAGE_NAME)-*.sha256* tag-* maintainer-clean-local: make -f debian/rules clean || true rm -rf m4 $(DEBCHANGELOG) $(DEBPOSTINST) debian/patches ## make rpm/srpm section. $(SPEC): $(SPEC).in .version config.status rm -f $@-t $@ date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ if [ -f .tarball-version ]; then \ gitver="$(shell cat .tarball-version)" && \ rpmver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ else \ gitver="$(shell GIT_DIR=$(abs_srcdir)/.git git describe --abbrev=4 --match='v*' HEAD 2>/dev/null)" && \ rpmver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \ alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \ vtag=`echo $$gitver | sed -e "s/-.*//g"` && \ numcomm=`GIT_DIR=$(abs_srcdir)/.git git rev-list $$vtag..HEAD | wc -l` && \ GIT_DIR=$(abs_srcdir)/.git git update-index --refresh > /dev/null 2>&1 || true && \ dirty=`GIT_DIR=$(abs_srcdir)/.git git diff-index --name-only HEAD 2>/dev/null`; \ fi && \ if [ -n "$$dirty" ]; then dirty="dirty"; else dirty=""; fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#%glo.*alpha.*##g" \ -e "s#%glo.*numcomm.*##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ else \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ fi; \ if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi if BUILDSCTP sed -i -e "s#@sctp@#bcond_without#g" $@-t else sed -i -e "s#@sctp@#bcond_with#g" $@-t endif if BUILDCRYPTONSS sed -i -e "s#@nss@#bcond_without#g" $@-t else sed -i -e "s#@nss@#bcond_with#g" $@-t endif +if BUILDCRYPTOOPENSSL + sed -i -e "s#@openssl@#bcond_without#g" $@-t +else + sed -i -e "s#@openssl@#bcond_with#g" $@-t +endif if BUILDCOMPZLIB sed -i -e "s#@zlib@#bcond_without#g" $@-t else sed -i -e "s#@zlib@#bcond_with#g" $@-t endif if BUILDCOMPLZ4 sed -i -e "s#@lz4@#bcond_without#g" $@-t else sed -i -e "s#@lz4@#bcond_with#g" $@-t endif if BUILDCOMPLZO2 sed -i -e "s#@lzo2@#bcond_without#g" $@-t else sed -i -e "s#@lzo2@#bcond_with#g" $@-t endif if BUILDCOMPLZMA sed -i -e "s#@lzma@#bcond_without#g" $@-t else sed -i -e "s#@lzma@#bcond_with#g" $@-t endif if BUILDCOMPBZIP2 sed -i -e "s#@bzip2@#bcond_without#g" $@-t else sed -i -e "s#@bzip2@#bcond_with#g" $@-t endif if BUILD_KRONOSNETD sed -i -e "s#@kronosnetd@#bcond_without#g" $@-t else sed -i -e "s#@kronosnetd@#bcond_with#g" $@-t endif if BUILD_LIBTAP sed -i -e "s#@libtap@#bcond_without#g" $@-t else sed -i -e "s#@libtap@#bcond_with#g" $@-t endif sed -i -e "s#@defaultadmgroup@#$(DEFAULTADMGROUP)#g" $@-t chmod a-w $@-t mv $@-t $@ $(TARGZFILE): $(MAKE) dist RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" srpm: clean $(MAKE) $(SPEC) $(TARGZFILE) rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean $(MAKE) $(SPEC) $(TARGZFILE) rpmbuild $(RPMBUILDOPTS) -ba $(SPEC) ## make deb $(DEBPOSTINST): $(DEBPOSTINST).in config.h mkdir -p debian rm -f $@-t $@ sed -e "s#@defaultadmgroup@#$(DEFAULTADMGROUP)#g" $< > $@-t chmod a-w $@-t mv $@-t $@ $(DEBCHANGELOG): $(DEBCHANGELOG).in .version mkdir -p debian rm -f $@-t $@ date="$(shell LC_ALL=C date -R)" && \ if [ -f .tarball-version ]; then \ gitver="$(shell cat .tarball-version)" && \ debver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ else \ gitver="$(shell GIT_DIR=$(abs_srcdir)/.git git describe --abbrev=4 --match='v*' HEAD 2>/dev/null)" && \ debver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \ alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \ vtag=`echo $$gitver | sed -e "s/-.*//g"` && \ numcomm=`GIT_DIR=$(abs_srcdir)/.git git rev-list $$vtag..HEAD | wc -l` && \ GIT_DIR=$(abs_srcdir)/.git git update-index --refresh > /dev/null 2>&1 || true && \ dirty=`GIT_DIR=$(abs_srcdir)/.git git diff-index --name-only HEAD 2>/dev/null`; \ fi && \ if [ -n "$$dirty" ]; then dirty="-dirty"; else dirty=""; fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$debver#g" \ -e "s#-@alphatag@##g" \ -e "s#.@numcomm@##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ else \ sed \ -e "s#@version@#$$debver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ fi; chmod a-w $@-t mv $@-t $@ deb-clean: make -f debian/rules clean ./configure debs: deb-clean dist if [ -f .tarball-version ]; then \ gitver="$(shell cat .tarball-version)"; \ else \ gitver="$(shell git describe --abbrev=4 --match='v*' HEAD 2>/dev/null)" && \ gitver=`echo $$gitver | sed -e "s/^v//" -e "s/-g/-/" -e "s/-/./"`; \ fi && \ cp $(TARGZFILE) ../$(PACKAGE_NAME)_$$gitver.orig.tar.gz dpkg-buildpackage -uc -us # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: gen-ChangeLog echo $(VERSION) > $(distdir)/.tarball-version gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d .git; then \ LC_ALL=C $(top_srcdir)/build-aux/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi diff --git a/configure.ac b/configure.ac index 5fb2f63c..5dabc80f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,488 +1,498 @@ # # Copyright (C) 2010-2017 Red Hat, Inc. All rights reserved. # # Authors: Fabio M. Di Nitto # Federico Simoncelli # # This software licensed under GPL-2.0+, LGPL-2.0+ # # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # AC_PREREQ([2.63]) AC_INIT([kronosnet], m4_esyscmd([build-aux/git-version-gen .tarball-version]), [devel@lists.kronosnet.org]) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([1.11.1 dist-bzip2 dist-xz color-tests -Wno-portability subdir-objects]) LT_PREREQ([2.2.6]) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([kronosnetd/main.c]) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) systemddir=${prefix}/lib/systemd/system if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$systemddir" = "NONE/lib/systemd/system"; then systemddir=/lib/systemd/system fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi # Checks for programs. if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR(["you don't seem to have GNU make; it is required"]) fi AC_PROG_AWK AC_PROG_GREP AC_PROG_SED AC_PROG_CPP AC_PROG_CC AC_PROG_CC_C99 if test "x$ac_cv_prog_cc_c99" = "xno"; then AC_MSG_ERROR(["C99 support is required"]) fi AC_PROG_LN_S AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_CXX AC_PROG_RANLIB AC_CHECK_PROGS([PUBLICAN], [publican], [:]) AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) AC_ARG_ENABLE([libknet-sctp], [ --disable-libknet-sctp : disable libknet SCTP support ],, [ enable_libknet_sctp="yes" ]) AM_CONDITIONAL([BUILDSCTP], [test x$enable_libknet_sctp = xyes]) AC_ARG_ENABLE([crypto-all], [ --disable-crypto-all : disable libknet all crypto modules support ],, [ enable_crypto_all="yes" ]) AC_ARG_ENABLE([crypto-nss], [ --disable-crypto-nss : disable libknet nss support ],, [ enable_crypto_nss="$enable_crypto_all" ]) AM_CONDITIONAL([BUILDCRYPTONSS], [test x$enable_crypto_nss = xyes]) +AC_ARG_ENABLE([crypto-openssl], + [ --disable-crypto-openssl : disable libknet openssl support ],, + [ enable_crypto_openssl="$enable_crypto_all" ]) +AM_CONDITIONAL([BUILDCRYPTOOPENSSL], [test x$enable_crypto_openssl = xyes]) + AC_ARG_ENABLE([compress-all], [ --disable-compress-all : disable libknet all compress modules support ],, [ enable_compress_all="yes" ]) AC_ARG_ENABLE([compress-zlib], [ --disable-compress-zlib : disable libknet zlib support ],, [ enable_compress_zlib="$enable_compress_all" ]) AM_CONDITIONAL([BUILDCOMPZLIB], [test x$enable_compress_zlib = xyes]) AC_ARG_ENABLE([compress-lz4], [ --disable-compress-lz4 : disable libknet lz4 support ],, [ enable_compress_lz4="$enable_compress_all" ]) AM_CONDITIONAL([BUILDCOMPLZ4], [test x$enable_compress_lz4 = xyes]) AC_ARG_ENABLE([compress-lzo2], [ --disable-compress-lzo2 : disable libknet lzo2 support ],, [ enable_compress_lzo2="$enable_compress_all" ]) AM_CONDITIONAL([BUILDCOMPLZO2], [test x$enable_compress_lzo2 = xyes]) AC_ARG_ENABLE([compress-lzma], [ --disable-compress-lzma : disable libknet lzma support ],, [ enable_compress_lzma="$enable_compress_all" ]) AM_CONDITIONAL([BUILDCOMPLZMA], [test x$enable_compress_lzma = xyes]) AC_ARG_ENABLE([compress-bzip2], [ --disable-compress-bzip2 : disable libknet bzip2 support ],, [ enable_compress_bzip2="$enable_compress_all" ]) AM_CONDITIONAL([BUILDCOMPBZIP2], [test x$enable_compress_bzip2 = xyes]) AC_ARG_ENABLE([poc], [ --disable-poc : disable building poc code ],, [ enable_poc="yes" ]) AM_CONDITIONAL([BUILD_POC], [test x$enable_poc = xyes]) AC_ARG_ENABLE([kronosnetd], [ --enable-kronosnetd : Kronosnetd support ],, [ enable_kronosnetd="no" ]) AM_CONDITIONAL([BUILD_KRONOSNETD], [test x$enable_kronosnetd = xyes]) AC_ARG_ENABLE([libtap], [ --enable-libtap : libtap support ],, [ enable_libtap="no" ]) if test "x$enable_kronosnetd" = xyes; then enable_libtap=yes fi AM_CONDITIONAL([BUILD_LIBTAP], [test x$enable_libtap = xyes]) ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { saveCPPFLAGS="$CPPFLAGS" CPPFLAGS="$@" if echo $CC | grep -q clang; then CPPFLAGS="-Werror $CPPFLAGS" fi AC_MSG_CHECKING([whether $CC supports "$@"]) AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) CPPFLAGS="$saveCPPFLAGS" return $RC } # helper macro to check libs without adding them to LIBS check_lib_no_libs() { lib_no_libs_arg1=$1 shift lib_no_libs_arg2=$1 shift lib_no_libs_args=$@ AC_CHECK_LIB([$lib_no_libs_arg1], [$lib_no_libs_arg2],,, [$lib_no_libs_args]) LIBS=$ac_check_lib_save_LIBS } # Checks for C features AC_C_INLINE # Checks for libraries. AC_CHECK_LIB([pthread], [pthread_create], [AC_SUBST([pthread_LIBS], [-lpthread])], [AC_MSG_ERROR([kronosnet requires pthread library])]) AC_CHECK_LIB([m], [ceil], [AC_SUBST([m_LIBS], [-lm])], [AC_MSG_ERROR([kronosnet requires m library])]) AC_CHECK_LIB([rt], [clock_gettime], [AC_SUBST([rt_LIBS], [-lrt])], [AC_MSG_ERROR([kronosnet requires rt library])]) AC_SEARCH_LIBS([dlopen], [dl dld], [AC_SUBST([dl_LIBS], [$ac_res])], [AC_MSG_ERROR([kronosnet requires dl library])]) # workaround pkg-config bootstrapping PKG_CHECK_MODULES([foobarbaz],[foobarbaz], [AC_MSG_NOTICE([bootstrapping pkg-config])], [AC_MSG_NOTICE([bootstrapping pkg-config])]) # crypto libraries checks if test "x$enable_crypto_nss" = xyes; then PKG_CHECK_MODULES([nss],[nss]) AC_DEFINE_UNQUOTED([BUILDCRYPTONSS], [1], [Enable nss crypto]) fi +if test "x$enable_crypto_openssl" = xyes; then + PKG_CHECK_MODULES([openssl],[openssl]) + AC_DEFINE_UNQUOTED([BUILDCRYPTOOPENSSL], [1], [Enable openssl crypto]) +fi + # compress libraries checks if test "x$enable_compress_zlib" = xyes; then PKG_CHECK_MODULES([zlib], [zlib]) AC_DEFINE_UNQUOTED([BUILDCOMPZLIB], [1], [Enable zlib compression]) fi if test "x$enable_compress_lz4" = xyes; then PKG_CHECK_MODULES([liblz4], [liblz4]) AC_DEFINE_UNQUOTED([BUILDCOMPLZ4], [1], [Enable lz4 compress]) fi if test "x$enable_compress_lzo2" = xyes; then PKG_CHECK_MODULES([lzo2], [lzo2],, [AC_CHECK_HEADERS([lzo/lzo1x.h], [AC_CHECK_LIB([lzo2], [lzo1x_decompress_safe], [AC_SUBST([lzo2_LIBS], [-llzo2])])], [AC_MSG_ERROR(["missing required lzo/lzo1x.h header"])])]) AC_DEFINE_UNQUOTED([BUILDCOMPLZO2], [1], [Enable lzo2 compress]) fi if test "x$enable_compress_lzma" = xyes; then PKG_CHECK_MODULES([liblzma], [liblzma]) AC_DEFINE_UNQUOTED([BUILDCOMPLZMA], [1], [Enable lzma compress]) fi if test "x$enable_compress_bzip2" = xyes; then PKG_CHECK_MODULES([bzip2], [bzip2],, [AC_CHECK_HEADERS([bzlib.h], [AC_CHECK_LIB([bz2], [BZ2_bzBuffToBuffCompress], [AC_SUBST([bzip2_LIBS], [-lbz2])])], [AC_MSG_ERROR(["missing required bzlib.h"])])]) AC_DEFINE_UNQUOTED([BUILDCOMPBZIP2], [1], [Enable bzip2 compress]) fi # Checks for header files. AC_CHECK_HEADERS([fcntl.h]) AC_CHECK_HEADERS([stdlib.h]) AC_CHECK_HEADERS([string.h]) AC_CHECK_HEADERS([strings.h]) AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADERS([syslog.h]) AC_CHECK_HEADERS([unistd.h]) AC_CHECK_HEADERS([netinet/in.h]) AC_CHECK_HEADERS([sys/socket.h]) AC_CHECK_HEADERS([arpa/inet.h]) AC_CHECK_HEADERS([netdb.h]) AC_CHECK_HEADERS([limits.h]) AC_CHECK_HEADERS([stdint.h]) AC_CHECK_HEADERS([sys/epoll.h]) if test "x$enable_libknet_sctp" = xyes; then AC_CHECK_HEADERS([netinet/sctp.h],, [AC_MSG_ERROR(["missing required SCTP headers"])]) fi # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_PID_T AC_TYPE_SSIZE_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_INT32_T # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([memset]) AC_CHECK_FUNCS([strdup]) AC_CHECK_FUNCS([strerror]) AC_CHECK_FUNCS([dup2]) AC_CHECK_FUNCS([select]) AC_CHECK_FUNCS([socket]) AC_CHECK_FUNCS([inet_ntoa]) AC_CHECK_FUNCS([memmove]) AC_CHECK_FUNCS([strchr]) AC_CHECK_FUNCS([atexit]) AC_CHECK_FUNCS([ftruncate]) AC_CHECK_FUNCS([strrchr]) AC_CHECK_FUNCS([strstr]) AC_CHECK_FUNCS([clock_gettime]) AC_CHECK_FUNCS([strcasecmp]) AC_CHECK_FUNCS([kevent]) # if neither sys/epoll.h nor kevent are present, we should fail. if test "x$ac_cv_header_sys_epoll_h" = xno && test "x$ac_cv_func_kevent" = xno; then AC_MSG_ERROR([Both epoll and kevent unavailable on this OS]) fi if test "x$ac_cv_header_sys_epoll_h" = xyes && test "x$ac_cv_func_kevent" = xyes; then AC_MSG_ERROR([Both epoll and kevent available on this OS, please contact the maintainers to fix the code]) fi # checks (for kronosnetd) if test "x$enable_kronosnetd" = xyes; then AC_CHECK_HEADERS([security/pam_appl.h], [AC_CHECK_LIB([pam], [pam_start])], [AC_MSG_ERROR([Unable to find LinuxPAM devel files])]) AC_CHECK_HEADERS([security/pam_misc.h], [AC_CHECK_LIB([pam_misc], [misc_conv])], [AC_MSG_ERROR([Unable to find LinuxPAM MISC devel files])]) PKG_CHECK_MODULES([libqb], [libqb]) AC_CHECK_LIB([qb], [qb_log_thread_priority_set], [have_qb_log_thread_priority_set="yes"], [have_qb_log_thread_priority_set="no"]) if test "x${have_qb_log_thread_priority_set}" = xyes; then AC_DEFINE_UNQUOTED([HAVE_QB_LOG_THREAD_PRIORITY_SET], [1], [have qb_log_thread_priority_set]) fi fi # local options AC_ARG_ENABLE([debug], [ --enable-debug enable debug build. ], [ default="no" ]) AC_ARG_ENABLE([publicandocs], [ --enable-publicandocs enable docs build. ], [ default="no" ]) AC_ARG_WITH([initdefaultdir], [ --with-initdefaultdir : path to /etc/sysconfig/.. or /etc/default dir. ], [ INITDEFAULTDIR="$withval" ], [ INITDEFAULTDIR="$sysconfdir/default" ]) AC_ARG_WITH([initddir], [ --with-initddir=DIR : path to init script directory. ], [ INITDDIR="$withval" ], [ INITDDIR="$sysconfdir/init.d" ]) AC_ARG_WITH([systemddir], [ --with-systemddir=DIR : path to systemd unit files directory. ], [ SYSTEMDDIR="$withval" ], [ SYSTEMDDIR="$systemddir" ]) AC_ARG_WITH([syslogfacility], [ --with-syslogfacility=FACILITY default syslog facility. ], [ SYSLOGFACILITY="$withval" ], [ SYSLOGFACILITY="LOG_DAEMON" ]) AC_ARG_WITH([sysloglevel], [ --with-sysloglevel=LEVEL default syslog level. ], [ SYSLOGLEVEL="$withval" ], [ SYSLOGLEVEL="LOG_INFO" ]) AC_ARG_WITH([defaultadmgroup], [ --with-defaultadmgroup=GROUP define PAM group. Users part of this group will be allowed to configure kronosnet. Others will only receive read-only rights. ], [ DEFAULTADMGROUP="$withval" ], [ DEFAULTADMGROUP="kronosnetadm" ]) ## random vars LOGDIR=${localstatedir}/log/ RUNDIR=${localstatedir}/run/ DEFAULT_CONFIG_DIR=${sysconfdir}/kronosnet ## do subst AM_CONDITIONAL([BUILD_DOCS], [test "x${enable_publicandocs}" = xyes]) AM_CONDITIONAL([DEBUG], [test "x${enable_debug}" = xyes]) AC_SUBST([DEFAULT_CONFIG_DIR]) AC_SUBST([INITDEFAULTDIR]) AC_SUBST([INITDDIR]) AC_SUBST([SYSTEMDDIR]) AC_SUBST([LOGDIR]) AC_SUBST([DEFAULTADMGROUP]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_DIR], ["$(eval echo ${DEFAULT_CONFIG_DIR})"], [Default config directory]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE], ["$(eval echo ${DEFAULT_CONFIG_DIR}/kronosnetd.conf)"], [Default config file]) AC_DEFINE_UNQUOTED([LOGDIR], ["$(eval echo ${LOGDIR})"], [Default logging directory]) AC_DEFINE_UNQUOTED([DEFAULT_LOG_FILE], ["$(eval echo ${LOGDIR}/kronosnetd.log)"], [Default log file]) AC_DEFINE_UNQUOTED([RUNDIR], ["$(eval echo ${RUNDIR})"], [Default run directory]) AC_DEFINE_UNQUOTED([SYSLOGFACILITY], [$(eval echo ${SYSLOGFACILITY})], [Default syslog facility]) AC_DEFINE_UNQUOTED([SYSLOGLEVEL], [$(eval echo ${SYSLOGLEVEL})], [Default syslog level]) AC_DEFINE_UNQUOTED([DEFAULTADMGROUP], ["$(eval echo ${DEFAULTADMGROUP})"], [Default admin group]) ## *FLAGS handling ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" else OPT_CFLAGS="-O3" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" all extra unused shadow missing-prototypes missing-declarations suggest-attribute=noreturn suggest-attribute=format strict-prototypes declaration-after-statement pointer-arith write-strings cast-align bad-function-cast missing-format-attribute float-equal format=2 format-signedness format-security format-nonliteral no-long-long unsigned-char gnu89-inline no-strict-aliasing error address cpp overflow parentheses sequence-point switch shift-overflow=2 overlength-strings retundent-decls init-self uninitialized unused-but-set-variable unused-function unused-result unused-value unused-variable unknown-pragmas no-unused-parameter " for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done CFLAGS="$ENV_CFLAGS $lt_prog_compiler_pic $OPT_CFLAGS $GDB_FLAGS \ $EXTRA_WARNINGS $WERROR_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS $lt_prog_compiler_pic -Wl,--as-needed" AC_CONFIG_FILES([ Makefile init/Makefile libtap/Makefile libtap/libtap.pc kronosnetd/Makefile kronosnetd/kronosnetd.logrotate libknet/Makefile libknet/libknet.pc libknet/tests/Makefile docs/Makefile poc-code/Makefile poc-code/iov-hash/Makefile poc-code/access-list/Makefile ]) AC_OUTPUT diff --git a/debian/control b/debian/control index 9fe1eaa0..0e6dd15f 100644 --- a/debian/control +++ b/debian/control @@ -1,67 +1,67 @@ Source: kronosnet Priority: extra Maintainer: Fabio M. Di Nitto -Build-Depends: debhelper (>= 7.0.50~), autoconf, autotools-dev, libtool, libqb-dev (>= 0.14.3), libnss3-dev, libnspr4-dev, pkg-config, libpam-dev, zlib1g-dev, liblz4-dev (>= 1.7.3), liblzo2-dev, liblzma-dev, libbz2-dev, libsctp-dev +Build-Depends: debhelper (>= 7.0.50~), autoconf, autotools-dev, libtool, libqb-dev (>= 0.14.3), libnss3-dev, libnspr4-dev, libssl-dev, pkg-config, libpam-dev, zlib1g-dev, liblz4-dev (>= 1.7.3), liblzo2-dev, liblzma-dev, libbz2-dev, libsctp-dev Standards-Version: 3.8.4 Section: admin Package: kronosnetd Section: admin Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Multipoint-to-Multipoint VPN daemon The kronosnet daemon is a bridge between kronosnet switching engine and kernel network tap devices, to create and administer a distributed LAN over multipoint-to-multipoint VPNs. The daemon does a poor attempt to provide a configure UI similar to other known network devices/tools (Cisco, quagga). Beside looking horrific, it allows runtime changes and reconfiguration of the kronosnet(s) without daemon reload or service disruption. . This package contains the kronosnet daemon and utils. Package: libtap1 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Simple userland wrapper around kernel tap devices This is an over-engineered commodity library to manage a pool of tap devices and provides the basic pre-up.d/up.d/down.d/post-down.d infrastructure. . This package contains libraries that should be used by libtap clients. Package: libtap-dev Section: libdevel Architecture: any Depends: libtap1 (= ${binary:Version}), ${misc:Depends} Description: Simple userland wrapper around kernel tap devices (developer files) This is an over-engineered commodity library to manage a pool of tap devices and provides the basic pre-up.d/up.d/down.d/post-down.d infrastructure. . This package contains header files required to build clients for libtap clients. Package: libknet1 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: kronosnet core switching implementation The whole kronosnet core is implemented in this library. Please refer to the not-yet-existing documentation for further information. . This package contains libraries that should be used by libknet clients. Package: libknet-dev Section: libdevel Architecture: any Depends: libknet1 (= ${binary:Version}), ${misc:Depends} Description: kronosnet core switching implementation (developer files) The whole kronosnet core is implemented in this library. Please refer to the not-yet-existing documentation for further information. . This package contains header files required to build clients for libknet clients. diff --git a/kronosnet.spec.in b/kronosnet.spec.in index 5e9f70ef..89d46b0f 100644 --- a/kronosnet.spec.in +++ b/kronosnet.spec.in @@ -1,336 +1,348 @@ ############################################################################### ############################################################################### ## ## Copyright (C) 2010-2017 Red Hat, Inc. All rights reserved. ## ## This copyrighted material is made available to anyone wishing to use, ## modify, copy, or redistribute it subject to the terms and conditions ## of the GNU General Public License v.2 or higher ## ############################################################################### ############################################################################### # keep around ready for later user %global alphatag @alphatag@ %global numcomm @numcomm@ %global dirty @dirty@ # set defaults from ./configure invokation %@sctp@ sctp %@nss@ nss +%@openssl@ openssl %@zlib@ zlib %@lz4@ lz4 %@lzo2@ lzo2 %@lzma@ lzma %@bzip2@ bzip2 %@kronosnetd@ kronosnetd %@libtap@ libtap %if %{with sctp} %global buildsctp 1 %endif %if %{with nss} %global buildcryptonss 1 %endif +%if %{with openssl} +%global buildcryptoopenssl 1 +%endif %if %{with zlib} %global buildcompresszlib 1 %endif %if %{with lz4} %global buildcompresslz4 1 %endif %if %{with lzo2} %global buildcompresslzo2 1 %endif %if %{with lzma} %global buildcompresslzma 1 %endif %if %{with bzip2} %global buildcompressbzip2 1 %endif %if %{with libtap} %global buildlibtap 1 %endif %if %{with kronosnetd} %global buildlibtap 1 %global buildkronosnetd 1 %endif # main (empty) package # http://www.rpm.org/max-rpm/s1-rpm-subpack-spec-file-changes.html Name: kronosnet Summary: Multipoint-to-Multipoint VPN daemon Version: @version@ Release: 1%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist} License: GPLv2+ and LGPLv2+ Group: System Environment/Base URL: https://github.com/fabbione/kronosnet/ Source0: https://github.com/fabbione/kronosnet/archive/%{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}.tar.gz ## Setup/build bits BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) # Build dependencies %if %{defined buildsctp} BuildRequires: lksctp-tools-devel %endif %if %{defined buildcryptonss} BuildRequires: nss-devel %endif +%if %{defined buildcryptoopenssl} +BuildRequires: openssl-devel +%endif %if %{defined buildcompresszlib} BuildRequires: zlib-devel %endif %if %{defined buildcompresslz4} BuildRequires: lz4-devel %endif %if %{defined buildcompresslzo2} BuildRequires: lzo-devel %endif %if %{defined buildcompresslzma} BuildRequires: xz-devel %endif %if %{defined buildcompressbzip2} BuildRequires: bzip2-devel %endif %if %{defined buildkronosnetd} BuildRequires: libqb-devel pam-devel %endif %prep %setup -q -n %{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} %build %{configure} --disable-poc \ %if %{defined buildsctp} --enable-libknet-sctp \ %else --disable-libknet-sctp \ %endif %if %{defined buildcryptonss} --enable-crypto-nss \ %else --disable-crypto-nss \ %endif +%if %{defined buildcryptoopenssl} + --enable-crypto-openssl \ +%else + --disable-crypto-openssl \ +%endif %if %{defined buildcompresszlib} --enable-compress-zlib \ %else --disable-compress-zlib \ %endif %if %{defined buildcompresslz4} --enable-compress-lz4 \ %else --disable-compress-lz4 \ %endif %if %{defined buildcompresslzo2} --enable-compress-lzo2 \ %else --disable-compress-lzo2 \ %endif %if %{defined buildcompresslzma} --enable-compress-lzma \ %else --disable-compress-lzma \ %endif %if %{defined buildcompressbzip2} --enable-compress-bzip2 \ %else --disable-compress-bzip2 \ %endif %if %{defined buildkronosnetd} --enable-kronosnetd \ %endif %if %{defined buildlibtap} --enable-libtap \ %endif --with-initdefaultdir=%{_sysconfdir}/sysconfig/ \ %if %{defined _unitdir} --with-systemddir=%{_unitdir} %else --with-initddir=%{_sysconfdir}/rc.d/init.d/ %endif make %{_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} # tree cleanup # remove static libraries find %{buildroot} -name "*.a" -exec rm {} \; # remove libtools leftovers find %{buildroot} -name "*.la" -exec rm {} \; # handle systemd vs init script %if %{defined _unitdir} # remove init scripts rm -rf %{buildroot}/etc/init.d %else # remove systemd specific bits find %{buildroot} -name "*.service" -exec rm {} \; %endif # remove docs rm -rf %{buildroot}/usr/share/doc/kronosnet %clean rm -rf %{buildroot} # main empty package %description kronosnet source %if %{defined buildkronosnetd} ## Runtime and subpackages section %package -n kronosnetd Group: System Environment/Base Summary: Multipoint-to-Multipoint VPN daemon %if %{defined _unitdir} # Needed for systemd unit Requires(post): systemd-sysv Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units %else Requires(post): chkconfig Requires(preun): chkconfig, initscripts %endif Requires(post): shadow-utils Requires(preun): shadow-utils Requires: pam, /etc/pam.d/passwd %description -n kronosnetd The kronosnet daemon is a bridge between kronosnet switching engine and kernel network tap devices, to create and administer a distributed LAN over multipoint-to-multipoint VPNs. The daemon does a poor attempt to provide a configure UI similar to other known network devices/tools (Cisco, quagga). Beside looking horrific, it allows runtime changes and reconfiguration of the kronosnet(s) without daemon reload or service disruption. %post -n kronosnetd %if %{defined _unitdir} %if 0%{?systemd_post:1} %systemd_post kronosnetd.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif %else /sbin/chkconfig --add kronosnetd %endif /usr/sbin/groupadd --force --system @defaultadmgroup@ %preun -n kronosnetd %if %{defined _unitdir} %if 0%{?systemd_preun:1} %systemd_preun kronosnetd.service %else if [ "$1" -eq 0 ]; then /bin/systemctl --no-reload disable kronosnetd.service /bin/systemctl stop kronosnetd.service >/dev/null 2>&1 fi %endif %else if [ "$1" = 0 ]; then /sbin/service kronosnetd stop >/dev/null 2>&1 /sbin/chkconfig --del kronosnetd fi %endif %files -n kronosnetd %defattr(-,root,root,-) %doc COPYING.* COPYRIGHT %dir %{_sysconfdir}/kronosnet %dir %{_sysconfdir}/kronosnet/* %config(noreplace) %{_sysconfdir}/sysconfig/kronosnetd %config(noreplace) %{_sysconfdir}/pam.d/kronosnetd %config(noreplace) %{_sysconfdir}/logrotate.d/kronosnetd %if %{defined _unitdir} %{_unitdir}/kronosnetd.service %else %config(noreplace) %{_sysconfdir}/rc.d/init.d/kronosnetd %endif %{_sbindir}/* %{_mandir}/man8/* %endif %if %{defined buildlibtap} %package -n libtap1 Group: System Environment/Libraries Summary: Simple userland wrapper around kernel tap devices %description -n libtap1 This is an over-engineered commodity library to manage a pool of tap devices and provides the basic pre-up.d/up.d/down.d/post-down.d infrastructure. %files -n libtap1 %defattr(-,root,root,-) %doc COPYING.* COPYRIGHT %{_libdir}/libtap.so.* %post -n libtap1 -p /sbin/ldconfig %postun -n libtap1 -p /sbin/ldconfig %package -n libtap1-devel Group: Development/Libraries Summary: Simple userland wrapper around kernel tap devices (developer files) Requires: libtap1 = %{version}-%{release} Requires: pkgconfig %description -n libtap1-devel This is an over-engineered commodity library to manage a pool of tap devices and provides the basic pre-up.d/up.d/down.d/post-down.d infrastructure. %files -n libtap1-devel %defattr(-,root,root,-) %doc COPYING.* COPYRIGHT %{_libdir}/libtap.so %{_includedir}/libtap.h %{_libdir}/pkgconfig/libtap.pc %endif %package -n libknet1 Group: System Environment/Libraries Summary: Kronosnet core switching implementation %description -n libknet1 The whole kronosnet core is implemented in this library. Please refer to the not-yet-existing documentation for further information. %files -n libknet1 %defattr(-,root,root,-) %doc COPYING.* COPYRIGHT %{_libdir}/libknet.so.* %post -n libknet1 -p /sbin/ldconfig %postun -n libknet1 -p /sbin/ldconfig %package -n libknet1-devel Group: Development/Libraries Summary: Kronosnet core switching implementation (developer files) Requires: libknet1 = %{version}-%{release} Requires: pkgconfig %description -n libknet1-devel The whole kronosnet core is implemented in this library. Please refer to the not-yet-existing documentation for further information. %files -n libknet1-devel %defattr(-,root,root,-) %doc COPYING.* COPYRIGHT %{_libdir}/libknet.so %{_includedir}/libknet.h %{_libdir}/pkgconfig/libknet.pc %changelog * @date@ Autotools generated version - @version@-1-@numcomm@.@alphatag@.@dirty@ - These aren't the droids you're looking for. diff --git a/libknet/Makefile.am b/libknet/Makefile.am index a1399827..4b03e90d 100644 --- a/libknet/Makefile.am +++ b/libknet/Makefile.am @@ -1,96 +1,98 @@ # # Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. # # Authors: Fabio M. Di Nitto # Federico Simoncelli # # This software licensed under GPL-2.0+, LGPL-2.0+ # MAINTAINERCLEANFILES = Makefile.in include $(top_srcdir)/build-aux/check.mk SYMFILE = libknet_exported_syms EXTRA_DIST = $(SYMFILE) SUBDIRS = . tests libversion = 1:0:0 # override global LIBS that pulls in lots of craft we don't need here LIBS = sources = \ common.c \ compat.c \ compress.c \ compress_zlib.c \ compress_lz4.c \ compress_lzo2.c \ compress_lzma.c \ compress_bzip2.c \ crypto.c \ crypto_nss.c \ + crypto_openssl.c \ handle.c \ host.c \ links.c \ logging.c \ netutils.c \ threads_common.c \ threads_dsthandler.c \ threads_heartbeat.c \ threads_pmtud.c \ threads_rx.c \ threads_tx.c \ transport_udp.c \ transport_sctp.c \ transport_loopback.c \ transport_common.c include_HEADERS = libknet.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libknet.pc noinst_HEADERS = \ common.h \ compat.h \ compress.h \ compress_zlib.h \ compress_lz4.h \ compress_lzo2.h \ compress_lzma.h \ compress_bzip2.h \ crypto.h \ crypto_nss.h \ + crypto_openssl.h \ host.h \ internals.h \ links.h \ logging.h \ netutils.h \ onwire.h \ threads_common.h \ threads_dsthandler.h \ threads_heartbeat.h \ threads_pmtud.h \ threads_rx.h \ threads_tx.h \ transports.h lib_LTLIBRARIES = libknet.la libknet_la_SOURCES = $(sources) -libknet_la_CFLAGS = $(nss_CFLAGS) \ +libknet_la_CFLAGS = $(nss_CFLAGS) $(openssl_CFLAGS) \ $(zlib_CFLAGS) $(liblz4_CFLAGS) $(lzo2_CFLAGS) $(liblzma_CFLAGS) $(bzip2_CFLAGS) EXTRA_libknet_la_DEPENDENCIES = $(SYMFILE) libknet_la_LDFLAGS = -Wl,--version-script=$(srcdir)/$(SYMFILE) \ --export-dynamic \ -version-number $(libversion) -libknet_la_LIBADD = $(dl_LIBS) $(pthread_LIBS) $(rt_LIBS) $(m_LIBS) +libknet_la_LIBADD = $(dl_LIBS) $(pthread_LIBS) $(rt_LIBS) $(m_LIBS) $(openssl_LIBS) diff --git a/libknet/crypto.c b/libknet/crypto.c index d3a5becf..b0f9bdbc 100644 --- a/libknet/crypto.c +++ b/libknet/crypto.c @@ -1,189 +1,196 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include "crypto.h" #include "crypto_nss.h" +#include "crypto_openssl.h" #include "internals.h" #include "logging.h" /* * internal module switch data */ #define empty_module NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }, crypto_model_t crypto_modules_cmds[] = { { "nss", #ifdef BUILDCRYPTONSS 1, nsscrypto_load_lib, nsscrypto_unload_lib, 0, 0, nsscrypto_init, nsscrypto_fini, nsscrypto_encrypt_and_sign, nsscrypto_encrypt_and_signv, nsscrypto_authenticate_and_decrypt }, #else 0,empty_module +#endif + { "openssl", +#ifdef BUILDCRYPTOOPENSSL + 1, opensslcrypto_load_lib, opensslcrypto_unload_lib, 0, 0, opensslcrypto_init, opensslcrypto_fini, opensslcrypto_encrypt_and_sign, opensslcrypto_encrypt_and_signv, opensslcrypto_authenticate_and_decrypt }, +#else + 0,empty_module #endif { NULL, 0, empty_module }; static int crypto_get_model(const char *model) { int idx = 0; while (crypto_modules_cmds[idx].model_name != NULL) { if (!strcmp(crypto_modules_cmds[idx].model_name, model)) return idx; idx++; } return -1; } /* * exported API */ int crypto_encrypt_and_sign ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].crypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_encrypt_and_signv ( knet_handle_t knet_h, const struct iovec *iov_in, int iovcnt_in, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].cryptv(knet_h, iov_in, iovcnt_in, buf_out, buf_out_len); } int crypto_authenticate_and_decrypt ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].decrypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_init( knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { int savederrno = 0; int model = 0; model = crypto_get_model(knet_handle_crypto_cfg->crypto_model); if (model < 0) { log_err(knet_h, KNET_SUB_CRYPTO, "model %s not supported", knet_handle_crypto_cfg->crypto_model); return -1; } if (crypto_modules_cmds[model].built_in == 0) { log_err(knet_h, KNET_SUB_CRYPTO, "this version of libknet was built without %s support. Please contact your vendor or fix the build.", knet_handle_crypto_cfg->crypto_model); return -1; } savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return -1; } if (crypto_modules_cmds[model].load_lib(knet_h) < 0) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to load %s lib", crypto_modules_cmds[model].model_name); goto out_err; } crypto_modules_cmds[model].loaded = 1; log_debug(knet_h, KNET_SUB_CRYPTO, "Initizializing crypto module [%s/%s/%s]", knet_handle_crypto_cfg->crypto_model, knet_handle_crypto_cfg->crypto_cipher_type, knet_handle_crypto_cfg->crypto_hash_type); knet_h->crypto_instance = malloc(sizeof(struct crypto_instance)); if (!knet_h->crypto_instance) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto instance"); pthread_rwlock_unlock(&shlib_rwlock); goto out_err; } /* * if crypto_modules_cmds.init fails, it is expected that * it will clean everything by itself. * crypto_modules_cmds.fini is not invoked on error. */ knet_h->crypto_instance->model = model; if (crypto_modules_cmds[knet_h->crypto_instance->model].init(knet_h, knet_handle_crypto_cfg)) goto out_err; log_debug(knet_h, KNET_SUB_CRYPTO, "security network overhead: %u", knet_h->sec_header_size); crypto_modules_cmds[model].libref++; pthread_rwlock_unlock(&shlib_rwlock); return 0; out_err: if (knet_h->crypto_instance) { free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } if ((crypto_modules_cmds[model].libref == 0) && (crypto_modules_cmds[model].loaded == 1)) { log_debug(knet_h, KNET_SUB_CRYPTO, "Unloading %s library", crypto_modules_cmds[model].model_name); crypto_modules_cmds[model].unload_lib(knet_h); crypto_modules_cmds[model].loaded = 0; } pthread_rwlock_unlock(&shlib_rwlock); return -1; } void crypto_fini( knet_handle_t knet_h) { int savederrno = 0; int model = 0; savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return; } if (knet_h->crypto_instance) { model = knet_h->crypto_instance->model; if (crypto_modules_cmds[model].fini != NULL) { crypto_modules_cmds[model].fini(knet_h); } free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; crypto_modules_cmds[model].libref--; if ((crypto_modules_cmds[model].libref == 0) && (crypto_modules_cmds[model].loaded == 1)) { log_debug(knet_h, KNET_SUB_CRYPTO, "Unloading %s library", crypto_modules_cmds[model].model_name); crypto_modules_cmds[model].unload_lib(knet_h); crypto_modules_cmds[model].loaded = 0; } } pthread_rwlock_unlock(&shlib_rwlock); return; } diff --git a/libknet/crypto_openssl.c b/libknet/crypto_openssl.c new file mode 100644 index 00000000..9e874bea --- /dev/null +++ b/libknet/crypto_openssl.c @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2010-2017 Red Hat, Inc. All rights reserved. + * + * Author: Fabio M. Di Nitto + * + * This software licensed under GPL-2.0+, LGPL-2.0+ + */ + +#include "config.h" + +#include +#include +#include +#ifdef BUILDCRYPTOOPENSSL +#include +#include +#include +#include +#include + +#include "common.h" +#include "crypto.h" +#include "crypto_openssl.h" +#include "logging.h" + +#define SSLERR_BUF_SIZE 256 /* https://www.openssl.org/docs/man1.0.2/crypto/ERR_error_string.html */ + +/* + * global vars for dlopen + */ +//static void *openssl_lib; + +/* + * symbols remapping + */ + + +#if 0 +static int opensslcrypto_remap_symbols(knet_handle_t knet_h) +{ + int err = 0; + char *error = NULL; + + _int_PK11_GetBestWrapMechanism = dlsym(openssl_lib, "PK11_GetBestWrapMechanism"); + if (!_int_PK11_GetBestWrapMechanism) { + error = dlerror(); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "unable to map PK11_GetBestWrapMechanism: %s", error); + err = -1; + goto out; + } + +out: + if (err) { + _int_PK11_GetBestWrapMechanism = NULL; + } + return err; +} + +static int init_openssl_db(knet_handle_t knet_h) +{ + if ((*_int_NSS_NoDB_Init)(".") != SECSuccess) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "NSS DB initialization failed (err %d): %s", + (*_int_PR_GetError)(), (*_int_PR_ErrorToString)((*_int_PR_GetError)(), PR_LANGUAGE_I_DEFAULT)); + return -1; + } + return 0; +} +#endif + +void opensslcrypto_unload_lib( + knet_handle_t knet_h) +{ + EVP_cleanup(); + ERR_free_strings(); +#if 0 + if (openssl_lib) { + dlclose(openssl_lib); + openssl_lib = NULL; + } +#endif + return; +} + +int opensslcrypto_load_lib( + knet_handle_t knet_h) +{ +#if 0 + int err = 0, savederrno = 0; + + if (!openssl_lib) { + openssl_lib = open_lib(knet_h, "libopenssl3.so", RTLD_NODELETE); + if (!openssl_lib) { + savederrno = errno; + err = -1; + goto out; + } + + if (opensslcrypto_remap_symbols(knet_h) < 0) { + savederrno = errno; + err = -1; + goto out; + } + + if (init_openssl_db(knet_h) < 0) { + savederrno = EAGAIN; + err = -1; + goto out; + } + } + +out: + if (err) { + opensslcrypto_unload_lib(knet_h); + } + errno = savederrno; + return err; +#endif + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + OPENSSL_config(NULL); + + return 0; +} + +/* + * crypto definitions and conversion tables + */ + +#define SALT_SIZE 16 + +struct opensslcrypto_instance { + void *private_key; + + int private_key_len; + + const EVP_CIPHER *crypto_cipher_type; + + const EVP_MD *crypto_hash_type; +}; + +/* + * crypt/decrypt functions + */ + +static int encrypt_openssl( + knet_handle_t knet_h, + const struct iovec *iov, + int iovcnt, + unsigned char *buf_out, + ssize_t *buf_out_len) +{ + struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; + EVP_CIPHER_CTX ctx; + int tmplen = 0, offset = 0; + unsigned char *salt = buf_out; + unsigned char *data = buf_out + SALT_SIZE; + int err = 0; + int i; + char sslerr[SSLERR_BUF_SIZE]; + + EVP_CIPHER_CTX_init(&ctx); + + /* + * contribute to PRNG for each packet we send/receive + */ + RAND_seed((unsigned char *)iov[iovcnt - 1].iov_base, iov[iovcnt - 1].iov_len); + if (!RAND_bytes(salt, SALT_SIZE)) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to get random salt data"); + err = -1; + goto out; + } + + /* + * add warning re keylength + */ + EVP_EncryptInit_ex(&ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); + + if (!EVP_CIPHER_CTX_set_key_length(&ctx, instance->private_key_len)) { + ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to set keylen: %s", sslerr); + err = -1; + goto out; + } + + for (i=0; icrypto_instance->model_instance; + EVP_CIPHER_CTX ctx; + int tmplen1 = 0, tmplen2 = 0; + unsigned char *salt = (unsigned char *)buf_in; + unsigned char *data = salt + SALT_SIZE; + int datalen = buf_in_len - SALT_SIZE; + int err = 0; + char sslerr[SSLERR_BUF_SIZE]; + + EVP_CIPHER_CTX_init(&ctx); + + /* + * contribute to PRNG for each packet we send/receive + */ + RAND_seed(buf_in, buf_in_len); + + /* + * add warning re keylength + */ + EVP_DecryptInit_ex(&ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); + + if (!EVP_CIPHER_CTX_set_key_length(&ctx, instance->private_key_len)) { + ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to set keylen: %s", sslerr); + err = -1; + goto out; + } + + if (!EVP_DecryptUpdate(&ctx, buf_out, &tmplen1, data, datalen)) { + ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to decrypt: %s", sslerr); + err = -1; + goto out; + } + + if (!EVP_DecryptFinal_ex(&ctx, buf_out + tmplen1, &tmplen2)) { + ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to finalize decrypt: %s", sslerr); + err = -1; + goto out; + } + + *buf_out_len = tmplen1 + tmplen2; + +out: + EVP_CIPHER_CTX_cleanup(&ctx); + return err; +} + +/* + * hash/hmac/digest functions + */ + +static int calculate_openssl_hash( + knet_handle_t knet_h, + const unsigned char *buf, + const size_t buf_len, + unsigned char *hash) +{ + struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; + unsigned int hash_len = 0; + unsigned char *hash_out = NULL; + char sslerr[SSLERR_BUF_SIZE]; + + hash_out = HMAC(instance->crypto_hash_type, + instance->private_key, instance->private_key_len, + buf, buf_len, + hash, &hash_len); + + if ((!hash_out) || (hash_len != knet_h->sec_hash_size)) { + ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to calculate hash: %s", sslerr); + return -1; + } + + return 0; +} + +/* + * exported API + */ + +int opensslcrypto_encrypt_and_sign ( + knet_handle_t knet_h, + const unsigned char *buf_in, + const ssize_t buf_in_len, + unsigned char *buf_out, + ssize_t *buf_out_len) +{ + struct iovec iov_in; + + memset(&iov_in, 0, sizeof(iov_in)); + iov_in.iov_base = (unsigned char *)buf_in; + iov_in.iov_len = buf_in_len; + + return opensslcrypto_encrypt_and_signv(knet_h, &iov_in, 1, buf_out, buf_out_len); +} + +int opensslcrypto_encrypt_and_signv ( + knet_handle_t knet_h, + const struct iovec *iov_in, + int iovcnt_in, + unsigned char *buf_out, + ssize_t *buf_out_len) +{ + struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; + int i; + + if (instance->crypto_cipher_type) { + if (encrypt_openssl(knet_h, iov_in, iovcnt_in, buf_out, buf_out_len) < 0) { + return -1; + } + } else { + *buf_out_len = 0; + for (i=0; icrypto_hash_type) { + if (calculate_openssl_hash(knet_h, buf_out, *buf_out_len, buf_out + *buf_out_len) < 0) { + return -1; + } + *buf_out_len = *buf_out_len + knet_h->sec_hash_size; + } + + return 0; +} + +int opensslcrypto_authenticate_and_decrypt ( + knet_handle_t knet_h, + const unsigned char *buf_in, + const ssize_t buf_in_len, + unsigned char *buf_out, + ssize_t *buf_out_len) +{ + struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; + ssize_t temp_len = buf_in_len; + + if (instance->crypto_hash_type) { + unsigned char tmp_hash[knet_h->sec_hash_size]; + ssize_t temp_buf_len = buf_in_len - knet_h->sec_hash_size; + + if ((temp_buf_len < 0) || (temp_buf_len > KNET_MAX_PACKET_SIZE)) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Incorrect packet size."); + return -1; + } + + if (calculate_openssl_hash(knet_h, buf_in, temp_buf_len, tmp_hash) < 0) { + return -1; + } + + if (memcmp(tmp_hash, buf_in + temp_buf_len, knet_h->sec_hash_size) != 0) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Digest does not match"); + return -1; + } + + temp_len = temp_len - knet_h->sec_hash_size; + *buf_out_len = temp_len; + } + if (instance->crypto_cipher_type) { + if (decrypt_openssl(knet_h, buf_in, temp_len, buf_out, buf_out_len) < 0) { + return -1; + } + } else { + memmove(buf_out, buf_in, temp_len); + *buf_out_len = temp_len; + } + + return 0; +} + +int opensslcrypto_init( + knet_handle_t knet_h, + struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) +{ + struct opensslcrypto_instance *opensslcrypto_instance = NULL; + + log_debug(knet_h, KNET_SUB_OPENSSLCRYPTO, + "Initizializing openssl crypto module [%s/%s]", + knet_handle_crypto_cfg->crypto_cipher_type, + knet_handle_crypto_cfg->crypto_hash_type); + + knet_h->crypto_instance->model_instance = malloc(sizeof(struct opensslcrypto_instance)); + if (!knet_h->crypto_instance->model_instance) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to allocate memory for openssl model instance"); + return -1; + } + + opensslcrypto_instance = knet_h->crypto_instance->model_instance; + + memset(opensslcrypto_instance, 0, sizeof(struct opensslcrypto_instance)); + + opensslcrypto_instance->crypto_cipher_type = EVP_get_cipherbyname(knet_handle_crypto_cfg->crypto_cipher_type); + if (!opensslcrypto_instance->crypto_cipher_type) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "unknown crypto cipher type requested"); + goto out_err; + } + + opensslcrypto_instance->crypto_hash_type = EVP_get_digestbyname(knet_handle_crypto_cfg->crypto_hash_type); + if (!opensslcrypto_instance->crypto_hash_type) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "unknown crypto hash type requested"); + goto out_err; + } + + if ((opensslcrypto_instance->crypto_cipher_type) && + (!opensslcrypto_instance->crypto_hash_type)) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "crypto communication requires hash specified"); + goto out_err; + } + + opensslcrypto_instance->private_key = malloc(knet_handle_crypto_cfg->private_key_len); + if (!opensslcrypto_instance->private_key) { + log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to allocate memory for openssl private key"); + goto out_err; + } + memmove(opensslcrypto_instance->private_key, knet_handle_crypto_cfg->private_key, knet_handle_crypto_cfg->private_key_len); + opensslcrypto_instance->private_key_len = knet_handle_crypto_cfg->private_key_len; + + knet_h->sec_header_size = 0; + + if (opensslcrypto_instance->crypto_hash_type) { + knet_h->sec_hash_size = EVP_MD_size(opensslcrypto_instance->crypto_hash_type); + knet_h->sec_header_size += knet_h->sec_hash_size; + } + + if (opensslcrypto_instance->crypto_cipher_type) { + int block_size; + + block_size = EVP_CIPHER_block_size(opensslcrypto_instance->crypto_cipher_type); + if (block_size < 0) { + goto out_err; + } + + knet_h->sec_header_size += (block_size * 2); + knet_h->sec_header_size += SALT_SIZE; + knet_h->sec_salt_size = SALT_SIZE; + knet_h->sec_block_size = block_size; + } + + return 0; + +out_err: + opensslcrypto_fini(knet_h); + + return -1; +} + +void opensslcrypto_fini( + knet_handle_t knet_h) +{ + struct opensslcrypto_instance *opensslcrypto_instance = knet_h->crypto_instance->model_instance; + + if (opensslcrypto_instance) { + if (opensslcrypto_instance->private_key) { + free(opensslcrypto_instance->private_key); + opensslcrypto_instance->private_key = NULL; + } + free(opensslcrypto_instance); + knet_h->crypto_instance->model_instance = NULL; + knet_h->sec_header_size = 0; + } + + return; +} +#endif diff --git a/libknet/crypto_openssl.h b/libknet/crypto_openssl.h new file mode 100644 index 00000000..e598d2fe --- /dev/null +++ b/libknet/crypto_openssl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. All rights reserved. + * + * Author: Fabio M. Di Nitto + * + * This software licensed under GPL-2.0+, LGPL-2.0+ + */ + +#ifndef __KNET_OPENSSLCRYPTO_H__ +#define __KNET_OPENSSLCRYPTO_H__ + +#include "internals.h" + +struct opensslcrypto_instance; + +int opensslcrypto_load_lib( + knet_handle_t knet_h); + +void opensslcrypto_unload_lib( + knet_handle_t knet_h); + +int opensslcrypto_init( + knet_handle_t knet_h, + struct knet_handle_crypto_cfg *knet_handle_crypto_cfg); + +void opensslcrypto_fini( + knet_handle_t knet_h); + +int opensslcrypto_authenticate_and_decrypt ( + knet_handle_t knet_h, + const unsigned char *buf_in, + const ssize_t buf_in_len, + unsigned char *buf_out, + ssize_t *buf_out_len); + +int opensslcrypto_encrypt_and_sign ( + knet_handle_t knet_h, + const unsigned char *buf_in, + const ssize_t buf_in_len, + unsigned char *buf_out, + ssize_t *buf_out_len); + +int opensslcrypto_encrypt_and_signv ( + knet_handle_t knet_h, + const struct iovec *iov_in, + int iovcnt_in, + unsigned char *buf_out, + ssize_t *buf_out_len); + +#endif diff --git a/libknet/libknet.h b/libknet/libknet.h index ea101f67..ba7fb85d 100644 --- a/libknet/libknet.h +++ b/libknet/libknet.h @@ -1,1713 +1,1714 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __LIBKNET_H__ #define __LIBKNET_H__ #include #include #include /* * libknet limits */ /* * Maximum number of hosts */ typedef uint16_t knet_node_id_t; #define KNET_MAX_HOST 65536 /* * Maximum number of links between 2 hosts */ #define KNET_MAX_LINK 8 /* * Maximum packet size that should be written to datafd * see knet_handle_new for details */ #define KNET_MAX_PACKET_SIZE 65536 /* * Buffers used for pretty logging * host is used to store both ip addresses and hostnames */ #define KNET_MAX_HOST_LEN 256 #define KNET_MAX_PORT_LEN 6 /* * Some notifications can be generated either on TX or RX */ #define KNET_NOTIFY_TX 0 #define KNET_NOTIFY_RX 1 /* * Link flags */ /* * Where possible, set traffic priority to high. * On Linux this sets the TOS to INTERACTIVE (6), * see tc-prio(8) for more infomation */ #define KNET_LINK_FLAG_TRAFFICHIPRIO (1ULL << 0) typedef struct knet_handle *knet_handle_t; /* * Handle structs/API calls */ /* * knet_handle_new * * host_id - Each host in a knet is identified with a unique * ID. when creating a new handle local host_id * must be specified (0 to UINT16T_MAX are all valid). * It is the user's responsibility to check that the value * is unique, or bad things might happen. * * log_fd - Write file descriptor. If set to a value > 0, it will be used * to write log packets (see below) from libknet to the application. * Setting to 0 will disable logging from libknet. * It is possible to enable logging at any given time (see logging API * below). * Make sure to either read from this filedescriptor properly and/or * mark it O_NONBLOCK, otherwise if the fd becomes full, libknet could * block. * * default_log_level - * If logfd is specified, it will initialize all subsystems to log * at default_log_level value. (see logging API below) * * on success, a new knet_handle_t is returned. * on failure, NULL is returned and errno is set. */ knet_handle_t knet_handle_new(knet_node_id_t host_id, int log_fd, uint8_t default_log_level); /* * knet_handle_free * * knet_h - pointer to knet_handle_t * * Destroy a knet handle, free all resources * * knet_handle_free returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_free(knet_handle_t knet_h); /* * knet_handle_enable_sock_notify * * knet_h - pointer to knet_handle_t * * sock_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * sock_notify_fn * A callback function that is invoked every time * a socket in the datafd pool will report an error (-1) * or an end of read (0) (see socket.7). * This function MUST NEVER block or add substantial delays. * The callback is invoked in an internal unlocked area * to allow calls to knet_handle_add_datafd/knet_handle_remove_datafd * to swap/replace the bad fd. * if both err and errno are 0, it means that the socket * has received a 0 byte packet (EOF?). * The callback function must either remove the fd from knet * (by calling knet_handle_remove_fd()) or dup a new fd in its place. * Failure to do this can cause problems. * * knet_handle_enable_sock_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_sock_notify(knet_handle_t knet_h, void *sock_notify_fn_private_data, void (*sock_notify_fn) ( void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno)); /* sorry! can't call it errno ;) */ /* * knet_handle_add_datafd * * IMPORTANT: In order to add datafd to knet, knet_handle_enable_sock_notify * _MUST_ be set and be able to handle both errors (-1) and * 0 bytes read / write from the provided datafd. * On read error (< 0) from datafd, the socket is automatically * removed from polling to avoid spinning on dead sockets. * It is safe to call knet_handle_remove_datafd even on sockets * that have been removed. * * knet_h - pointer to knet_handle_t * * *datafd - read/write file descriptor. * knet will read data here to send to the other hosts * and will write data received from the network. * Each data packet can be of max size KNET_MAX_PACKET_SIZE! * Applications using knet_send/knet_recv will receive a * proper error if the packet size is not within boundaries. * Applications using their own functions to write to the * datafd should NOT write more than KNET_MAX_PACKET_SIZE. * * Please refer to handle.c on how to set up a socketpair. * * datafd can be 0, and knet_handle_add_datafd will create a properly * populated socket pair the same way as ping_test, or a value * higher than 0. A negative number will return an error. * On exit knet_handle_free will take care to cleanup the * socketpair only if they have been created by knet_handle_add_datafd. * * It is possible to pass either sockets or normal fds. * User provided datafd will be marked as non-blocking and close-on-exit. * * *channel - This value has the same effect of VLAN tagging. * A negative value will auto-allocate a channel. * Setting a value between 0 and 31 will try to allocate that * specific channel (unless already in use). * * It is possible to add up to 32 datafds but be aware that each * one of them must have a receiving end on the other host. * * Example: * hostA channel 0 will be delivered to datafd on hostB channel 0 * hostA channel 1 to hostB channel 1. * * Each channel must have a unique file descriptor. * * If your application could have 2 channels on one host and one * channel on another host, then you can use dst_host_filter * to manipulate channel values on TX and RX. * * knet_handle_add_datafd returns: * * 0 on success * *datafd will be populated with a socket if the original value was 0 * or if a specific fd was set, the value is untouched. * *channel will be populated with a channel number if the original value * was negative or the value is untouched if a specific channel * was requested. * * -1 on error and errno is set. * *datafd and *channel are untouched or empty. */ #define KNET_DATAFD_MAX 32 int knet_handle_add_datafd(knet_handle_t knet_h, int *datafd, int8_t *channel); /* * knet_handle_remove_datafd * * knet_h - pointer to knet_handle_t * * datafd - file descriptor to remove. * NOTE that if the socket/fd was created by knet_handle_add_datafd, * the socket will be closed by libknet. * * knet_handle_remove_datafd returns: * * 0 on success * * -1 on error and errno is set. */ int knet_handle_remove_datafd(knet_handle_t knet_h, int datafd); /* * knet_handle_get_channel * * knet_h - pointer to knet_handle_t * * datafd - get the channel associated to this datafd * * *channel - will contain the result * * knet_handle_get_channel returns: * * 0 on success * and *channel will contain the result * * -1 on error and errno is set. * and *channel content is meaningless */ int knet_handle_get_channel(knet_handle_t knet_h, const int datafd, int8_t *channel); /* * knet_handle_get_datafd * * knet_h - pointer to knet_handle_t * * channel - get the datafd associated to this channel * * *datafd - will contain the result * * knet_handle_get_datafd returns: * * 0 on success * and *datafd will contain the results * * -1 on error and errno is set. * and *datafd content is meaningless */ int knet_handle_get_datafd(knet_handle_t knet_h, const int8_t channel, int *datafd); /* * knet_recv * * knet_h - pointer to knet_handle_t * * buff - pointer to buffer to store the received data * * buff_len - buffer lenght * * knet_recv is a commodity function to wrap iovec operations * around a socket. It returns a call to readv(2). */ ssize_t knet_recv(knet_handle_t knet_h, char *buff, const size_t buff_len, const int8_t channel); /* * knet_send * * knet_h - pointer to knet_handle_t * * buff - pointer to the buffer of data to send * * buff_len - length of data to send * * knet_send is a commodity function to wrap iovec operations * around a socket. It returns a call to writev(2). */ ssize_t knet_send(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel); /* * knet_send_sync * * knet_h - pointer to knet_handle_t * * buff - pointer to the buffer of data to send * * buff_len - length of data to send * * channel - data channel to use (see knet_handle_add_datafd) * * All knet RX/TX operations are async for performance reasons. * There are applications that might need a sync version of data * transmission and receive errors in case of failure to deliver * to another host. * knet_send_sync bypasses the whole TX async layer and delivers * data directly to the link layer, and returns errors accordingly. * knet_send_sync allows to send only one packet to one host at * a time. It does NOT support multiple destinations or multicast * packets. Decision is still based on dst_host_filter_fn. * * knet_send_sync returns 0 on success and -1 on error. * * In addition to normal sendmmsg errors, knet_send_sync can fail * due to: * * ECANCELED - data forward is disabled * EFAULT - dst_host_filter fatal error * EINVAL - dst_host_filter did not provide * dst_host_ids_entries on unicast pckts * E2BIG - dst_host_filter did return more than one * dst_host_ids_entries on unicast pckts * ENOMSG - received unknown message type * EHOSTDOWN - unicast pckt cannot be delivered because * dest host is not connected yet * ECHILD - crypto failed * EAGAIN - sendmmsg was unable to send all messages and * there was no progress during retry */ int knet_send_sync(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel); /* * knet_handle_enable_filter * * knet_h - pointer to knet_handle_t * * dst_host_filter_fn_private_data * void pointer to data that can be used to identify * the callback. * * dst_host_filter_fn - * is a callback function that is invoked every time * a packet hits datafd (see knet_handle_new). * the function allows users to tell libknet where the * packet has to be delivered. * * const unsigned char *outdata - is a pointer to the * current packet * ssize_t outdata_len - lenght of the above data * uint8_t tx_rx - filter is called on tx or rx * (see defines below) * knet_node_id_t this_host_id - host_id processing the packet * knet_node_id_t src_host_id - host_id that generated the * packet * knet_node_id_t *dst_host_ids - array of KNET_MAX_HOST knet_node_id_t * where to store the destinations * size_t *dst_host_ids_entries - number of hosts to send the message * * dst_host_filter_fn should return * -1 on error, packet is discarded. * 0 packet is unicast and should be sent to dst_host_ids and there are * dst_host_ids_entries in the buffer. * 1 packet is broadcast/multicast and is sent all hosts. * contents of dst_host_ids and dst_host_ids_entries are ignored. * (see also kronosnetd/etherfilter.* for an example that filters based * on ether protocol) * * knet_handle_enable_filter returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_filter(knet_handle_t knet_h, void *dst_host_filter_fn_private_data, int (*dst_host_filter_fn) ( void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries)); /* * knet_handle_setfwd * * knet_h - pointer to knet_handle_t * * enable - set to 1 to allow data forwarding, 0 to disable data forwarding. * * knet_handle_setfwd returns: * * 0 on success * -1 on error and errno is set. * * By default data forwarding is off and no traffic will pass through knet until * it is set on. */ int knet_handle_setfwd(knet_handle_t knet_h, unsigned int enabled); /* * knet_handle_pmtud_setfreq * * knet_h - pointer to knet_handle_t * * interval - define the interval in seconds between PMTUd scans * range from 1 to 86400 (24h) * * knet_handle_pmtud_setfreq returns: * * 0 on success * -1 on error and errno is set. * * default interval is 60. */ #define KNET_PMTUD_DEFAULT_INTERVAL 60 int knet_handle_pmtud_setfreq(knet_handle_t knet_h, unsigned int interval); /* * knet_handle_pmtud_getfreq * * knet_h - pointer to knet_handle_t * * interval - pointer where to store the current interval value * * knet_handle_pmtud_setfreq returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_pmtud_getfreq(knet_handle_t knet_h, unsigned int *interval); /* * knet_handle_enable_pmtud_notify * * knet_h - pointer to knet_handle_t * * pmtud_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * pmtud_notify_fn * is a callback function that is invoked every time * a path MTU size change is detected. * The function allows libknet to notify the user * of data MTU, that's the max value that can be send * onwire without fragmentation. The data MTU will always * be lower than real link MTU because it accounts for * protocol overhead, knet packet header and (if configured) * crypto overhead, * This function MUST NEVER block or add substantial delays. * * knet_handle_enable_pmtud_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_pmtud_notify(knet_handle_t knet_h, void *pmtud_notify_fn_private_data, void (*pmtud_notify_fn) ( void *private_data, unsigned int data_mtu)); /* * knet_handle_pmtud_get * * knet_h - pointer to knet_handle_t * * data_mtu - pointer where to store data_mtu (see above) * * knet_handle_pmtud_get returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_pmtud_get(knet_handle_t knet_h, unsigned int *data_mtu); /* * knet_handle_crypto * * knet_h - pointer to knet_handle_t * * knet_handle_crypto_cfg - * pointer to a knet_handle_crypto_cfg structure * * crypto_model should contain the model name. * Currently only "nss" is supported. * Setting to "none" will disable crypto. * * crypto_cipher_type * should contain the cipher algo name. * It can be set to "none" to disable * encryption. * Currently supported by "nss" model: * "3des", "aes128", "aes192" and "aes256". * * crypto_hash_type * should contain the hashing algo name. * It can be set to "none" to disable * hashing. * Currently supported by "nss" model: * "md5", "sha1", "sha256", "sha384" and "sha512". * * private_key will contain the private shared key. * It has to be at least KNET_MIN_KEY_LEN long. * * private_key_len * length of the provided private_key. * * Implementation notes/current limitations: * - enabling crypto, will increase latency as packets have * to processed. * - enabling crypto might reduce the overall throughtput * due to crypto data overhead. * - re-keying is not implemented yet. * - private/public key encryption/hashing is not currently * planned. * - crypto key must be the same for all hosts in the same * knet instance. * - it is safe to call knet_handle_crypto multiple times at runtime. * The last config will be used. * IMPORTANT: a call to knet_handle_crypto can fail due to: * 1) failure to obtain locking * 2) errors to initializing the crypto level. * This can happen even in subsequent calls to knet_handle_crypto. * A failure in crypto init, might leave your traffic unencrypted! * It's best to stop data forwarding (see above), change crypto config, * start forward again. * * knet_handle_crypto returns: * * 0 on success * -1 on error and errno is set. * -2 on crypto subsystem initialization error. No errno is provided at the moment (yet). */ -#define KNET_MIN_KEY_LEN 256 +#define KNET_MIN_KEY_LEN 16 #define KNET_MAX_KEY_LEN 4096 struct knet_handle_crypto_cfg { char crypto_model[16]; char crypto_cipher_type[16]; char crypto_hash_type[16]; unsigned char private_key[KNET_MAX_KEY_LEN]; unsigned int private_key_len; }; int knet_handle_crypto(knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg); /* * knet_handle_compress * * knet_h - pointer to knet_handle_t * * knet_handle_compress_cfg - * pointer to a knet_handle_compress_cfg structure * * compress_model should contain the mode name. * Currently only "zlib" and "lz4" are supported. * Setting to "none" will disable compression. * * compress_threshold * tells the transmission thread to NOT compress * any packets that are smaller than the value * indicated. Default 100 bytes. * Set to 0 to reset to the default. * Set to 1 to compress everything. * Max accepted value is KNET_MAX_PACKET_SIZE. * * compress_level some compression libraries allow tuning of compression * parameters. * For example zlib value ranges from 0 to 9 where 0 is no * compression and 9 is max compression. * This value is passed pristine to the compression library. * zlib: 0 (no compression), 1 (minimal) .. 9 (max compression). * lz4: 1 (max compression)... 9 (fastest compression). * lz4hc: 1 (min compression) ... LZ4HC_MAX_CLEVEL (16) or LZ4HC_CLEVEL_MAX (12) * depends on the installed version of lz4hc. libknet can detects the max * value and will print an appropriate warning. * lzo2: accepts only some specific values depending on the * requested algorithm: * 1 : lzo1x_1_compress (default) * 11 : lzo1x_1_11_compress * 12 : lzo1x_1_12_compress * 15 : lzo1x_1_15_compress * 999: lzo1x_999_compress * every other values will use default * lzma: 0 (minimal) .. 9 (max compression) * bzip2: 1 (minimal) .. 9 (max compression) * Please refer to the library man pages * on how to be set this value, as it is passed * unmodified to the compression algorithm where supported. * * Implementation notes: * - it is possible to enable/disable compression at any time. * - nodes can be using a different compression algorithm at any time. * - knet does NOT implement the compression algorithm directly. it relies * on external libraries for this functionality. Please read * the libraries man pages to figure out which algorithm/compression * level is best for the data you are planning to transmit. * * knet_handle_compress returns: * * 0 on success * -1 on error and errno is set. EINVAL means that either the model or the * level are not supported. */ #define KNET_COMPRESS_THRESHOLD 100 struct knet_handle_compress_cfg { char compress_model[16]; uint32_t compress_threshold; int compress_level; }; int knet_handle_compress(knet_handle_t knet_h, struct knet_handle_compress_cfg *knet_handle_compress_cfg); /* * knet_handle_get_stats * * knet_h - pointer to knet_handle_t * * knet_handle_stats * pointer to a knet_handle_stats structure * * struct_size * size of knet_handle_stats structure to allow * for backwards compatibility. libknet will only * copy this much data into the stats structure * so that older callers will not get overflowed if * new fields are added. * * 0 on success * -1 on error and errno is set. * */ struct knet_handle_stats { size_t size; uint64_t tx_uncompressed_packets; uint64_t tx_compressed_packets; uint64_t tx_compressed_original_bytes; uint64_t tx_compressed_size_bytes; uint64_t tx_compress_time_ave; uint64_t tx_compress_time_min; uint64_t tx_compress_time_max; uint64_t rx_compressed_packets; uint64_t rx_compressed_original_bytes; uint64_t rx_compressed_size_bytes; uint64_t rx_compress_time_ave; uint64_t rx_compress_time_min; uint64_t rx_compress_time_max; /* Overhead times, measured in usecs */ uint64_t tx_crypt_packets; uint64_t tx_crypt_byte_overhead; uint64_t tx_crypt_time_ave; uint64_t tx_crypt_time_min; uint64_t tx_crypt_time_max; uint64_t rx_crypt_packets; uint64_t rx_crypt_time_ave; uint64_t rx_crypt_time_min; uint64_t rx_crypt_time_max; }; int knet_handle_get_stats(knet_handle_t knet_h, struct knet_handle_stats *stats, size_t struct_size); /* * host structs/API calls */ /* * knet_host_add * * knet_h - pointer to knet_handle_t * * host_id - each host in a knet is identified with a unique ID * (see also knet_handle_new documentation above) * * knet_host_add returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_add(knet_handle_t knet_h, knet_node_id_t host_id); /* * knet_host_remove * * knet_h - pointer to knet_handle_t * * host_id - each host in a knet is identified with a unique ID * (see also knet_handle_new documentation above) * * knet_host_remove returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_remove(knet_handle_t knet_h, knet_node_id_t host_id); /* * knet_host_set_name * * knet_h - pointer to knet_handle_t * * host_id - see above * * name - this name will be used for pretty logging and eventually * search for hosts (see also get_name and get_id below). * Only up to KNET_MAX_HOST_LEN - 1 bytes will be accepted and * name has to be unique for each host. * * knet_host_set_name returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_set_name(knet_handle_t knet_h, knet_node_id_t host_id, const char *name); /* * knet_host_get_name_by_host_id * * knet_h - pointer to knet_handle_t * * host_id - see above * * name - pointer to a preallocated buffer of at least size KNET_MAX_HOST_LEN * where the current host name will be stored * (as set by knet_host_set_name or default by knet_host_add) * * knet_host_get_name_by_host_id returns: * * 0 on success * -1 on error and errno is set (name is left untouched) */ int knet_host_get_name_by_host_id(knet_handle_t knet_h, knet_node_id_t host_id, char *name); /* * knet_host_get_id_by_host_name * * knet_h - pointer to knet_handle_t * * name - name to lookup, max len KNET_MAX_HOST_LEN * * host_id - where to store the result * * knet_host_get_id_by_host_name returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_id_by_host_name(knet_handle_t knet_h, const char *name, knet_node_id_t *host_id); /* * knet_host_get_host_list * * knet_h - pointer to knet_handle_t * * host_ids - array of at lest KNET_MAX_HOST size * * host_ids_entries - * number of entries writted in host_ids * * knet_host_get_host_list returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_host_list(knet_handle_t knet_h, knet_node_id_t *host_ids, size_t *host_ids_entries); /* * define switching policies */ #define KNET_LINK_POLICY_PASSIVE 0 #define KNET_LINK_POLICY_ACTIVE 1 #define KNET_LINK_POLICY_RR 2 /* * knet_host_set_policy * * knet_h - pointer to knet_handle_t * * host_id - see above * * policy - there are currently 3 kind of simple switching policies * as defined above, based on link configuration. * KNET_LINK_POLICY_PASSIVE - the active link with the lowest * priority will be used. * if one or more active links share * the same priority, the one with * lowest link_id will be used. * * KNET_LINK_POLICY_ACTIVE - all active links will be used * simultaneously to send traffic. * link priority is ignored. * * KNET_LINK_POLICY_RR - round-robin policy, every packet * will be send on a different active * link. * * knet_host_set_policy returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_set_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t policy); /* * knet_host_get_policy * * knet_h - pointer to knet_handle_t * * host_id - see above * * policy - will contain the current configured switching policy. * Default is passive when creating a new host. * * knet_host_get_policy returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *policy); /* * knet_host_enable_status_change_notify * * knet_h - pointer to knet_handle_t * * host_status_change_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * host_status_change_notify_fn * is a callback function that is invoked every time * there is a change in the host status. * host status is identified by: * - reachable, this host can send/receive data to/from host_id * - remote, 0 if the host_id is connected locally or 1 if * the there is one or more knet host(s) in between. * NOTE: re-switching is NOT currently implemented, * but this is ready for future and can avoid * an API/ABI breakage later on. * - external, 0 if the host_id is configured locally or 1 if * it has been added from remote nodes config. * NOTE: dynamic topology is NOT currently implemented, * but this is ready for future and can avoid * an API/ABI breakage later on. * This function MUST NEVER block or add substantial delays. * * knet_host_status_change_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_enable_status_change_notify(knet_handle_t knet_h, void *host_status_change_notify_fn_private_data, void (*host_status_change_notify_fn) ( void *private_data, knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external)); /* * define host status structure for quick lookup * struct is in flux as more stats will be added soon * * reachable host_id can be seen either directly connected * or via another host_id * * remote 0 = node is connected locally, 1 is visible via * via another host_id * * external 0 = node is configured/known locally, * 1 host_id has been received via another host_id */ struct knet_host_status { uint8_t reachable; uint8_t remote; uint8_t external; /* add host statistics */ }; /* * knet_host_status_get * * knet_h - pointer to knet_handle_t * * status - pointer to knet_host_status struct (see above) * * knet_handle_pmtud_get returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_status(knet_handle_t knet_h, knet_node_id_t host_id, struct knet_host_status *status); /* * link structs/API calls * * every host allocated/managed by knet_host_* has * KNET_MAX_LINK structures to define the network * paths that connect 2 hosts. * * Each link is identified by a link_id that has a * values between 0 and KNET_MAX_LINK - 1. * * KNOWN LIMITATIONS: * * - let's assume the scenario where two hosts are connected * with any number of links. link_id must match on both sides. * If host_id 0 link_id 0 is configured to connect IP1 to IP2 and * host_id 0 link_id 1 is configured to connect IP3 to IP4, * host_id 1 link_id 0 _must_ connect IP2 to IP1 and likewise * host_id 1 link_id 1 _must_ connect IP4 to IP3. * We might be able to lift this restriction in future, by using * other data to determine src/dst link_id, but for now, deal with it. */ /* * commodity functions to convert strings to sockaddr and viceversa */ /* * knet_strtoaddr * * host - IPaddr/hostname to convert * be aware only the first IP address will be returned * in case a hostname resolves to multiple IP * * port - port to connect to * * ss - sockaddr_storage where to store the converted data * * sslen - len of the sockaddr_storage * * knet_strtoaddr returns same error codes as getaddrinfo * */ int knet_strtoaddr(const char *host, const char *port, struct sockaddr_storage *ss, socklen_t sslen); /* * knet_addrtostr * * ss - sockaddr_storage to convert * * sslen - len of the sockaddr_storage * * host - IPaddr/hostname where to store data * (recommended size: KNET_MAX_HOST_LEN) * * port - port buffer where to store data * (recommended size: KNET_MAX_PORT_LEN) * * knet_strtoaddr returns same error codes as getnameinfo */ int knet_addrtostr(const struct sockaddr_storage *ss, socklen_t sslen, char *addr_buf, size_t addr_buf_size, char *port_buf, size_t port_buf_size); /* * knet_handle_get_transport_list * * knet_h - pointer to knet_handle_t * * transport_list - an array of struct transport_info that must be * at least of size struct transport_info * KNET_MAX_TRANSPORTS * * transport_list_entries - pointer to a size_t where to store how many transports * are available in this build of libknet. * * knet_handle_get_transport_list returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_TRANSPORT_LOOPBACK 0 #define KNET_TRANSPORT_UDP 1 #define KNET_TRANSPORT_SCTP 2 #define KNET_MAX_TRANSPORTS 3 /* * The Loopback transport is only valid for connections to localhost, the host * with the same node_id specified in knet_handle_new(). Only one link of this * type is allowed. Data sent down a LOOPBACK link will be copied directly from * the knet send datafd to the knet receive datafd so the application must be set * up to take data from that socket at least as often as it is sent or deadlocks * could occur. If used, a LOOPBACK link must be the only link configured to the * local host. */ struct transport_info { const char *name; /* UDP/SCTP/etc... */ uint8_t id; /* value that can be used for link_set_config */ uint8_t properties; /* currently unused */ }; int knet_handle_get_transport_list(knet_handle_t knet_h, struct transport_info *transport_list, size_t *transport_list_entries); /* * knet_handle_get_transport_name_by_id * * knet_h - pointer to knet_handle_t * * transport - one of the above KNET_TRANSPORT_xxx constants * * knet_handle_get_transport_name_by_id returns: * * pointer to the name on success or * NULL on error and errno is set. */ const char *knet_handle_get_transport_name_by_id(knet_handle_t knet_h, uint8_t transport); /* * knet_handle_get_transport_id_by_name * * knet_h - pointer to knet_handle_t * * name - transport name (UDP/SCTP/etc) * * knet_handle_get_transport_name_by_id returns: * * KNET_MAX_TRANSPORTS on error and errno is set accordingly * KNET_TRANSPORT_xxx on success. */ uint8_t knet_handle_get_transport_id_by_name(knet_handle_t knet_h, const char *name); /* * knet_handle_set_transport_reconnect_interval * * knet_h - pointer to knet_handle_t * * msecs - milliseconds * * knet_handle_set_transport_reconnect_interval returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_TRANSPORT_DEFAULT_RECONNECT_INTERVAL 1000 int knet_handle_set_transport_reconnect_interval(knet_handle_t knet_h, uint32_t msecs); /* * knet_handle_get_transport_reconnect_interval * * knet_h - pointer to knet_handle_t * * msecs - milliseconds * * knet_handle_get_transport_reconnect_interval returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_get_transport_reconnect_interval(knet_handle_t knet_h, uint32_t *msecs); /* * knet_link_set_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * transport - one of the above KNET_TRANSPORT_xxx constants * * src_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dst_addr - sockaddr_storage that can be either IPv4 or IPv6 * this can be null if we don't know the incoming * IP address/port and the link will remain quiet * till the node on the other end will initiate a * connection * * flags - KNET_LINK_FLAG_* * * knet_link_set_config returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr, uint64_t flags); /* * knet_link_get_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * transport - see above * * src_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dst_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dynamic - 0 if dst_addr is static or 1 if dst_addr is dynamic. * In case of 1, dst_addr can be NULL and it will be left * untouched. * * flags - KNET_LINK_FLAG_* * * knet_link_get_config returns: * * 0 on success. * -1 on error and errno is set. */ int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr, uint8_t *dynamic, uint64_t *flags); /* * knet_link_clear_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * knet_link_clear_config returns: * * 0 on success. * -1 on error and errno is set. */ int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id); /* * knet_link_set_enable * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * enabled - 0 disable the link, 1 enable the link * * knet_link_set_enable returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int enabled); /* * knet_link_get_enable * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * enabled - 0 disable the link, 1 enable the link * * knet_link_get_enable returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int *enabled); /* * knet_link_set_ping_timers * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * interval - specify the ping interval * * timeout - if no pong is received within this time, * the link is declared dead * * precision - how many values of latency are used to calculate * the average link latency (see also get_status below) * * knet_link_set_ping_timers returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_LINK_DEFAULT_PING_INTERVAL 1000 /* 1 second */ #define KNET_LINK_DEFAULT_PING_TIMEOUT 2000 /* 2 seconds */ #define KNET_LINK_DEFAULT_PING_PRECISION 2048 /* samples */ int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t interval, time_t timeout, unsigned int precision); /* * knet_link_get_ping_timers * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * interval - ping intervall * * timeout - if no pong is received within this time, * the link is declared dead * * precision - how many values of latency are used to calculate * the average link latency (see also get_status below) * * knet_link_get_ping_timers returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t *interval, time_t *timeout, unsigned int *precision); /* * knet_link_set_pong_count * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * pong_count - how many valid ping/pongs before a link is marked UP. * default: 5, value should be > 0 * * knet_link_set_pong_count returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_LINK_DEFAULT_PONG_COUNT 5 int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t pong_count); /* * knet_link_get_pong_count * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * pong_count - see above * * knet_link_get_pong_count returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *pong_count); /* * knet_link_set_priority * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * priority - specify the switching priority for this link * see also knet_host_set_policy * * knet_link_set_priority returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t priority); /* * knet_link_get_priority * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * priority - gather the switching priority for this link * see also knet_host_set_policy * * knet_link_get_priority returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *priority); /* * knet_link_get_link_list * * knet_h - pointer to knet_handle_t * * link_ids - array of at lest KNET_MAX_LINK size * with the list of configured links for a certain host. * * link_ids_entries - * number of entries contained in link_ids * * knet_link_get_link_list returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *link_ids, size_t *link_ids_entries); /* * define link status structure for quick lookup * * src/dst_{ipaddr,port} strings are filled by * getnameinfo(3) when configuring the link. * if the link is dynamic (see knet_link_set_config) * dst_ipaddr/port will contain ipaddr/port of the currently * connected peer or "Unknown" if it was not possible * to determine the ipaddr/port at runtime. * * enabled see also knet_link_set/get_enable. * * connected the link is connected to a peer and ping/pong traffic * is flowing. * * dynconnected the link has dynamic ip on the other end, and * we can see the other host is sending pings to us. * * latency average latency of this link * see also knet_link_set/get_timeout. * * pong_last if the link is down, this value tells us how long * ago this link was active. A value of 0 means that the link * has never been active. * * knet_link_stats structure that contains details statistics for the link */ #define MAX_LINK_EVENTS 16 struct knet_link_stats { /* onwire values */ uint64_t tx_data_packets; uint64_t rx_data_packets; uint64_t tx_data_bytes; uint64_t rx_data_bytes; uint64_t rx_ping_packets; uint64_t tx_ping_packets; uint64_t rx_ping_bytes; uint64_t tx_ping_bytes; uint64_t rx_pong_packets; uint64_t tx_pong_packets; uint64_t rx_pong_bytes; uint64_t tx_pong_bytes; uint64_t rx_pmtu_packets; uint64_t tx_pmtu_packets; uint64_t rx_pmtu_bytes; uint64_t tx_pmtu_bytes; /* Only filled in when requested */ uint64_t tx_total_packets; uint64_t rx_total_packets; uint64_t tx_total_bytes; uint64_t rx_total_bytes; uint64_t tx_total_errors; uint64_t tx_total_retries; uint32_t tx_pmtu_errors; uint32_t tx_pmtu_retries; uint32_t tx_ping_errors; uint32_t tx_ping_retries; uint32_t tx_pong_errors; uint32_t tx_pong_retries; uint32_t tx_data_errors; uint32_t tx_data_retries; /* measured in usecs */ uint32_t latency_min; uint32_t latency_max; uint32_t latency_ave; uint32_t latency_samples; /* how many times the link has been going up/down */ uint32_t down_count; uint32_t up_count; /* * circular buffer of time_t structs collecting the history * of up/down events on this link. * the index indicates current/last event. * it is safe to walk back the history by decreasing the index */ time_t last_up_times[MAX_LINK_EVENTS]; time_t last_down_times[MAX_LINK_EVENTS]; int8_t last_up_time_index; int8_t last_down_time_index; /* Always add new stats at the end */ }; struct knet_link_status { size_t size; /* For ABI checking */ char src_ipaddr[KNET_MAX_HOST_LEN]; char src_port[KNET_MAX_PORT_LEN]; char dst_ipaddr[KNET_MAX_HOST_LEN]; char dst_port[KNET_MAX_PORT_LEN]; uint8_t enabled; /* link is configured and admin enabled for traffic */ uint8_t connected; /* link is connected for data (local view) */ uint8_t dynconnected; /* link has been activated by remote dynip */ unsigned long long latency; /* average latency computed by fix/exp */ struct timespec pong_last; unsigned int mtu; /* current detected MTU on this link */ unsigned int proto_overhead; /* contains the size of the IP protocol, knet headers and * crypto headers (if configured). This value is filled in * ONLY after the first PMTUd run on that given link, * and can change if link configuration or crypto configuration * changes at runtime. * WARNING: in general mtu + proto_overhead might or might * not match the output of ifconfig mtu due to crypto * requirements to pad packets to some specific boundaries. */ /* Link statistics */ struct knet_link_stats stats; }; /* * knet_link_get_status * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * status - pointer to knet_link_status struct (see above) * * struct_size - max size of knet_link_status - allows library to * add fields without ABI change. Returned structure * will be truncated to this length and .size member * indicates the full size. * * knet_link_get_status returns: * * 0 on success * -1 on error and errno is set. */ int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, struct knet_link_status *status, size_t struct_size); /* * logging structs/API calls */ /* * libknet is composed of several subsystems. In order * to easily distinguish log messages coming from different * places, each subsystem has its own ID. * * 0-19 config/management * 20-39 internal threads * 40-59 transports * 60-69 crypto implementations */ -#define KNET_SUB_COMMON 0 /* common.c */ -#define KNET_SUB_HANDLE 1 /* handle.c alloc/dealloc config changes */ -#define KNET_SUB_HOST 2 /* host add/del/modify */ -#define KNET_SUB_LISTENER 3 /* listeners add/del/modify... */ -#define KNET_SUB_LINK 4 /* link add/del/modify */ -#define KNET_SUB_TRANSPORT 5 /* Transport common */ -#define KNET_SUB_CRYPTO 6 /* crypto.c config generic layer */ -#define KNET_SUB_COMPRESS 7 /* compress.c config generic layer */ +#define KNET_SUB_COMMON 0 /* common.c */ +#define KNET_SUB_HANDLE 1 /* handle.c alloc/dealloc config changes */ +#define KNET_SUB_HOST 2 /* host add/del/modify */ +#define KNET_SUB_LISTENER 3 /* listeners add/del/modify... */ +#define KNET_SUB_LINK 4 /* link add/del/modify */ +#define KNET_SUB_TRANSPORT 5 /* Transport common */ +#define KNET_SUB_CRYPTO 6 /* crypto.c config generic layer */ +#define KNET_SUB_COMPRESS 7 /* compress.c config generic layer */ -#define KNET_SUB_FILTER 19 /* allocated for users to log from dst_filter */ +#define KNET_SUB_FILTER 19 /* allocated for users to log from dst_filter */ -#define KNET_SUB_DSTCACHE 20 /* switching thread (destination cache handling) */ -#define KNET_SUB_HEARTBEAT 21 /* heartbeat thread */ -#define KNET_SUB_PMTUD 22 /* Path MTU Discovery thread */ -#define KNET_SUB_TX 23 /* send to link thread */ -#define KNET_SUB_RX 24 /* recv from link thread */ +#define KNET_SUB_DSTCACHE 20 /* switching thread (destination cache handling) */ +#define KNET_SUB_HEARTBEAT 21 /* heartbeat thread */ +#define KNET_SUB_PMTUD 22 /* Path MTU Discovery thread */ +#define KNET_SUB_TX 23 /* send to link thread */ +#define KNET_SUB_RX 24 /* recv from link thread */ -#define KNET_SUB_TRANSP_BASE 40 /* Base log level for transports */ +#define KNET_SUB_TRANSP_BASE 40 /* Base log level for transports */ #define KNET_SUB_TRANSP_LOOPBACK (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_LOOPBACK) #define KNET_SUB_TRANSP_UDP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_UDP) #define KNET_SUB_TRANSP_SCTP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_SCTP) -#define KNET_SUB_NSSCRYPTO 60 /* nsscrypto.c */ +#define KNET_SUB_NSSCRYPTO 60 /* nsscrypto.c */ +#define KNET_SUB_OPENSSLCRYPTO 61 /* opensslcrypto.c */ -#define KNET_SUB_ZLIBCOMP 70 /* compress_zlib.c */ -#define KNET_SUB_LZ4COMP 71 /* compress_lz4.c */ -#define KNET_SUB_LZ4HCCOMP 72 /* compress_lz4.c */ -#define KNET_SUB_LZO2COMP 73 /* compress_lzo.c */ -#define KNET_SUB_LZMACOMP 74 /* compress_lzma.c */ -#define KNET_SUB_BZIP2COMP 75 /* compress_bzip2.c */ +#define KNET_SUB_ZLIBCOMP 70 /* compress_zlib.c */ +#define KNET_SUB_LZ4COMP 71 /* compress_lz4.c */ +#define KNET_SUB_LZ4HCCOMP 72 /* compress_lz4.c */ +#define KNET_SUB_LZO2COMP 73 /* compress_lzo.c */ +#define KNET_SUB_LZMACOMP 74 /* compress_lzma.c */ +#define KNET_SUB_BZIP2COMP 75 /* compress_bzip2.c */ -#define KNET_SUB_UNKNOWN 254 -#define KNET_MAX_SUBSYSTEMS KNET_SUB_UNKNOWN + 1 +#define KNET_SUB_UNKNOWN 254 +#define KNET_MAX_SUBSYSTEMS KNET_SUB_UNKNOWN + 1 /* * Convert between subsystem IDs and names */ /* * knet_log_get_subsystem_name * * return internal name of the subsystem or "common" */ const char *knet_log_get_subsystem_name(uint8_t subsystem); /* * knet_log_get_subsystem_id * * return internal ID of the subsystem or KNET_SUB_COMMON */ uint8_t knet_log_get_subsystem_id(const char *name); /* * 4 log levels are enough for everybody */ #define KNET_LOG_ERR 0 /* unrecoverable errors/conditions */ #define KNET_LOG_WARN 1 /* recoverable errors/conditions */ #define KNET_LOG_INFO 2 /* info, link up/down, config changes.. */ #define KNET_LOG_DEBUG 3 /* * Convert between log level values and names */ /* * knet_log_get_loglevel_name * * return internal name of the log level or "ERROR" for unknown values */ const char *knet_log_get_loglevel_name(uint8_t level); /* * knet_log_get_loglevel_id * * return internal log level ID or KNET_LOG_ERR for invalid names */ uint8_t knet_log_get_loglevel_id(const char *name); /* * every log message is composed by a text message (including a trailing \n) * and message level/subsystem IDs. * In order to make debugging easier it is possible to send those packets * straight to stdout/stderr (see knet_bench.c stdout option). */ #define KNET_MAX_LOG_MSG_SIZE 256 struct knet_log_msg { char msg[KNET_MAX_LOG_MSG_SIZE - (sizeof(uint8_t)*2)]; uint8_t subsystem; /* KNET_SUB_* */ uint8_t msglevel; /* KNET_LOG_* */ }; /* * knet_log_set_log_level * * knet_h - same as above * * subsystem - same as above * * level - same as above * * knet_log_set_loglevel allows fine control of log levels by subsystem. * See also knet_handle_new for defaults. * * knet_log_set_loglevel returns: * * 0 on success * -1 on error and errno is set. */ int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t level); /* * knet_log_get_log_level * * knet_h - same as above * * subsystem - same as above * * level - same as above * * knet_log_get_loglevel returns: * * 0 on success * -1 on error and errno is set. */ int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t *level); #endif diff --git a/libknet/logging.c b/libknet/logging.c index f801813c..758a2a58 100644 --- a/libknet/logging.c +++ b/libknet/logging.c @@ -1,255 +1,256 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include "internals.h" #include "logging.h" struct pretty_names { const char *name; uint8_t val; }; static struct pretty_names subsystem_names[] = { { "common", KNET_SUB_COMMON }, { "handle", KNET_SUB_HANDLE }, { "host", KNET_SUB_HOST }, { "listener", KNET_SUB_LISTENER }, { "link", KNET_SUB_LINK }, { "transport", KNET_SUB_TRANSPORT }, { "crypto", KNET_SUB_CRYPTO }, { "compress", KNET_SUB_COMPRESS }, { "filter", KNET_SUB_FILTER }, { "dstcache", KNET_SUB_DSTCACHE }, { "heartbeat", KNET_SUB_HEARTBEAT }, { "pmtud", KNET_SUB_PMTUD }, { "tx", KNET_SUB_TX }, { "rx", KNET_SUB_RX }, { "loopback", KNET_SUB_TRANSP_LOOPBACK }, { "udp", KNET_SUB_TRANSP_UDP }, { "sctp", KNET_SUB_TRANSP_SCTP }, { "nsscrypto", KNET_SUB_NSSCRYPTO }, + { "opensslcrypto", KNET_SUB_OPENSSLCRYPTO }, { "zlibcomp", KNET_SUB_ZLIBCOMP }, { "lz4comp", KNET_SUB_LZ4COMP }, { "lz4hccomp", KNET_SUB_LZ4HCCOMP }, { "lzo2comp", KNET_SUB_LZO2COMP }, { "lzmacomp", KNET_SUB_LZMACOMP }, { "bzip2comp", KNET_SUB_BZIP2COMP }, { "unknown", KNET_SUB_UNKNOWN } /* unknown MUST always be last in this array */ }; const char *knet_log_get_subsystem_name(uint8_t subsystem) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if (subsystem_names[i].val == KNET_SUB_UNKNOWN) { break; } if (subsystem_names[i].val == subsystem) { return subsystem_names[i].name; } } return "unknown"; } uint8_t knet_log_get_subsystem_id(const char *name) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if (subsystem_names[i].val == KNET_SUB_UNKNOWN) { break; } if (strcasecmp(name, subsystem_names[i].name) == 0) { return subsystem_names[i].val; } } return KNET_SUB_UNKNOWN; } static int is_valid_subsystem(uint8_t subsystem) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if ((subsystem != KNET_SUB_UNKNOWN) && (subsystem_names[i].val == KNET_SUB_UNKNOWN)) { break; } if (subsystem_names[i].val == subsystem) { return 0; } } return -1; } static struct pretty_names loglevel_names[] = { { "ERROR", KNET_LOG_ERR }, { "WARNING", KNET_LOG_WARN }, { "info", KNET_LOG_INFO }, { "debug", KNET_LOG_DEBUG } }; const char *knet_log_get_loglevel_name(uint8_t level) { unsigned int i; for (i = 0; i <= KNET_LOG_DEBUG; i++) { if (loglevel_names[i].val == level) { return loglevel_names[i].name; } } return "ERROR"; } uint8_t knet_log_get_loglevel_id(const char *name) { unsigned int i; for (i = 0; i <= KNET_LOG_DEBUG; i++) { if (strcasecmp(name, loglevel_names[i].name) == 0) { return loglevel_names[i].val; } } return KNET_LOG_ERR; } int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t level) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (is_valid_subsystem(subsystem) < 0) { errno = EINVAL; return -1; } if (level > KNET_LOG_DEBUG) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, subsystem, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->log_levels[subsystem] = level; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t *level) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (is_valid_subsystem(subsystem) < 0) { errno = EINVAL; return -1; } if (!level) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, subsystem, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } *level = knet_h->log_levels[subsystem]; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } void log_msg(knet_handle_t knet_h, uint8_t subsystem, uint8_t msglevel, const char *fmt, ...) { va_list ap; struct knet_log_msg msg; size_t byte_cnt = 0; int len, err; if ((!knet_h) || (subsystem == KNET_MAX_SUBSYSTEMS) || (msglevel > knet_h->log_levels[subsystem])) return; /* * most logging calls will take place with locking in place. * if we get an EINVAL and locking is initialized, then * we are getting a real error and we need to stop */ err = pthread_rwlock_tryrdlock(&knet_h->global_rwlock); if ((err == EAGAIN) && (knet_h->lock_init_done)) return; if (knet_h->logfd <= 0) goto out_unlock; memset(&msg, 0, sizeof(struct knet_log_msg)); msg.subsystem = subsystem; msg.msglevel = msglevel; va_start(ap, fmt); vsnprintf(msg.msg, sizeof(msg.msg) - 2, fmt, ap); va_end(ap); len = strlen(msg.msg); msg.msg[len+1] = '\n'; while (byte_cnt < sizeof(struct knet_log_msg)) { len = write(knet_h->logfd, &msg, sizeof(struct knet_log_msg) - byte_cnt); if (len <= 0) { goto out_unlock; } byte_cnt += len; } out_unlock: /* * unlock only if we are holding the lock */ if (!err) pthread_rwlock_unlock(&knet_h->global_rwlock); return; } diff --git a/libknet/tests/Makefile.am b/libknet/tests/Makefile.am index 23227338..9621b68c 100644 --- a/libknet/tests/Makefile.am +++ b/libknet/tests/Makefile.am @@ -1,94 +1,96 @@ # # Copyright (C) 2016 Red Hat, Inc. All rights reserved. # # Authors: Fabio M. Di Nitto # # This software licensed under GPL-2.0+, LGPL-2.0+ # MAINTAINERCLEANFILES = Makefile.in include $(top_srcdir)/build-aux/check.mk include $(top_srcdir)/libknet/tests/api-check.mk EXTRA_DIST = \ api-test-coverage \ api-check.mk AM_CPPFLAGS = -I$(top_srcdir)/libknet # override global LIBS that pulls in lots of craft we don't need here LIBS = $(top_builddir)/libknet/libknet.la \ $(dl_LIBS) $(pthread_LIBS) noinst_HEADERS = \ test-common.h # the order of those tests is NOT random. # some functions can only be tested properly after some dependents # API have been validated upfront. check_PROGRAMS = \ $(api_checks) \ $(int_checks) \ $(fun_checks) int_checks = \ int_lib_load_unload \ int_crypto_test \ int_timediff_test fun_checks = benchmarks = \ crypto_bench_test \ knet_bench_test noinst_PROGRAMS = \ api_knet_handle_new_limit_test \ pckt_test \ $(benchmarks) \ $(check_PROGRAMS) noinst_SCRIPTS = \ api-test-coverage TESTS = $(check_PROGRAMS) check-local: check-api-test-coverage check-api-test-coverage: chmod u+x $(top_srcdir)/libknet/tests/api-test-coverage $(top_srcdir)/libknet/tests/api-test-coverage $(top_srcdir) $(top_builddir) pckt_test_SOURCES = pckt_test.c int_lib_load_unload_SOURCES = int_lib_load_unload.c \ test-common.c int_crypto_test_SOURCES = int_crypto.c \ ../common.c \ ../crypto.c \ ../crypto_nss.c \ + ../crypto_openssl.c \ ../logging.c \ test-common.c int_crypto_test_CFLAGS = $(nss_CFLAGS) int_timediff_test_SOURCES = int_timediff.c crypto_bench_test_SOURCES = crypto_bench.c \ ../common.c \ ../crypto.c \ ../crypto_nss.c \ + ../crypto_openssl.c \ ../logging.c \ test-common.c crypto_bench_test_CFLAGS = $(nss_CFLAGS) knet_bench_test_SOURCES = knet_bench.c \ test-common.c \ ../common.c \ ../logging.c \ ../compat.c \ ../transport_common.c diff --git a/libknet/tests/knet_bench.c b/libknet/tests/knet_bench.c index ea3dc91a..24cae2d1 100644 --- a/libknet/tests/knet_bench.c +++ b/libknet/tests/knet_bench.c @@ -1,1026 +1,1026 @@ /* * Copyright (C) 2016 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "libknet.h" #include "compat.h" #include "internals.h" #include "netutils.h" #include "transports.h" #include "threads_common.h" #include "test-common.h" #define MAX_NODES 128 static int senderid = -1; static int thisnodeid = -1; static knet_handle_t knet_h; static int datafd = 0; static int8_t channel = 0; static int globallistener = 0; static int continous = 0; static struct sockaddr_storage allv4; static struct sockaddr_storage allv6; static int broadcast_test = 1; static pthread_t rx_thread = (pthread_t)NULL; static char *rx_buf[PCKT_FRAG_MAX]; static int wait_for_perf_rx = 0; static char *compresscfg = NULL; static int bench_shutdown_in_progress = 0; static pthread_mutex_t shutdown_mutex = PTHREAD_MUTEX_INITIALIZER; #define TEST_PING 0 #define TEST_PING_AND_DATA 1 #define TEST_PERF_BY_SIZE 2 #define TEST_PERF_BY_TIME 3 static int test_type = TEST_PING; #define TEST_START 2 #define TEST_STOP 4 #define TEST_COMPLETE 6 #define ONE_GIGABYTE 1073741824 static uint64_t perf_by_size_size = 1 * ONE_GIGABYTE; static uint64_t perf_by_time_secs = 10; struct node { int nodeid; int links; struct sockaddr_storage address[KNET_MAX_LINK]; }; static void print_help(void) { printf("knet_bench usage:\n"); printf(" -h print this help (no really)\n"); printf(" -d enable debug logs (default INFO)\n"); printf(" -c [implementation]:[crypto]:[hashing] crypto configuration. (default disabled)\n"); printf(" Example: -c nss:aes128:sha1\n"); printf(" -z [implementation]:[level]:[threshold] compress configuration. (default disabled)\n"); printf(" Example: -z zlib:5:100\n"); printf(" -p [active|passive|rr] (default: passive)\n"); printf(" -P [udp|sctp] (default: udp) protocol (transport) to use\n"); printf(" -t [nodeid] This nodeid (required)\n"); printf(" -n [nodeid],[link1_ip_addr],[link2_..] Other nodes information (at least one required)\n"); printf(" Example: -t 1,192.168.8.1,3ffe::8:1,..\n"); printf(" can be repeated up to %d and should contain also the localnode info\n", MAX_NODES); printf(" -b [port] baseport (default: 50000)\n"); printf(" -l enable global listener on 0.0.0.0/:: (default: off, incompatible with -o)\n"); printf(" -o enable baseport offset per nodeid\n"); printf(" -w dont wait for all nodes to be up before starting the test (default: wait)\n"); printf(" -T [ping|ping_data|perf-by-size|perf-by-time]\n"); printf(" test type (default: ping)\n"); printf(" ping: will wait for all hosts to join the knet network, sleep 5 seconds and quit\n"); printf(" ping_data: will wait for all hosts to join the knet network, sends some data to all nodes and quit\n"); printf(" perf-by-size: will wait for all hosts to join the knet network,\n"); printf(" perform a series of benchmarks by transmitting a known\n"); printf(" size/quantity of packets and measuring the time, then quit\n"); printf(" perf-by-time: will wait for all hosts to join the knet network,\n"); printf(" perform a series of benchmarks by transmitting a known\n"); printf(" size of packets for a given amount of time (10 seconds)\n"); printf(" and measuring the quantity of data transmitted, then quit\n"); printf(" -s nodeid that will generate traffic for benchmarks\n"); printf(" -S [size|seconds] when used in combination with -T perf-by-size it indicates how many GB of traffic to generate for the test. (default: 1GB)\n"); printf(" when used in combination with -T perf-by-time it indicates how many Seconds of traffic to generate for the test. (default: 10 seconds)\n"); printf(" -C repeat the test continously (default: off)\n"); } static void parse_nodes(char *nodesinfo[MAX_NODES], int onidx, int port, struct node nodes[MAX_NODES], int *thisidx) { int i; char *temp = NULL; char port_str[10]; memset(port_str, 0, sizeof(port_str)); sprintf(port_str, "%d", port); for (i = 0; i < onidx; i++) { nodes[i].nodeid = atoi(strtok(nodesinfo[i], ",")); if ((nodes[i].nodeid < 0) || (nodes[i].nodeid > KNET_MAX_HOST)) { printf("Invalid nodeid: %d (0 - %d)\n", nodes[i].nodeid, KNET_MAX_HOST); exit(FAIL); } if (thisnodeid == nodes[i].nodeid) { *thisidx = i; } while((temp = strtok(NULL, ","))) { if (nodes[i].links == KNET_MAX_LINK) { printf("Too many links configured. Max %d\n", KNET_MAX_LINK); exit(FAIL); } if (knet_strtoaddr(temp, port_str, &nodes[i].address[nodes[i].links], sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert %s to sockaddress\n", temp); exit(FAIL); } nodes[i].links++; } } if (knet_strtoaddr("0.0.0.0", port_str, &allv4, sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert 0.0.0.0 to sockaddress\n"); exit(FAIL); } if (knet_strtoaddr("::", port_str, &allv6, sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert :: to sockaddress\n"); exit(FAIL); } for (i = 1; i < onidx; i++) { if (nodes[0].links != nodes[i].links) { printf("knet_bench does not support unbalanced link configuration\n"); exit(FAIL); } } return; } static int private_data; static void sock_notify(void *pvt_data, int local_datafd, int8_t local_channel, uint8_t tx_rx, int error, int errorno) { printf("Error (%d - %d - %s) from socket: %d\n", error, errorno, strerror(errno), local_datafd); return; } static int ping_dst_host_filter(void *pvt_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *dst_channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { if (broadcast_test) { return 1; } if (tx_rx == KNET_NOTIFY_TX) { memmove(&dst_host_ids[0], outdata, 2); } else { dst_host_ids[0] = this_host_id; } *dst_host_ids_entries = 1; return 0; } static void setup_knet(int argc, char *argv[]) { int logfd; int rv; char *cryptocfg = NULL, *policystr = NULL, *protostr = NULL; char *othernodeinfo[MAX_NODES]; struct node nodes[MAX_NODES]; int thisidx = -1; int onidx = 0; int debug = KNET_LOG_INFO; int port = 50000, portoffset = 0; int thisport = 0, otherport = 0; int thisnewport = 0, othernewport = 0; struct sockaddr_in *so_in; struct sockaddr_in6 *so_in6; struct sockaddr_storage *src; int i, link_idx, allnodesup = 0; int policy = KNET_LINK_POLICY_PASSIVE, policyfound = 0; int protocol = KNET_TRANSPORT_UDP, protofound = 0; int wait = 1; struct knet_handle_crypto_cfg knet_handle_crypto_cfg; char *cryptomodel = NULL, *cryptotype = NULL, *cryptohash = NULL; struct knet_handle_compress_cfg knet_handle_compress_cfg; memset(nodes, 0, sizeof(nodes)); while ((rv = getopt(argc, argv, "CT:S:s:ldowb:t:n:c:p:P:z:h")) != EOF) { switch(rv) { case 'h': print_help(); exit(PASS); break; case 'd': debug = KNET_LOG_DEBUG; break; case 'c': if (cryptocfg) { printf("Error: -c can only be specified once\n"); exit(FAIL); } cryptocfg = optarg; break; case 'p': if (policystr) { printf("Error: -p can only be specified once\n"); exit(FAIL); } policystr = optarg; if (!strcmp(policystr, "active")) { policy = KNET_LINK_POLICY_ACTIVE; policyfound = 1; } /* * we can't use rr because clangs can't compile * an array of 3 strings, one of which is 2 bytes long */ if (!strcmp(policystr, "round-robin")) { policy = KNET_LINK_POLICY_RR; policyfound = 1; } if (!strcmp(policystr, "passive")) { policy = KNET_LINK_POLICY_PASSIVE; policyfound = 1; } if (!policyfound) { printf("Error: invalid policy %s specified. -p accepts active|passive|rr\n", policystr); exit(FAIL); } break; case 'P': if (protostr) { printf("Error: -P can only be specified once\n"); exit(FAIL); } protostr = optarg; if (!strcmp(protostr, "udp")) { protocol = KNET_TRANSPORT_UDP; protofound = 1; } if (!strcmp(protostr, "sctp")) { protocol = KNET_TRANSPORT_SCTP; protofound = 1; } if (!protofound) { printf("Error: invalid protocol %s specified. -P accepts udp|sctp\n", policystr); exit(FAIL); } break; case 't': if (thisnodeid >= 0) { printf("Error: -t can only be specified once\n"); exit(FAIL); } thisnodeid = atoi(optarg); if ((thisnodeid < 0) || (thisnodeid > 65536)) { printf("Error: -t nodeid out of range %d (1 - 65536)\n", thisnodeid); exit(FAIL); } break; case 'n': if (onidx == MAX_NODES) { printf("Error: too many other nodes. Max %d\n", MAX_NODES); exit(FAIL); } othernodeinfo[onidx] = optarg; onidx++; break; case 'b': port = atoi(optarg); if ((port < 1) || (port > 65536)) { printf("Error: port %d out of range (1 - 65536)\n", port); exit(FAIL); } break; case 'o': if (globallistener) { printf("Error: -l cannot be used with -o\n"); exit(FAIL); } portoffset = 1; break; case 'l': if (portoffset) { printf("Error: -o cannot be used with -l\n"); exit(FAIL); } globallistener = 1; break; case 'w': wait = 0; break; case 's': if (senderid >= 0) { printf("Error: -s can only be specified once\n"); exit(FAIL); } senderid = atoi(optarg); if ((senderid < 0) || (senderid > 65536)) { printf("Error: -s nodeid out of range %d (1 - 65536)\n", senderid); exit(FAIL); } break; case 'T': if (!strcmp("ping", optarg)) { test_type = TEST_PING; } if (!strcmp("ping_data", optarg)) { test_type = TEST_PING_AND_DATA; } if (!strcmp("perf-by-size", optarg)) { test_type = TEST_PERF_BY_SIZE; } if (!strcmp("perf-by-time", optarg)) { test_type = TEST_PERF_BY_TIME; } break; case 'S': perf_by_size_size = (uint64_t)atoi(optarg) * ONE_GIGABYTE; perf_by_time_secs = (uint64_t)atoi(optarg); break; case 'C': continous = 1; break; case 'z': if (compresscfg) { printf("Error: -c can only be specified once\n"); exit(FAIL); } compresscfg = optarg; break; default: break; } } if (thisnodeid < 0) { printf("Who am I?!? missing -t from command line?\n"); exit(FAIL); } if (onidx < 1) { printf("no other nodes configured?!? missing -n from command line\n"); exit(FAIL); } parse_nodes(othernodeinfo, onidx, port, nodes, &thisidx); if (thisidx < 0) { printf("no config for this node found\n"); exit(FAIL); } if (senderid >= 0) { for (i=0; i < onidx; i++) { if (senderid == nodes[i].nodeid) { break; } } if (i == onidx) { printf("Unable to find senderid in nodelist\n"); exit(FAIL); } } if (((test_type == TEST_PERF_BY_SIZE) || (test_type == TEST_PERF_BY_TIME)) && (senderid < 0)) { printf("Error: performance test requires -s to be set (for now)\n"); exit(FAIL); } logfd = start_logging(stdout); knet_h = knet_handle_new(thisnodeid, logfd, debug); if (!knet_h) { printf("Unable to knet_handle_new: %s\n", strerror(errno)); exit(FAIL); } if (cryptocfg) { memset(&knet_handle_crypto_cfg, 0, sizeof(knet_handle_crypto_cfg)); cryptomodel = strtok(cryptocfg, ":"); cryptotype = strtok(NULL, ":"); cryptohash = strtok(NULL, ":"); if (cryptomodel) { strncpy(knet_handle_crypto_cfg.crypto_model, cryptomodel, sizeof(knet_handle_crypto_cfg.crypto_model) - 1); } if (cryptotype) { strncpy(knet_handle_crypto_cfg.crypto_cipher_type, cryptotype, sizeof(knet_handle_crypto_cfg.crypto_cipher_type) - 1); } if (cryptohash) { strncpy(knet_handle_crypto_cfg.crypto_hash_type, cryptohash, sizeof(knet_handle_crypto_cfg.crypto_hash_type) - 1); } - knet_handle_crypto_cfg.private_key_len = KNET_MAX_KEY_LEN; + knet_handle_crypto_cfg.private_key_len = 16; if (knet_handle_crypto(knet_h, &knet_handle_crypto_cfg)) { printf("Unable to init crypto\n"); exit(FAIL); } } if (compresscfg) { memset(&knet_handle_compress_cfg, 0, sizeof(struct knet_handle_compress_cfg)); snprintf(knet_handle_compress_cfg.compress_model, 16, "%s", strtok(compresscfg, ":")); knet_handle_compress_cfg.compress_level = atoi(strtok(NULL, ":")); knet_handle_compress_cfg.compress_threshold = atoi(strtok(NULL, ":")); if (knet_handle_compress(knet_h, &knet_handle_compress_cfg)) { printf("Unable to configure compress\n"); exit(FAIL); } } if (knet_handle_enable_sock_notify(knet_h, &private_data, sock_notify) < 0) { printf("knet_handle_enable_sock_notify failed: %s\n", strerror(errno)); knet_handle_free(knet_h); exit(FAIL); } datafd = 0; channel = -1; if (knet_handle_add_datafd(knet_h, &datafd, &channel) < 0) { printf("knet_handle_add_datafd failed: %s\n", strerror(errno)); knet_handle_free(knet_h); exit(FAIL); } if (knet_handle_pmtud_setfreq(knet_h, 60) < 0) { printf("knet_handle_pmtud_setfreq failed: %s\n", strerror(errno)); knet_handle_free(knet_h); exit(FAIL); } for (i=0; i < onidx; i++) { if (i == thisidx) { continue; } if (knet_host_add(knet_h, nodes[i].nodeid) < 0) { printf("knet_host_add failed: %s\n", strerror(errno)); exit(FAIL); } if (knet_host_set_policy(knet_h, nodes[i].nodeid, policy) < 0) { printf("knet_host_set_policy failed: %s\n", strerror(errno)); exit(FAIL); } for (link_idx = 0; link_idx < nodes[i].links; link_idx++) { if (portoffset) { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { so_in = (struct sockaddr_in *)&nodes[thisidx].address[link_idx]; thisport = ntohs(so_in->sin_port); thisnewport = thisport + nodes[i].nodeid; so_in->sin_port = (htons(thisnewport)); so_in = (struct sockaddr_in *)&nodes[i].address[link_idx]; otherport = ntohs(so_in->sin_port); othernewport = otherport + nodes[thisidx].nodeid; so_in->sin_port = (htons(othernewport)); } else { so_in6 = (struct sockaddr_in6 *)&nodes[thisidx].address[link_idx]; thisport = ntohs(so_in6->sin6_port); thisnewport = thisport + nodes[i].nodeid; so_in6->sin6_port = (htons(thisnewport)); so_in6 = (struct sockaddr_in6 *)&nodes[i].address[link_idx]; otherport = ntohs(so_in6->sin6_port); othernewport = otherport + nodes[thisidx].nodeid; so_in6->sin6_port = (htons(othernewport)); } } if (!globallistener) { src = &nodes[thisidx].address[link_idx]; } else { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { src = &allv4; } else { src = &allv6; } } if (knet_link_set_config(knet_h, nodes[i].nodeid, link_idx, protocol, src, &nodes[i].address[link_idx], 0) < 0) { printf("Unable to configure link: %s\n", strerror(errno)); exit(FAIL); } if (portoffset) { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { so_in = (struct sockaddr_in *)&nodes[thisidx].address[link_idx]; so_in->sin_port = (htons(thisport)); so_in = (struct sockaddr_in *)&nodes[i].address[link_idx]; so_in->sin_port = (htons(otherport)); } else { so_in6 = (struct sockaddr_in6 *)&nodes[thisidx].address[link_idx]; so_in6->sin6_port = (htons(thisport)); so_in6 = (struct sockaddr_in6 *)&nodes[i].address[link_idx]; so_in6->sin6_port = (htons(otherport)); } } if (knet_link_set_enable(knet_h, nodes[i].nodeid, link_idx, 1) < 0) { printf("knet_link_set_enable failed: %s\n", strerror(errno)); exit(FAIL); } if (knet_link_set_ping_timers(knet_h, nodes[i].nodeid, link_idx, 1000, 10000, 2048) < 0) { printf("knet_link_set_ping_timers failed: %s\n", strerror(errno)); exit(FAIL); } if (knet_link_set_pong_count(knet_h, nodes[i].nodeid, link_idx, 2) < 0) { printf("knet_link_set_pong_count failed: %s\n", strerror(errno)); exit(FAIL); } } } if (knet_handle_enable_filter(knet_h, NULL, ping_dst_host_filter)) { printf("Unable to enable dst_host_filter: %s\n", strerror(errno)); exit(FAIL); } if (knet_handle_setfwd(knet_h, 1) < 0) { printf("knet_handle_setfwd failed: %s\n", strerror(errno)); exit(FAIL); } if (wait) { while(!allnodesup) { allnodesup = 1; for (i=0; i < onidx; i++) { if (i == thisidx) { continue; } if(knet_h->host_index[nodes[i].nodeid]->status.reachable != 1) { printf("waiting host %d to be reachable\n", nodes[i].nodeid); allnodesup = 0; } } if (!allnodesup) { sleep(1); } } sleep(1); } } static void *_rx_thread(void *args) { int rx_epoll; struct epoll_event ev; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; struct sockaddr_storage address[PCKT_FRAG_MAX]; struct knet_mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_in[PCKT_FRAG_MAX]; int i, msg_recv; struct timespec clock_start, clock_end; unsigned long long time_diff = 0; uint64_t rx_pkts = 0; uint64_t rx_bytes = 0; unsigned int current_pckt_size = 0; for (i = 0; i < PCKT_FRAG_MAX; i++) { rx_buf[i] = malloc(KNET_MAX_PACKET_SIZE); if (!rx_buf[i]) { printf("RXT: Unable to malloc!\n"); return NULL; } memset(rx_buf[i], 0, KNET_MAX_PACKET_SIZE); iov_in[i].iov_base = (void *)rx_buf[i]; iov_in[i].iov_len = KNET_MAX_PACKET_SIZE; memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr)); msg[i].msg_hdr.msg_name = &address[i]; msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); msg[i].msg_hdr.msg_iov = &iov_in[i]; msg[i].msg_hdr.msg_iovlen = 1; } rx_epoll = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (rx_epoll < 0) { printf("RXT: Unable to create epoll!\nHALTING RX THREAD!\n"); return NULL; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = datafd; if (epoll_ctl(rx_epoll, EPOLL_CTL_ADD, datafd, &ev)) { printf("RXT: Unable to add datafd to epoll\nHALTING RX THREAD!\n"); return NULL; } memset(&clock_start, 0, sizeof(clock_start)); memset(&clock_end, 0, sizeof(clock_start)); while (!bench_shutdown_in_progress) { if (epoll_wait(rx_epoll, events, KNET_EPOLL_MAX_EVENTS, 1) >= 1) { msg_recv = _recvmmsg(datafd, &msg[0], PCKT_FRAG_MAX, MSG_DONTWAIT | MSG_NOSIGNAL); if (msg_recv < 0) { printf("RXT: error from recvmmsg: %s\n", strerror(errno)); } switch(test_type) { case TEST_PING_AND_DATA: for (i = 0; i < msg_recv; i++) { if (msg[i].msg_len == 0) { printf("RXT: received 0 bytes message?\n"); } printf("received %u bytes message: %s\n", msg[i].msg_len, (char *)msg[i].msg_hdr.msg_iov->iov_base); } break; case TEST_PERF_BY_TIME: case TEST_PERF_BY_SIZE: for (i = 0; i < msg_recv; i++) { if (msg[i].msg_len < 64) { if (msg[i].msg_len == 0) { printf("RXT: received 0 bytes message?\n"); } if (msg[i].msg_len == TEST_START) { if (clock_gettime(CLOCK_MONOTONIC, &clock_start) != 0) { printf("Unable to get start time!\n"); } } if (msg[i].msg_len == TEST_STOP) { double average_rx_mbytes; double average_rx_pkts; double time_diff_sec; if (clock_gettime(CLOCK_MONOTONIC, &clock_end) != 0) { printf("Unable to get end time!\n"); } timespec_diff(clock_start, clock_end, &time_diff); /* * adjust for sleep(2) between sending the last data and TEST_STOP */ time_diff = time_diff - 2000000000llu; /* * convert to seconds */ time_diff_sec = (double)time_diff / 1000000000llu; average_rx_mbytes = (double)((rx_bytes / time_diff_sec) / (1024 * 1024)); average_rx_pkts = (double)(rx_pkts / time_diff_sec); printf("Execution time: %8.4f secs Average speed: %8.4f MB/sec %8.4f pckts/sec (size: %u total: %" PRIu64 ")\n", time_diff_sec, average_rx_mbytes, average_rx_pkts, current_pckt_size, rx_pkts); rx_pkts = 0; rx_bytes = 0; current_pckt_size = 0; } if (msg[i].msg_len == TEST_COMPLETE) { wait_for_perf_rx = 1; } continue; } rx_pkts++; rx_bytes = rx_bytes + msg[i].msg_len; current_pckt_size = msg[i].msg_len; } break; } } } epoll_ctl(rx_epoll, EPOLL_CTL_DEL, datafd, &ev); close(rx_epoll); return NULL; } static void setup_data_txrx_common(void) { if (!rx_thread) { if (knet_handle_enable_filter(knet_h, NULL, ping_dst_host_filter)) { printf("Unable to enable dst_host_filter: %s\n", strerror(errno)); exit(FAIL); } printf("Setting up rx thread\n"); if (pthread_create(&rx_thread, 0, _rx_thread, NULL)) { printf("Unable to start rx thread\n"); exit(FAIL); } } } static void stop_rx_thread(void) { void *retval; int i; if (rx_thread) { printf("Shutting down rx thread\n"); sleep(2); pthread_cancel(rx_thread); pthread_join(rx_thread, &retval); for (i = 0; i < PCKT_FRAG_MAX; i ++) { free(rx_buf[i]); } } } static void send_ping_data(void) { char buf[65535]; ssize_t len; memset(&buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf), "Hello world!"); if (compresscfg) { len = sizeof(buf); } else { len = strlen(buf); } if (knet_send(knet_h, buf, len, channel) != len) { printf("Error sending hello world: %s\n", strerror(errno)); } sleep(1); } static int send_messages(struct knet_mmsghdr *msg, int msgs_to_send) { int sent_msgs, prev_sent, progress, total_sent; total_sent = 0; sent_msgs = 0; prev_sent = 0; progress = 1; retry: errno = 0; sent_msgs = _sendmmsg(datafd, &msg[0], msgs_to_send, MSG_NOSIGNAL); if (sent_msgs < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { usleep(KNET_THREADS_TIMERES / 16); goto retry; } printf("Unable to send messages: %s\n", strerror(errno)); return -1; } total_sent = total_sent + sent_msgs; if ((sent_msgs >= 0) && (sent_msgs < msgs_to_send)) { if ((sent_msgs) || (progress)) { msgs_to_send = msgs_to_send - sent_msgs; prev_sent = prev_sent + sent_msgs; if (sent_msgs) { progress = 1; } else { progress = 0; } goto retry; } if (!progress) { printf("Unable to send more messages after retry\n"); } } return total_sent; } static int setup_send_buffers_common(struct knet_mmsghdr *msg, struct iovec *iov_out, char *tx_buf[]) { int i; for (i = 0; i < PCKT_FRAG_MAX; i++) { tx_buf[i] = malloc(KNET_MAX_PACKET_SIZE); if (!tx_buf[i]) { printf("TXT: Unable to malloc!\n"); return -1; } memset(tx_buf[i], 0, KNET_MAX_PACKET_SIZE); iov_out[i].iov_base = (void *)tx_buf[i]; memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr)); msg[i].msg_hdr.msg_iov = &iov_out[i]; msg[i].msg_hdr.msg_iovlen = 1; } return 0; } static void send_perf_data_by_size(void) { char *tx_buf[PCKT_FRAG_MAX]; struct knet_mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_out[PCKT_FRAG_MAX]; char ctrl_message[16]; int sent_msgs; int i; uint64_t total_pkts_to_tx; uint64_t packets_to_send; uint32_t packetsize = 64; setup_send_buffers_common(msg, iov_out, tx_buf); while (packetsize <= KNET_MAX_PACKET_SIZE) { for (i = 0; i < PCKT_FRAG_MAX; i++) { iov_out[i].iov_len = packetsize; } total_pkts_to_tx = perf_by_size_size / packetsize; printf("Testing with %u packet size. Total bytes to transfer: %" PRIu64 " (%" PRIu64 " packets)\n", packetsize, perf_by_size_size, total_pkts_to_tx); memset(ctrl_message, 0, sizeof(ctrl_message)); knet_send(knet_h, ctrl_message, TEST_START, channel); while (total_pkts_to_tx > 0) { if (total_pkts_to_tx >= PCKT_FRAG_MAX) { packets_to_send = PCKT_FRAG_MAX; } else { packets_to_send = total_pkts_to_tx; } sent_msgs = send_messages(&msg[0], packets_to_send); if (sent_msgs < 0) { printf("Something went wrong, aborting\n"); exit(FAIL); } total_pkts_to_tx = total_pkts_to_tx - sent_msgs; } sleep(2); knet_send(knet_h, ctrl_message, TEST_STOP, channel); if (packetsize == KNET_MAX_PACKET_SIZE) { break; } /* * Use a multiplier that can always divide properly a GB * into smaller chunks without worry about boundaries */ packetsize *= 4; if (packetsize > KNET_MAX_PACKET_SIZE) { packetsize = KNET_MAX_PACKET_SIZE; } } knet_send(knet_h, ctrl_message, TEST_COMPLETE, channel); for (i = 0; i < PCKT_FRAG_MAX; i++) { free(tx_buf[i]); } } static void send_perf_data_by_time(void) { char *tx_buf[PCKT_FRAG_MAX]; struct knet_mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_out[PCKT_FRAG_MAX]; char ctrl_message[16]; int sent_msgs; int i; uint32_t packetsize = 65536; struct timespec clock_start, clock_end; unsigned long long time_diff = 0; setup_send_buffers_common(msg, iov_out, tx_buf); memset(&clock_start, 0, sizeof(clock_start)); memset(&clock_end, 0, sizeof(clock_start)); while (packetsize <= KNET_MAX_PACKET_SIZE) { for (i = 0; i < PCKT_FRAG_MAX; i++) { iov_out[i].iov_len = packetsize; } printf("Testing with %u bytes packet size for %" PRIu64 " seconds.\n", packetsize, perf_by_time_secs); memset(ctrl_message, 0, sizeof(ctrl_message)); knet_send(knet_h, ctrl_message, TEST_START, channel); if (clock_gettime(CLOCK_MONOTONIC, &clock_start) != 0) { printf("Unable to get start time!\n"); } time_diff = 0; while (time_diff < (perf_by_time_secs * 1000000000llu)) { sent_msgs = send_messages(&msg[0], PCKT_FRAG_MAX); if (sent_msgs < 0) { printf("Something went wrong, aborting\n"); exit(FAIL); } if (clock_gettime(CLOCK_MONOTONIC, &clock_end) != 0) { printf("Unable to get end time!\n"); } timespec_diff(clock_start, clock_end, &time_diff); } sleep(2); knet_send(knet_h, ctrl_message, TEST_STOP, channel); if (packetsize == KNET_MAX_PACKET_SIZE) { break; } /* * Use a multiplier that can always divide properly a GB * into smaller chunks without worry about boundaries */ packetsize *= 4; if (packetsize > KNET_MAX_PACKET_SIZE) { packetsize = KNET_MAX_PACKET_SIZE; } } knet_send(knet_h, ctrl_message, TEST_COMPLETE, channel); for (i = 0; i < PCKT_FRAG_MAX; i++) { free(tx_buf[i]); } } static void cleanup_all(void) { if (pthread_mutex_lock(&shutdown_mutex)) { return; } if (bench_shutdown_in_progress) { pthread_mutex_unlock(&shutdown_mutex); return; } bench_shutdown_in_progress = 1; pthread_mutex_unlock(&shutdown_mutex); if (rx_thread) { stop_rx_thread(); } knet_handle_stop(knet_h); } static void sigint_handler(int signum) { printf("Cleaning up... got signal: %d\n", signum); cleanup_all(); exit(PASS); } int main(int argc, char *argv[]) { if (signal(SIGINT, sigint_handler) == SIG_ERR) { printf("Unable to configure SIGINT handler\n"); exit(FAIL); } need_root(); setup_knet(argc, argv); setup_data_txrx_common(); sleep(5); restart: switch(test_type) { default: case TEST_PING: /* basic ping, no data */ sleep(5); break; case TEST_PING_AND_DATA: send_ping_data(); break; case TEST_PERF_BY_SIZE: if (senderid == thisnodeid) { send_perf_data_by_size(); } else { printf("Waiting for perf rx thread to finish\n"); while(!wait_for_perf_rx) { sleep(1); } } break; case TEST_PERF_BY_TIME: if (senderid == thisnodeid) { send_perf_data_by_time(); } else { printf("Waiting for perf rx thread to finish\n"); while(!wait_for_perf_rx) { sleep(1); } } break; } if (continous) { goto restart; } cleanup_all(); return PASS; }