summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/woodpecker/01-build.yaml111
-rw-r--r--CMakeLists.txt206
-rw-r--r--appveyor.yml34
-rw-r--r--cmake/AddCompileFlags.cmake17
-rw-r--r--cmake/CmakeUninstall.cmake.in29
-rw-r--r--cmake/OuroborosConfig.cmake.in23
-rw-r--r--cmake/compiler.cmake64
-rw-r--r--cmake/config/global.cmake45
-rw-r--r--cmake/config/ipcp/broadcast.cmake4
-rw-r--r--cmake/config/ipcp/common.cmake43
-rw-r--r--cmake/config/ipcp/eth.cmake15
-rw-r--r--cmake/config/ipcp/local.cmake7
-rw-r--r--cmake/config/ipcp/udp.cmake9
-rw-r--r--cmake/config/ipcp/unicast.cmake12
-rw-r--r--cmake/config/irmd.cmake36
-rw-r--r--cmake/config/lib.cmake94
-rw-r--r--cmake/config/ssm.cmake131
-rw-r--r--cmake/config/tests.cmake17
-rw-r--r--cmake/dependencies.cmake43
-rw-r--r--cmake/dependencies/coverage/gcov.cmake21
-rw-r--r--cmake/dependencies/coverage/lcov.cmake17
-rw-r--r--cmake/dependencies/crypt/libgcrypt.cmake55
-rw-r--r--cmake/dependencies/crypt/openssl.cmake35
-rw-r--r--cmake/dependencies/eth/bpf.cmake20
-rw-r--r--cmake/dependencies/eth/netmap.cmake18
-rw-r--r--cmake/dependencies/eth/rawsockets.cmake12
-rw-r--r--cmake/dependencies/irmd/libtoml.cmake29
-rw-r--r--cmake/dependencies/system/explicit_bzero.cmake4
-rw-r--r--cmake/dependencies/system/fuse.cmake44
-rw-r--r--cmake/dependencies/system/libraries.cmake17
-rw-r--r--cmake/dependencies/system/protobufc.cmake13
-rw-r--r--cmake/dependencies/system/robustmutex.cmake18
-rw-r--r--cmake/dependencies/system/sysrandom.cmake14
-rw-r--r--cmake/dependencies/udp/ddns.cmake31
-rw-r--r--cmake/doc.cmake (renamed from doc/man/CMakeLists.txt)24
-rw-r--r--cmake/include.cmake24
-rw-r--r--cmake/install.cmake101
-rw-r--r--cmake/package.cmake29
-rw-r--r--cmake/tests.cmake30
-rw-r--r--cmake/utils/AddCompileFlags.cmake6
-rw-r--r--cmake/utils/CMakeUninstall.cmake.in21
-rw-r--r--cmake/utils/CompilerUtils.cmake (renamed from cmake/CompilerUtils.cmake)10
-rw-r--r--cmake/utils/DebugTargets.cmake16
-rw-r--r--cmake/utils/DisableTestLogging.cmake11
-rw-r--r--cmake/utils/FindProtobufC.cmake (renamed from cmake/FindProtobufC.cmake)38
-rw-r--r--cmake/utils/GenCoverage.cmake94
-rw-r--r--cmake/utils/HumanReadable.cmake17
-rw-r--r--cmake/utils/ParseCoverage.cmake120
-rw-r--r--cmake/utils/PrintCoverage.cmake333
-rw-r--r--cmake/utils/TestUtils.cmake38
-rw-r--r--cmake/version.cmake5
-rw-r--r--doc/CMakeLists.txt1
-rw-r--r--doc/man/ouroboros.843
-rw-r--r--enc.conf.in150
-rw-r--r--include/CMakeLists.txt1
-rw-r--r--include/ouroboros/CMakeLists.txt25
-rw-r--r--include/ouroboros/crypt.h348
-rw-r--r--include/ouroboros/dev.h3
-rw-r--r--include/ouroboros/endian.h2
-rw-r--r--include/ouroboros/errno.h7
-rw-r--r--include/ouroboros/flow.h8
-rw-r--r--include/ouroboros/hash.h7
-rw-r--r--include/ouroboros/ipcp-dev.h26
-rw-r--r--include/ouroboros/ipcp.h251
-rw-r--r--include/ouroboros/irm.h3
-rw-r--r--include/ouroboros/local-dev.h8
-rw-r--r--include/ouroboros/logs.h26
-rw-r--r--include/ouroboros/name.h10
-rw-r--r--include/ouroboros/np1_flow.h4
-rw-r--r--include/ouroboros/proc.h5
-rw-r--r--include/ouroboros/protobuf.h51
-rw-r--r--include/ouroboros/qos.h67
-rw-r--r--include/ouroboros/rib.h2
-rw-r--r--include/ouroboros/serdes-irm.h9
-rw-r--r--include/ouroboros/serdes-oep.h1
-rw-r--r--include/ouroboros/shm_rdrbuff.h67
-rw-r--r--include/ouroboros/sockets.h.in12
-rw-r--r--include/ouroboros/ssm_flow_set.h (renamed from include/ouroboros/shm_flow_set.h)28
-rw-r--r--include/ouroboros/ssm_pk_buff.h (renamed from include/ouroboros/shm_du_buff.h)30
-rw-r--r--include/ouroboros/ssm_pool.h74
-rw-r--r--include/ouroboros/ssm_rbuff.h (renamed from include/ouroboros/shm_rbuff.h)36
-rw-r--r--include/ouroboros/time.h20
-rw-r--r--include/ouroboros/tpm.h6
-rw-r--r--include/ouroboros/utils.h39
-rw-r--r--include/test/certs.h125
-rw-r--r--include/test/certs_pqc.h656
-rw-r--r--include/test/test.h (renamed from include/ouroboros/test.h)49
-rw-r--r--irmd.conf.in47
-rw-r--r--ouroboros-dev.pc.in4
-rw-r--r--ouroboros-irm.pc.in4
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/ipcpd/CMakeLists.txt74
-rw-r--r--src/ipcpd/broadcast/CMakeLists.txt41
-rw-r--r--src/ipcpd/broadcast/main.c71
-rw-r--r--src/ipcpd/common/connmgr.c29
-rw-r--r--src/ipcpd/common/enroll.c34
-rw-r--r--src/ipcpd/config.h.in24
-rw-r--r--src/ipcpd/eth/CMakeLists.txt158
-rw-r--r--src/ipcpd/eth/eth.c459
-rw-r--r--src/ipcpd/ipcp.c685
-rw-r--r--src/ipcpd/ipcp.h59
-rw-r--r--src/ipcpd/local/CMakeLists.txt41
-rw-r--r--src/ipcpd/local/main.c44
-rw-r--r--src/ipcpd/np1.h41
-rw-r--r--src/ipcpd/shim-data.c34
-rw-r--r--src/ipcpd/shim-data.h19
-rw-r--r--src/ipcpd/udp/CMakeLists.txt78
-rw-r--r--src/ipcpd/udp/udp.c (renamed from src/ipcpd/udp/main.c)378
-rw-r--r--src/ipcpd/udp/udp4.c42
-rw-r--r--src/ipcpd/udp/udp6.c42
-rw-r--r--src/ipcpd/unicast/CMakeLists.txt68
-rw-r--r--src/ipcpd/unicast/addr-auth.h8
-rw-r--r--src/ipcpd/unicast/addr-auth/flat.c28
-rw-r--r--src/ipcpd/unicast/ca.c1
-rw-r--r--src/ipcpd/unicast/connmgr.c4
-rw-r--r--src/ipcpd/unicast/dir.c51
-rw-r--r--src/ipcpd/unicast/dir.h9
-rw-r--r--src/ipcpd/unicast/dir/dht.c4919
-rw-r--r--src/ipcpd/unicast/dir/dht.h17
-rw-r--r--src/ipcpd/unicast/dir/dht.proto41
-rw-r--r--src/ipcpd/unicast/dir/ops.h18
-rw-r--r--src/ipcpd/unicast/dir/tests/CMakeLists.txt42
-rw-r--r--src/ipcpd/unicast/dir/tests/dht_test.c1921
-rw-r--r--src/ipcpd/unicast/dt.c163
-rw-r--r--src/ipcpd/unicast/dt.h12
-rw-r--r--src/ipcpd/unicast/fa.c159
-rw-r--r--src/ipcpd/unicast/fa.h2
-rw-r--r--src/ipcpd/unicast/main.c96
-rw-r--r--src/ipcpd/unicast/pff/tests/CMakeLists.txt36
-rw-r--r--src/ipcpd/unicast/psched.c12
-rw-r--r--src/ipcpd/unicast/psched.h4
-rw-r--r--src/ipcpd/unicast/routing.c30
-rw-r--r--src/ipcpd/unicast/routing.h7
-rw-r--r--src/ipcpd/unicast/routing/graph.c156
-rw-r--r--src/ipcpd/unicast/routing/link-state.c598
-rw-r--r--src/ipcpd/unicast/routing/link-state.h7
-rw-r--r--src/ipcpd/unicast/routing/ops.h7
-rw-r--r--src/ipcpd/unicast/routing/tests/CMakeLists.txt36
-rw-r--r--src/irmd/CMakeLists.txt120
-rw-r--r--src/irmd/config.h.in63
-rw-r--r--src/irmd/configfile.c332
-rw-r--r--src/irmd/ipcp.c45
-rw-r--r--src/irmd/irmd.h2
-rw-r--r--src/irmd/main.c854
-rw-r--r--src/irmd/oap.c130
-rw-r--r--src/irmd/oap.h67
-rw-r--r--src/irmd/oap/auth.c252
-rw-r--r--src/irmd/oap/auth.h35
-rw-r--r--src/irmd/oap/cli.c553
-rw-r--r--src/irmd/oap/hdr.c456
-rw-r--r--src/irmd/oap/hdr.h159
-rw-r--r--src/irmd/oap/internal.h133
-rw-r--r--src/irmd/oap/io.c132
-rw-r--r--src/irmd/oap/io.h40
-rw-r--r--src/irmd/oap/srv.c462
-rw-r--r--src/irmd/oap/tests/CMakeLists.txt64
-rw-r--r--src/irmd/oap/tests/common.c457
-rw-r--r--src/irmd/oap/tests/common.h100
-rw-r--r--src/irmd/oap/tests/oap_test.c951
-rw-r--r--src/irmd/oap/tests/oap_test_pqc.c363
-rw-r--r--src/irmd/reg/CMakeLists.txt7
-rw-r--r--src/irmd/reg/flow.c17
-rw-r--r--src/irmd/reg/flow.h18
-rw-r--r--src/irmd/reg/ipcp.c7
-rw-r--r--src/irmd/reg/name.c74
-rw-r--r--src/irmd/reg/name.h28
-rw-r--r--src/irmd/reg/pool.c101
-rw-r--r--src/irmd/reg/pool.h48
-rw-r--r--src/irmd/reg/proc.c14
-rw-r--r--src/irmd/reg/proc.h10
-rw-r--r--src/irmd/reg/reg.c394
-rw-r--r--src/irmd/reg/reg.h33
-rw-r--r--src/irmd/reg/tests/CMakeLists.txt34
-rw-r--r--src/irmd/reg/tests/flow_test.c42
-rw-r--r--src/irmd/reg/tests/ipcp_test.c11
-rw-r--r--src/irmd/reg/tests/name_test.c94
-rw-r--r--src/irmd/reg/tests/proc_test.c42
-rw-r--r--src/irmd/reg/tests/prog_test.c22
-rw-r--r--src/irmd/reg/tests/reg_test.c570
-rw-r--r--src/irmd/tests/CMakeLists.txt19
-rw-r--r--src/lib/CMakeLists.txt386
-rw-r--r--src/lib/config.h.in31
-rw-r--r--src/lib/crypt.c1172
-rw-r--r--src/lib/crypt/openssl.c1880
-rw-r--r--src/lib/crypt/openssl.h172
-rw-r--r--src/lib/dev.c1087
-rw-r--r--src/lib/frct.c78
-rw-r--r--src/lib/irm.c26
-rw-r--r--src/lib/notifier.c18
-rw-r--r--src/lib/pb/enroll.proto1
-rw-r--r--src/lib/pb/ipcp.proto1
-rw-r--r--src/lib/pb/ipcp_config.proto54
-rw-r--r--src/lib/pb/irm.proto34
-rw-r--r--src/lib/pb/model.proto24
-rw-r--r--src/lib/protobuf.c366
-rw-r--r--src/lib/random.c20
-rw-r--r--src/lib/serdes-irm.c33
-rw-r--r--src/lib/shm_rbuff.c215
-rw-r--r--src/lib/shm_rbuff_ll.c249
-rw-r--r--src/lib/shm_rbuff_pthr.c304
-rw-r--r--src/lib/shm_rdrbuff.c610
-rw-r--r--src/lib/sockets.c22
-rw-r--r--src/lib/ssm/flow_set.c (renamed from src/lib/shm_flow_set.c)53
-rw-r--r--src/lib/ssm/pool.c953
-rw-r--r--src/lib/ssm/rbuff.c449
-rw-r--r--src/lib/ssm/ssm.h.in171
-rw-r--r--src/lib/ssm/tests/CMakeLists.txt22
-rw-r--r--src/lib/ssm/tests/flow_set_test.c255
-rw-r--r--src/lib/ssm/tests/pool_sharding_test.c496
-rw-r--r--src/lib/ssm/tests/pool_test.c1036
-rw-r--r--src/lib/ssm/tests/rbuff_test.c675
-rw-r--r--src/lib/tests/CMakeLists.txt23
-rw-r--r--src/lib/tests/auth_test.c548
-rw-r--r--src/lib/tests/auth_test_pqc.c356
-rw-r--r--src/lib/tests/crypt_test.c459
-rw-r--r--src/lib/tests/hash_test.c2
-rw-r--r--src/lib/tests/kex_test.c844
-rw-r--r--src/lib/tests/kex_test_pqc.c549
-rw-r--r--src/lib/tests/shm_rbuff_test.c113
-rw-r--r--src/lib/tests/sockets_test.c102
-rw-r--r--src/lib/tests/time_test.c543
-rw-r--r--src/lib/tests/tpm_test.c104
-rw-r--r--src/lib/timerwheel.c58
-rw-r--r--src/lib/tpm.c197
-rw-r--r--src/lib/utils.c105
-rw-r--r--src/tools/CMakeLists.txt80
-rw-r--r--src/tools/irm/CMakeLists.txt39
-rw-r--r--src/tools/irm/irm_ipcp_bootstrap.c345
-rw-r--r--src/tools/irm/irm_ipcp_create.c11
-rw-r--r--src/tools/irm/irm_ipcp_list.c17
-rw-r--r--src/tools/irm/irm_name_create.c113
-rw-r--r--src/tools/irm/irm_name_reg.c37
-rw-r--r--src/tools/obc/CMakeLists.txt16
-rw-r--r--src/tools/obc/obc.c4
-rw-r--r--src/tools/ocbr/CMakeLists.txt21
-rw-r--r--src/tools/ocbr/ocbr_client.c6
-rw-r--r--src/tools/ocbr/ocbr_server.c6
-rw-r--r--src/tools/oecho/CMakeLists.txt16
-rw-r--r--src/tools/operf/CMakeLists.txt26
-rw-r--r--src/tools/operf/operf.c1
-rw-r--r--src/tools/operf/operf_client.c6
-rw-r--r--src/tools/operf/operf_server.c2
-rw-r--r--src/tools/oping/CMakeLists.txt28
-rw-r--r--src/tools/oping/oping.c15
-rw-r--r--src/tools/oping/oping_client.c151
-rw-r--r--src/tools/oping/oping_server.c20
-rw-r--r--src/tools/ovpn/CMakeLists.txt21
-rw-r--r--src/tools/ovpn/ovpn.c5
-rw-r--r--src/tools/time_utils.h10
249 files changed, 29816 insertions, 8700 deletions
diff --git a/.ci/woodpecker/01-build.yaml b/.ci/woodpecker/01-build.yaml
new file mode 100644
index 00000000..f8109c94
--- /dev/null
+++ b/.ci/woodpecker/01-build.yaml
@@ -0,0 +1,111 @@
+matrix:
+ IMAGE:
+ - dstaesse/debian:o7s
+ - dstaesse/ubuntu:o7s
+ FLAGS:
+ - ''
+ - -m32
+ COMPILER:
+ - clang
+ - gcc
+ BUILD_TYPE:
+ - Debug
+ - Release
+ DISABLE_FUSE:
+ - TRUE
+ - FALSE
+ DISABLE_OPENSSL:
+ - TRUE
+ - FALSE
+ DISABLE_LIBGCRYPT:
+ - TRUE
+ - FALSE
+ SANITIZER:
+ - DebugASan
+ - DebugUSan
+ - DebugLSan
+
+steps:
+ - name: build
+ image: ${IMAGE}
+ pull: true
+ when:
+ branch: [testing, be]
+ event: [push, pull_request]
+ commands:
+ - apt-get update -y
+ - apt-get install bash clang -y
+ - apt-get install git protobuf-c-compiler cmake -y
+ - apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils cmake-curses-gui -y
+ - apt-get install libprotobuf-c-dev -y || true
+ - mkdir build
+ - cd build
+ - CC=${COMPILER} cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DDISABLE_FUSE=${DISABLE_FUSE} \
+ -DDISABLE_OPENSSL=${DISABLE_OPENSSL} -DDISABLE_LIBGCRYPT=${DISABLE_LIBGCRYPT}
+ - make CFLAGS="${FLAGS}" -s -j2
+ - env CTEST_OUTPUT_ON_FAILURE=1 make CFLAGS="${FLAGS}" -s check
+ - cd ..
+ - rm -rf build
+
+ - name: sanitizers
+ image: ${IMAGE}
+ pull: true
+ when:
+ branch: [testing, be]
+ event: [push, pull_request]
+ commands:
+ - apt-get update -y
+ - apt-get install bash clang -y
+ - apt-get install git protobuf-c-compiler cmake -y
+ - apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils cmake-curses-gui -y
+ - apt-get install libprotobuf-c-dev -y || true
+ - mkdir build
+ - cd build
+ - CC=${COMPILER} cmake .. -DCMAKE_BUILD_TYPE=${SANITIZER} -DDISABLE_FUSE=${DISABLE_FUSE} \
+ -DDISABLE_OPENSSL=${DISABLE_OPENSSL} -DDISABLE_LIBGCRYPT=${DISABLE_LIBGCRYPT} \
+ - make -s -j2
+ - env CTEST_OUTPUT_ON_FAILURE=1 make -s check
+ - cd ..
+ - rm -rf build
+
+ - name: build (manual)
+ image: ${IMAGE}
+ pull: true
+ when:
+ event: manual
+ commands:
+ - apt-get update -y
+ - apt-get install bash clang -y
+ - apt-get install git protobuf-c-compiler cmake -y
+ - apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils cmake-curses-gui -y
+ - apt-get install libprotobuf-c-dev -y || true
+ - mkdir build
+ - cd build
+ - CC=${COMPILER} cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DDISABLE_FUSE=${DISABLE_FUSE} \
+ -DDISABLE_OPENSSL=${DISABLE_OPENSSL} -DDISABLE_LIBGCRYPT=${DISABLE_LIBGCRYPT}
+ - make CFLAGS="${FLAGS}" -s -j2
+ - env CTEST_OUTPUT_ON_FAILURE=1 make CFLAGS="${FLAGS}" -s check
+ - cd ..
+ - rm -rf build
+
+ - name: sanitizers (manual)
+ image: ${IMAGE}
+ pull: true
+ when:
+ event: manual
+ commands:
+ - apt-get update -y
+ - apt-get install bash clang -y
+ - apt-get install git protobuf-c-compiler cmake -y
+ - apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils cmake-curses-gui -y
+ - apt-get install libprotobuf-c-dev -y || true
+ - mkdir build
+ - cd build
+ - CC=${COMPILER} cmake .. -DCMAKE_BUILD_TYPE=${SANITIZER} -DDISABLE_FUSE=${DISABLE_FUSE} \
+ -DDISABLE_OPENSSL=${DISABLE_OPENSSL} -DDISABLE_LIBGCRYPT=${DISABLE_LIBGCRYPT} \
+ - make -s -j2
+ - env CTEST_OUTPUT_ON_FAILURE=1 make -s check
+ - cd ..
+ - rm -rf build
+
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c298b981..d225d29d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12.2...3.28.1)
-cmake_policy(VERSION ${CMAKE_VERSION})
+cmake_minimum_required(VERSION 3.19)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
@@ -7,186 +6,67 @@ project(ouroboros C)
include(GNUInstallDirs)
-set(PACKAGE_VERSION_MAJOR 0)
-set(PACKAGE_VERSION_MINOR 21)
-set(PACKAGE_VERSION_PATCH 3)
+include(utils/DebugTargets)
-set(PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
-set(PACKAGE_DESCRIPTION "The Ouroboros prototype")
-set(PACKAGE_URL "http://ouroboros.rocks")
-set(PACKAGE_BUGREPORT "http://ouroboros.rocks/bugzilla/")
-set(PACKAGE_VERSION
- "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}")
+include(version)
+include(package)
-if (NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE "Release" CACHE STRING
- "Build type (Release, Debug, DebugASan, DebugTSan, DebugLSan, DebugUSan, DebugAnalyzer)" FORCE)
-endif()
+include(compiler)
-if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
- if (APPLE)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ if(APPLE)
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE STRING
"Installation Prefix" FORCE)
else()
set(CMAKE_INSTALL_PREFIX "/usr" CACHE STRING
"Installation Prefix" FORCE)
endif()
-endif ()
+endif()
-if (APPLE)
+if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
+ # Homebrew installs to /usr/local/include on Intel, /opt/homebrew/include on ARM
+ set(APPLE_INCLUDE_DIRS "/usr/local/include" "/opt/homebrew/include"
+ CACHE INTERNAL "Apple system include directories")
endif()
-if (CMAKE_INSTALL_PREFIX STREQUAL "/usr")
+if(CMAKE_INSTALL_PREFIX STREQUAL "/usr")
set(RPATH_PREFIX "")
-else ()
+else()
set(RPATH_PREFIX ${CMAKE_INSTALL_PREFIX})
-endif ()
+endif()
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
- "${RPATH_PREFIX}/lib" isSystemDir)
-IF ("${isSystemDir}" STREQUAL "-1")
- set(CMAKE_INSTALL_RPATH "${RPATH_PREFIX}/lib")
-ENDIF ("${isSystemDir}" STREQUAL "-1")
-
-message(STATUS "Package name is: ${PACKAGE_NAME}")
-message(STATUS "Package description is: ${PACKAGE_DESCRIPTION}")
-message(STATUS "Package version is: ${PACKAGE_VERSION}")
-message(STATUS "Package URL is: ${PACKAGE_URL}")
-message(STATUS "Package bug-report address: ${PACKAGE_BUGREPORT}")
-message(STATUS "Package install prefix: ${CMAKE_INSTALL_PREFIX}")
-
-include(FindPkgConfig)
-
-include(CompilerUtils)
-test_and_set_c_compiler_flag_global(-std=c89)
-test_and_set_c_compiler_flag_global(-Wall)
-# -Wextra may fail on clobbered warning due to pthread_cleanup
-test_and_set_c_compiler_flag_global(-Wno-clobbered)
-test_and_set_c_compiler_flag_global(-Wextra)
-# explicitly add other flags in -Wextra
-test_and_set_c_compiler_flag_global(-Wempty-body)
-test_and_set_c_compiler_flag_global(-Wignored-qualifiers)
-test_and_set_c_compiler_flag_global(-Wimplicit-fallthrough=4)
-test_and_set_c_compiler_flag_global(-Wmissing-field-initializers)
-test_and_set_c_compiler_flag_global(-Wmissing-parameter-type)
-test_and_set_c_compiler_flag_global(-Wold-style-declaration)
-test_and_set_c_compiler_flag_global(-Woverride-init)
-test_and_set_c_compiler_flag_global(-Wsign-compare)
-test_and_set_c_compiler_flag_global(-Wtype-limits)
-test_and_set_c_compiler_flag_global(-Wuninitialized)
-test_and_set_c_compiler_flag_global(-Wshift-negative-value)
-test_and_set_c_compiler_flag_global(-Wunused-parameter)
-test_and_set_c_compiler_flag_global(-Wunused-but-set-parameter)
-test_and_set_c_compiler_flag_global(-Werror)
-test_and_set_c_compiler_flag_global(-Wundef)
-test_and_set_c_compiler_flag_global(-Wpointer-arith)
-test_and_set_c_compiler_flag_global(-Wstrict-prototypes)
-test_and_set_c_compiler_flag_global(-Wvla)
-test_and_set_c_compiler_flag_global(-Wswitch-default)
-test_and_set_c_compiler_flag_global(-Wreturn-type)
-test_and_set_c_compiler_flag_global(-Wunreachable-code)
-test_and_set_c_compiler_flag_global(-Wdeclaration-after-statement)
-test_and_set_c_compiler_flag_global(-Winfinite-recursion)
-test_and_set_c_compiler_flag_global(-fmax-errors=5)
-
-if (CMAKE_BUILD_TYPE STREQUAL "Release")
- test_and_set_c_compiler_flag_global(-O3)
-elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
- test_and_set_c_compiler_flag_global(-g)
-elseif (CMAKE_BUILD_TYPE STREQUAL "DebugASan")
- test_and_set_c_compiler_flag_global(-g)
- test_and_set_c_compiler_flag_global(-fsanitize=address)
-elseif (CMAKE_BUILD_TYPE STREQUAL "DebugTSan")
- test_and_set_c_compiler_flag_global(-g)
- test_and_set_c_compiler_flag_global(-fsanitize=thread)
-elseif (CMAKE_BUILD_TYPE STREQUAL "DebugLSan")
- test_and_set_c_compiler_flag_global(-g)
- test_and_set_c_compiler_flag_global(-fsanitize=leak)
-elseif (CMAKE_BUILD_TYPE STREQUAL "DebugUSan")
- test_and_set_c_compiler_flag_global(-g)
- test_and_set_c_compiler_flag_global(-fsanitize=undefined)
-elseif (CMAKE_BUILD_TYPE STREQUAL "DebugAnalyzer")
- test_and_set_c_compiler_flag_global(-g)
- test_and_set_c_compiler_flag_global(-fanalyzer)
-else ()
- message(FATAL_ERROR "Unkown build type ${CMAKE_BUILD_TYPE}")
-endif ()
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ouroboros-dev.pc.in"
- "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-dev.pc" @ONLY)
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ouroboros-irm.pc.in"
- "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-irm.pc" @ONLY)
-
-install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-dev.pc"
- DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-
-install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-irm.pc"
- DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-
-# modified from https://github.com/ximion/limba
-pkg_check_modules(SYSTEMD "systemd")
-if (SYSTEMD_FOUND)
- if ("${SYSTEMD_UNITDIR}" STREQUAL "")
- execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
- --variable=systemdsystemunitdir systemd
- OUTPUT_VARIABLE SYSTEMD_UNITDIR)
- string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNITDIR
- "${SYSTEMD_UNITDIR}"
- )
- endif ()
- if (NOT ${SYSTEMD_UNITDIR} STREQUAL "")
- message(STATUS "Installing systemd service in: ${SYSTEMD_UNITDIR}")
- if (LIBTOML_LIBRARIES AND NOT DISABLE_CONFIGFILE)
- set (CONFIGURE_STRING "--config ${OUROBOROS_CONFIG_DIR}${OUROBOROS_CONFIG_FILE}")
- else ()
- set (CONFIGURE_STRING "")
- endif ()
- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ouroboros.service.in"
- "${CMAKE_CURRENT_BINARY_DIR}/ouroboros.service" @ONLY)
- install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ouroboros.service"
- DESTINATION "${SYSTEMD_UNITDIR}")
- endif ()
-endif ()
-
-enable_testing()
-add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
-
-find_package(ProtobufC QUIET)
-if (NOT (PROTOBUF_C_INCLUDE_DIRS AND PROTOBUF_C_LIBRARY
- AND PROTOBUF_PROTOC_C_EXECUTABLE))
- message(FATAL_ERROR "Protobuf C compiler required but not found. "
- "Please install Google Protocol Buffers.")
-endif ()
-include_directories(${PROTOBUF_C_INCLUDE_DIRS})
-
-add_subdirectory(include)
-add_subdirectory(src)
-add_subdirectory(doc)
-
-# Uninstall target
-configure_file("${CMAKE_SOURCE_DIR}/cmake/CmakeUninstall.cmake.in"
- "${CMAKE_BINARY_DIR}/cmake/CmakeUninstall.cmake" IMMEDIATE @ONLY)
-
-add_custom_target(uninstall
- COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake/CmakeUninstall.cmake)
-
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE_DESCRIPTION}")
-set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README")
-set(CPACK_PACKAGE_VERSION_MAJOR "${PACKAGE_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${PACKAGE_VERSION_MINOR}")
-set(CPACK_PACKAGE_VERSION_PATCH "${PACKAGE_VERSION_PATCH}")
-set(CPACK_PACKAGE_INSTALL_DIRECTORY
- "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}")
-set(CPACK_GENERATOR "TGZ")
-set(CPACK_SOURCE_GENERATOR "TGZ")
-
-include(CPack)
+ "${RPATH_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir)
+if(isSystemDir STREQUAL "-1")
+ set(CMAKE_INSTALL_RPATH "${RPATH_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+endif()
-#include(FeatureSummary)
-#print_enabled_features()
+include(dependencies)
+
+# Configuration options (must be loaded before component modules)
+include(config/global)
+include(config/lib)
+include(config/ssm)
+include(config/irmd)
+include(config/ipcp/common)
+include(config/ipcp/unicast)
+include(config/ipcp/broadcast)
+include(config/ipcp/local)
+include(config/ipcp/eth)
+include(config/ipcp/udp)
+
+include(tests)
+include(include)
+add_subdirectory(src/lib)
+add_subdirectory(src/irmd)
+add_subdirectory(src/ipcpd)
+add_subdirectory(src/tools)
+setup_coverage_target()
+include(doc)
+
+include(install)
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index bf1d8076..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-image:
- - Ubuntu2004
- - Ubuntu2204
-
-platform:
- - x64
- - x86
-
-configuration:
- - Release
- - Debug
-
-environment:
- matrix:
- - CC: clang
- - CC: gcc
-
-install:
- - sudo apt-get update
- - sudo apt-get install clang
- - sudo apt-get install protobuf-c-compiler --yes
- - sudo apt-get install libprotobuf-c-dev --yes || true
- - sudo apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils --yes
-
-before_build:
- - mkdir -p build
- - cd build
- - cmake -DCMAKE_BUILD_TYPE=$CONFIGURATION ..
-
-build_script:
- - make
-
-test_script:
- - env CTEST_OUTPUT_ON_FAILURE=1 make check
diff --git a/cmake/AddCompileFlags.cmake b/cmake/AddCompileFlags.cmake
deleted file mode 100644
index 8f3877d9..00000000
--- a/cmake/AddCompileFlags.cmake
+++ /dev/null
@@ -1,17 +0,0 @@
-# - MACRO_ADD_COMPILE_FLAGS(<_target> "flags...")
-
-# Copyright (c) 2006, Oswald Buddenhagen, <ossi@kde.org>
-#
-# Redistribution and use is allowed according to the terms of the BSD license.
-
-macro(add_compile_flags _target _flg)
-
- get_target_property(_flags ${_target} COMPILE_FLAGS)
- if (_flags)
- set(_flags "${_flags} ${_flg}")
- else (_flags)
- set(_flags "${_flg}")
- endif (_flags)
- set_target_properties(${_target} PROPERTIES COMPILE_FLAGS "${_flags}")
-
-endmacro(add_compile_flags)
diff --git a/cmake/CmakeUninstall.cmake.in b/cmake/CmakeUninstall.cmake.in
deleted file mode 100644
index 985b31b2..00000000
--- a/cmake/CmakeUninstall.cmake.in
+++ /dev/null
@@ -1,29 +0,0 @@
-if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
- message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
-endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
-
-file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
-string(REGEX REPLACE "\n" ";" files "${files}")
-foreach(file ${files})
- message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
- if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
- if(CMAKE_VERSION VERSION_LESS "3.28.0")
- exec_program(
- "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
- OUTPUT_VARIABLE rm_out
- RETURN_VALUE rm_retval
- )
- else()
- execute_process(
- COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
- RESULT_VARIABLE rm_out
- ERROR_VARIABLE rm_retval
- )
- endif ()
- if(NOT "${rm_retval}" STREQUAL "" AND NOT "${rm_retval}" STREQUAL 0)
- message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
- endif(NOT "${rm_retval}" STREQUAL "" AND NOT "${rm_retval}" STREQUAL 0)
- else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
- message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
- endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
-endforeach(file)
diff --git a/cmake/OuroborosConfig.cmake.in b/cmake/OuroborosConfig.cmake.in
new file mode 100644
index 00000000..41ff7fdd
--- /dev/null
+++ b/cmake/OuroborosConfig.cmake.in
@@ -0,0 +1,23 @@
+# Ouroboros CMake Package Configuration
+#
+# This file allows other CMake projects to find and use Ouroboros libraries
+# via find_package(Ouroboros).
+#
+# Exported targets:
+# Ouroboros::dev - Development library (libouroboros-dev)
+# Ouroboros::irm - IRM management library (libouroboros-irm)
+#
+# Example usage:
+# find_package(Ouroboros REQUIRED)
+# target_link_libraries(myapp Ouroboros::dev)
+
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+
+# Required dependencies for Ouroboros libraries
+find_dependency(Threads)
+
+include("${CMAKE_CURRENT_LIST_DIR}/OuroborosTargets.cmake")
+
+check_required_components(Ouroboros)
diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake
new file mode 100644
index 00000000..5d452e02
--- /dev/null
+++ b/cmake/compiler.cmake
@@ -0,0 +1,64 @@
+include(utils/CompilerUtils)
+
+test_and_set_c_compiler_flag_global(-std=c89)
+test_and_set_c_compiler_flag_global(-Wall)
+# -Wextra may fail on clobbered warning due to pthread_cleanup
+test_and_set_c_compiler_flag_global(-Wno-clobbered)
+test_and_set_c_compiler_flag_global(-Wextra)
+# explicitly add other flags in -Wextra
+test_and_set_c_compiler_flag_global(-Wempty-body)
+test_and_set_c_compiler_flag_global(-Wignored-qualifiers)
+test_and_set_c_compiler_flag_global(-Wimplicit-fallthrough=4)
+test_and_set_c_compiler_flag_global(-Wmissing-field-initializers)
+test_and_set_c_compiler_flag_global(-Wmissing-parameter-type)
+test_and_set_c_compiler_flag_global(-Wold-style-declaration)
+test_and_set_c_compiler_flag_global(-Woverride-init)
+test_and_set_c_compiler_flag_global(-Wsign-compare)
+test_and_set_c_compiler_flag_global(-Wtype-limits)
+test_and_set_c_compiler_flag_global(-Wuninitialized)
+test_and_set_c_compiler_flag_global(-Wshift-negative-value)
+test_and_set_c_compiler_flag_global(-Wunused-parameter)
+test_and_set_c_compiler_flag_global(-Wunused-but-set-parameter)
+test_and_set_c_compiler_flag_global(-Werror)
+test_and_set_c_compiler_flag_global(-Wundef)
+test_and_set_c_compiler_flag_global(-Wpointer-arith)
+test_and_set_c_compiler_flag_global(-Wstrict-prototypes)
+test_and_set_c_compiler_flag_global(-Wvla)
+test_and_set_c_compiler_flag_global(-Wswitch-default)
+test_and_set_c_compiler_flag_global(-Wreturn-type)
+test_and_set_c_compiler_flag_global(-Wunreachable-code)
+test_and_set_c_compiler_flag_global(-Wdeclaration-after-statement)
+test_and_set_c_compiler_flag_global(-Winfinite-recursion)
+test_and_set_c_compiler_flag_global(-fmax-errors=5)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+ "Build type (Release, Debug, DebugASan, DebugTSan, DebugLSan, DebugUSan, DebugAnalyzer)" FORCE)
+endif()
+
+if(CMAKE_BUILD_TYPE STREQUAL "Release")
+ test_and_set_c_compiler_flag_global(-O3)
+elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ test_and_set_c_compiler_flag_global(-g)
+elseif(CMAKE_BUILD_TYPE STREQUAL "DebugASan")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(-fsanitize=address)
+ add_link_options(-fsanitize=address)
+elseif(CMAKE_BUILD_TYPE STREQUAL "DebugTSan")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(-fsanitize=thread)
+ add_link_options(-fsanitize=thread)
+elseif(CMAKE_BUILD_TYPE STREQUAL "DebugLSan")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(-fsanitize=leak)
+ add_link_options(-fsanitize=leak)
+elseif(CMAKE_BUILD_TYPE STREQUAL "DebugUSan")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(-fsanitize=undefined)
+ add_link_options(-fsanitize=undefined)
+elseif(CMAKE_BUILD_TYPE STREQUAL "DebugAnalyzer")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(-fanalyzer)
+else()
+ message(FATAL_ERROR "Unknown build type ${CMAKE_BUILD_TYPE}")
+endif()
diff --git a/cmake/config/global.cmake b/cmake/config/global.cmake
new file mode 100644
index 00000000..243e1ba0
--- /dev/null
+++ b/cmake/config/global.cmake
@@ -0,0 +1,45 @@
+# Global configuration options for Ouroboros
+# These options affect the entire framework
+
+# Installation directories
+set(OUROBOROS_CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/ouroboros" CACHE PATH
+ "Configuration directory")
+
+# Security directories
+set(OUROBOROS_SECURITY_DIR "${OUROBOROS_CONFIG_DIR}/security" CACHE PATH
+ "Security directory holding authentication information")
+set(OUROBOROS_CA_CRT_DIR "${OUROBOROS_SECURITY_DIR}/cacert" CACHE PATH
+ "Directory holding trusted CA certificates")
+set(OUROBOROS_SRV_CRT_DIR "${OUROBOROS_SECURITY_DIR}/server" CACHE PATH
+ "Directory holding server certificates")
+set(OUROBOROS_CLI_CRT_DIR "${OUROBOROS_SECURITY_DIR}/client" CACHE PATH
+ "Directory holding client certificates")
+set(OUROBOROS_UNTRUSTED_DIR "${OUROBOROS_SECURITY_DIR}/untrusted" CACHE PATH
+ "Directory holding untrusted intermediate certificates")
+
+# Shared memory naming
+set(SHM_PREFIX "ouroboros" CACHE STRING
+ "String to prepend to POSIX shared memory filenames")
+set(SHM_LOCKFILE_NAME "/${SHM_PREFIX}.lockfile" CACHE INTERNAL
+ "Filename for the POSIX shared memory lockfile")
+
+# FUSE configuration
+if(HAVE_FUSE)
+ set(FUSE_PREFIX "/tmp/ouroboros" CACHE STRING
+ "Mountpoint for RIB filesystem")
+endif()
+
+# Secure memory configuration
+set(IRMD_SECMEM_MAX 1048576 CACHE STRING "IRMd secure heap size")
+set(PROC_SECMEM_MAX 1048576 CACHE STRING "Process secure heap size")
+set(SECMEM_GUARD 32 CACHE STRING "Secure heap min size")
+
+# Container/deployment options
+set(BUILD_CONTAINER FALSE CACHE BOOL
+ "Disable thread priority setting for container compatibility")
+set(DISABLE_CORE_LOCK TRUE CACHE BOOL
+ "Disable locking performance threads to a core")
+
+# IPC socket configuration
+set(SOCK_BUF_SIZE 10240 CACHE STRING
+ "Size of the buffer used by the UNIX sockets for local IPC")
diff --git a/cmake/config/ipcp/broadcast.cmake b/cmake/config/ipcp/broadcast.cmake
new file mode 100644
index 00000000..3c4d98da
--- /dev/null
+++ b/cmake/config/ipcp/broadcast.cmake
@@ -0,0 +1,4 @@
+# Broadcast IPCP configuration options for Ouroboros
+
+set(IPCP_BROADCAST_MPL 100 CACHE STRING
+ "Default maximum packet lifetime for the Broadcast IPCP, in ms")
diff --git a/cmake/config/ipcp/common.cmake b/cmake/config/ipcp/common.cmake
new file mode 100644
index 00000000..ffd5dc32
--- /dev/null
+++ b/cmake/config/ipcp/common.cmake
@@ -0,0 +1,43 @@
+# Common IPCP configuration options for Ouroboros
+# Options affecting all IPC Process types
+
+# Connection manager
+set(CONNMGR_RCV_TIMEOUT 1000 CACHE STRING
+ "Timeout for the connection manager to wait for OCEP info (ms).")
+
+# Debugging
+set(IPCP_DEBUG_LOCAL FALSE CACHE BOOL
+ "Use PID as address for local debugging")
+
+# QoS cube priorities (0-99, higher = more priority)
+set(IPCP_QOS_CUBE_BE_PRIO 50 CACHE STRING
+ "Priority for best effort QoS cube (0-99)")
+set(IPCP_QOS_CUBE_VIDEO_PRIO 90 CACHE STRING
+ "Priority for video QoS cube (0-99)")
+set(IPCP_QOS_CUBE_VOICE_PRIO 99 CACHE STRING
+ "Priority for voice QoS cube (0-99)")
+
+# Validate QoS cube priorities
+if((IPCP_QOS_CUBE_BE_PRIO LESS 0) OR (IPCP_QOS_CUBE_BE_PRIO GREATER 99))
+ message(FATAL_ERROR "Invalid priority for best effort QoS cube (must be 0-99)")
+endif()
+if((IPCP_QOS_CUBE_VIDEO_PRIO LESS 0) OR (IPCP_QOS_CUBE_VIDEO_PRIO GREATER 99))
+ message(FATAL_ERROR "Invalid priority for video QoS cube (must be 0-99)")
+endif()
+if((IPCP_QOS_CUBE_VOICE_PRIO LESS 0) OR (IPCP_QOS_CUBE_VOICE_PRIO GREATER 99))
+ message(FATAL_ERROR "Invalid priority for voice QoS cube (must be 0-99)")
+endif()
+
+# Threading
+set(IPCP_MIN_THREADS 4 CACHE STRING
+ "Minimum number of worker threads in the IPCP")
+set(IPCP_ADD_THREADS 4 CACHE STRING
+ "Number of extra threads to start when an IPCP faces thread starvation")
+set(IPCP_SCHED_THR_MUL 2 CACHE STRING
+ "Number of scheduler threads per QoS cube")
+
+# Linux-specific
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(IPCP_LINUX_TIMERSLACK_NS 100 CACHE STRING
+ "Slack value for high resolution timers on Linux systems.")
+endif()
diff --git a/cmake/config/ipcp/eth.cmake b/cmake/config/ipcp/eth.cmake
new file mode 100644
index 00000000..2f50d24d
--- /dev/null
+++ b/cmake/config/ipcp/eth.cmake
@@ -0,0 +1,15 @@
+# Ethernet IPCP configuration options for Ouroboros
+# Options for eth-llc and eth-dix IPCPs
+
+set(IPCP_ETH_RD_THR 1 CACHE STRING
+ "Number of reader threads in Ethernet IPCP")
+set(IPCP_ETH_WR_THR 1 CACHE STRING
+ "Number of writer threads in Ethernet IPCP")
+set(IPCP_ETH_QDISC_BYPASS false CACHE BOOL
+ "Bypass the Qdisc in the kernel when using raw sockets")
+set(IPCP_ETH_LO_MTU 9000 CACHE STRING
+ "Restrict Ethernet MTU over loopback interfaces")
+set(IPCP_ETH_MGMT_FRAME_SIZE 9000 CACHE STRING
+ "Management frame buffer size for Ethernet IPCPs")
+set(IPCP_ETH_MPL 100 CACHE STRING
+ "Default maximum packet lifetime for the Ethernet IPCPs, in ms")
diff --git a/cmake/config/ipcp/local.cmake b/cmake/config/ipcp/local.cmake
new file mode 100644
index 00000000..df47c45b
--- /dev/null
+++ b/cmake/config/ipcp/local.cmake
@@ -0,0 +1,7 @@
+# Local IPCP configuration options for Ouroboros
+
+set(IPCP_LOCAL_MPL 100 CACHE STRING
+ "Default maximum packet lifetime for the Local IPCP, in ms")
+
+set(IPCP_LOCAL_POLLING FALSE CACHE BOOL
+ "Enable active polling in the Local IPCP for low-latency mode")
diff --git a/cmake/config/ipcp/udp.cmake b/cmake/config/ipcp/udp.cmake
new file mode 100644
index 00000000..691948ab
--- /dev/null
+++ b/cmake/config/ipcp/udp.cmake
@@ -0,0 +1,9 @@
+# UDP IPCP configuration options for Ouroboros
+# Options for udp4 and udp6 IPCPs
+
+set(IPCP_UDP_RD_THR 3 CACHE STRING
+ "Number of reader threads in UDP IPCPs")
+set(IPCP_UDP_WR_THR 3 CACHE STRING
+ "Number of writer threads in UDP IPCPs")
+set(IPCP_UDP_MPL 5000 CACHE STRING
+ "Default maximum packet lifetime for the UDP IPCPs, in ms")
diff --git a/cmake/config/ipcp/unicast.cmake b/cmake/config/ipcp/unicast.cmake
new file mode 100644
index 00000000..905c661a
--- /dev/null
+++ b/cmake/config/ipcp/unicast.cmake
@@ -0,0 +1,12 @@
+# Unicast IPCP configuration options for Ouroboros
+
+set(IPCP_UNICAST_MPL 100 CACHE STRING
+ "Default maximum packet lifetime for the Unicast IPCP, in ms")
+set(PFT_SIZE 256 CACHE STRING
+ "Prefix forwarding table size for the Unicast IPCP")
+
+# Protocol debugging
+set(DEBUG_PROTO_DHT FALSE CACHE BOOL
+ "Add DHT protocol debug logging")
+set(DEBUG_PROTO_LS FALSE CACHE BOOL
+ "Add link state protocol debug logging")
diff --git a/cmake/config/irmd.cmake b/cmake/config/irmd.cmake
new file mode 100644
index 00000000..9795e4a4
--- /dev/null
+++ b/cmake/config/irmd.cmake
@@ -0,0 +1,36 @@
+# IRMd configuration options for Ouroboros
+# Options affecting the IPC Resource Manager daemon
+
+# Timeouts (all in milliseconds unless noted)
+set(IRMD_REQ_ARR_TIMEOUT 1000 CACHE STRING
+ "Timeout for an application to respond to a new flow (ms)")
+set(BOOTSTRAP_TIMEOUT 5000 CACHE STRING
+ "Timeout for an IPCP to bootstrap (ms)")
+set(ENROLL_TIMEOUT 20000 CACHE STRING
+ "Timeout for an IPCP to enroll (ms)")
+set(REG_TIMEOUT 20000 CACHE STRING
+ "Timeout for registering a name (ms)")
+set(QUERY_TIMEOUT 200 CACHE STRING
+ "Timeout to query a name with an IPCP (ms)")
+set(CONNECT_TIMEOUT 20000 CACHE STRING
+ "Timeout to connect an IPCP to another IPCP (ms)")
+set(FLOW_ALLOC_TIMEOUT 20000 CACHE STRING
+ "Timeout for a flow allocation response (ms)")
+
+# OAP (Ouroboros Authentication Protocol)
+set(OAP_REPLAY_TIMER 20 CACHE STRING
+ "OAP replay protection window (s)")
+set(DEBUG_PROTO_OAP FALSE CACHE BOOL
+ "Add Flow allocation protocol message output to IRMd debug logging")
+
+# Threading
+set(IRMD_MIN_THREADS 8 CACHE STRING
+ "Minimum number of worker threads in the IRMd")
+set(IRMD_ADD_THREADS 8 CACHE STRING
+ "Number of extra threads to start when the IRMD faces thread starvation")
+
+# Process management
+set(IRMD_PKILL_TIMEOUT 30 CACHE STRING
+ "Number of seconds to wait before sending SIGKILL to subprocesses on exit")
+set(IRMD_KILL_ALL_PROCESSES TRUE CACHE BOOL
+ "Kill all processes on exit")
diff --git a/cmake/config/lib.cmake b/cmake/config/lib.cmake
new file mode 100644
index 00000000..287f30dc
--- /dev/null
+++ b/cmake/config/lib.cmake
@@ -0,0 +1,94 @@
+# Library configuration options for Ouroboros
+# Options affecting libouroboros-common, libouroboros-dev, libouroboros-irm
+
+# Flow limits
+set(SYS_MAX_FLOWS 10240 CACHE STRING
+ "Maximum number of total flows for this system")
+set(PROG_MAX_FLOWS 4096 CACHE STRING
+ "Maximum number of flows in an application")
+set(PROG_RES_FDS 64 CACHE STRING
+ "Number of reserved flow descriptors per application")
+set(PROG_MAX_FQUEUES 32 CACHE STRING
+ "Maximum number of flow sets per application")
+
+# Threading
+if(NOT APPLE)
+ set(PTHREAD_COND_CLOCK "CLOCK_MONOTONIC" CACHE STRING
+ "Clock to use for condition variable timing")
+else()
+ set(PTHREAD_COND_CLOCK "CLOCK_REALTIME" CACHE INTERNAL
+ "Clock to use for condition variable timing")
+endif()
+
+# Timeouts
+set(SOCKET_TIMEOUT 500 CACHE STRING
+ "Default timeout for responses from IPCPs (ms)")
+
+# QoS settings
+set(QOS_DISABLE_CRC TRUE CACHE BOOL
+ "Ignores ber setting on all QoS cubes")
+
+# Delta-t protocol timers
+set(DELTA_T_MPL 60 CACHE STRING
+ "Maximum packet lifetime (s)")
+set(DELTA_T_ACK 10 CACHE STRING
+ "Maximum time to acknowledge a packet (s)")
+set(DELTA_T_RTX 120 CACHE STRING
+ "Maximum time to retransmit a packet (s)")
+
+# FRCT configuration
+set(FRCT_REORDER_QUEUE_SIZE 256 CACHE STRING
+ "Size of the reordering queue, must be a power of 2")
+set(FRCT_START_WINDOW 64 CACHE STRING
+ "Start window, must be a power of 2")
+set(FRCT_LINUX_RTT_ESTIMATOR TRUE CACHE BOOL
+ "Use Linux RTT estimator formula instead of the TCP RFC formula")
+set(FRCT_RTO_MDEV_MULTIPLIER 2 CACHE STRING
+ "Multiplier for deviation term in the RTO: RTO = sRTT + (mdev << X)")
+set(FRCT_RTO_INC_FACTOR 0 CACHE STRING
+ "Divisor for RTO increase after timeout: RTO += RTX >> X, 0: Karn/Partridge")
+set(FRCT_RTO_MIN 250 CACHE STRING
+ "Minimum Retransmission Timeout (RTO) for FRCT (us)")
+set(FRCT_TICK_TIME 5000 CACHE STRING
+ "Tick time for FRCT activity (retransmission, acknowledgments) (us)")
+
+# Retransmission (RXM) configuration
+set(RXM_BUFFER_ON_HEAP FALSE CACHE BOOL
+ "Store packets for retransmission on the heap instead of in packet buffer")
+set(RXM_BLOCKING TRUE CACHE BOOL
+ "Use blocking writes for retransmission")
+set(RXM_MIN_RESOLUTION 20 CACHE STRING
+ "Minimum retransmission delay (ns), as a power to 2")
+set(RXM_WHEEL_MULTIPLIER 4 CACHE STRING
+ "Factor for retransmission wheel levels as a power to 2")
+set(RXM_WHEEL_LEVELS 3 CACHE STRING
+ "Number of levels in the retransmission wheel")
+set(RXM_WHEEL_SLOTS_PER_LEVEL 256 CACHE STRING
+ "Number of slots per level in the retransmission wheel, must be a power of 2")
+
+# Acknowledgment wheel configuration
+set(ACK_WHEEL_SLOTS 256 CACHE STRING
+ "Number of slots in the acknowledgment wheel, must be a power of 2")
+set(ACK_WHEEL_RESOLUTION 18 CACHE STRING
+ "Minimum acknowledgment delay (ns), as a power to 2")
+
+# Thread pool manager (TPM) debugging
+set(TPM_DEBUG_REPORT_INTERVAL 0 CACHE STRING
+ "Interval at wich the TPM will report long running threads (s), 0 disables")
+set(TPM_DEBUG_ABORT_TIMEOUT 0 CACHE STRING
+ "TPM abort process after a thread reaches this timeout (s), 0 disables")
+
+# Encryption
+set(KEY_ROTATION_BIT 20 CACHE STRING
+ "Bit position in packet counter that triggers key rotation (default 20 = every 2^20 packets)")
+
+# Flow statistics (requires FUSE)
+if(HAVE_FUSE)
+ set(PROC_FLOW_STATS TRUE CACHE BOOL
+ "Enable flow statistics tracking for application flows")
+ if(PROC_FLOW_STATS)
+ message(STATUS "Application flow statistics enabled")
+ else()
+ message(STATUS "Application flow statistics disabled")
+ endif()
+endif()
diff --git a/cmake/config/ssm.cmake b/cmake/config/ssm.cmake
new file mode 100644
index 00000000..c1f34655
--- /dev/null
+++ b/cmake/config/ssm.cmake
@@ -0,0 +1,131 @@
+# Secure Shared Memory (SSM) pool configuration for Ouroboros
+# This file defines the allocation parameters for the secure shared memory
+# pool allocator
+
+# Shared memory pool naming configuration
+set(SSM_PREFIX "ouroboros" CACHE STRING
+ "Prefix for secure shared memory pools")
+
+# Pool naming (internal)
+set(SSM_GSPP_NAME "/${SSM_PREFIX}.gspp" CACHE INTERNAL
+ "Name for the Global Shared Packet Pool")
+set(SSM_PUP_NAME_FMT "/${SSM_PREFIX}.pup.%d" CACHE INTERNAL
+ "Format string for Per-User Pool names (uid as argument)")
+
+# Packet buffer configuration
+set(SSM_POOL_NAME "/${SHM_PREFIX}.pool" CACHE INTERNAL
+ "Name for the main POSIX shared memory pool")
+set(SSM_POOL_BLOCKS 16384 CACHE STRING
+ "Number of blocks in SSM packet pool, must be a power of 2")
+set(SSM_PK_BUFF_HEADSPACE 256 CACHE STRING
+ "Bytes of headspace to reserve for future headers")
+set(SSM_PK_BUFF_TAILSPACE 32 CACHE STRING
+ "Bytes of tailspace to reserve for future tails")
+set(SSM_RBUFF_SIZE 1024 CACHE STRING
+ "Number of blocks in rbuff buffer, must be a power of 2")
+set(SSM_RBUFF_PREFIX "/${SHM_PREFIX}.rbuff." CACHE INTERNAL
+ "Prefix for rbuff POSIX shared memory filenames")
+set(SSM_FLOW_SET_PREFIX "/${SHM_PREFIX}.set." CACHE INTERNAL
+ "Prefix for the POSIX shared memory flow set")
+
+# Number of shards per size class for reducing contention
+set(SSM_POOL_SHARDS 4 CACHE STRING
+ "Number of allocator shards per size class")
+
+# Global Shared Packet Pool (GSPP) - for privileged processes
+# Shared by all processes in 'ouroboros' group (~60 MB total)
+set(SSM_GSPP_256_BLOCKS 1024 CACHE STRING
+ "GSPP: Number of 256B blocks")
+set(SSM_GSPP_512_BLOCKS 768 CACHE STRING
+ "GSPP: Number of 512B blocks")
+set(SSM_GSPP_1K_BLOCKS 512 CACHE STRING
+ "GSPP: Number of 1KB blocks")
+set(SSM_GSPP_2K_BLOCKS 384 CACHE STRING
+ "GSPP: Number of 2KB blocks")
+set(SSM_GSPP_4K_BLOCKS 256 CACHE STRING
+ "GSPP: Number of 4KB blocks")
+set(SSM_GSPP_16K_BLOCKS 128 CACHE STRING
+ "GSPP: Number of 16KB blocks")
+set(SSM_GSPP_64K_BLOCKS 64 CACHE STRING
+ "GSPP: Number of 64KB blocks")
+set(SSM_GSPP_256K_BLOCKS 32 CACHE STRING
+ "GSPP: Number of 256KB blocks")
+set(SSM_GSPP_1M_BLOCKS 16 CACHE STRING
+ "GSPP: Number of 1MB blocks")
+
+# Per-User Pool (PUP) - for unprivileged applications
+# Each unprivileged app gets its own smaller pool (~7.5 MB total)
+set(SSM_PUP_256_BLOCKS 128 CACHE STRING
+ "PUP: Number of 256B blocks")
+set(SSM_PUP_512_BLOCKS 96 CACHE STRING
+ "PUP: Number of 512B blocks")
+set(SSM_PUP_1K_BLOCKS 64 CACHE STRING
+ "PUP: Number of 1KB blocks")
+set(SSM_PUP_2K_BLOCKS 48 CACHE STRING
+ "PUP: Number of 2KB blocks")
+set(SSM_PUP_4K_BLOCKS 32 CACHE STRING
+ "PUP: Number of 4KB blocks")
+set(SSM_PUP_16K_BLOCKS 16 CACHE STRING
+ "PUP: Number of 16KB blocks")
+set(SSM_PUP_64K_BLOCKS 8 CACHE STRING
+ "PUP: Number of 64KB blocks")
+set(SSM_PUP_256K_BLOCKS 2 CACHE STRING
+ "PUP: Number of 256KB blocks")
+set(SSM_PUP_1M_BLOCKS 0 CACHE STRING
+ "PUP: Number of 1MB blocks")
+
+# SSM pool size calculations
+include(utils/HumanReadable)
+
+math(EXPR SSM_GSPP_TOTAL_SIZE
+ "(1 << 8) * ${SSM_GSPP_256_BLOCKS} + \
+ (1 << 9) * ${SSM_GSPP_512_BLOCKS} + \
+ (1 << 10) * ${SSM_GSPP_1K_BLOCKS} + \
+ (1 << 11) * ${SSM_GSPP_2K_BLOCKS} + \
+ (1 << 12) * ${SSM_GSPP_4K_BLOCKS} + \
+ (1 << 14) * ${SSM_GSPP_16K_BLOCKS} + \
+ (1 << 16) * ${SSM_GSPP_64K_BLOCKS} + \
+ (1 << 18) * ${SSM_GSPP_256K_BLOCKS} + \
+ (1 << 20) * ${SSM_GSPP_1M_BLOCKS}")
+
+set(SSM_GSPP_TOTAL_SIZE ${SSM_GSPP_TOTAL_SIZE} CACHE INTERNAL
+ "GSPP total size in bytes")
+
+math(EXPR SSM_PUP_TOTAL_SIZE
+ "(1 << 8) * ${SSM_PUP_256_BLOCKS} + \
+ (1 << 9) * ${SSM_PUP_512_BLOCKS} + \
+ (1 << 10) * ${SSM_PUP_1K_BLOCKS} + \
+ (1 << 11) * ${SSM_PUP_2K_BLOCKS} + \
+ (1 << 12) * ${SSM_PUP_4K_BLOCKS} + \
+ (1 << 14) * ${SSM_PUP_16K_BLOCKS} + \
+ (1 << 16) * ${SSM_PUP_64K_BLOCKS} + \
+ (1 << 18) * ${SSM_PUP_256K_BLOCKS} + \
+ (1 << 20) * ${SSM_PUP_1M_BLOCKS}")
+
+set(SSM_PUP_TOTAL_SIZE ${SSM_PUP_TOTAL_SIZE} CACHE INTERNAL
+ "PUP total size in bytes")
+
+set(SSM_POOL_TOTAL_SIZE ${SSM_GSPP_TOTAL_SIZE} CACHE INTERNAL
+ "Total shared memory pool size in bytes")
+
+format_bytes_human_readable(${SSM_GSPP_TOTAL_SIZE} SSM_GSPP_SIZE_DISPLAY)
+format_bytes_human_readable(${SSM_PUP_TOTAL_SIZE} SSM_PUP_SIZE_DISPLAY)
+
+message(STATUS "Secure Shared Memory Pool Configuration:")
+message(STATUS " Pool prefix: ${SSM_PREFIX}")
+message(STATUS " Size classes: "
+ "256B, 512B, 1KiB, 2KiB, 4KiB, 16KiB, 64KiB, 256KiB, 1MiB")
+message(STATUS " Max allocation: 1 MB")
+message(STATUS " Shards per class: ${SSM_POOL_SHARDS}")
+message(STATUS " GSPP (privileged): ${SSM_GSPP_SIZE_DISPLAY} "
+ "(${SSM_GSPP_TOTAL_SIZE} bytes)")
+message(STATUS " Blocks: ${SSM_GSPP_256_BLOCKS}, ${SSM_GSPP_512_BLOCKS}, "
+ "${SSM_GSPP_1K_BLOCKS}, ${SSM_GSPP_2K_BLOCKS}, ${SSM_GSPP_4K_BLOCKS}, "
+ "${SSM_GSPP_16K_BLOCKS}, ${SSM_GSPP_64K_BLOCKS}, ${SSM_GSPP_256K_BLOCKS}, "
+ "${SSM_GSPP_1M_BLOCKS}")
+message(STATUS " PUP (unprivileged): ${SSM_PUP_SIZE_DISPLAY} "
+ "(${SSM_PUP_TOTAL_SIZE} bytes)")
+message(STATUS " Blocks: ${SSM_PUP_256_BLOCKS}, ${SSM_PUP_512_BLOCKS}, "
+ "${SSM_PUP_1K_BLOCKS}, ${SSM_PUP_2K_BLOCKS}, ${SSM_PUP_4K_BLOCKS}, "
+ "${SSM_PUP_16K_BLOCKS}, ${SSM_PUP_64K_BLOCKS}, ${SSM_PUP_256K_BLOCKS}, "
+ "${SSM_PUP_1M_BLOCKS}")
diff --git a/cmake/config/tests.cmake b/cmake/config/tests.cmake
new file mode 100644
index 00000000..24730989
--- /dev/null
+++ b/cmake/config/tests.cmake
@@ -0,0 +1,17 @@
+# Test configuration options
+
+set(DISABLE_TESTS_LOGGING TRUE CACHE BOOL
+ "Disable Ouroboros log output in tests")
+if(DISABLE_TESTS_LOGGING)
+ message(STATUS "Ouroboros logging in test output disabled")
+else()
+ message(STATUS "Ouroboros logging in test output enabled")
+endif()
+
+set(DISABLE_TESTS_CORE_DUMPS TRUE CACHE BOOL
+ "Enable core dumps for tests (useful for debugging)")
+if(DISABLE_TESTS_CORE_DUMPS)
+ message(STATUS "Core dumps in tests enabled")
+else()
+ message(STATUS "Core dumps in tests disabled")
+endif()
diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake
new file mode 100644
index 00000000..109fe1d6
--- /dev/null
+++ b/cmake/dependencies.cmake
@@ -0,0 +1,43 @@
+find_package(PkgConfig QUIET)
+include(CheckSymbolExists)
+
+# System libraries and features
+include(dependencies/system/protobufc)
+include(dependencies/system/libraries)
+include(dependencies/system/explicit_bzero)
+include(dependencies/system/robustmutex)
+include(dependencies/system/fuse)
+include(dependencies/system/sysrandom)
+
+# Cryptography
+include(dependencies/crypt/openssl)
+include(dependencies/crypt/libgcrypt)
+
+# IRMd
+include(dependencies/irmd/libtoml)
+
+# Ethernet IPCP backends
+include(dependencies/eth/rawsockets)
+include(dependencies/eth/bpf)
+include(dependencies/eth/netmap)
+if(HAVE_RAW_SOCKETS OR HAVE_BPF OR HAVE_NETMAP)
+ set(HAVE_ETH TRUE CACHE INTERNAL "Ethernet IPCP support available")
+else()
+ unset(HAVE_ETH CACHE)
+endif()
+
+# UDP IPCP
+include(dependencies/udp/ddns)
+
+# Coverage tools
+include(dependencies/coverage/gcov)
+include(dependencies/coverage/lcov)
+
+# Validate that at least one secure random generator is available
+if(NOT ((CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") OR APPLE OR
+ HAVE_SYS_RANDOM OR HAVE_OPENSSL_RNG OR HAVE_LIBGCRYPT))
+ message(FATAL_ERROR "No secure random generator found, "
+ "please install libgcrypt (> 1.7.0) or OpenSSL")
+endif()
+
+
diff --git a/cmake/dependencies/coverage/gcov.cmake b/cmake/dependencies/coverage/gcov.cmake
new file mode 100644
index 00000000..1b593155
--- /dev/null
+++ b/cmake/dependencies/coverage/gcov.cmake
@@ -0,0 +1,21 @@
+include(utils/CompilerUtils)
+
+find_program(GCOV_PATH gcov)
+
+if(GCOV_PATH)
+ set(HAVE_GCOV TRUE CACHE INTERNAL "gcov coverage tool available")
+ set(DISABLE_COVERAGE ON CACHE BOOL "Disable code coverage analysis")
+ if(DISABLE_COVERAGE)
+ message(STATUS "gcov found - coverage analysis available (disabled by user)")
+ else()
+ message(STATUS "Code coverage analysis enabled")
+ test_and_set_c_compiler_flag_global(-g)
+ test_and_set_c_compiler_flag_global(--coverage)
+ add_link_options(--coverage)
+ endif()
+else()
+ set(HAVE_GCOV FALSE CACHE INTERNAL "gcov coverage tool available")
+ message(STATUS "gcov not found - coverage analysis not available")
+endif()
+
+mark_as_advanced(GCOV_PATH)
diff --git a/cmake/dependencies/coverage/lcov.cmake b/cmake/dependencies/coverage/lcov.cmake
new file mode 100644
index 00000000..65ed316e
--- /dev/null
+++ b/cmake/dependencies/coverage/lcov.cmake
@@ -0,0 +1,17 @@
+find_program(LCOV_PATH lcov)
+find_program(GENHTML_PATH genhtml)
+
+if(LCOV_PATH AND GENHTML_PATH)
+ set(HAVE_LCOV TRUE CACHE INTERNAL "lcov HTML coverage reports available")
+ message(STATUS "lcov and genhtml found - HTML coverage reports available")
+else()
+ set(HAVE_LCOV FALSE CACHE INTERNAL "lcov HTML coverage reports available")
+ if(NOT LCOV_PATH)
+ message(STATUS "lcov not found - HTML coverage reports not available")
+ endif()
+ if(NOT GENHTML_PATH)
+ message(STATUS "genhtml not found - HTML coverage reports not available")
+ endif()
+endif()
+
+mark_as_advanced(LCOV_PATH GENHTML_PATH)
diff --git a/cmake/dependencies/crypt/libgcrypt.cmake b/cmake/dependencies/crypt/libgcrypt.cmake
new file mode 100644
index 00000000..4f8a4cfe
--- /dev/null
+++ b/cmake/dependencies/crypt/libgcrypt.cmake
@@ -0,0 +1,55 @@
+# Try pkg-config first, fall back to find_library
+if(PkgConfig_FOUND)
+ pkg_check_modules(LIBGCRYPT QUIET IMPORTED_TARGET libgcrypt>=1.7.0)
+ if(LIBGCRYPT_FOUND AND NOT TARGET Gcrypt::Gcrypt)
+ add_library(Gcrypt::Gcrypt ALIAS PkgConfig::LIBGCRYPT)
+ endif()
+endif()
+
+if(NOT LIBGCRYPT_FOUND)
+ find_library(LIBGCRYPT_LIBRARIES gcrypt QUIET)
+ if(LIBGCRYPT_LIBRARIES)
+ find_path(LIBGCRYPT_INCLUDE_DIR gcrypt.h
+ HINTS /usr/include /usr/local/include)
+ if(LIBGCRYPT_INCLUDE_DIR)
+ file(STRINGS ${LIBGCRYPT_INCLUDE_DIR}/gcrypt.h GCSTR
+ REGEX "^#define GCRYPT_VERSION ")
+ string(REGEX REPLACE "^#define GCRYPT_VERSION \"(.*)\".*$" "\\1"
+ LIBGCRYPT_VERSION "${GCSTR}")
+ if(NOT LIBGCRYPT_VERSION VERSION_LESS "1.7.0")
+ set(LIBGCRYPT_FOUND TRUE)
+ if(NOT TARGET Gcrypt::Gcrypt)
+ add_library(Gcrypt::Gcrypt UNKNOWN IMPORTED)
+ set_target_properties(Gcrypt::Gcrypt PROPERTIES
+ IMPORTED_LOCATION "${LIBGCRYPT_LIBRARIES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${LIBGCRYPT_INCLUDE_DIR}")
+ endif()
+ endif()
+ endif()
+ endif()
+endif()
+
+if(LIBGCRYPT_FOUND)
+ set(DISABLE_LIBGCRYPT FALSE CACHE BOOL "Disable libgcrypt support")
+ if(NOT DISABLE_LIBGCRYPT)
+ if(LIBGCRYPT_VERSION)
+ message(STATUS "libgcrypt support enabled (version ${LIBGCRYPT_VERSION})")
+ else()
+ message(STATUS "libgcrypt support enabled")
+ endif()
+ set(HAVE_LIBGCRYPT TRUE CACHE INTERNAL "libgcrypt cryptography support available")
+ else()
+ message(STATUS "libgcrypt support disabled by user")
+ unset(HAVE_LIBGCRYPT CACHE)
+ endif()
+else()
+ message(STATUS "Install libgcrypt >= 1.7.0 to enable libgcrypt support")
+ unset(HAVE_LIBGCRYPT CACHE)
+endif()
+
+if(NOT HAVE_LIBGCRYPT)
+ set(LIBGCRYPT_LIBRARIES "")
+ set(LIBGCRYPT_INCLUDE_DIR "")
+endif()
+
+mark_as_advanced(LIBGCRYPT_LIBRARIES LIBGCRYPT_INCLUDE_DIR)
diff --git a/cmake/dependencies/crypt/openssl.cmake b/cmake/dependencies/crypt/openssl.cmake
new file mode 100644
index 00000000..ed07cc9d
--- /dev/null
+++ b/cmake/dependencies/crypt/openssl.cmake
@@ -0,0 +1,35 @@
+find_package(OpenSSL QUIET)
+if(OPENSSL_FOUND)
+ set(HAVE_OPENSSL_RNG TRUE)
+ if(OPENSSL_VERSION VERSION_LESS "3.0.0")
+ message(STATUS "Install version >= 3.0.0 to enable OpenSSL support "
+ "(found version \"${OPENSSL_VERSION}\")")
+ else()
+ set(DISABLE_OPENSSL FALSE CACHE BOOL "Disable OpenSSL support")
+ if(NOT DISABLE_OPENSSL)
+ message(STATUS "OpenSSL support enabled, found version ${OPENSSL_VERSION}")
+ set(HAVE_OPENSSL TRUE CACHE INTERNAL "OpenSSL cryptography support available")
+ set(DISABLE_PQC FALSE CACHE BOOL "Disable post-quantum cryptography support")
+ if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.4.0")
+ if(NOT DISABLE_PQC)
+ set(HAVE_OPENSSL_PQC TRUE CACHE INTERNAL "OpenSSL post-quantum cryptography available")
+ message(STATUS "OpenSSL PQC support enabled")
+ else()
+ message(STATUS "OpenSSL PQC support disabled by user")
+ unset(HAVE_OPENSSL_PQC CACHE)
+ endif()
+ else()
+ message(STATUS "Install OpenSSL >= 3.4.0 for PQC support")
+ endif()
+ else()
+ message(STATUS "OpenSSL support disabled")
+ unset(HAVE_OPENSSL CACHE)
+ endif()
+ endif()
+else()
+ message(STATUS "Install OpenSSL version >= 3.0.0 to enable OpenSSL support")
+ unset(HAVE_OPENSSL_RNG)
+ unset(HAVE_OPENSSL CACHE)
+endif()
+
+# Secure memory options are in cmake/config/global.cmake
diff --git a/cmake/dependencies/eth/bpf.cmake b/cmake/dependencies/eth/bpf.cmake
new file mode 100644
index 00000000..c2f69e79
--- /dev/null
+++ b/cmake/dependencies/eth/bpf.cmake
@@ -0,0 +1,20 @@
+# Berkeley Packet Filter support (BSD/macOS only)
+if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ find_path(BPF_C_INCLUDE_DIR
+ net/bpf.h
+ HINTS /usr/include /usr/local/include)
+
+ mark_as_advanced(BPF_C_INCLUDE_DIR)
+
+ if(BPF_C_INCLUDE_DIR)
+ set(DISABLE_BPF FALSE CACHE BOOL
+ "Disable Berkeley Packet Filter support for Ethernet IPCPs")
+ if(NOT DISABLE_BPF)
+ message(STATUS "Berkeley Packet Filter support for Ethernet IPCPs enabled")
+ set(HAVE_BPF TRUE)
+ else()
+ message(STATUS "Berkeley Packet Filter support for Ethernet IPCPs disabled by user")
+ unset(HAVE_BPF)
+ endif()
+ endif()
+endif()
diff --git a/cmake/dependencies/eth/netmap.cmake b/cmake/dependencies/eth/netmap.cmake
new file mode 100644
index 00000000..94ecd634
--- /dev/null
+++ b/cmake/dependencies/eth/netmap.cmake
@@ -0,0 +1,18 @@
+# netmap support (optional acceleration)
+find_path(NETMAP_C_INCLUDE_DIR
+ net/netmap_user.h
+ HINTS /usr/include /usr/local/include)
+
+mark_as_advanced(NETMAP_C_INCLUDE_DIR)
+
+if(NOT HAVE_RAW_SOCKETS AND NOT HAVE_BPF AND NETMAP_C_INCLUDE_DIR)
+ set(DISABLE_NETMAP FALSE CACHE BOOL
+ "Disable netmap support for ETH IPCPs")
+ if(NOT DISABLE_NETMAP)
+ message(STATUS "Netmap support for Ethernet IPCPs enabled")
+ set(HAVE_NETMAP TRUE)
+ else()
+ message(STATUS "Netmap support for Ethernet IPCPs disabled by user")
+ unset(HAVE_NETMAP)
+ endif()
+endif()
diff --git a/cmake/dependencies/eth/rawsockets.cmake b/cmake/dependencies/eth/rawsockets.cmake
new file mode 100644
index 00000000..395d9efb
--- /dev/null
+++ b/cmake/dependencies/eth/rawsockets.cmake
@@ -0,0 +1,12 @@
+# Raw sockets support (Linux only)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(DISABLE_RAW_SOCKETS FALSE CACHE BOOL
+ "Disable raw socket support for Ethernet IPCPs")
+ if(NOT DISABLE_RAW_SOCKETS)
+ message(STATUS "Raw socket support for Ethernet IPCPs enabled")
+ set(HAVE_RAW_SOCKETS TRUE)
+ else()
+ message(STATUS "Raw socket support for Ethernet IPCPs disabled by user")
+ unset(HAVE_RAW_SOCKETS)
+ endif()
+endif()
diff --git a/cmake/dependencies/irmd/libtoml.cmake b/cmake/dependencies/irmd/libtoml.cmake
new file mode 100644
index 00000000..dcbc17e4
--- /dev/null
+++ b/cmake/dependencies/irmd/libtoml.cmake
@@ -0,0 +1,29 @@
+find_library(LIBTOML_LIBRARY toml QUIET)
+if(LIBTOML_LIBRARY)
+ find_path(LIBTOML_INCLUDE_DIR toml.h)
+ set(DISABLE_CONFIGFILE FALSE CACHE BOOL
+ "Disable configuration file support")
+ if(NOT DISABLE_CONFIGFILE)
+ set(OUROBOROS_CONFIG_FILE irmd.conf CACHE STRING
+ "Name of the IRMd configuration file")
+ set(HAVE_TOML TRUE CACHE INTERNAL "TOML configuration file support available")
+ message(STATUS "Configuration file support enabled")
+ message(STATUS "Configuration directory: ${OUROBOROS_CONFIG_DIR}")
+ # Create imported target for consistency with other dependencies
+ if(NOT TARGET toml::toml)
+ add_library(toml::toml UNKNOWN IMPORTED)
+ set_target_properties(toml::toml PROPERTIES
+ IMPORTED_LOCATION "${LIBTOML_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${LIBTOML_INCLUDE_DIR}")
+ endif()
+ else()
+ message(STATUS "Configuration file support disabled by user")
+ unset(OUROBOROS_CONFIG_FILE CACHE)
+ unset(HAVE_TOML CACHE)
+ endif()
+ mark_as_advanced(LIBTOML_LIBRARY LIBTOML_INCLUDE_DIR)
+else()
+ message(STATUS "Install tomlc99 for config file support")
+ message(STATUS " https://github.com/cktan/tomlc99")
+ unset(HAVE_TOML CACHE)
+endif()
diff --git a/cmake/dependencies/system/explicit_bzero.cmake b/cmake/dependencies/system/explicit_bzero.cmake
new file mode 100644
index 00000000..89ab3abc
--- /dev/null
+++ b/cmake/dependencies/system/explicit_bzero.cmake
@@ -0,0 +1,4 @@
+# Check for explicit_bzero in string.h
+# glibc requires _DEFAULT_SOURCE to expose it; harmless on other platforms
+list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_DEFAULT_SOURCE)
+check_symbol_exists(explicit_bzero "string.h" HAVE_EXPLICIT_BZERO)
diff --git a/cmake/dependencies/system/fuse.cmake b/cmake/dependencies/system/fuse.cmake
new file mode 100644
index 00000000..7de12b31
--- /dev/null
+++ b/cmake/dependencies/system/fuse.cmake
@@ -0,0 +1,44 @@
+# Try pkg-config first, fall back to find_library
+if(PkgConfig_FOUND)
+ pkg_check_modules(FUSE QUIET IMPORTED_TARGET fuse>=2.6)
+ if(FUSE_FOUND AND NOT TARGET Fuse::Fuse)
+ add_library(Fuse::Fuse ALIAS PkgConfig::FUSE)
+ endif()
+endif()
+
+if(NOT FUSE_FOUND)
+ find_library(FUSE_LIBRARIES fuse QUIET)
+ if(FUSE_LIBRARIES)
+ set(FUSE_FOUND TRUE)
+ if(NOT TARGET Fuse::Fuse)
+ add_library(Fuse::Fuse UNKNOWN IMPORTED)
+ set_target_properties(Fuse::Fuse PROPERTIES
+ IMPORTED_LOCATION "${FUSE_LIBRARIES}")
+ endif()
+ endif()
+endif()
+
+if(FUSE_FOUND)
+ set(DISABLE_FUSE FALSE CACHE BOOL "Disable FUSE support")
+ if(NOT DISABLE_FUSE)
+ if(FUSE_VERSION)
+ message(STATUS "FUSE support enabled (version ${FUSE_VERSION})")
+ else()
+ message(STATUS "FUSE support enabled")
+ endif()
+ # FUSE_PREFIX is set in cmake/config/global.cmake
+ set(HAVE_FUSE TRUE CACHE INTERNAL "FUSE filesystem support available")
+ else()
+ message(STATUS "FUSE support disabled by user")
+ unset(HAVE_FUSE CACHE)
+ endif()
+else()
+ message(STATUS "Install FUSE version >= 2.6 to enable RIB access")
+ unset(HAVE_FUSE CACHE)
+endif()
+
+if(NOT HAVE_FUSE)
+ set(FUSE_LIBRARIES "")
+endif()
+
+mark_as_advanced(FUSE_LIBRARIES)
diff --git a/cmake/dependencies/system/libraries.cmake b/cmake/dependencies/system/libraries.cmake
new file mode 100644
index 00000000..55c22d6a
--- /dev/null
+++ b/cmake/dependencies/system/libraries.cmake
@@ -0,0 +1,17 @@
+if(NOT APPLE)
+ find_library(LIBRT_LIBRARIES rt)
+ mark_as_advanced(LIBRT_LIBRARIES)
+ if(NOT LIBRT_LIBRARIES)
+ message(FATAL_ERROR "Could not find librt")
+ endif()
+else()
+ set(LIBRT_LIBRARIES "")
+endif()
+
+find_package(Threads REQUIRED)
+
+find_library(LIBM_LIBRARIES m)
+mark_as_advanced(LIBM_LIBRARIES)
+if(NOT LIBM_LIBRARIES)
+ message(FATAL_ERROR "Could not find libm")
+endif()
diff --git a/cmake/dependencies/system/protobufc.cmake b/cmake/dependencies/system/protobufc.cmake
new file mode 100644
index 00000000..b7e0062a
--- /dev/null
+++ b/cmake/dependencies/system/protobufc.cmake
@@ -0,0 +1,13 @@
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/utils")
+
+find_package(ProtobufC QUIET)
+if(NOT (PROTOBUF_C_INCLUDE_DIRS AND PROTOBUF_C_LIBRARY
+ AND PROTOBUF_PROTOC_C_EXECUTABLE))
+ message(FATAL_ERROR "Protobuf C compiler required but not found. "
+ "Please install Google Protocol Buffers.")
+else()
+ message(STATUS "Found protobuf C compiler in ${PROTOBUF_PROTOC_C_EXECUTABLE}")
+endif()
+
+# Note: Include dirs are added per-target via target_include_directories
+# using ${PROTOBUF_C_INCLUDE_DIRS}
diff --git a/cmake/dependencies/system/robustmutex.cmake b/cmake/dependencies/system/robustmutex.cmake
new file mode 100644
index 00000000..89b7325b
--- /dev/null
+++ b/cmake/dependencies/system/robustmutex.cmake
@@ -0,0 +1,18 @@
+list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L)
+list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__XSI_VISIBLE=500)
+list(APPEND CMAKE_REQUIRED_LIBRARIES pthread)
+check_symbol_exists(pthread_mutexattr_setrobust pthread.h HAVE_ROBUST_MUTEX)
+
+if(HAVE_ROBUST_MUTEX)
+ set(DISABLE_ROBUST_MUTEXES FALSE CACHE BOOL "Disable robust mutex support")
+ if(NOT DISABLE_ROBUST_MUTEXES)
+ message(STATUS "Robust mutex support enabled")
+ set(HAVE_ROBUST_MUTEX TRUE)
+ else()
+ message(STATUS "Robust mutex support disabled by user")
+ unset(HAVE_ROBUST_MUTEX)
+ endif()
+else()
+ message(STATUS "Robust mutex support not available")
+ unset(HAVE_ROBUST_MUTEX)
+endif()
diff --git a/cmake/dependencies/system/sysrandom.cmake b/cmake/dependencies/system/sysrandom.cmake
new file mode 100644
index 00000000..dc8443a1
--- /dev/null
+++ b/cmake/dependencies/system/sysrandom.cmake
@@ -0,0 +1,14 @@
+if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ set(SYS_RND_HDR "")
+else()
+ find_path(SYS_RND_HDR sys/random.h PATH /usr/include/ /usr/local/include/)
+ if(SYS_RND_HDR)
+ message(STATUS "Found sys/random.h in ${SYS_RND_HDR}")
+ set(HAVE_SYS_RANDOM TRUE)
+ else()
+ set(SYS_RND_HDR "")
+ unset(HAVE_SYS_RANDOM)
+ endif()
+endif()
+
+mark_as_advanced(SYS_RND_HDR)
diff --git a/cmake/dependencies/udp/ddns.cmake b/cmake/dependencies/udp/ddns.cmake
new file mode 100644
index 00000000..e8208e47
--- /dev/null
+++ b/cmake/dependencies/udp/ddns.cmake
@@ -0,0 +1,31 @@
+# DDNS (Dynamic DNS) support detection
+# Requires nsupdate and nslookup tools
+
+find_program(NSUPDATE_EXECUTABLE
+ NAMES nsupdate
+ DOC "The nsupdate tool that enables DDNS")
+
+find_program(NSLOOKUP_EXECUTABLE
+ NAMES nslookup
+ DOC "The nslookup tool that resolves DNS names")
+
+mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE)
+
+if(NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE)
+ set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support")
+ if(NOT DISABLE_DDNS)
+ message(STATUS "DDNS support enabled")
+ set(HAVE_DDNS TRUE CACHE INTERNAL "Dynamic DNS support available")
+ else()
+ message(STATUS "DDNS support disabled by user")
+ unset(HAVE_DDNS CACHE)
+ endif()
+else()
+ if(NSLOOKUP_EXECUTABLE)
+ message(STATUS "Install nsupdate to enable DDNS support")
+ elseif(NSUPDATE_EXECUTABLE)
+ message(STATUS "Install nslookup to enable DDNS support")
+ else()
+ message(STATUS "Install nslookup and nsupdate to enable DDNS support")
+ endif()
+endif()
diff --git a/doc/man/CMakeLists.txt b/cmake/doc.cmake
index add68d62..f75cde5b 100644
--- a/doc/man/CMakeLists.txt
+++ b/cmake/doc.cmake
@@ -1,3 +1,6 @@
+set(DOC_SOURCE_DIR "${CMAKE_SOURCE_DIR}/doc")
+set(DOC_BINARY_DIR "${CMAKE_BINARY_DIR}/doc")
+
set(MAN_NAMES
# Add man page sources here
flow_accept.3
@@ -25,27 +28,22 @@ set(MAN_NAMES
irm.8
)
-macro(INSTALL_MAN __mans)
- foreach (_man ${ARGV})
- string(REGEX REPLACE "^.+[.]([1-9]).gz" "\\1" _mansect ${_man})
- install(FILES ${_man} DESTINATION "${CMAKE_INSTALL_MANDIR}/man${_mansect}")
- endforeach (_man)
-endmacro(INSTALL_MAN __mans)
-
find_program(GZIP_EXECUTABLE
NAMES gzip
DOC "Will gzip the man pages")
-
mark_as_advanced(GZIP_EXECUTABLE)
-if (GZIP_EXECUTABLE)
+if(GZIP_EXECUTABLE)
+ # Create the doc output directory
+ file(MAKE_DIRECTORY ${DOC_BINARY_DIR})
+
foreach (m ${MAN_NAMES})
- set(md ${CMAKE_CURRENT_BINARY_DIR}/${m}.gz)
+ set(md ${DOC_BINARY_DIR}/${m}.gz)
add_custom_command(
OUTPUT ${md}
COMMAND ${GZIP_EXECUTABLE}
- ARGS -c ${CMAKE_CURRENT_SOURCE_DIR}/${m} > ${md}
+ ARGS -c ${DOC_SOURCE_DIR}/man/${m} > ${md}
COMMENT "Compressing manpage ${m}"
VERBATIM)
@@ -53,6 +51,4 @@ if (GZIP_EXECUTABLE)
endforeach ()
add_custom_target(man ALL DEPENDS ${MAN_FILES})
-
- INSTALL_MAN(${MAN_FILES})
-endif ()
+endif()
diff --git a/cmake/include.cmake b/cmake/include.cmake
new file mode 100644
index 00000000..ace51d0f
--- /dev/null
+++ b/cmake/include.cmake
@@ -0,0 +1,24 @@
+set(HEADERS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/include/ouroboros")
+
+# SOCK_BUF_SIZE is in cmake/config/global.cmake
+
+configure_file("${CMAKE_SOURCE_DIR}/include/ouroboros/version.h.in"
+ "${CMAKE_BINARY_DIR}/include/ouroboros/version.h" @ONLY)
+
+configure_file("${CMAKE_SOURCE_DIR}/include/ouroboros/sockets.h.in"
+ "${CMAKE_BINARY_DIR}/include/ouroboros/sockets.h" @ONLY)
+
+set(PUBLIC_HEADER_FILES
+ ${HEADERS_SOURCE_DIR}/cep.h
+ ${HEADERS_SOURCE_DIR}/cdefs.h
+ ${HEADERS_SOURCE_DIR}/dev.h
+ ${HEADERS_SOURCE_DIR}/errno.h
+ ${HEADERS_SOURCE_DIR}/fccntl.h
+ ${HEADERS_SOURCE_DIR}/fqueue.h
+ ${HEADERS_SOURCE_DIR}/ipcp.h
+ ${HEADERS_SOURCE_DIR}/irm.h
+ ${HEADERS_SOURCE_DIR}/name.h
+ ${HEADERS_SOURCE_DIR}/proto.h
+ ${HEADERS_SOURCE_DIR}/qos.h
+ ${CMAKE_BINARY_DIR}/include/ouroboros/version.h
+)
diff --git a/cmake/install.cmake b/cmake/install.cmake
new file mode 100644
index 00000000..79714df2
--- /dev/null
+++ b/cmake/install.cmake
@@ -0,0 +1,101 @@
+# Installation configuration
+
+include(CMakePackageConfigHelpers)
+
+# Public headers
+install(FILES ${PUBLIC_HEADER_FILES}
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ouroboros)
+
+# Man pages
+if(GZIP_EXECUTABLE)
+ foreach(_man ${MAN_FILES})
+ string(REGEX REPLACE "^.+[.]([1-9]).gz" "\\1" _mansect ${_man})
+ install(FILES ${_man} DESTINATION "${CMAKE_INSTALL_MANDIR}/man${_mansect}")
+ endforeach()
+endif()
+
+# pkg-config files
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-dev.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ouroboros-irm.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+set(OUROBOROS_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Ouroboros")
+
+install(EXPORT OuroborosTargets
+ FILE OuroborosTargets.cmake
+ NAMESPACE Ouroboros::
+ DESTINATION ${OUROBOROS_CMAKE_DIR})
+
+configure_package_config_file(
+ "${CMAKE_SOURCE_DIR}/cmake/OuroborosConfig.cmake.in"
+ "${CMAKE_BINARY_DIR}/OuroborosConfig.cmake"
+ INSTALL_DESTINATION ${OUROBOROS_CMAKE_DIR})
+
+write_basic_package_version_file(
+ "${CMAKE_BINARY_DIR}/OuroborosConfigVersion.cmake"
+ VERSION ${PACKAGE_VERSION}
+ COMPATIBILITY SameMajorVersion)
+
+install(FILES
+ "${CMAKE_BINARY_DIR}/OuroborosConfig.cmake"
+ "${CMAKE_BINARY_DIR}/OuroborosConfigVersion.cmake"
+ DESTINATION ${OUROBOROS_CMAKE_DIR})
+
+# Systemd service file installation
+set(SYSTEMD_INSTALL_FILES "DETECT" CACHE STRING
+ "Install systemd .service files (NO (never), DETECT (use pkg-config - default),\
+ FORCE (always - see SYSTEMD_UNITDIR_OVERRIDE))")
+set(SYSTEMD_UNITDIR_OVERRIDE "" CACHE PATH
+ "Path to install systemd files. When SYSTEMD_INSTALL_FILES == DETECT, this\
+ can be empty to automatically determine the path. Cannot be empty when FORCE.")
+
+if(SYSTEMD_INSTALL_FILES STREQUAL "DETECT" OR SYSTEMD_INSTALL_FILES STREQUAL "FORCE")
+ if(SYSTEMD_INSTALL_FILES STREQUAL "DETECT")
+ if(PkgConfig_FOUND)
+ pkg_check_modules(SYSTEMD "systemd")
+ endif()
+ if(SYSTEMD_FOUND)
+ if(SYSTEMD_UNITDIR_OVERRIDE STREQUAL "")
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
+ --variable=systemdsystemunitdir systemd
+ OUTPUT_VARIABLE SYSTEMD_UNITDIR_INTERNAL)
+ string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNITDIR_INTERNAL
+ "${SYSTEMD_UNITDIR_INTERNAL}"
+ )
+ else()
+ set(SYSTEMD_UNITDIR_INTERNAL "${SYSTEMD_UNITDIR_OVERRIDE}")
+ endif()
+ else()
+ set(SYSTEMD_UNITDIR_INTERNAL "")
+ endif()
+ elseif(SYSTEMD_INSTALL_FILES STREQUAL "FORCE")
+ if(SYSTEMD_UNITDIR_OVERRIDE STREQUAL "")
+ message(FATAL_ERROR "Systemd installation required by user, but no path\
+ provided with SYSTEMD_UNITDIR_OVERRIDE.")
+ else()
+ set(SYSTEMD_UNITDIR_INTERNAL "${SYSTEMD_UNITDIR_OVERRIDE}")
+ endif()
+ endif()
+ if(NOT SYSTEMD_UNITDIR_INTERNAL STREQUAL "")
+ message(STATUS "Systemd service installation enabled to: ${SYSTEMD_UNITDIR_INTERNAL}")
+ if(LIBTOML_LIBRARIES AND NOT DISABLE_CONFIGFILE)
+ set (CONFIGURE_STRING "--config ${OUROBOROS_CONFIG_DIR}/${OUROBOROS_CONFIG_FILE}")
+ else()
+ set (CONFIGURE_STRING "")
+ endif()
+ configure_file("${CMAKE_SOURCE_DIR}/ouroboros.service.in"
+ "${CMAKE_BINARY_DIR}/ouroboros.service" @ONLY)
+ install(FILES "${CMAKE_BINARY_DIR}/ouroboros.service"
+ DESTINATION "${SYSTEMD_UNITDIR_INTERNAL}")
+ endif()
+else()
+ message(STATUS "Systemd service installation disabled by user")
+endif()
+
+configure_file("${CMAKE_SOURCE_DIR}/cmake/utils/CMakeUninstall.cmake.in"
+ "${CMAKE_BINARY_DIR}/cmake/cmakeuninstall.cmake" @ONLY)
+
+add_custom_target(uninstall
+ COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake/cmakeuninstall.cmake)
diff --git a/cmake/package.cmake b/cmake/package.cmake
new file mode 100644
index 00000000..71105ee8
--- /dev/null
+++ b/cmake/package.cmake
@@ -0,0 +1,29 @@
+set(PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
+set(PACKAGE_DESCRIPTION "The Ouroboros prototype")
+set(PACKAGE_URL "http://ouroboros.rocks")
+set(PACKAGE_BUGREPORT "http://ouroboros.rocks/bugzilla/")
+
+message(STATUS "Package name is: ${PACKAGE_NAME}")
+message(STATUS "Package description is: ${PACKAGE_DESCRIPTION}")
+message(STATUS "Package version is: ${PACKAGE_VERSION}")
+message(STATUS "Package URL is: ${PACKAGE_URL}")
+message(STATUS "Package bug-report address: ${PACKAGE_BUGREPORT}")
+message(STATUS "Package install prefix: ${CMAKE_INSTALL_PREFIX}")
+
+configure_file("${CMAKE_SOURCE_DIR}/ouroboros-dev.pc.in"
+ "${CMAKE_BINARY_DIR}/ouroboros-dev.pc" @ONLY)
+
+configure_file("${CMAKE_SOURCE_DIR}/ouroboros-irm.pc.in"
+ "${CMAKE_BINARY_DIR}/ouroboros-irm.pc" @ONLY)
+
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE_DESCRIPTION}")
+set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README")
+set(CPACK_PACKAGE_VERSION_MAJOR "${PACKAGE_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${PACKAGE_VERSION_MINOR}")
+set(CPACK_PACKAGE_VERSION_PATCH "${PACKAGE_VERSION_PATCH}")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY
+ "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}")
+set(CPACK_GENERATOR "TGZ")
+set(CPACK_SOURCE_GENERATOR "TGZ")
+
+include(CPack)
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
new file mode 100644
index 00000000..edc2987c
--- /dev/null
+++ b/cmake/tests.cmake
@@ -0,0 +1,30 @@
+include(CTest) # Sets BUILD_TESTING by default to on.
+include(utils/TestUtils)
+
+# Test configuration options
+include(config/tests)
+include(utils/DisableTestLogging)
+
+if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
+ set(BUILD_TESTS ON)
+else()
+ set(BUILD_TESTS OFF)
+endif()
+
+add_custom_target(build_tests)
+
+if(BUILD_TESTS)
+ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
+ add_dependencies(check build_tests)
+endif()
+
+# Test subdirectories are added from their parent CMakeLists.txt files
+# via add_subdirectory(tests) - keeping tests with their source code
+
+# Coverage target setup (called after all targets are defined)
+function(setup_coverage_target)
+ if(BUILD_TESTS)
+ include(utils/GenCoverage)
+ create_coverage_target()
+ endif()
+endfunction()
diff --git a/cmake/utils/AddCompileFlags.cmake b/cmake/utils/AddCompileFlags.cmake
new file mode 100644
index 00000000..b81ccc67
--- /dev/null
+++ b/cmake/utils/AddCompileFlags.cmake
@@ -0,0 +1,6 @@
+# - add_compile_flags(<target> <flags>...)
+# Add compile flags to a target using modern CMake
+
+macro(add_compile_flags _target)
+ target_compile_options(${_target} PRIVATE ${ARGN})
+endmacro()
diff --git a/cmake/utils/CMakeUninstall.cmake.in b/cmake/utils/CMakeUninstall.cmake.in
new file mode 100644
index 00000000..b8bba5fd
--- /dev/null
+++ b/cmake/utils/CMakeUninstall.cmake.in
@@ -0,0 +1,21 @@
+if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+ message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach (file ${files})
+ message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+ if (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ execute_process(
+ COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
+ RESULT_VARIABLE rm_out
+ ERROR_VARIABLE rm_retval
+ )
+ if (NOT "${rm_retval}" STREQUAL "" AND NOT "${rm_retval}" STREQUAL 0)
+ message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+ endif()
+ else()
+ message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+ endif()
+endforeach ()
diff --git a/cmake/CompilerUtils.cmake b/cmake/utils/CompilerUtils.cmake
index 7c8b022f..35548f4f 100644
--- a/cmake/CompilerUtils.cmake
+++ b/cmake/utils/CompilerUtils.cmake
@@ -1,13 +1,13 @@
include(CheckCCompilerFlag)
function(test_and_set_c_compiler_flag_global _flag)
-
string(REGEX REPLACE "-" "_" _sflag ${_flag})
- set(CMAKE_REQUIRED_FLAGS ${_flag})
+ string(REGEX REPLACE "=" "_" _sflag ${_sflag})
+ # Use -Werror during test so clang rejects unknown flags
+ set(CMAKE_REQUIRED_FLAGS "-Werror ${_flag}")
check_c_compiler_flag(${_flag} COMPILER_SUPPORTS_FLAG_${_sflag})
if(COMPILER_SUPPORTS_FLAG_${_sflag})
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_flag}" PARENT_SCOPE)
+ add_compile_options(${_flag})
endif()
-
-endfunction(test_and_set_c_compiler_flag_global)
+endfunction()
diff --git a/cmake/utils/DebugTargets.cmake b/cmake/utils/DebugTargets.cmake
new file mode 100644
index 00000000..78899213
--- /dev/null
+++ b/cmake/utils/DebugTargets.cmake
@@ -0,0 +1,16 @@
+# Utility functions for Ouroboros target configuration
+set(OUROBOROS_DEBUG_CONFIGS
+ Debug
+ DebugASan
+ DebugTSan
+ DebugLSan
+ DebugUSan
+ DebugAnalyzer
+)
+
+# Add CONFIG_OUROBOROS_DEBUG definition for debug build types
+function(ouroboros_target_debug_definitions target)
+ list(JOIN OUROBOROS_DEBUG_CONFIGS "," _configs)
+ target_compile_definitions(${target} PRIVATE
+ "$<$<CONFIG:${_configs}>:CONFIG_OUROBOROS_DEBUG>")
+endfunction()
diff --git a/cmake/utils/DisableTestLogging.cmake b/cmake/utils/DisableTestLogging.cmake
new file mode 100644
index 00000000..40c16668
--- /dev/null
+++ b/cmake/utils/DisableTestLogging.cmake
@@ -0,0 +1,11 @@
+# Macro to apply test logging settings to a target
+# Configuration options are in cmake/config/tests.cmake
+
+macro(disable_test_logging_for_target target)
+ if(DISABLE_TESTS_LOGGING)
+ target_compile_definitions(${target} PRIVATE OUROBOROS_DISABLE_LOGGING)
+ endif()
+ if(DISABLE_TESTS_CORE_DUMPS)
+ target_compile_definitions(${target} PRIVATE DISABLE_TESTS_CORE_DUMPS)
+ endif()
+endmacro()
diff --git a/cmake/FindProtobufC.cmake b/cmake/utils/FindProtobufC.cmake
index ff532a46..4632dfcb 100644
--- a/cmake/FindProtobufC.cmake
+++ b/cmake/utils/FindProtobufC.cmake
@@ -1,22 +1,22 @@
-function(PROTOBUF_GENERATE_C SRCS HDRS)
- if (NOT ARGN)
- message(SEND_ERROR "Error: PROTOBUF_GENERATE_C() called without any proto files")
+function(protobuf_generate_c SRCS HDRS)
+ if(NOT ARGN)
+ message(SEND_ERROR "Error: protobuf_generate_c() called without any proto files")
return()
- endif ()
+ endif()
- if (PROTOBUF_GENERATE_C_APPEND_PATH)
+ if(PROTOBUF_GENERATE_C_APPEND_PATH)
# Create an include path for each file specified
foreach (FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
- if (${_contains_already} EQUAL -1)
+ if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
- endif ()
+ endif()
endforeach ()
- else ()
+ else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
- endif ()
+ endif()
set(${SRCS})
set(${HDRS})
@@ -42,33 +42,37 @@ function(PROTOBUF_GENERATE_C SRCS HDRS)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()
-# By default have PROTOBUF_GENERATE_C macro pass -I to protoc
+# By default have protobuf_generate_c function pass -I to protoc
# for each directory where a proto file is referenced.
-if (NOT DEFINED PROTOBUF_GENERATE_C_APPEND_PATH)
+if(NOT DEFINED PROTOBUF_GENERATE_C_APPEND_PATH)
set(PROTOBUF_GENERATE_C_APPEND_PATH TRUE)
-endif ()
+endif()
-# Find library
find_library(PROTOBUF_C_LIBRARY
NAMES libprotobuf-c.so libprotobuf-c libprotobuf-c.dylib
)
mark_as_advanced(PROTOBUF_C_LIBRARY)
-# Find the include directory
find_path(PROTOBUF_C_INCLUDE_DIR
google/protobuf-c/protobuf-c.h
)
mark_as_advanced(PROTOBUF_C_INCLUDE_DIR)
-# Find the protoc-c Executable
find_program(PROTOBUF_PROTOC_C_EXECUTABLE
- NAMES protoc-c
+ NAMES protoc protoc-c
DOC "The Google Protocol Buffers C Compiler"
)
mark_as_advanced(PROTOBUF_PROTOC_C_EXECUTABLE)
-find_package(PackageHandleStandardArgs)
+include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ProtobufC DEFAULT_MSG
PROTOBUF_C_LIBRARY PROTOBUF_C_INCLUDE_DIR PROTOBUF_PROTOC_C_EXECUTABLE)
set(PROTOBUF_C_INCLUDE_DIRS ${PROTOBUF_C_INCLUDE_DIR})
+
+if(ProtobufC_FOUND AND NOT TARGET ProtobufC::ProtobufC)
+ add_library(ProtobufC::ProtobufC UNKNOWN IMPORTED)
+ set_target_properties(ProtobufC::ProtobufC PROPERTIES
+ IMPORTED_LOCATION "${PROTOBUF_C_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${PROTOBUF_C_INCLUDE_DIR}")
+endif()
diff --git a/cmake/utils/GenCoverage.cmake b/cmake/utils/GenCoverage.cmake
new file mode 100644
index 00000000..0ae11ee8
--- /dev/null
+++ b/cmake/utils/GenCoverage.cmake
@@ -0,0 +1,94 @@
+# Creates the coverage target for test coverage analysis
+# Requires HAVE_GCOV to be set (from dependencies/gcov.cmake)
+# Uses HAVE_LCOV for optional HTML generation (from dependencies/lcov.cmake)
+
+# Filter patterns for lcov --remove
+set(LCOV_FILTERS '*_test.c' '*.h')
+
+# Ignore inconsistent coverage: legitimate gaps in error paths and
+# edge cases that are difficult to exercise in unit tests.
+function(get_html_coverage_commands OUTPUT_VAR)
+ if(HAVE_LCOV)
+ set(COMMANDS
+ COMMAND ${LCOV_PATH}
+ --capture --directory .
+ --output-file coverage.info
+ > /dev/null 2>&1
+ COMMAND ${LCOV_PATH}
+ --remove coverage.info ${LCOV_FILTERS}
+ --output-file coverage_filtered.info
+ --ignore-errors inconsistent
+ > /dev/null 2>&1
+ COMMAND ${GENHTML_PATH}
+ coverage_filtered.info
+ --output-directory coverage_html
+ > /dev/null 2>&1
+ COMMAND ${CMAKE_COMMAND} -E echo ""
+ COMMAND ${CMAKE_COMMAND} -E echo "HTML report: ${CMAKE_BINARY_DIR}/coverage_html/index.html"
+ COMMAND ${CMAKE_COMMAND} -E echo ""
+ )
+ set(${OUTPUT_VAR} "${COMMANDS}" PARENT_SCOPE)
+ else()
+ set(${OUTPUT_VAR} "" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(create_informational_target)
+ # MESSAGE lines are passed as ARGV, last arg is COMMENT
+ list(LENGTH ARGV NUM_ARGS)
+ math(EXPR COMMENT_IDX "${NUM_ARGS} - 1")
+ list(GET ARGV ${COMMENT_IDX} COMMENT_TEXT)
+
+ # Build command list
+ set(COMMANDS
+ COMMAND ${CMAKE_COMMAND} -E echo ""
+ )
+ foreach(i RANGE 0 ${COMMENT_IDX})
+ if(NOT i EQUAL ${COMMENT_IDX})
+ list(GET ARGV ${i} LINE)
+ list(APPEND COMMANDS COMMAND ${CMAKE_COMMAND} -E echo "${LINE}")
+ endif()
+ endforeach()
+ list(APPEND COMMANDS COMMAND ${CMAKE_COMMAND} -E echo "")
+
+ add_custom_target(coverage
+ ${COMMANDS}
+ COMMENT "${COMMENT_TEXT}"
+ )
+endfunction()
+
+function(create_coverage_target)
+ if(HAVE_GCOV AND NOT DISABLE_COVERAGE)
+ get_html_coverage_commands(HTML_COVERAGE_COMMANDS)
+
+ add_custom_target(coverage
+ COMMAND ${CMAKE_CTEST_COMMAND} -D ExperimentalStart > /dev/null 2>&1
+ COMMAND ${CMAKE_CTEST_COMMAND} -D ExperimentalConfigure > /dev/null 2>&1
+ COMMAND ${CMAKE_CTEST_COMMAND} -D ExperimentalBuild > /dev/null 2>&1
+ COMMAND ${CMAKE_CTEST_COMMAND} -D ExperimentalTest > /dev/null 2>&1
+ COMMAND ${CMAKE_CTEST_COMMAND} -D ExperimentalCoverage > /dev/null 2>&1
+ COMMAND ${CMAKE_COMMAND}
+ -DPROJECT_SOURCE_DIR=${CMAKE_SOURCE_DIR}
+ -DPROJECT_BINARY_DIR=${CMAKE_BINARY_DIR}
+ -P ${CMAKE_SOURCE_DIR}/cmake/utils/PrintCoverage.cmake
+ ${HTML_COVERAGE_COMMANDS}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ DEPENDS build_tests
+ COMMENT "Running tests with coverage analysis using CTest"
+ )
+ elseif(HAVE_GCOV)
+ create_informational_target(
+ "Coverage is currently disabled"
+ "To enable coverage analysis, reconfigure with"
+ " cmake -DDISABLE_COVERAGE=OFF .."
+ "Coverage disabled"
+ )
+ message(STATUS "Coverage disabled. Use 'make coverage' for instructions to enable.")
+ else()
+ create_informational_target(
+ "Coverage analysis is not available"
+ "Install gcov to enable coverage support"
+ "Coverage not available"
+ )
+ endif()
+endfunction()
diff --git a/cmake/utils/HumanReadable.cmake b/cmake/utils/HumanReadable.cmake
new file mode 100644
index 00000000..8bc1722f
--- /dev/null
+++ b/cmake/utils/HumanReadable.cmake
@@ -0,0 +1,17 @@
+# Human-readable size conversion utilities
+
+# Convert bytes to human-readable format (GB, MB, KB)
+# Usage: format_bytes_human_readable(<bytes> <output_var>)
+function(format_bytes_human_readable bytes output_var)
+ math(EXPR size_gb "${bytes} / 1073741824")
+ math(EXPR size_mb "${bytes} / 1048576")
+ math(EXPR size_kb "${bytes} / 1024")
+
+ if(size_gb GREATER 0)
+ set(${output_var} "${size_gb} GB" PARENT_SCOPE)
+ elseif(size_mb GREATER 0)
+ set(${output_var} "${size_mb} MB" PARENT_SCOPE)
+ else()
+ set(${output_var} "${size_kb} KB" PARENT_SCOPE)
+ endif()
+endfunction()
diff --git a/cmake/utils/ParseCoverage.cmake b/cmake/utils/ParseCoverage.cmake
new file mode 100644
index 00000000..0bbed0de
--- /dev/null
+++ b/cmake/utils/ParseCoverage.cmake
@@ -0,0 +1,120 @@
+# Parse CTest Coverage.xml file and extract structured coverage data.
+#
+# Sets in PARENT_SCOPE:
+# LOC_TESTED, LOC_UNTESTED - Overall metrics
+# FILE_COVERAGE_LIST - "path|name|tested|untested|total|percent" entries
+# COVERED_FILES - Absolute paths of covered files
+
+# Regex building blocks
+set(R_NUMBER "[0-9]+")
+set(R_NON_TAG "[^<]*")
+set(R_NON_BRACKET "[^>]*")
+set(R_NON_QUOTE "[^\"]+")
+
+# XML element patterns
+set(R_LOC_TESTED_ELEM "<LOCTested>(${R_NUMBER})</LOCTested>")
+set(R_LOC_TESTED_SKIP "<LOCTested>${R_NUMBER}</LOCTested>")
+set(R_LOC_UNTESTED_ELEM "<LOCUntested>(${R_NUMBER})</LOCUntested>")
+set(R_LOC_UNTESTED_CAP "<LOCUnTested>(${R_NUMBER})</LOCUnTested>")
+
+# Regex patterns for XML parsing
+set(REGEX_LOC_TESTED "</File>${R_NON_TAG}${R_LOC_TESTED_ELEM}")
+set(REGEX_LOC_UNTESTED "</File>${R_NON_TAG}${R_LOC_TESTED_SKIP}${R_NON_TAG}${R_LOC_UNTESTED_ELEM}")
+set(REGEX_FILE_ENTRY "<File${R_NON_BRACKET}Name=\"(${R_NON_QUOTE})\"${R_NON_BRACKET}>${R_NON_TAG}${R_LOC_TESTED_ELEM}${R_NON_TAG}${R_LOC_UNTESTED_CAP}")
+
+# Regex patterns for filtering
+set(REGEX_TEST_FILE "test")
+set(REGEX_TEST_DIR "/tests/")
+set(REGEX_PROTOBUF_C "\\.pb-c\\.c$")
+set(REGEX_PROTOBUF_ALT "\\.pb\\.c$")
+set(REGEX_DOTSLASH_SRC "^\\.\\/src\\/")
+
+function(should_skip_file FILE_PATH OUTPUT_VAR)
+ if(FILE_PATH MATCHES "${REGEX_TEST_FILE}" OR FILE_PATH MATCHES "${REGEX_TEST_DIR}" OR
+ FILE_PATH MATCHES "${REGEX_PROTOBUF_C}" OR FILE_PATH MATCHES "${REGEX_PROTOBUF_ALT}")
+ set(${OUTPUT_VAR} TRUE PARENT_SCOPE)
+ else()
+ set(${OUTPUT_VAR} FALSE PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(normalize_coverage_path FILE_PATH PROJECT_SOURCE_DIR OUTPUT_VAR)
+ if(NOT IS_ABSOLUTE "${FILE_PATH}")
+ string(REGEX REPLACE "${REGEX_DOTSLASH_SRC}" "src/" FILE_PATH "${FILE_PATH}")
+ get_filename_component(FILE_PATH "${PROJECT_SOURCE_DIR}/${FILE_PATH}" ABSOLUTE)
+ endif()
+ set(${OUTPUT_VAR} "${FILE_PATH}" PARENT_SCOPE)
+endfunction()
+
+function(extract_xml_attribute XML_STRING ATTRIBUTE OUTPUT_VAR)
+ string(REGEX MATCH "${ATTRIBUTE}=\"([^\"]+)\"" _ "${XML_STRING}")
+ set(${OUTPUT_VAR} "${CMAKE_MATCH_1}" PARENT_SCOPE)
+endfunction()
+
+function(extract_xml_element XML_STRING ELEMENT OUTPUT_VAR)
+ string(REGEX MATCH "<${ELEMENT}>([^<]+)</${ELEMENT}>" _ "${XML_STRING}")
+ set(${OUTPUT_VAR} "${CMAKE_MATCH_1}" PARENT_SCOPE)
+endfunction()
+
+function(build_coverage_entry PATH NAME TESTED UNTESTED OUTPUT_VAR)
+ math(EXPR TOTAL "${TESTED} + ${UNTESTED}")
+ if(NOT TOTAL GREATER 0)
+ set(${OUTPUT_VAR} "" PARENT_SCOPE)
+ return()
+ endif()
+ math(EXPR PERCENT "(${TESTED} * 100) / ${TOTAL}")
+ set(${OUTPUT_VAR} "${PATH}|${NAME}|${TESTED}|${UNTESTED}|${TOTAL}|${PERCENT}" PARENT_SCOPE)
+endfunction()
+
+function(parse_coverage_xml COVERAGE_FILE PROJECT_SOURCE_DIR)
+ if(NOT EXISTS "${COVERAGE_FILE}")
+ return()
+ endif()
+
+ file(READ "${COVERAGE_FILE}" COVERAGE_XML)
+
+ string(REGEX MATCH "${REGEX_LOC_TESTED}" _ "${COVERAGE_XML}")
+ set(TESTED "${CMAKE_MATCH_1}")
+
+ string(REGEX MATCH "${REGEX_LOC_UNTESTED}" _ "${COVERAGE_XML}")
+ set(UNTESTED "${CMAKE_MATCH_1}")
+
+ if(NOT TESTED OR NOT UNTESTED)
+ return()
+ endif()
+
+ string(REGEX MATCHALL "${REGEX_FILE_ENTRY}" FILE_MATCHES "${COVERAGE_XML}")
+
+ set(COVERED_LIST "")
+ set(COVERAGE_DATA "")
+
+ foreach(MATCH ${FILE_MATCHES})
+ extract_xml_attribute("${MATCH}" "FullPath" PATH)
+ extract_xml_attribute("${MATCH}" "Name" NAME)
+
+ should_skip_file("${PATH}" SKIP)
+ if(SKIP)
+ continue()
+ endif()
+
+ normalize_coverage_path("${PATH}" "${PROJECT_SOURCE_DIR}" ABS_PATH)
+ list(APPEND COVERED_LIST "${ABS_PATH}")
+
+ extract_xml_element("${MATCH}" "LOCTested" TESTED_LINES)
+ extract_xml_element("${MATCH}" "LOCUnTested" UNTESTED_LINES)
+
+ if(NOT TESTED_LINES OR NOT UNTESTED_LINES)
+ continue()
+ endif()
+
+ build_coverage_entry("${PATH}" "${NAME}" "${TESTED_LINES}" "${UNTESTED_LINES}" ENTRY)
+ if(ENTRY)
+ list(APPEND COVERAGE_DATA "${ENTRY}")
+ endif()
+ endforeach()
+
+ set(LOC_TESTED "${TESTED}" PARENT_SCOPE)
+ set(LOC_UNTESTED "${UNTESTED}" PARENT_SCOPE)
+ set(FILE_COVERAGE_LIST "${COVERAGE_DATA}" PARENT_SCOPE)
+ set(COVERED_FILES "${COVERED_LIST}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/utils/PrintCoverage.cmake b/cmake/utils/PrintCoverage.cmake
new file mode 100644
index 00000000..4b75fa09
--- /dev/null
+++ b/cmake/utils/PrintCoverage.cmake
@@ -0,0 +1,333 @@
+# Script to parse and display coverage results from CTest
+#
+# This script is invoked by the 'make coverage' target and parses the CTest
+# Coverage.xml file to generate a formatted coverage report grouped by component.
+#
+# This script is run with cmake -P, so CMAKE_SOURCE_DIR won't be set correctly.
+# Use PROJECT_SOURCE_DIR and PROJECT_BINARY_DIR passed as -D arguments.
+
+if(NOT DEFINED PROJECT_SOURCE_DIR)
+ message(FATAL_ERROR "PROJECT_SOURCE_DIR must be defined")
+endif()
+if(NOT DEFINED PROJECT_BINARY_DIR)
+ message(FATAL_ERROR "PROJECT_BINARY_DIR must be defined")
+endif()
+
+# Include coverage parsing functions
+include(${CMAKE_CURRENT_LIST_DIR}/ParseCoverage.cmake)
+
+# Create padding strings (CMake 2.8 compatible)
+function(make_padding LENGTH OUTPUT_VAR)
+ set(RESULT "")
+ if(LENGTH GREATER 0)
+ foreach(i RANGE 1 ${LENGTH})
+ set(RESULT "${RESULT} ")
+ endforeach()
+ endif()
+ set(${OUTPUT_VAR} "${RESULT}" PARENT_SCOPE)
+endfunction()
+
+# Format a number with padding for right alignment
+function(format_number VALUE WIDTH OUTPUT_VAR)
+ string(LENGTH "${VALUE}" VALUE_LEN)
+ math(EXPR PAD_LEN "${WIDTH} - ${VALUE_LEN}")
+ make_padding(${PAD_LEN} PADDING)
+ set(${OUTPUT_VAR} "${PADDING}${VALUE}" PARENT_SCOPE)
+endfunction()
+
+# Format a complete coverage row with consistent alignment
+function(format_coverage_row LABEL TESTED TESTED_FC UNTESTED UNTESTED_FC TOTAL PERCENT OUTPUT_VAR)
+ string(LENGTH "${LABEL}" LABEL_LEN)
+ math(EXPR LABEL_PAD "28 - ${LABEL_LEN}")
+ make_padding(${LABEL_PAD} LP)
+
+ format_number(${TESTED} 6 TS)
+ format_number(${TESTED_FC} 3 TFC)
+ format_number(${UNTESTED} 8 US)
+ format_number(${UNTESTED_FC} 3 UFC)
+ format_number(${TOTAL} 5 TT)
+ format_number(${PERCENT} 3 PC)
+ set(${OUTPUT_VAR} " ${LABEL}${LP}${TS}[${TFC}] ${US}[${UFC}] ${TT} ${PC}%" PARENT_SCOPE)
+endfunction()
+
+# Format the header row to align with data columns
+function(format_coverage_header OUTPUT_VAR)
+ set(HEADER " Component Tested Untested Total %")
+ set(${OUTPUT_VAR} "${HEADER}" PARENT_SCOPE)
+endfunction()
+
+# Calculate metrics from entry list (pipe-delimited: path|name|tested|untested|total|percent)
+function(calculate_metrics ENTRIES OUT_TESTED OUT_UNTESTED OUT_TESTED_FC OUT_UNTESTED_FC)
+ set(TESTED 0)
+ set(UNTESTED 0)
+ set(TESTED_FC 0)
+ set(UNTESTED_FC 0)
+
+ foreach(ENTRY ${ENTRIES})
+ string(REPLACE "|" ";" PARTS "${ENTRY}")
+ list(GET PARTS 2 ENTRY_TESTED)
+ list(GET PARTS 3 ENTRY_UNTESTED)
+
+ math(EXPR TESTED "${TESTED} + ${ENTRY_TESTED}")
+ math(EXPR UNTESTED "${UNTESTED} + ${ENTRY_UNTESTED}")
+
+ if(ENTRY_TESTED EQUAL 0)
+ math(EXPR UNTESTED_FC "${UNTESTED_FC} + 1")
+ else()
+ math(EXPR TESTED_FC "${TESTED_FC} + 1")
+ endif()
+ endforeach()
+
+ set(${OUT_TESTED} "${TESTED}" PARENT_SCOPE)
+ set(${OUT_UNTESTED} "${UNTESTED}" PARENT_SCOPE)
+ set(${OUT_TESTED_FC} "${TESTED_FC}" PARENT_SCOPE)
+ set(${OUT_UNTESTED_FC} "${UNTESTED_FC}" PARENT_SCOPE)
+endfunction()
+
+# Discover components and sub-components from source tree
+function(discover_components PROJECT_SOURCE_DIR OUT_COMPONENTS OUT_COMP_SUBCOMPS)
+ file(GLOB COMPONENT_DIRS "${PROJECT_SOURCE_DIR}/src/*")
+ set(COMPONENTS "")
+ set(SKIP_DIRS "include;doc;tests")
+
+ foreach(DIR ${COMPONENT_DIRS})
+ if(IS_DIRECTORY ${DIR})
+ get_filename_component(COMP_NAME ${DIR} NAME)
+ list(FIND SKIP_DIRS ${COMP_NAME} SKIP_IDX)
+ if(SKIP_IDX EQUAL -1)
+ list(APPEND COMPONENTS ${COMP_NAME})
+
+ file(GLOB SUBCOMP_DIRS "${DIR}/*")
+ set(COMP_${COMP_NAME}_SUBCOMPS "")
+ foreach(SUBDIR ${SUBCOMP_DIRS})
+ if(IS_DIRECTORY ${SUBDIR})
+ get_filename_component(SUBCOMP_NAME ${SUBDIR} NAME)
+ list(FIND SKIP_DIRS ${SUBCOMP_NAME} SKIP_IDX2)
+ if(SKIP_IDX2 EQUAL -1)
+ list(APPEND COMP_${COMP_NAME}_SUBCOMPS ${SUBCOMP_NAME})
+ set(COMP_${COMP_NAME}_${SUBCOMP_NAME} "")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ endif()
+ endforeach()
+
+ set(${OUT_COMPONENTS} "${COMPONENTS}" PARENT_SCOPE)
+ foreach(COMP ${COMPONENTS})
+ set(COMP_${COMP}_SUBCOMPS "${COMP_${COMP}_SUBCOMPS}" PARENT_SCOPE)
+ endforeach()
+endfunction()
+
+# Print a coverage line
+function(print_coverage_line LABEL TESTED TESTED_FC UNTESTED UNTESTED_FC TOTAL PERCENT)
+ format_coverage_row("${LABEL}" ${TESTED} ${TESTED_FC} ${UNTESTED} ${UNTESTED_FC} ${TOTAL} ${PERCENT} ROW)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${ROW}")
+endfunction()
+
+# Locate the coverage file
+if(NOT EXISTS "${CMAKE_BINARY_DIR}/Testing/TAG")
+ message(WARNING "Testing/TAG file not found. Coverage may not have run successfully.")
+ return()
+endif()
+
+file(STRINGS "${CMAKE_BINARY_DIR}/Testing/TAG" TAG_CONTENTS LIMIT_COUNT 1)
+string(STRIP "${TAG_CONTENTS}" TEST_DIR)
+set(COVERAGE_FILE "${CMAKE_BINARY_DIR}/Testing/${TEST_DIR}/Coverage.xml")
+
+if(NOT EXISTS "${COVERAGE_FILE}")
+ message(WARNING "Coverage file not found: ${COVERAGE_FILE}")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E echo "Note: Coverage results should be in Testing/${TEST_DIR}/")
+ return()
+endif()
+
+# Parse the coverage XML file
+parse_coverage_xml("${COVERAGE_FILE}" "${PROJECT_SOURCE_DIR}")
+
+if(NOT LOC_TESTED OR NOT LOC_UNTESTED)
+ message(WARNING "Could not parse coverage metrics")
+ return()
+endif()
+
+math(EXPR TOTAL_LOC "${LOC_TESTED} + ${LOC_UNTESTED}")
+if(NOT TOTAL_LOC GREATER 0)
+ message(WARNING "No coverage data found")
+ return()
+endif()
+
+math(EXPR COVERAGE_PERCENT "(${LOC_TESTED} * 100) / ${TOTAL_LOC}")
+
+# Discover and group files by component
+if(NOT FILE_COVERAGE_LIST)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E echo "No coverage data for source files (excluding tests)")
+ return()
+endif()
+
+# Discover components from source tree (only src/ directory)
+discover_components("${PROJECT_SOURCE_DIR}" COMPONENTS COMP_SUBCOMPS)
+
+# Find all source files and identify completely untested ones
+file(GLOB_RECURSE ALL_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/*.c")
+set(UNCOVERED_FILES "")
+foreach(SRC_FILE ${ALL_SOURCE_FILES})
+ if(SRC_FILE MATCHES "test" OR SRC_FILE MATCHES "/tests/")
+ continue()
+ endif()
+
+ list(FIND COVERED_FILES "${SRC_FILE}" FILE_IDX)
+ if(FILE_IDX EQUAL -1)
+ file(STRINGS "${SRC_FILE}" FILE_LINES)
+ list(LENGTH FILE_LINES LINE_COUNT)
+ if(LINE_COUNT GREATER 0)
+ get_filename_component(FILE_NAME "${SRC_FILE}" NAME)
+ list(APPEND UNCOVERED_FILES "${SRC_FILE}|${FILE_NAME}|0|${LINE_COUNT}|${LINE_COUNT}|0")
+ endif()
+ endif()
+endforeach()
+
+# Combine covered and uncovered files
+set(ALL_FILES ${FILE_COVERAGE_LIST})
+if(UNCOVERED_FILES)
+ list(APPEND ALL_FILES ${UNCOVERED_FILES})
+endif()
+
+# Group files into components and sub-components
+foreach(ENTRY ${ALL_FILES})
+ string(REPLACE "|" ";" ENTRY_PARTS "${ENTRY}")
+ list(GET ENTRY_PARTS 0 F_PATH)
+
+ # Normalize path and extract relative path from src/
+ # Remove leading ./ if present, then ensure path starts with src/
+ string(REGEX REPLACE "^\\./" "" REL_PATH "${F_PATH}")
+ string(REGEX REPLACE "^.*/src/" "src/" REL_PATH "${REL_PATH}")
+
+ # Tokenize on /: ["src", "component", "subcomponent", "file.c"]
+ string(REPLACE "/" ";" PATH_TOKENS "${REL_PATH}")
+ list(LENGTH PATH_TOKENS TOKEN_COUNT)
+
+ if(TOKEN_COUNT EQUAL 3)
+ # src/component/file.c - add to component main
+ list(GET PATH_TOKENS 1 FILE_COMP)
+ list(FIND COMPONENTS ${FILE_COMP} COMP_IDX)
+ if(COMP_IDX GREATER -1)
+ list(APPEND COMP_${FILE_COMP} "${ENTRY}")
+ list(APPEND COMP_${FILE_COMP}_main "${ENTRY}")
+ endif()
+ elseif(TOKEN_COUNT GREATER 3)
+ # src/component/subdir/.../file.c - check if subdir is a known subcomponent
+ list(GET PATH_TOKENS 1 FILE_COMP)
+ list(GET PATH_TOKENS 2 FILE_SUBCOMP)
+
+ list(FIND COMPONENTS ${FILE_COMP} COMP_IDX)
+ if(COMP_IDX GREATER -1)
+ list(APPEND COMP_${FILE_COMP} "${ENTRY}")
+
+ # Check if subdir is a recognized subcomponent
+ list(FIND COMP_${FILE_COMP}_SUBCOMPS ${FILE_SUBCOMP} SUBCOMP_IDX)
+ if(SUBCOMP_IDX GREATER -1)
+ list(APPEND COMP_${FILE_COMP}_${FILE_SUBCOMP} "${ENTRY}")
+ else()
+ # Subdir not recognized, treat as main
+ list(APPEND COMP_${FILE_COMP}_main "${ENTRY}")
+ endif()
+ endif()
+ endif()
+endforeach()
+
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "")
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "=============================================================================")
+format_coverage_header(HEADER)
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${HEADER}")
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "=============================================================================")
+
+# Process and display each component
+foreach(COMP_NAME ${COMPONENTS})
+ set(COMP_LIST ${COMP_${COMP_NAME}})
+
+ if(NOT COMP_LIST)
+ continue()
+ endif()
+
+ calculate_metrics("${COMP_LIST}" COMP_TESTED COMP_UNTESTED COMP_TESTED_FILE_COUNT COMP_UNTESTED_FILE_COUNT)
+ math(EXPR COMP_TOTAL "${COMP_TESTED} + ${COMP_UNTESTED}")
+ if(COMP_TOTAL GREATER 0)
+ math(EXPR COMP_PERCENT "(${COMP_TESTED} * 100) / ${COMP_TOTAL}")
+ else()
+ set(COMP_PERCENT 0)
+ endif()
+
+ print_coverage_line("${COMP_NAME}" ${COMP_TESTED} ${COMP_TESTED_FILE_COUNT} ${COMP_UNTESTED} ${COMP_UNTESTED_FILE_COUNT} ${COMP_TOTAL} ${COMP_PERCENT})
+
+ # Display "main" sub-component first if it has files
+ set(MAIN_LIST ${COMP_${COMP_NAME}_main})
+ if(MAIN_LIST)
+ calculate_metrics("${MAIN_LIST}" MAIN_TESTED MAIN_UNTESTED MAIN_TESTED_FILE_COUNT MAIN_UNTESTED_FILE_COUNT)
+ math(EXPR MAIN_TOTAL "${MAIN_TESTED} + ${MAIN_UNTESTED}")
+ if(MAIN_TOTAL GREATER 0)
+ math(EXPR MAIN_PERCENT "(${MAIN_TESTED} * 100) / ${MAIN_TOTAL}")
+ else()
+ set(MAIN_PERCENT 0)
+ endif()
+
+ print_coverage_line(" ." ${MAIN_TESTED} ${MAIN_TESTED_FILE_COUNT} ${MAIN_UNTESTED} ${MAIN_UNTESTED_FILE_COUNT} ${MAIN_TOTAL} ${MAIN_PERCENT})
+ endif()
+
+ # Display sub-components
+ foreach(SUBCOMP ${COMP_${COMP_NAME}_SUBCOMPS})
+ set(SUBCOMP_LIST ${COMP_${COMP_NAME}_${SUBCOMP}})
+ if(NOT SUBCOMP_LIST)
+ continue()
+ endif()
+
+ calculate_metrics("${SUBCOMP_LIST}" SUBCOMP_TESTED SUBCOMP_UNTESTED SUBCOMP_TESTED_FILE_COUNT SUBCOMP_UNTESTED_FILE_COUNT)
+ math(EXPR SUBCOMP_TOTAL "${SUBCOMP_TESTED} + ${SUBCOMP_UNTESTED}")
+ if(SUBCOMP_TOTAL GREATER 0)
+ math(EXPR SUBCOMP_PERCENT "(${SUBCOMP_TESTED} * 100) / ${SUBCOMP_TOTAL}")
+ else()
+ set(SUBCOMP_PERCENT 0)
+ endif()
+
+ print_coverage_line(" ${SUBCOMP}" ${SUBCOMP_TESTED} ${SUBCOMP_TESTED_FILE_COUNT} ${SUBCOMP_UNTESTED} ${SUBCOMP_UNTESTED_FILE_COUNT} ${SUBCOMP_TOTAL} ${SUBCOMP_PERCENT})
+ endforeach()
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E echo "")
+endforeach()
+
+# Calculate overall coverage
+set(TOTAL_TESTED ${LOC_TESTED})
+set(TOTAL_UNTESTED ${LOC_UNTESTED})
+
+# Count file coverage for totals
+set(TOTAL_TESTED_FILE_COUNT 0)
+set(TOTAL_UNTESTED_FILE_COUNT 0)
+
+foreach(ENTRY ${FILE_COVERAGE_LIST})
+ string(REPLACE "|" ";" PARTS "${ENTRY}")
+ list(GET PARTS 2 ENTRY_TESTED)
+ if(ENTRY_TESTED EQUAL 0)
+ math(EXPR TOTAL_UNTESTED_FILE_COUNT "${TOTAL_UNTESTED_FILE_COUNT} + 1")
+ else()
+ math(EXPR TOTAL_TESTED_FILE_COUNT "${TOTAL_TESTED_FILE_COUNT} + 1")
+ endif()
+endforeach()
+
+# Add untested file counts
+foreach(ENTRY ${UNCOVERED_FILES})
+ string(REPLACE "|" ";" PARTS "${ENTRY}")
+ list(GET PARTS 3 ENTRY_UNTESTED)
+ math(EXPR TOTAL_UNTESTED "${TOTAL_UNTESTED} + ${ENTRY_UNTESTED}")
+ math(EXPR TOTAL_UNTESTED_FILE_COUNT "${TOTAL_UNTESTED_FILE_COUNT} + 1")
+endforeach()
+
+math(EXPR TOTAL_ALL_LOC "${TOTAL_TESTED} + ${TOTAL_UNTESTED}")
+if(TOTAL_ALL_LOC GREATER 0)
+ math(EXPR OVERALL_COVERAGE_PERCENT "(${TOTAL_TESTED} * 100) / ${TOTAL_ALL_LOC}")
+else()
+ set(OVERALL_COVERAGE_PERCENT 0)
+endif()
+
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "=============================================================================")
+print_coverage_line("Total" ${TOTAL_TESTED} ${TOTAL_TESTED_FILE_COUNT} ${TOTAL_UNTESTED} ${TOTAL_UNTESTED_FILE_COUNT} ${TOTAL_ALL_LOC} ${OVERALL_COVERAGE_PERCENT})
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "=============================================================================")
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "")
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo "Detailed XML report: ${COVERAGE_FILE}")
diff --git a/cmake/utils/TestUtils.cmake b/cmake/utils/TestUtils.cmake
new file mode 100644
index 00000000..e602690c
--- /dev/null
+++ b/cmake/utils/TestUtils.cmake
@@ -0,0 +1,38 @@
+# Compute test name prefix from directory structure
+function(compute_test_prefix)
+ file(RELATIVE_PATH _prefix "${CMAKE_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}")
+ string(REGEX REPLACE "/tests$" "" _prefix "${_prefix}")
+ set(TEST_PREFIX "${_prefix}" PARENT_SCOPE)
+endfunction()
+
+# Register tests from a test executable with the test framework
+# Usage: ouroboros_register_tests(TARGET <target> TESTS <test_list> [ENVIRONMENT <env>])
+# The TESTS argument should be the test list variable created by create_test_sourcelist
+function(ouroboros_register_tests)
+ cmake_parse_arguments(PARSE_ARGV 0 ARG "" "TARGET;ENVIRONMENT" "TESTS")
+
+ if(NOT ARG_TARGET)
+ message(FATAL_ERROR "ouroboros_register_tests: TARGET required")
+ endif()
+
+ if(NOT ARG_TESTS)
+ message(FATAL_ERROR "ouroboros_register_tests: TESTS required")
+ endif()
+
+ # First entry is the test driver, skip it
+ set(_tests ${ARG_TESTS})
+ list(POP_FRONT _tests)
+
+ foreach (test_src ${_tests})
+ get_filename_component(test_name ${test_src} NAME_WE)
+ add_test(${TEST_PREFIX}/${test_name}
+ ${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET} ${test_name})
+ # All Ouroboros tests support skip return code
+ set_property(TEST ${TEST_PREFIX}/${test_name} PROPERTY SKIP_RETURN_CODE 1)
+ # Optional environment variables
+ if(ARG_ENVIRONMENT)
+ set_property(TEST ${TEST_PREFIX}/${test_name}
+ PROPERTY ENVIRONMENT "${ARG_ENVIRONMENT}")
+ endif()
+ endforeach ()
+endfunction()
diff --git a/cmake/version.cmake b/cmake/version.cmake
new file mode 100644
index 00000000..00183d34
--- /dev/null
+++ b/cmake/version.cmake
@@ -0,0 +1,5 @@
+set(PACKAGE_VERSION_MAJOR 0)
+set(PACKAGE_VERSION_MINOR 22)
+set(PACKAGE_VERSION_PATCH 0)
+set(PACKAGE_VERSION
+ "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}")
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
deleted file mode 100644
index 5cf30050..00000000
--- a/doc/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(man)
diff --git a/doc/man/ouroboros.8 b/doc/man/ouroboros.8
index df328fcc..759b1433 100644
--- a/doc/man/ouroboros.8
+++ b/doc/man/ouroboros.8
@@ -127,7 +127,9 @@ creates an IPCP process of type \fItype\fR in the system with name
.PP
\fBeth-dix\fR - create an IPCP that attaches to Ethernet using DIX frames.
.PP
-\fBudp\fR - create an IPCP that attaches to a UDP socket.
+\fBudp4\fR - create an IPCP that attaches to a UDP/IPv4 socket.
+.PP
+\fBudp6\fR - create an IPCP that attaches to a UDP/IPv6 socket.
.PP
\fBunicast\fR - create a unicast IPCP that uses lower level layers.
.PP
@@ -190,17 +192,17 @@ default: SHA3_256.
.RE
.PP
-\fBudp\fR
+\fBudp4\fR
.RS 4
.PP
-ip \fIip\fR specifies the local IP address to bind to
+ip \fIip\fR specifies the local IPv4 address to bind to
.PP
[dns \fIdns\fR] specifies an optional DDNS server that will be used for
the directory.
.PP
[port \fIport\fR] specifies a UDP port that is used for sending and
-receiving ouroboros traffic. This must be the same for the entire UDP
-layer. Parallel UDP layers should use different ports. This UDP port
+receiving ouroboros traffic. This must be the same for the entire UDP4
+layer. Parallel UDP4 layers should use different ports. This UDP port
needs to be forwarded if the server is behind a NAT and wants to
receive incoming requests.
.br
@@ -208,6 +210,22 @@ default: 3435
.RE
.PP
+\fBudp6\fR
+.RS 4
+.PP
+ip \fIip\fR specifies the local IPv6 address to bind to
+.PP
+[dns \fIdns\fR] specifies an optional DDNS server that will be used for
+the directory.
+.PP
+[port \fIport\fR] specifies a UDP port that is used for sending and
+receiving ouroboros traffic. This must be the same for the entire UDP6
+layer. Parallel UDP6 layers should use different ports.
+.br
+default: 3435
+.RE
+
+.PP
\fBunicast\fR
.RS 4
.PP
@@ -370,12 +388,23 @@ not accept future flow allocation requests for \fIname\fR.
.SH IRM NAME COMMANDS
.PP
-\fBirm name create \fIname\fR \fIlb\fR policy
+\fBirm name create \fIname\fR lb \fIpolicy\fR
+[sencpath \fI/path/to/server/enc.conf\fR]
+[scrtpath \fI/path/to/server/crt.pem\fR]
+[skeypath \fI/path/to/server/key.pem\fR]
+
+[cencpath \fI/path/to/client/enc.conf\fR]
+[ccrtpath \fI/path/to/client/crt.pem\fR]
+[ckeypath \fI/path/to/client/key.pem\fR]
.RS 4
-Create a name \fIname\fR with a load-balancing policy
+Create a name \fIname\fR with a load-balancing policy and security credentials
.br
\fIpolicy\fR: round-robin, spillover
.br
+\fI/path/to/enc.conf\fR: The path to the server and client encryption configuration.
+\fI/path/to/pem\fR: The path to the server and client certificates and
+private keys, in pem format.
+.br
.RE
.PP
diff --git a/enc.conf.in b/enc.conf.in
new file mode 100644
index 00000000..64502fbb
--- /dev/null
+++ b/enc.conf.in
@@ -0,0 +1,150 @@
+### Example Ouroboros encryption configuration file
+#
+# This file specifies the key exchange (KEX) algorithm and cipher to use
+# for encrypted flows.
+#
+# File Locations:
+# ---------------
+#
+# This file should be placed at one of:
+# @OUROBOROS_CONFIG_DIR@/security/server/<name>/enc.conf (server-side config)
+# @OUROBOROS_CONFIG_DIR@/security/client/<name>/enc.conf (client-side config)
+#
+# Where <name> is the service name registered with 'irm name create'.
+#
+# You can override the default paths using:
+# irm name create <name> sencpath <server-enc-path> cencpath <client-enc-path>
+#
+# Configuration Options:
+# ----------------------
+#
+# kex=<algorithm> Key exchange/encapsulation algorithm
+# cipher=<cipher> Symmetric cipher algorithm
+# kdf=<hash> Key derivation function hash algorithm
+# kem_mode=<mode> KEM encapsulation mode (server or client)
+# none Explicitly disable encryption
+#
+# Supported KEX algorithms (kex=):
+# --------------------------------
+#
+# ECDH Curves:
+# prime256v1 NIST P-256 (default)
+# secp384r1 NIST P-384
+# secp521r1 NIST P-521
+# X25519 Curve25519
+# X448 Curve448
+#
+# Finite Field Diffie-Hellman (RFC 7919):
+# ffdhe2048 2048-bit MODP Group
+# ffdhe3072 3072-bit MODP Group
+# ffdhe4096 4096-bit MODP Group
+#
+# ML-KEM (FIPS 203):
+# ML-KEM-512 CRYSTALS-Kyber-512
+# ML-KEM-768 CRYSTALS-Kyber-768
+# ML-KEM-1024 CRYSTALS-Kyber-1024
+#
+# Hybrid KEMs:
+# X25519MLKEM768 X25519 + ML-KEM-768
+# X448MLKEM1024 X448 + ML-KEM-1024
+#
+# Supported cipher algorithms (cipher=):
+# --------------------------------------
+#
+# Authenticated encryption:
+# aes-128-gcm AES-128 in GCM mode
+# aes-192-gcm AES-192 in GCM mode
+# aes-256-gcm AES-256 in GCM mode (default)
+# chacha20-poly1305 ChaCha20-Poly1305
+#
+# Stream ciphers (not recommended):
+# aes-128-ctr AES-128 in CTR mode
+# aes-192-ctr AES-192 in CTR mode
+# aes-256-ctr AES-256 in CTR mode
+#
+# Key Derivation Functions (kdf=):
+# ---------------------------------
+#
+# Hash algorithms for key derivation in KEX operations:
+#
+# sha256 SHA-256 (default)
+# sha384 SHA-384
+# sha512 SHA-512
+# sha3-256 SHA3-256
+# sha3-384 SHA3-384
+# sha3-512 SHA3-512
+# blake2b512 BLAKE2b-512 (requires OpenSSL 1.1.0+)
+# blake2s256 BLAKE2s-256 (requires OpenSSL 1.1.0+)
+#
+# KEM Mode (kem_mode=):
+# ---------------------
+#
+# For KEM algorithms (ML-KEM-* and hybrid KEMs), specify which side
+# performs the encapsulation operation:
+#
+# server Server encapsulates to client's ephemeral public key (default, matches TLS 1.3)
+# - Client generates ephemeral keypair, sends public key in request
+# - Server encapsulates and sends ciphertext in response
+# - Client decapsulates with ephemeral private key
+# - Standard approach, no pre-shared keys needed
+#
+# client Client encapsulates to server's static public key (alternative)
+# - Requires cached server public key at:
+# @OUROBOROS_CONFIG_DIR@/security/client/<service>/kex.srv.pub.[pem|raw]
+# - Client encapsulates and sends ciphertext in initial request
+# - Server decapsulates with its static private key from:
+# @OUROBOROS_CONFIG_DIR@/security/server/kex.key.pem
+# - More efficient (0 round-trip) but requires key distribution
+# and forfeits forward secrecy
+#
+# Note: Both sides must use the same kem_mode setting.
+# This option is ignored for ECDH/DH key exchange algorithms.
+#
+# Key Management for Client Mode:
+# --------------------------------
+#
+# For client encapsulation mode, you must:
+# 1. Generate server KEM keypair:
+# openssl genpkey -algorithm ML-KEM-768 \
+# -out @OUROBOROS_CONFIG_DIR@/security/server/kex.key.pem
+# 2. Extract and distribute server public key:
+# openssl pkey -in kex.key.pem -pubout -out kex.srv.pub.pem
+# 3. Cache on clients at:
+# @OUROBOROS_CONFIG_DIR@/security/client/<service-name>/kex.srv.pub.pem
+#
+# File formats:
+# - Pure ML-KEM: PEM format (.pem extension)
+# - Hybrid KEMs: Raw bytes (.raw extension)
+#
+# Examples:
+# ---------
+#
+# Default configuration (NIST P-256 ECDH + AES-256-GCM):
+kex=prime256v1
+cipher=aes-256-gcm
+kdf=sha256
+#
+# Post-quantum KEX with server encapsulation (default, like TLS 1.3):
+# kex=ML-KEM-768
+# cipher=chacha20-poly1305
+# kdf=sha256
+# kem_mode=server
+#
+# Post-quantum KEX with client encapsulation (requires key distribution):
+# kex=ML-KEM-768
+# cipher=chacha20-poly1305
+# kdf=sha256
+# kem_mode=client
+#
+# Hybrid KEX (quantum-resistant):
+# kex=X25519MLKEM768
+# cipher=aes-256-gcm
+# kdf=sha256
+#
+# High security configuration:
+# kex=secp521r1
+# cipher=aes-256-gcm
+# kdf=sha512
+#
+# Disable encryption:
+# none
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
deleted file mode 100644
index 8895c582..00000000
--- a/include/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(ouroboros)
diff --git a/include/ouroboros/CMakeLists.txt b/include/ouroboros/CMakeLists.txt
deleted file mode 100644
index 4e90bc59..00000000
--- a/include/ouroboros/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in"
- "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY)
-
-set(SOCK_BUF_SIZE 10240 CACHE STRING
- "Size of the buffer used by the UNIX sockets for local IPC")
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sockets.h.in"
- "${CMAKE_CURRENT_BINARY_DIR}/sockets.h" @ONLY)
-
-set(HEADER_FILES
- cep.h
- cdefs.h
- dev.h
- errno.h
- fccntl.h
- fqueue.h
- ipcp.h
- irm.h
- name.h
- proto.h
- qos.h
- ${CMAKE_CURRENT_BINARY_DIR}/version.h
- )
-
-install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ouroboros)
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h
index 28fe63b2..5b39fb5f 100644
--- a/include/ouroboros/crypt.h
+++ b/include/ouroboros/crypt.h
@@ -23,34 +23,346 @@
#ifndef OUROBOROS_LIB_CRYPT_H
#define OUROBOROS_LIB_CRYPT_H
-#include <ouroboros/shm_du_buff.h>
+#include <ouroboros/ssm_pk_buff.h>
#include <ouroboros/utils.h>
-#define SYMMKEYSZ 32
+#include <assert.h>
-struct crypt_info {
- uint16_t flags;
- void * ctx;
- uint8_t key[SYMMKEYSZ];
+#define IVSZ 16
+#define SYMMKEYSZ 32
+#define MAX_HASH_SIZE 64 /* SHA-512/BLAKE2b max */
+#define KEX_ALGO_BUFSZ 32
+#define KEX_CIPHER_BUFSZ 32
+#define MSGBUFSZ 2048
+
+/*
+ * On OSX the OpenSSL NIDs are automatically loaded with evp.h.
+ * Some have a different spelling. This header avoids the double definitions.
+ */
+
+ #define NID_undef 0
+
+/* Cipher NIDs (match OpenSSL values) */
+#define NID_aes_128_gcm 895
+#define NID_aes_192_gcm 898
+#define NID_aes_256_gcm 901
+#define NID_aes_128_ctr 904
+#define NID_aes_192_ctr 905
+#define NID_aes_256_ctr 906
+#define NID_chacha20_poly1305 1018
+
+ #if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+/* KEX algorithm NIDs (match OpenSSL values) */
+#define NID_X9_62_prime256v1 415
+#define NID_secp384r1 715
+#define NID_secp521r1 716
+#define NID_X25519 1034
+#define NID_X448 1035
+#define NID_ffdhe2048 1126
+#define NID_ffdhe3072 1127
+#define NID_ffdhe4096 1128
+#endif /* __APPLE__ */
+#define NID_MLKEM512 1454
+#define NID_MLKEM768 1455
+#define NID_MLKEM1024 1456
+#define NID_X25519MLKEM768 2053 /* !! not in OpenSSL */
+#define NID_X448MLKEM1024 2054 /* !! not in OpenSSL */
+
+/* KDF NIDs (match OpenSSL values) */
+#define NID_hkdf 1036
+#define NID_sha256 672
+#define NID_sha384 673
+#define NID_sha512 674
+#if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+#define NID_sha3_256 1096
+#define NID_sha3_384 1097
+#define NID_sha3_512 1098
+#endif /* __APPLE__ */
+#define NID_blake2b512 1056
+#define NID_blake2s256 1057
+
+
+#define IS_KEM_ALGORITHM(algo) \
+ (strstr(algo, "ML-KEM") != NULL || strstr(algo, "MLKEM") != NULL)
+
+#define IS_HYBRID_KEM(algo) \
+ ((strstr(algo, "X25519") != NULL || strstr(algo, "X448") != NULL) && \
+ strstr(algo, "MLKEM") != NULL)
+
+#define X25519MLKEM768_PKSZ 1216 /* 32 + 1184 */
+#define X25519MLKEM768_CTSZ 1120 /* 32 + 1088 */
+#define X25519MLKEM768_SKSZ 2432 /* 32 + 2400 */
+#define X448MLKEM1024_PKSZ 1624 /* 56 + 1568 */
+#define X448MLKEM1024_SKSZ 3224 /* 56 + 3168 */
+
+#define KEM_MODE_SERVER_ENCAP 0 /* Server encapsulates (default) */
+#define KEM_MODE_CLIENT_ENCAP 1 /* Client encapsulates */
+#define IS_KEX_ALGO_SET(cfg) ((cfg)->x.nid != NID_undef)
+#define IS_KEX_CIPHER_SET(cfg) ((cfg)->c.nid != NID_undef)
+
+
+struct crypt_sk {
+ int nid;
+ uint8_t * key;
+ uint8_t rot_bit; /* Rotation bit to control epoch */
+};
+
+struct sec_config {
+ struct {
+ const char * str;
+ int nid;
+ int mode;
+ } x; /* key exchange */
+ struct {
+ const char * str;
+ int nid;
+ } k; /* kdf */
+ struct {
+ const char * str;
+ int nid;
+ } c; /* cipher */
+ struct {
+ const char * str;
+ int nid;
+ } d; /* digest */
};
-int crypt_dh_pkp_create(void ** pkp,
- uint8_t * pk);
+/* Helper macros to set sec_config fields consistently */
+#define SET_KEX_ALGO(cfg, algo_str) do { \
+ (cfg)->x.nid = kex_str_to_nid(algo_str); \
+ (cfg)->x.str = kex_nid_to_str((cfg)->x.nid); \
+ assert((cfg)->x.nid != NID_undef || (cfg)->x.str == NULL); \
+} while (0)
+
+#define SET_KEX_ALGO_NID(cfg, nid_val) do { \
+ (cfg)->x.nid = (nid_val); \
+ (cfg)->x.str = kex_nid_to_str((cfg)->x.nid); \
+ assert((cfg)->x.nid != NID_undef || (cfg)->x.str == NULL); \
+} while (0)
+
+#define SET_KEX_KEM_MODE(cfg, mode_val) do { \
+ (cfg)->x.mode = (mode_val); \
+} while (0)
+
+#define SET_KEX_KDF(cfg, kdf_str) do { \
+ (cfg)->k.nid = md_str_to_nid(kdf_str); \
+ (cfg)->k.str = md_nid_to_str((cfg)->k.nid); \
+ assert((cfg)->k.nid != NID_undef || (cfg)->k.str == NULL); \
+} while (0)
+
+#define SET_KEX_KDF_NID(cfg, nid_val) do { \
+ (cfg)->k.nid = (nid_val); \
+ (cfg)->k.str = md_nid_to_str((cfg)->k.nid); \
+ assert((cfg)->k.nid != NID_undef || (cfg)->k.str == NULL); \
+} while (0)
+
+#define SET_KEX_CIPHER(cfg, cipher_str) do { \
+ (cfg)->c.nid = crypt_str_to_nid(cipher_str); \
+ (cfg)->c.str = crypt_nid_to_str((cfg)->c.nid); \
+ assert((cfg)->c.nid != NID_undef || (cfg)->c.str == NULL); \
+} while (0)
+
+#define SET_KEX_CIPHER_NID(cfg, nid_val) do { \
+ (cfg)->c.nid = (nid_val); \
+ (cfg)->c.str = crypt_nid_to_str((cfg)->c.nid); \
+ assert((cfg)->c.nid != NID_undef || (cfg)->c.str == NULL); \
+} while (0)
+
+#define SET_KEX_DIGEST(cfg, digest_str) do { \
+ (cfg)->d.nid = md_str_to_nid(digest_str); \
+ (cfg)->d.str = md_nid_to_str((cfg)->d.nid); \
+ assert((cfg)->d.nid != NID_undef || (cfg)->d.str == NULL); \
+} while (0)
+
+#define SET_KEX_DIGEST_NID(cfg, nid_val) do { \
+ (cfg)->d.nid = (nid_val); \
+ (cfg)->d.str = md_nid_to_str((cfg)->d.nid); \
+ assert((cfg)->d.nid != NID_undef || (cfg)->d.str == NULL); \
+} while (0)
+
+#define CLEAR_KEX_ALGO(cfg) do { \
+ (cfg)->x.nid = NID_undef; \
+ (cfg)->x.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_KDF(cfg) do { \
+ (cfg)->k.nid = NID_undef; \
+ (cfg)->k.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_CIPHER(cfg) do { \
+ (cfg)->c.nid = NID_undef; \
+ (cfg)->c.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_DIGEST(cfg) do { \
+ (cfg)->d.nid = NID_undef; \
+ (cfg)->d.str = NULL; \
+} while (0)
+
+struct auth_ctx;
+struct crypt_ctx;
+
+struct auth_ctx * auth_create_ctx(void);
+
+void auth_destroy_ctx(struct auth_ctx * ctx);
+
+int auth_add_crt_to_store(struct auth_ctx * ctx,
+ void * crt);
+
+int auth_verify_crt(struct auth_ctx * ctx,
+ void * crt);
+
+int auth_sign(void * pkp,
+ int md_nid,
+ buffer_t msg,
+ buffer_t * sig);
+
+int auth_verify_sig(void * pk,
+ int md_nid,
+ buffer_t msg,
+ buffer_t sig);
+
+int load_sec_config_file(struct sec_config * cfg,
+ const char * path);
+
+int kex_pkp_create(struct sec_config * cfg,
+ void ** pkp,
+ uint8_t * pk);
+
+void kex_pkp_destroy(void * pkp);
+
+int kex_dhe_derive(struct sec_config * cfg,
+ void * pkp,
+ buffer_t pk,
+ uint8_t * s);
+
+ssize_t kex_kem_encap(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+ssize_t kex_kem_encap_raw(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int kex_kem_decap(void * pkp,
+ buffer_t ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int kex_get_algo_from_pk_der(buffer_t pk,
+ char * algo);
+
+int kex_get_algo_from_pk_raw(buffer_t pk,
+ char * algo);
+
+int kex_validate_algo(const char * algo);
+
+int kex_validate_nid(int nid);
+
+const char * kex_nid_to_str(uint16_t nid);
+
+uint16_t kex_str_to_nid(const char * algo);
+
+struct crypt_ctx * crypt_create_ctx(struct crypt_sk * sk);
+
+void crypt_destroy_ctx(struct crypt_ctx * ctx);
+
+int crypt_validate_nid(int nid);
+
+const char * crypt_nid_to_str(uint16_t nid);
+
+uint16_t crypt_str_to_nid(const char * cipher);
+
+int md_validate_nid(int nid);
+
+const char * md_nid_to_str(uint16_t nid);
+
+uint16_t md_str_to_nid(const char * kdf);
+
+ssize_t md_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out);
+
+ssize_t md_len(int md_nid);
+
+int crypt_encrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_decrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_get_ivsz(struct crypt_ctx * ctx);
+
+int crypt_get_tagsz(struct crypt_ctx * ctx);
+
+int crypt_load_crt_file(const char * path,
+ void ** crt);
+
+int crypt_load_crt_str(const char * str,
+ void ** crt);
+
+int crypt_load_crt_der(buffer_t buf,
+ void ** crt);
+
+int crypt_get_pubkey_crt(void * crt,
+ void ** pk);
+
+void crypt_free_crt(void * crt);
+
+int crypt_load_privkey_file(const char * path,
+ void ** key);
+
+int crypt_load_privkey_str(const char * str,
+ void ** key);
+
+int crypt_load_pubkey_str(const char * str,
+ void ** key);
+
+int crypt_load_pubkey_file(const char * path,
+ void ** key);
+
+int crypt_load_pubkey_file_to_der(const char * path,
+ buffer_t * buf);
+
+int crypt_load_pubkey_raw_file(const char * path,
+ buffer_t * buf);
+
+int crypt_load_privkey_raw_file(const char * path,
+ void ** key);
+
+int crypt_cmp_key(const void * key1,
+ const void * key2);
+
+void crypt_free_key(void * key);
+
+int crypt_crt_str(const void * crt,
+ char * buf);
+
+int crypt_crt_der(const void * crt,
+ buffer_t * buf);
+
+int crypt_check_crt_name(void * crt,
+ const char * name);
-void crypt_dh_pkp_destroy(void * pkp);
+int crypt_get_crt_name(void * crt,
+ char * name);
-int crypt_dh_derive(void * pkp,
- buffer_t pk,
- uint8_t * s);
+/* Secure memory allocation for sensitive data (keys, secrets) */
+int crypt_secure_malloc_init(size_t max);
-int crypt_encrypt(struct crypt_info * info,
- struct shm_du_buff * sdb);
+void crypt_secure_malloc_fini(void);
-int crypt_decrypt(struct crypt_info * info,
- struct shm_du_buff * sdb);
+void * crypt_secure_malloc(size_t size);
-int crypt_init(struct crypt_info * info);
+void crypt_secure_free(void * ptr,
+ size_t size);
-void crypt_fini(struct crypt_info * info);
+void crypt_secure_clear(void * ptr,
+ size_t size);
#endif /* OUROBOROS_LIB_CRYPT_H */
diff --git a/include/ouroboros/dev.h b/include/ouroboros/dev.h
index 6e643a2c..61464fbf 100644
--- a/include/ouroboros/dev.h
+++ b/include/ouroboros/dev.h
@@ -40,9 +40,8 @@ int flow_alloc(const char * dst_name,
int flow_accept(qosspec_t * qs,
const struct timespec * timeo);
-/* Returns flow descriptor, qs updates to supplied QoS. */
+/* Returns flow descriptor. */
int flow_join(const char * bc,
- qosspec_t * qs,
const struct timespec * timeo);
int flow_dealloc(int fd);
diff --git a/include/ouroboros/endian.h b/include/ouroboros/endian.h
index addb2ed3..6c3493d9 100644
--- a/include/ouroboros/endian.h
+++ b/include/ouroboros/endian.h
@@ -66,8 +66,8 @@
#endif
#define hton64(x) htobe64(x)
-#define hton32(x) htobe32(x)
#define ntoh64(x) betoh64(x)
+#define hton32(x) htobe32(x)
#define ntoh32(x) betoh32(x)
#define hton16(x) htobe16(x)
#define ntoh16(x) betoh16(x)
diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h
index 25e776df..6b808241 100644
--- a/include/ouroboros/errno.h
+++ b/include/ouroboros/errno.h
@@ -32,7 +32,10 @@
#define EIPCPSTATE 1004 /* Target in wrong state */
#define EFLOWDOWN 1005 /* Flow is down */
#define EFLOWPEER 1006 /* Flow is down (peer timed out) */
-#define ECRYPT 1007 /* Encryption error */
-#define ENAME 1008 /* Naming error */
+#define ENAME 1007 /* Naming error */
+#define ECRYPT 1008 /* Encryption error */
+#ifndef EAUTH /* Exists on BSD */
+#define EAUTH 1009 /* Authentication error */
+#endif
#endif /* OUROBOROS_ERRNO_H */
diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h
index e6bf8886..6b3dcde4 100644
--- a/include/ouroboros/flow.h
+++ b/include/ouroboros/flow.h
@@ -27,14 +27,16 @@
#include <sys/types.h>
- enum flow_state { /* DO NOT CHANGE ORDER! */
+#define SYMMKEYSZ 32
+
+enum flow_state { /* DO NOT CHANGE ORDER! */
FLOW_INIT = 0,
FLOW_ALLOC_PENDING,
FLOW_ACCEPT_PENDING,
FLOW_ALLOCATED,
FLOW_DEALLOC_PENDING,
FLOW_DEALLOCATED,
- FLOW_DESTROY, /* TODO: REMOVE! */
+ FLOW_DESTROY, /* TODO: REMOVE! */
FLOW_NULL
};
@@ -44,6 +46,8 @@ struct flow_info {
pid_t n_pid;
pid_t n_1_pid;
+ uid_t uid; /* 0 = privileged (GSPP), > 0 = PUP uid */
+
time_t mpl;
struct qos_spec qs;
diff --git a/include/ouroboros/hash.h b/include/ouroboros/hash.h
index 6b0087ce..c44c2c8a 100644
--- a/include/ouroboros/hash.h
+++ b/include/ouroboros/hash.h
@@ -42,7 +42,8 @@ enum hash_algo {
#define HASH_FMT32 "%02x%02x%02x%02x"
#define HASH_VAL32(hash) \
- (hash)[0], (hash)[1], (hash)[2], (hash)[3]
+ ((uint8_t *) hash)[0], ((uint8_t *) hash)[1], \
+ ((uint8_t *) hash)[2], ((uint8_t *) hash)[3]
#define HASH_FMT64 HASH_FMT32 HASH_FMT32
#define HASH_VAL64(hash64) \
@@ -52,6 +53,10 @@ enum hash_algo {
#define HASH_VAL128(hash128) \
HASH_VAL64(hash128), HASH_VAL64(hash128 + 8)
+#define HASH_FMT192 HASH_FMT128 HASH_FMT64
+#define HASH_VAL192(hash192) \
+ HASH_VAL128(hash192), HASH_VAL64(hash192 + 16)
+
#define HASH_FMT224 HASH_FMT128 HASH_FMT64 HASH_FMT32
#define HASH_VAL224(hash224) \
HASH_VAL128(hash224), HASH_VAL64(hash224 + 16), \
diff --git a/include/ouroboros/ipcp-dev.h b/include/ouroboros/ipcp-dev.h
index 378d724a..37c8064f 100644
--- a/include/ouroboros/ipcp-dev.h
+++ b/include/ouroboros/ipcp-dev.h
@@ -25,15 +25,15 @@
#include <ouroboros/ipcp.h>
#include <ouroboros/qoscube.h>
-#include <ouroboros/shm_rdrbuff.h>
+#include <ouroboros/ssm_pool.h>
#include <ouroboros/utils.h>
-int ipcp_create_r(const struct ipcp_info * info);
+int ipcp_create_r(const struct ipcp_info * info);
-int ipcp_flow_req_arr(const buffer_t * dst,
- qosspec_t qs,
- time_t mpl,
- const buffer_t * data);
+int ipcp_flow_req_arr(const buffer_t * dst,
+ qosspec_t qs,
+ time_t mpl,
+ const buffer_t * data);
int ipcp_flow_alloc_reply(int fd,
int response,
@@ -41,16 +41,18 @@ int ipcp_flow_alloc_reply(int fd,
const buffer_t * data);
int ipcp_flow_read(int fd,
- struct shm_du_buff ** sdb);
+ struct ssm_pk_buff ** spb);
int ipcp_flow_write(int fd,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb);
int np1_flow_read(int fd,
- struct shm_du_buff ** sdb);
+ struct ssm_pk_buff ** spb,
+ struct ssm_pool * pool);
int np1_flow_write(int fd,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb,
+ struct ssm_pool * pool);
int ipcp_flow_dealloc(int fd);
@@ -61,9 +63,9 @@ int ipcp_flow_get_qoscube(int fd,
size_t ipcp_flow_queued(int fd);
-int ipcp_sdb_reserve(struct shm_du_buff ** sdb,
+int ipcp_spb_reserve(struct ssm_pk_buff ** spb,
size_t len);
-void ipcp_sdb_release(struct shm_du_buff * sdb);
+void ipcp_spb_release(struct ssm_pk_buff * spb);
#endif /* OUROBOROS_LIB_IPCP_DEV_H */
diff --git a/include/ouroboros/ipcp.h b/include/ouroboros/ipcp.h
index 42c4dfa4..c397f250 100644
--- a/include/ouroboros/ipcp.h
+++ b/include/ouroboros/ipcp.h
@@ -26,20 +26,22 @@
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
+#include <netinet/in.h>
#include <sys/types.h>
#define IPCP_NAME_SIZE 255
#define LAYER_NAME_SIZE 255
#define DEV_NAME_SIZE 255
+/* TODO: Move state to ipcpd/ipcp.h, requires small change to reg/ipcp.c */
enum ipcp_state {
- IPCP_INIT = 0,
+ IPCP_NULL = 0,
+ IPCP_INIT,
IPCP_BOOT,
- IPCP_OPERATIONAL,
IPCP_BOOTSTRAPPED,
IPCP_ENROLLED,
- IPCP_SHUTDOWN,
- IPCP_NULL
+ IPCP_OPERATIONAL,
+ IPCP_SHUTDOWN
};
enum ipcp_type { /* IRMd uses order to select an IPCP for flow allocation. */
@@ -48,7 +50,8 @@ enum ipcp_type { /* IRMd uses order to select an IPCP for flow allocation. */
IPCP_BROADCAST,
IPCP_ETH_LLC,
IPCP_ETH_DIX,
- IPCP_UDP,
+ IPCP_UDP4,
+ IPCP_UDP6,
IPCP_INVALID
};
@@ -56,7 +59,7 @@ struct ipcp_info {
enum ipcp_type type;
pid_t pid;
char name[IPCP_NAME_SIZE + 1];
- enum ipcp_state state;
+ enum ipcp_state state; /* TODO: remove. */
};
/* Unicast IPCP components. */
@@ -69,13 +72,50 @@ enum pol_addr_auth {
ADDR_AUTH_INVALID
};
+enum pol_link_state {
+ LS_SIMPLE = 0,
+ LS_LFA,
+ LS_ECMP,
+ LS_INVALID
+};
+
+struct ls_config {
+ enum pol_link_state pol; /* Link state policy */
+ time_t t_recalc; /* Time to recalculate PFF (s) */
+ time_t t_update; /* Time between updates (s) */
+ time_t t_timeo; /* Link timeout (s) */
+};
+
+static const struct ls_config default_ls_config = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+};
+
enum pol_routing {
ROUTING_LINK_STATE = 0,
- ROUTING_LINK_STATE_LFA,
- ROUTING_LINK_STATE_ECMP,
ROUTING_INVALID
};
+struct routing_config {
+ enum pol_routing pol; /* Routing policy */
+ union {
+ struct ls_config ls; /* Link state config */
+ /* struct pv_config pv */ /* Path vector config */
+ };
+};
+
+static const struct routing_config default_routing_config = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+};
+
enum pol_cong_avoid {
CA_NONE = 0,
CA_MB_ECN,
@@ -83,41 +123,158 @@ enum pol_cong_avoid {
};
struct dt_config {
- uint8_t addr_size;
- uint8_t eid_size;
- uint8_t max_ttl;
- enum pol_routing routing_type;
+ struct {
+ uint8_t addr_size;
+ uint8_t eid_size;
+ uint8_t max_ttl;
+ };
+ struct routing_config routing; /* Routing policy */
+};
+
+static const struct dt_config default_dt_config = {
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+};
+
+enum pol_dir {
+ DIR_DHT = 0,
+ DIR_INVALID
+};
+
+enum pol_dir_hash {
+ DIR_HASH_SHA3_224,
+ DIR_HASH_SHA3_256,
+ DIR_HASH_SHA3_384,
+ DIR_HASH_SHA3_512,
+ DIR_HASH_INVALID
+};
+
+enum dir_dht_config_limits {
+ DHT_ALPHA_MIN = 1,
+ DHT_K_MIN = 1,
+ DHT_T_EXPIRE_MIN = 10,
+ DHT_T_REFRESH_MIN = 3,
+ DHT_T_REPLICATE_MIN = 3,
+
+ DHT_ALPHA_MAX = 10,
+ DHT_K_MAX = 20,
+ DHT_T_EXPIRE_MAX = 86400,
+ DHT_T_REFRESH_MAX = 3600,
+ DHT_T_REPLICATE_MAX = 3600,
+};
+
+struct dir_dht_config {
+ struct {
+ uint32_t alpha; /* Parallel search factor */
+ uint32_t k; /* Replication factor */
+ uint32_t t_expire; /* Expire time (s) */
+ uint32_t t_refresh; /* Refresh time (s) */
+ uint32_t t_replicate; /* Replication time (s) */
+ } params;
+ uint64_t peer; /* Initial peer address */
+};
+
+static const struct dir_dht_config default_dht_config = {
+ .params = {
+ .alpha = 3, /* Proven optimal value */
+ .k = 8, /* MDHT value */
+ .t_expire = 86400, /* Expire after 1 day */
+ .t_refresh = 900, /* MDHT value. */
+ .t_replicate = 900 /* MDHT value. */
+ }
+};
+
+/* TODO: Move hash algorithm in directory config */
+struct dir_config {
+ enum pol_dir pol;
+ union {
+ struct dir_dht_config dht;
+ };
+};
+
+static const struct dir_config default_dir_config = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
};
/* IPCP configuration */
struct uni_config {
struct dt_config dt;
+ struct dir_config dir;
enum pol_addr_auth addr_auth_type;
enum pol_cong_avoid cong_avoid;
};
+static const struct uni_config default_uni_config = {
+ .dt = {
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+ },
+ .dir = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
+ },
+ .addr_auth_type = ADDR_AUTH_FLAT_RANDOM,
+ .cong_avoid = CA_MB_ECN
+};
+
struct eth_config {
char dev[DEV_NAME_SIZE + 1];
uint16_t ethertype; /* DIX only*/
};
-struct udp_config {
- uint32_t ip_addr;
- uint32_t dns_addr;
- uint16_t port;
+struct udp4_config {
+ struct in_addr ip_addr;
+ struct in_addr dns_addr;
+ uint16_t port;
};
-/* Layers */
-enum pol_dir_hash {
- DIR_HASH_SHA3_224,
- DIR_HASH_SHA3_256,
- DIR_HASH_SHA3_384,
- DIR_HASH_SHA3_512,
- DIR_HASH_INVALID
+struct udp6_config {
+ struct in6_addr ip_addr;
+ struct in6_addr dns_addr;
+ uint16_t port;
};
+/* Layers */
struct layer_info {
char name[LAYER_NAME_SIZE + 1];
+ /* TODO: Move this to directory info ? */
enum pol_dir_hash dir_hash_algo;
};
@@ -127,9 +284,10 @@ struct ipcp_config {
enum ipcp_type type;
union {
- struct uni_config unicast;
- struct udp_config udp;
- struct eth_config eth;
+ struct uni_config unicast;
+ struct udp4_config udp4;
+ struct udp6_config udp6;
+ struct eth_config eth;
};
};
@@ -158,9 +316,16 @@ static const struct ipcp_config eth_llc_default_conf = {
}
};
-static const struct ipcp_config udp_default_conf = {
- .type = IPCP_UDP,
- .udp = {
+static const struct ipcp_config udp4_default_conf = {
+ .type = IPCP_UDP4,
+ .udp4 = {
+ .port = 3435
+ }
+};
+
+static const struct ipcp_config udp6_default_conf = {
+ .type = IPCP_UDP6,
+ .udp6 = {
.port = 3435
}
};
@@ -172,10 +337,30 @@ static const struct ipcp_config uni_default_conf = {
},
.unicast = {
.dt = {
- .addr_size = 4,
- .eid_size = 8,
- .max_ttl = 60,
- .routing_type = ROUTING_LINK_STATE
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+ },
+ .dir = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
},
.addr_auth_type = ADDR_AUTH_FLAT_RANDOM,
.cong_avoid = CA_MB_ECN
diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h
index 0105f88e..70a21ed7 100644
--- a/include/ouroboros/irm.h
+++ b/include/ouroboros/irm.h
@@ -76,8 +76,7 @@ int irm_bind_process(pid_t pid,
int irm_unbind_process(pid_t pid,
const char * name);
-int irm_create_name(const char * name,
- enum pol_balance pol);
+int irm_create_name(struct name_info * info);
int irm_destroy_name(const char * name);
diff --git a/include/ouroboros/local-dev.h b/include/ouroboros/local-dev.h
index da62e31c..cd0298d3 100644
--- a/include/ouroboros/local-dev.h
+++ b/include/ouroboros/local-dev.h
@@ -23,9 +23,11 @@
#ifndef OUROBOROS_LIB_LOCAL_DEV_H
#define OUROBOROS_LIB_LOCAL_DEV_H
-ssize_t local_flow_read(int fd);
+#include <ouroboros/ssm_pool.h>
-int local_flow_write(int fd,
- size_t idx);
+int local_flow_transfer(int src_fd,
+ int dst_fd,
+ struct ssm_pool * src_pool,
+ struct ssm_pool * dst_pool);
#endif /* OUROBOROS_LIB_LOCAL_DEV_H */
diff --git a/include/ouroboros/logs.h b/include/ouroboros/logs.h
index db49ae32..31f11836 100644
--- a/include/ouroboros/logs.h
+++ b/include/ouroboros/logs.h
@@ -37,12 +37,14 @@
#define CLR_RED "\x1b[31m"
#define CLR_GREEN "\x1b[32m"
#define CLR_YELLOW "\x1b[33m"
+#define CLR_BLUE "\x1b[34m"
#define CLR_RESET "\x1b[0m"
#define DEBUG_CODE "DB"
#define ERROR_CODE "EE"
#define WARN_CODE "WW"
#define INFO_CODE "II"
+#define PROTO_CODE "PP"
extern bool log_syslog;
@@ -79,6 +81,7 @@ void log_fini(void);
} \
} while (0)
+#ifndef OUROBOROS_DISABLE_LOGGING
#define log_err(...) \
__olog(CLR_RED, ERROR_CODE, LOG_ERR, __VA_ARGS__)
#define log_warn(...) \
@@ -93,14 +96,29 @@ void log_fini(void);
__olog_id(CLR_YELLOW, WARN_CODE, LOG_WARNING, id, fmt, ## __VA_ARGS__)
#define log_info_id(id, fmt, ...) \
__olog_id(CLR_GREEN, INFO_CODE, LOG_INFO, id, fmt, ## __VA_ARGS__)
+#else /* OUROBOROS_DISABLE_LOGGING: all logging disabled */
+#define log_err(...) do { } while (0)
+#define log_warn(...) do { } while (0)
+#define log_info(...) do { } while (0)
-#ifdef CONFIG_OUROBOROS_DEBUG
+#define log_err_id(id, fmt, ...) do { (void)(id); } while (0)
+#define log_warn_id(id, fmt, ...) do { (void)(id); } while (0)
+#define log_info_id(id, fmt, ...) do { (void)(id); } while (0)
+
+#endif /* OUROBOROS_DISABLE_LOGGING */
+
+#if defined(OUROBOROS_DISABLE_LOGGING) || !defined(CONFIG_OUROBOROS_DEBUG)
+#define log_dbg(...) do { } while (0)
+#define log_dbg_id(id, ...) do { (void)(id); } while (0)
+#define log_proto(...) do { } while (0)
+#define log_proto_id(id, ...) do { (void)(id); } while (0)
+#else
#define log_dbg(...) __olog("", DEBUG_CODE, LOG_DEBUG, __VA_ARGS__)
#define log_dbg_id(id, fmt, ...) \
__olog_id("", DEBUG_CODE, LOG_DEBUG, id, fmt, ## __VA_ARGS__)
-#else
-#define log_dbg(...) do { } while (0)
-#define log_dbg_id(...) do { } while (0)
+#define log_proto(...) __olog(CLR_BLUE, PROTO_CODE, LOG_DEBUG, __VA_ARGS__)
+#define log_proto_id(id, fmt, ...) \
+ __olog_id(CLR_BLUE, INFO_CODE, LOG_INFO, id, fmt, ## __VA_ARGS__)
#endif
#endif /* OUROBOROS_LIB_LOGS_H */
diff --git a/include/ouroboros/name.h b/include/ouroboros/name.h
index 9d77a90b..892f2a1f 100644
--- a/include/ouroboros/name.h
+++ b/include/ouroboros/name.h
@@ -24,6 +24,7 @@
#define OUROBOROS_NAME_H
#define NAME_SIZE 255
+#define NAME_PATH_SIZE (NAME_SIZE + 256)
#define BIND_AUTO 0x01
enum pol_balance {
@@ -32,9 +33,18 @@ enum pol_balance {
LB_INVALID
};
+struct name_sec_paths {
+ char enc[NAME_PATH_SIZE + 1]; /* path to crypt for this name */
+ char key[NAME_PATH_SIZE + 1]; /* path to key for this name */
+ char crt[NAME_PATH_SIZE + 1]; /* path to crt for this name */
+};
+
struct name_info {
char name[NAME_SIZE + 1];
enum pol_balance pol_lb;
+
+ struct name_sec_paths s; /* server */
+ struct name_sec_paths c; /* client */
};
#endif /* OUROBOROS_NAME_H */
diff --git a/include/ouroboros/np1_flow.h b/include/ouroboros/np1_flow.h
index 31720eea..4110ab6a 100644
--- a/include/ouroboros/np1_flow.h
+++ b/include/ouroboros/np1_flow.h
@@ -30,7 +30,8 @@
int np1_flow_alloc(pid_t n_pid,
int flow_id);
-int np1_flow_resp(int flow_id);
+int np1_flow_resp(int flow_id,
+ int resp);
int np1_flow_dealloc(int flow_id,
time_t timeo);
@@ -43,7 +44,6 @@ static const qosspec_t qos_np1 = {
.ber = UINT32_MAX,
.in_order = 0,
.max_gap = UINT32_MAX,
- .cypher_s = 0,
.timeout = 0
};
diff --git a/include/ouroboros/proc.h b/include/ouroboros/proc.h
index 80c67227..0e27362e 100644
--- a/include/ouroboros/proc.h
+++ b/include/ouroboros/proc.h
@@ -31,8 +31,9 @@
/* Processes */
struct proc_info {
pid_t pid;
- char prog[PROG_NAME_SIZE + 1]; /* program instantiated */
-
+ char prog[PROG_NAME_SIZE + 1];
+ uid_t uid;
+ gid_t gid;
};
/* Programs */
diff --git a/include/ouroboros/protobuf.h b/include/ouroboros/protobuf.h
index 9d38afb1..780d58dc 100644
--- a/include/ouroboros/protobuf.h
+++ b/include/ouroboros/protobuf.h
@@ -31,31 +31,36 @@
#include <ouroboros/serdes-oep.h>
#include "ipcp_config.pb-c.h"
-typedef IpcpConfigMsg ipcp_config_msg_t;
-typedef DtConfigMsg dt_config_msg_t;
-typedef EthConfigMsg eth_config_msg_t;
-typedef UdpConfigMsg udp_config_msg_t;
-typedef UniConfigMsg uni_config_msg_t;
+typedef IpcpConfigMsg ipcp_config_msg_t;
+typedef LsConfigMsg ls_config_msg_t;
+typedef RoutingConfigMsg routing_config_msg_t;
+typedef DtConfigMsg dt_config_msg_t;
+typedef DirConfigMsg dir_config_msg_t;
+typedef DirDhtConfigMsg dir_dht_config_msg_t;
+typedef EthConfigMsg eth_config_msg_t;
+typedef Udp4ConfigMsg udp4_config_msg_t;
+typedef Udp6ConfigMsg udp6_config_msg_t;
+typedef UniConfigMsg uni_config_msg_t;
#include "ipcp.pb-c.h"
-typedef IpcpMsg ipcp_msg_t;
+typedef IpcpMsg ipcp_msg_t;
#include "irm.pb-c.h"
-typedef IrmMsg irm_msg_t;
-typedef TimespecMsg timespec_msg_t;
-typedef IpcpInfoMsg ipcp_info_msg_t;
-typedef IpcpListMsg ipcp_list_msg_t;
+typedef IrmMsg irm_msg_t;
+typedef TimespecMsg timespec_msg_t;
+typedef IpcpInfoMsg ipcp_info_msg_t;
+typedef IpcpListMsg ipcp_list_msg_t;
#include "model.pb-c.h"
-typedef FlowInfoMsg flow_info_msg_t;
-typedef LayerInfoMsg layer_info_msg_t;
-typedef NameInfoMsg name_info_msg_t;
-typedef QosspecMsg qosspec_msg_t;
+typedef FlowInfoMsg flow_info_msg_t;
+typedef NameInfoMsg name_info_msg_t;
+typedef LayerInfoMsg layer_info_msg_t;
+typedef QosspecMsg qosspec_msg_t;
#include "enroll.pb-c.h"
-typedef EnrollReqMsg enroll_req_msg_t;
-typedef EnrollRespMsg enroll_resp_msg_t;
-typedef EnrollAckMsg enroll_ack_msg_t;
+typedef EnrollReqMsg enroll_req_msg_t;
+typedef EnrollRespMsg enroll_resp_msg_t;
+typedef EnrollAckMsg enroll_ack_msg_t;
/* IPCP configuration */
timespec_msg_t * timespec_s_to_msg(const struct timespec * s);
@@ -66,6 +71,10 @@ flow_info_msg_t * flow_info_s_to_msg(const struct flow_info * s);
struct flow_info flow_info_msg_to_s(const flow_info_msg_t * msg);
+name_info_msg_t * name_info_s_to_msg(const struct name_info * s);
+
+struct name_info name_info_msg_to_s(const name_info_msg_t * msg);
+
layer_info_msg_t * layer_info_s_to_msg(const struct layer_info * s);
struct layer_info layer_info_msg_to_s(const layer_info_msg_t * msg);
@@ -86,9 +95,13 @@ eth_config_msg_t * eth_config_s_to_msg(const struct eth_config * s);
struct eth_config eth_config_msg_to_s(const eth_config_msg_t * msg);
-udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s);
+udp4_config_msg_t * udp4_config_s_to_msg(const struct udp4_config * s);
+
+struct udp4_config udp4_config_msg_to_s(const udp4_config_msg_t * msg);
+
+udp6_config_msg_t * udp6_config_s_to_msg(const struct udp6_config * s);
-struct udp_config udp_config_msg_to_s(const udp_config_msg_t * msg);
+struct udp6_config udp6_config_msg_to_s(const udp6_config_msg_t * msg);
ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s);
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index a45e8135..2be31305 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -36,7 +36,6 @@ typedef struct qos_spec {
uint32_t ber; /* Bit error rate, errors per billion bits. */
uint8_t in_order; /* In-order delivery, enables FRCT. */
uint32_t max_gap; /* In ms. */
- uint16_t cypher_s; /* Cypher strength (bits), 0 = no encryption. */
uint32_t timeout; /* Peer timeout time, in ms, 0 = no timeout. */
} qosspec_t;
@@ -48,7 +47,6 @@ static const qosspec_t qos_raw = {
.ber = 1,
.in_order = 0,
.max_gap = UINT32_MAX,
- .cypher_s = 0,
.timeout = DEFAULT_PEER_TIMEOUT
};
@@ -60,19 +58,6 @@ static const qosspec_t qos_raw_no_errors = {
.ber = 0,
.in_order = 0,
.max_gap = UINT32_MAX,
- .cypher_s = 0,
- .timeout = DEFAULT_PEER_TIMEOUT
-};
-
-static const qosspec_t qos_raw_crypt = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
- .loss = 1,
- .ber = 0,
- .in_order = 0,
- .max_gap = UINT32_MAX,
- .cypher_s = 256,
.timeout = DEFAULT_PEER_TIMEOUT
};
@@ -84,19 +69,6 @@ static const qosspec_t qos_best_effort = {
.ber = 0,
.in_order = 1,
.max_gap = UINT32_MAX,
- .cypher_s = 0,
- .timeout = DEFAULT_PEER_TIMEOUT
-};
-
-static const qosspec_t qos_best_effort_crypt = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = UINT32_MAX,
- .cypher_s = 256,
.timeout = DEFAULT_PEER_TIMEOUT
};
@@ -108,19 +80,6 @@ static const qosspec_t qos_video = {
.ber = 0,
.in_order = 1,
.max_gap = 100,
- .cypher_s = 0,
- .timeout = DEFAULT_PEER_TIMEOUT
-};
-
-static const qosspec_t qos_video_crypt = {
- .delay = 100,
- .bandwidth = UINT64_MAX,
- .availability = 3,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = 100,
- .cypher_s = 256,
.timeout = DEFAULT_PEER_TIMEOUT
};
@@ -132,19 +91,6 @@ static const qosspec_t qos_voice = {
.ber = 0,
.in_order = 1,
.max_gap = 50,
- .cypher_s = 0,
- .timeout = DEFAULT_PEER_TIMEOUT
-};
-
-static const qosspec_t qos_voice_crypt = {
- .delay = 50,
- .bandwidth = 100000,
- .availability = 5,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = 50,
- .cypher_s = 256,
.timeout = DEFAULT_PEER_TIMEOUT
};
@@ -156,19 +102,6 @@ static const qosspec_t qos_data = {
.ber = 0,
.in_order = 1,
.max_gap = 2000,
- .cypher_s = 0,
- .timeout = DEFAULT_PEER_TIMEOUT
-};
-
-static const qosspec_t qos_data_crypt = {
- .delay = 1000,
- .bandwidth = 0,
- .availability = 0,
- .loss = 0,
- .ber = 0,
- .in_order = 1,
- .max_gap = 2000,
- .cypher_s = 256,
.timeout = DEFAULT_PEER_TIMEOUT
};
diff --git a/include/ouroboros/rib.h b/include/ouroboros/rib.h
index 6aabe8f7..cdc5a9d5 100644
--- a/include/ouroboros/rib.h
+++ b/include/ouroboros/rib.h
@@ -25,6 +25,8 @@
#define RIB_PATH_LEN 300
#define RIB_SEPARATOR "/"
+#define RIB_TM_STRLEN 26
+#define RIB_TM_FORMAT "%F %T (UTC)"
#include <sys/types.h>
diff --git a/include/ouroboros/serdes-irm.h b/include/ouroboros/serdes-irm.h
index 1d041541..bd04fc57 100644
--- a/include/ouroboros/serdes-irm.h
+++ b/include/ouroboros/serdes-irm.h
@@ -23,8 +23,10 @@
#ifndef OUROBOROS_LIB_SERDES_IRM_H
#define OUROBOROS_LIB_SERDES_IRM_H
+#include <ouroboros/crypt.h>
#include <ouroboros/flow.h>
#include <ouroboros/ipcp.h>
+#include <ouroboros/proc.h>
#include <ouroboros/time.h>
#include <ouroboros/utils.h>
@@ -54,10 +56,9 @@ int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
int response,
const buffer_t * data);
-/* response to alloc / join / accept / flow_req_arr */
int flow__irm_result_des(buffer_t * buf,
struct flow_info * flow,
- buffer_t * sk);
+ struct crypt_sk * sk);
int flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
@@ -69,8 +70,8 @@ int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf,
int ipcp_create_r__irm_req_ser(buffer_t * buf,
const struct ipcp_info * ipcp);
-int proc_announce__irm_req_ser(buffer_t * buf,
- const char * prog);
+int proc_announce__irm_req_ser(buffer_t * buf,
+ const struct proc_info * proc);
int proc_exit__irm_req_ser(buffer_t * buf);
diff --git a/include/ouroboros/serdes-oep.h b/include/ouroboros/serdes-oep.h
index 69ba71a4..f7d8561a 100644
--- a/include/ouroboros/serdes-oep.h
+++ b/include/ouroboros/serdes-oep.h
@@ -33,7 +33,6 @@
#define ENROLL_ID_LEN 8
struct enroll_req {
- /* TODO: Authentication */
uint8_t id[ENROLL_ID_LEN];
};
diff --git a/include/ouroboros/shm_rdrbuff.h b/include/ouroboros/shm_rdrbuff.h
deleted file mode 100644
index 4f9a215a..00000000
--- a/include/ouroboros/shm_rdrbuff.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Random Deletion Ring Buffer for Data Units
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#ifndef OUROBOROS_LIB_SHM_RDRBUFF_H
-#define OUROBOROS_LIB_SHM_RDRBUFF_H
-
-#include <ouroboros/shm_du_buff.h>
-#include <ouroboros/time.h>
-
-#include <pthread.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-struct shm_rdrbuff;
-
-struct shm_rdrbuff * shm_rdrbuff_create(void);
-
-struct shm_rdrbuff * shm_rdrbuff_open(void);
-
-void shm_rdrbuff_close(struct shm_rdrbuff * rdrb);
-
-void shm_rdrbuff_destroy(struct shm_rdrbuff * rdrb);
-
-void shm_rdrbuff_purge(void);
-
-/* Returns block index, a ptr and du_buff. */
-ssize_t shm_rdrbuff_alloc(struct shm_rdrbuff * rdrb,
- size_t count,
- uint8_t ** ptr,
- struct shm_du_buff ** sdb);
-
-ssize_t shm_rdrbuff_alloc_b(struct shm_rdrbuff * rdrb,
- size_t count,
- uint8_t ** ptr,
- struct shm_du_buff ** sdb,
- const struct timespec * abstime);
-
-ssize_t shm_rdrbuff_read(uint8_t ** dst,
- struct shm_rdrbuff * rdrb,
- size_t idx);
-
-struct shm_du_buff * shm_rdrbuff_get(struct shm_rdrbuff * rdrb,
- size_t idx);
-
-int shm_rdrbuff_remove(struct shm_rdrbuff * rdrb,
- size_t idx);
-
-#endif /* OUROBOROS_LIB_SHM_RDRBUFF_H */
diff --git a/include/ouroboros/sockets.h.in b/include/ouroboros/sockets.h.in
index 095674a9..1a6974ac 100644
--- a/include/ouroboros/sockets.h.in
+++ b/include/ouroboros/sockets.h.in
@@ -27,16 +27,20 @@
#include <sys/types.h>
-#define SOCK_PATH "/var/run/ouroboros/"
+#ifndef OUROBOROS_TEST
+ #define SOCK_PATH "/var/run/ouroboros/"
+#else
+ #define SOCK_PATH "/tmp/"
+#endif
#define SOCK_PATH_SUFFIX ".sock"
#define IRM_SOCK_PATH SOCK_PATH "irm" SOCK_PATH_SUFFIX
-#define IPCP_SOCK_PATH_PREFIX SOCK_PATH "ipcp"
+#define IPCP_SOCK_PATH_PREFIX SOCK_PATH "ipcp."
#define SOCK_BUF_SIZE @SOCK_BUF_SIZE@
-/* Returns the full socket path of an IPCP */
-char * ipcp_sock_path(pid_t pid);
+char * sock_path(pid_t pid,
+ const char * path);
int server_socket_open(char * file_name);
diff --git a/include/ouroboros/shm_flow_set.h b/include/ouroboros/ssm_flow_set.h
index 09e37649..02a5cb71 100644
--- a/include/ouroboros/shm_flow_set.h
+++ b/include/ouroboros/ssm_flow_set.h
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LIB_SHM_FLOW_SET_H
-#define OUROBOROS_LIB_SHM_FLOW_SET_H
+#ifndef OUROBOROS_LIB_SSM_FLOW_SET_H
+#define OUROBOROS_LIB_SSM_FLOW_SET_H
#include <ouroboros/fqueue.h>
@@ -32,38 +32,38 @@ struct flowevent {
int event;
};
-struct shm_flow_set;
+struct ssm_flow_set;
-struct shm_flow_set * shm_flow_set_create(pid_t pid);
+struct ssm_flow_set * ssm_flow_set_create(pid_t pid);
-void shm_flow_set_destroy(struct shm_flow_set * set);
+void ssm_flow_set_destroy(struct ssm_flow_set * set);
-struct shm_flow_set * shm_flow_set_open(pid_t pid);
+struct ssm_flow_set * ssm_flow_set_open(pid_t pid);
-void shm_flow_set_close(struct shm_flow_set * set);
+void ssm_flow_set_close(struct ssm_flow_set * set);
-void shm_flow_set_zero(struct shm_flow_set * shm_set,
+void ssm_flow_set_zero(struct ssm_flow_set * set,
size_t idx);
-int shm_flow_set_add(struct shm_flow_set * shm_set,
+int ssm_flow_set_add(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-int shm_flow_set_has(struct shm_flow_set * shm_set,
+int ssm_flow_set_has(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-void shm_flow_set_del(struct shm_flow_set * shm_set,
+void ssm_flow_set_del(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-void shm_flow_set_notify(struct shm_flow_set * set,
+void ssm_flow_set_notify(struct ssm_flow_set * set,
int flow_id,
int event);
-ssize_t shm_flow_set_wait(const struct shm_flow_set * shm_set,
+ssize_t ssm_flow_set_wait(const struct ssm_flow_set * set,
size_t idx,
struct flowevent * fqueue,
const struct timespec * abstime);
-#endif /* OUROBOROS_LIB_SHM_FLOW_SET_H */
+#endif /* OUROBOROS_LIB_SSM_FLOW_SET_H */
diff --git a/include/ouroboros/shm_du_buff.h b/include/ouroboros/ssm_pk_buff.h
index c25d4b95..9b82ede3 100644
--- a/include/ouroboros/shm_du_buff.h
+++ b/include/ouroboros/ssm_pk_buff.h
@@ -20,39 +20,39 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LIB_SHM_DU_BUFF_H
-#define OUROBOROS_LIB_SHM_DU_BUFF_H
+#ifndef OUROBOROS_LIB_SSM_PK_BUFF_H
+#define OUROBOROS_LIB_SSM_PK_BUFF_H
#include <sys/types.h>
#include <stdint.h>
-struct shm_du_buff;
+struct ssm_pk_buff;
-size_t shm_du_buff_get_idx(struct shm_du_buff * sdb);
+size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_head(struct shm_du_buff * sdb);
+uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_tail(struct shm_du_buff * sdb);
+uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb);
-size_t shm_du_buff_len(struct shm_du_buff * sdb);
+size_t ssm_pk_buff_len(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_head_alloc(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_tail_alloc(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_head_release(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_tail_release(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb,
size_t size);
-void shm_du_buff_truncate(struct shm_du_buff * sdb,
+void ssm_pk_buff_truncate(struct ssm_pk_buff * spb,
size_t len);
-int shm_du_buff_wait_ack(struct shm_du_buff * sdb);
+int ssm_pk_buff_wait_ack(struct ssm_pk_buff * spb);
-int shm_du_buff_ack(struct shm_du_buff * sdb);
+int ssm_pk_buff_ack(struct ssm_pk_buff * spb);
-#endif /* OUROBOROS_LIB_SHM_DU_BUFF_H */
+#endif /* OUROBOROS_LIB_SSM_PK_BUFF_H */
diff --git a/include/ouroboros/ssm_pool.h b/include/ouroboros/ssm_pool.h
new file mode 100644
index 00000000..4becbdf5
--- /dev/null
+++ b/include/ouroboros/ssm_pool.h
@@ -0,0 +1,74 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Secure Shared Memory infrastructure (SSM) Packet Buffer
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_SSM_POOL_H
+#define OUROBOROS_LIB_SSM_POOL_H
+
+#include <ouroboros/ssm_pk_buff.h>
+#include <ouroboros/time.h>
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+struct ssm_pool;
+
+/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user) */
+struct ssm_pool * ssm_pool_create(uid_t uid,
+ gid_t gid);
+
+struct ssm_pool * ssm_pool_open(uid_t uid);
+
+void ssm_pool_close(struct ssm_pool * pool);
+
+void ssm_pool_destroy(struct ssm_pool * pool);
+
+int ssm_pool_mlock(struct ssm_pool * pool);
+
+void ssm_pool_gspp_purge(void);
+
+/* Alloc count bytes, returns block index, a ptr and pk_buff. */
+ssize_t ssm_pool_alloc(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb);
+
+ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb,
+ const struct timespec * abstime);
+
+ssize_t ssm_pool_read(uint8_t ** dst,
+ struct ssm_pool * pool,
+ size_t idx);
+
+struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool,
+ size_t idx);
+
+int ssm_pool_remove(struct ssm_pool * pool,
+ size_t idx);
+
+void ssm_pool_reclaim_orphans(struct ssm_pool * pool,
+ pid_t pid);
+
+#endif /* OUROBOROS_LIB_SSM_POOL_H */
diff --git a/include/ouroboros/shm_rbuff.h b/include/ouroboros/ssm_rbuff.h
index 4323d4e1..791befa2 100644
--- a/include/ouroboros/shm_rbuff.h
+++ b/include/ouroboros/ssm_rbuff.h
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LIB_SHM_RBUFF_H
-#define OUROBOROS_LIB_SHM_RBUFF_H
+#ifndef OUROBOROS_LIB_SSM_RBUFF_H
+#define OUROBOROS_LIB_SSM_RBUFF_H
#include <sys/types.h>
#include <sys/time.h>
@@ -33,37 +33,39 @@
#define ACL_FLOWDOWN 0002
#define ACL_FLOWPEER 0004
-struct shm_rbuff;
+struct ssm_rbuff;
-struct shm_rbuff * shm_rbuff_create(pid_t pid,
+struct ssm_rbuff * ssm_rbuff_create(pid_t pid,
int flow_id);
-struct shm_rbuff * shm_rbuff_open(pid_t pid,
- int flow_id);
+void ssm_rbuff_destroy(struct ssm_rbuff * rb);
-void shm_rbuff_close(struct shm_rbuff * rb);
+struct ssm_rbuff * ssm_rbuff_open(pid_t pid,
+ int flow_id);
-void shm_rbuff_destroy(struct shm_rbuff * rb);
+void ssm_rbuff_close(struct ssm_rbuff * rb);
-void shm_rbuff_set_acl(struct shm_rbuff * rb,
+void ssm_rbuff_set_acl(struct ssm_rbuff * rb,
uint32_t flags);
-uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb);
+uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb);
+
+void ssm_rbuff_fini(struct ssm_rbuff * rb);
-void shm_rbuff_fini(struct shm_rbuff * rb);
+int ssm_rbuff_mlock(struct ssm_rbuff * rb);
-int shm_rbuff_write(struct shm_rbuff * rb,
+int ssm_rbuff_write(struct ssm_rbuff * rb,
size_t idx);
-int shm_rbuff_write_b(struct shm_rbuff * rb,
+int ssm_rbuff_write_b(struct ssm_rbuff * rb,
size_t idx,
const struct timespec * abstime);
-ssize_t shm_rbuff_read(struct shm_rbuff * rb);
+ssize_t ssm_rbuff_read(struct ssm_rbuff * rb);
-ssize_t shm_rbuff_read_b(struct shm_rbuff * rb,
+ssize_t ssm_rbuff_read_b(struct ssm_rbuff * rb,
const struct timespec * abstime);
-size_t shm_rbuff_queued(struct shm_rbuff * rb);
+size_t ssm_rbuff_queued(struct ssm_rbuff * rb);
-#endif /* OUROBOROS_LIB_SHM_RBUFF_H */
+#endif /* OUROBOROS_LIB_SSM_RBUFF_H */
diff --git a/include/ouroboros/time.h b/include/ouroboros/time.h
index b274c35b..3bd6a257 100644
--- a/include/ouroboros/time.h
+++ b/include/ouroboros/time.h
@@ -31,34 +31,38 @@
#undef BILLION
#endif
-#define MILLION 1000000L
-#define BILLION 1000000000L
+#define MILLION 1000000LL
+#define BILLION 1000000000LL
#include <time.h>
#include <sys/time.h>
+#include <sys/types.h>
#define TIMESPEC_INIT_S(s) {(s), 0}
#define TIMESPEC_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * MILLION}
#define TIMESPEC_INIT_US(us) {(us) / MILLION, ((us) % MILLION) * 1000}
#define TIMESPEC_INIT_NS(ns) {(ns) / BILLION, ((ns) % BILLION)}
+#define TS_TO_UINT64(ts) \
+ ((uint64_t)(ts).tv_sec * BILLION + (uint64_t)(ts).tv_nsec)
+
#define TIMEVAL_INIT_S(s) {(s), 0}
#define TIMEVAL_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * 1000}
#define TIMEVAL_INIT_US(us) {(us) / MILLION, ((us) % MILLION)}
/* functions for timespecs */
-#define ts_diff_ns(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+#define ts_diff_ns(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec))
-#define ts_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define ts_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / 1000L)
-#define ts_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+#define ts_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / MILLION)
/* functions for timevals are the same */
-#define tv_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define tv_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ + ((tx)->tv_usec - (t0)->tv_usec))
+#define tv_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_usec - (t0)->tv_usec) / 1000L)
-#define tv_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
- + ((tx)->tv_usec - (t0)->tv_usec) / MILLION)
/* functions for timespecs */
diff --git a/include/ouroboros/tpm.h b/include/ouroboros/tpm.h
index 445f9306..3fb49b88 100644
--- a/include/ouroboros/tpm.h
+++ b/include/ouroboros/tpm.h
@@ -38,8 +38,10 @@ int tpm_start(struct tpm * tpm);
void tpm_stop(struct tpm * tpm);
-void tpm_dec(struct tpm * tpm);
+void tpm_begin_work(struct tpm * tpm);
-void tpm_inc(struct tpm * tpm);
+void tpm_wait_work(struct tpm * tpm);
+
+void tpm_end_work(struct tpm * tpm);
#endif /* OUROBOROS_LIB_TPM_H */
diff --git a/include/ouroboros/utils.h b/include/ouroboros/utils.h
index 93fbf402..f53361eb 100644
--- a/include/ouroboros/utils.h
+++ b/include/ouroboros/utils.h
@@ -23,30 +23,43 @@
#ifndef OUROBOROS_LIB_UTILS_H
#define OUROBOROS_LIB_UTILS_H
+#include <stdbool.h>
#include <stdint.h>
-#include <unistd.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ABS(a) ((a) > 0 ? (a) : -(a))
-#define clrbuf(buf) do { memset(&(buf), 0, sizeof(buf)); } while (0);
-#define freebuf(buf) do { free((buf).data); clrbuf(buf); } while (0);
+#define clrbuf(buf) do { memset(&(buf), 0, sizeof(buf)); } while (0)
+#define freebuf(buf) do { free((buf).data); clrbuf(buf); } while (0)
+#define BUF_INIT { 0, NULL }
+#define BUF_IS_EMPTY(buf) ((buf)->data == NULL && (buf)->len == 0)
typedef struct {
- uint8_t * data;
size_t len;
+ uint8_t * data;
} buffer_t;
+int bufcmp(const buffer_t * a,
+ const buffer_t * b);
+
/*
* Returns the number of characters a uint would
* need when represented as a string
*/
int n_digits(unsigned i);
-/* gets the application name */
char * path_strip(const char * src);
+char * trim_whitespace(char * str);
+
+bool is_ouroboros_member_uid(uid_t uid);
+
+bool is_ouroboros_member(void);
+
/* functions for copying and destroying arguments list */
size_t argvlen(const char ** argv);
@@ -57,11 +70,17 @@ void argvfree(char ** argv);
/* destroy a ** */
#define freepp(type, ptr, len) \
do { \
- if (len == 0) \
- break; \
- while (len > 0) \
- free(((type **) ptr)[--len]); \
+ while (len-- > 0) \
+ free(((type **) ptr)[len]); \
+ free(ptr); \
+ } while (0)
+
+/* destroys an array of buffers */
+#define freebufs(ptr, len) \
+ do { \
+ while ((len)-- > 0) \
+ freebuf((ptr)[len]); \
free(ptr); \
- } while (0);
+ } while (0)
#endif /* OUROBOROS_LIB_UTILS_H */
diff --git a/include/test/certs.h b/include/test/certs.h
new file mode 100644
index 00000000..1b847406
--- /dev/null
+++ b/include/test/certs.h
@@ -0,0 +1,125 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test certificates - ECDSA/P-256 signed certificates
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef TEST_CERTS_H
+#define TEST_CERTS_H
+
+/*
+* Certificates created following the guide
+* Building an openssl certificate authority
+* on
+* https://community.f5.com/kb/technicalarticles/
+*/
+
+/* Root certificate for CA ca.unittest.o7s */
+static const char * root_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICXTCCAgOgAwIBAgIURlENlCOy1OsA/AXFscPUQ2li8OYwCgYIKoZIzj0EAwIw\n"
+"fDELMAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAK\n"
+"BgNVBAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51\n"
+"bml0dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASAwHhcNMjUwODAzMTg1MzE1WhcN\n"
+"NDUwNzI5MTg1MzE1WjB8MQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYD\n"
+"VQQHDAVHaGVudDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n"
+"GDAWBgNVBAMMD2NhLnVuaXR0ZXN0Lm83czEQMA4GCSqGSIb3DQEJARYBIDBZMBMG\n"
+"ByqGSM49AgEGCCqGSM49AwEHA0IABEPMseCScbd/d5TlHmyYVszn/YGVeNdUCnFR\n"
+"naOr95WlTNo3MyKKBuoiEFwHhjPASgXr/VDVjJLSyM3JUPebAcGjYzBhMB0GA1Ud\n"
+"DgQWBBQkxjMILHH6lZ+rnCMnD/63GO3y1zAfBgNVHSMEGDAWgBQkxjMILHH6lZ+r\n"
+"nCMnD/63GO3y1zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAKBggq\n"
+"hkjOPQQDAgNIADBFAiEA1jVJWW4idkCgAYv0m2LT9C33Dq42aLyRkJ+9YdzDqLwC\n"
+"IHT6MS4I0k52YP/hxoqWVBbpOW79PKYMRLyXTk1r7+Fa\n"
+"-----END CERTIFICATE-----\n";
+
+/* Certificate for intermediary im.unittest.o7s used for signing */
+static const char * im_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICbTCCAhOgAwIBAgICEAMwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCQkUxDDAK\n"
+"BgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNVBAoMA283czEVMBMGA1UE\n"
+"CwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0dGVzdC5vN3MxEDAOBgkq\n"
+"hkiG9w0BCQEWASAwHhcNMjUwODAzMTkwMjU3WhcNNDUwNzI5MTkwMjU3WjBaMQsw\n"
+"CQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQwwCgYDVQQKDANvN3MxFTATBgNVBAsM\n"
+"DHVuaXR0ZXN0Lm83czEYMBYGA1UEAwwPaW0udW5pdHRlc3QubzdzMFkwEwYHKoZI\n"
+"zj0CAQYIKoZIzj0DAQcDQgAEdlra08XItIPtVl5veaq4UF6LIcBXj2mZFqKNEXFh\n"
+"l9uAz6UAbIc+FUPNfom6dwKbg/AjQ82a100eh6K/jCY7eKOBpjCBozAdBgNVHQ4E\n"
+"FgQUy8Go8BIO6i0lJ+mgBr9lvh2L0eswHwYDVR0jBBgwFoAUJMYzCCxx+pWfq5wj\n"
+"Jw/+txjt8tcwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEQYD\n"
+"VR0fBAowCDAGoASgAoYAMCoGCCsGAQUFBwEBBB4wHDAMBggrBgEFBQcwAoYAMAwG\n"
+"CCsGAQUFBzABhgAwCgYIKoZIzj0EAwIDSAAwRQIhAN3ZYhqu6mVLGidmONsbANk5\n"
+"rzT6aHJcmvj19OxMusaXAiBKy0gBFCri/GLizi4wZo09wf31yZMqfr8IrApvPaLw\n"
+"qA==\n"
+"-----END CERTIFICATE-----\n";
+
+/* Server test-1.unittest.o7s private-public key pair */
+static const char * server_pkp_ec = \
+"-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIA4/bcmquVvGrY4+TtfnFSy1SpXs896r5xJjGuD6NmGRoAoGCCqGSM49\n"
+"AwEHoUQDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2M0YzRKFKeV48tG5eD+MBaTrT\n"
+"eoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+/* Public key for the Private key */
+static __attribute__((unused)) const char * server_pk_ec = \
+"-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2\n"
+"M0YzRKFKeV48tG5eD+MBaTrTeoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n"
+"-----END PUBLIC KEY-----\n";
+
+/* Valid signed server certificate for test-1.unittest.o7s */
+#define SSC_TEXT_SIZE 2295 /* size of cleartext certificate */
+static const char * signed_server_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIDiTCCAy+gAwIBAgICEAUwCgYIKoZIzj0EAwIwWjELMAkGA1UEBhMCQkUxDDAK\n"
+"BgNVBAgMA09WTDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n"
+"GDAWBgNVBAMMD2ltLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDgxODQ4NTNaFw00NTA4\n"
+"MDMxODQ4NTNaMG4xCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcM\n"
+"BUdoZW50MQwwCgYDVQQKDANvN3MxFTATBgNVBAsMDHVuaXR0ZXN0Lm83czEcMBoG\n"
+"A1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49AwEH\n"
+"A0IABOAUjob9+quGwjC0pCWgr88Gd6T8tjNGM0ShSnlePLRuXg/jAWk603qB1HEX\n"
+"6c9BDv4p6txVQ8xKAENjVqZ89JCjggHPMIIByzAJBgNVHRMEAjAAMBEGCWCGSAGG\n"
+"+EIBAQQEAwIGQDA4BglghkgBhvhCAQ0EKxYpbzdzIHVuaXR0ZXN0IEdlbmVyYXRl\n"
+"ZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFI+htsK0xxy6e1CqCyxn7mqi\n"
+"wRrpMIGoBgNVHSMEgaAwgZ2AFMvBqPASDuotJSfpoAa/Zb4di9HroYGApH4wfDEL\n"
+"MAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNV\n"
+"BAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0\n"
+"dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASCCAhADMA4GA1UdDwEB/wQEAwIFoDAT\n"
+"BgNVHSUEDDAKBggrBgEFBQcDATAoBgNVHR8EITAfMB2gG6AZhhdodHRwczovL291\n"
+"cm9ib3Jvcy5yb2NrczBYBggrBgEFBQcBAQRMMEowIwYIKwYBBQUHMAKGF2h0dHBz\n"
+"Oi8vb3Vyb2Jvcm9zLnJvY2tzMCMGCCsGAQUFBzABhhdodHRwczovL291cm9ib3Jv\n"
+"cy5yb2NrczAKBggqhkjOPQQDAgNIADBFAiBZuw/Yb2pq925H7pEiOXr4fMo0wknz\n"
+"ktkxoHAFbjQEPQIhAMInHI7lvRmS0IMw1wBF/WlUZWKvhyU/TeMIZfk/JGCS\n"
+"-----END CERTIFICATE-----\n";
+
+/* Self-signed by server test-1.unittest.o7s using its key */
+static __attribute__((unused)) const char * server_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIBfjCCASWgAwIBAgIUB5VYxp7i+sgYjvLiwfpf0W5NfqQwCgYIKoZIzj0EAwIw\n"
+"HjEcMBoGA1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDMxOTI4MzVa\n"
+"Fw00NTA3MjkxOTI4MzVaMB4xHDAaBgNVBAMME3Rlc3QtMS51bml0dGVzdC5vN3Mw\n"
+"WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATgFI6G/fqrhsIwtKQloK/PBnek/LYz\n"
+"RjNEoUp5Xjy0bl4P4wFpOtN6gdRxF+nPQQ7+KercVUPMSgBDY1amfPSQo0EwPzAe\n"
+"BgNVHREEFzAVghN0ZXN0LTEudW5pdHRlc3QubzdzMB0GA1UdDgQWBBSPobbCtMcc\n"
+"untQqgssZ+5qosEa6TAKBggqhkjOPQQDAgNHADBEAiAoFC/rqgrRXmMUx4y5cPbv\n"
+"jOKpoL3FpehRgGkPatmL/QIgMRHc2TSGo6q1SG22Xt1dHAIBsaN2AlSfhjKULMH5\n"
+"gRo=\n"
+"-----END CERTIFICATE-----\n";
+
+#endif /* TEST_CERTS_H */
+
diff --git a/include/test/certs_pqc.h b/include/test/certs_pqc.h
new file mode 100644
index 00000000..b533ca60
--- /dev/null
+++ b/include/test/certs_pqc.h
@@ -0,0 +1,656 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test certificates - ML-DSA-65 (post-quantum) signed certificates
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef TEST_CERTS_PQC_H
+#define TEST_CERTS_PQC_H
+
+/*
+ * ML-DSA-65 certificates for testing post-quantum cryptography
+ * Root CA: ca.unittest.o7s
+ * Intermediate CA: im.unittest.o7s (pathlen:0)
+ * Server: test-1.unittest.o7s
+ */
+
+/* PEM certificate strings will go here */
+static const char * root_ca_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIWEDCCCQ2gAwIBAgIUKA3Abd0Hre9KpmyKRcMhFpm1QqcwCwYJYIZIAWUDBAMS\n"
+"MFMxCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcMBUdoZW50MQww\n"
+"CgYDVQQKDANvN3MxGDAWBgNVBAMMD2NhLnVuaXR0ZXN0Lm83czAeFw0yNjAxMDUx\n"
+"OTE1MTJaFw00NTEyMzExOTE1MTJaMFMxCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANP\n"
+"VkwxDjAMBgNVBAcMBUdoZW50MQwwCgYDVQQKDANvN3MxGDAWBgNVBAMMD2NhLnVu\n"
+"aXR0ZXN0Lm83czCCB7IwCwYJYIZIAWUDBAMSA4IHoQC+5s/VK8kGvHlluvSftuBs\n"
+"GWeyLsQp1vQLDet2gVp5tv7GaGWB5RCzNMw0KsGZqHX8oVZIUHHVpY3Dm7rSmlPQ\n"
+"zy1Bq0lv4nT+jc9vYlqlhlQtiefd8PJmyMvi/JM3Pxq1whyXXR30dr45FRCUIlKs\n"
+"nbIXeMOxpEDRhNpZBVBkppWuFf17+jykTVxh6uvNWYmnZhFv0XLY30wfRL8/fpmI\n"
+"eExKE3qmFk2CONXkXGjUcprHoaUHx+E54+hEPK5hPNP7K609z/GaP9VbbR+UtuXA\n"
+"6ndYwCSuF5MPWl66ov9Jtyc2odfodnIM+PR2tx/SzvOzgwUrINVtEdt1A3SbO8r6\n"
+"hY/4iFASG+kYhDIYrGmfIRI9E1BujsQBPONVAOWUQv2puMUEkPUaw/nuQkk2dbJ4\n"
+"YZ2lyhnRmmiy7XJlLhgqjONNrP3Yym9kB55A20eez7dnw6Su+pqwCtaEr9LStPKk\n"
+"pdoat8MKdXts+5RjgslDhNK/aySOvFBXzhs73lakxqUmjH2Cz4vqwRnzCyzQ/5Rf\n"
+"cnRBgdfqxWm4LvjrKaTcwAk6KEkloRF9QnwLiHsN6sT+GzNEPzUE2DTIRHDR9IrF\n"
+"gOlbHr0i9sMJ9HmrSw2qB2IjV053CEM88pQ6nqCwCpDFgULTw7S5awD30jDiJ3Ro\n"
+"8vu0qBilgHC7cWZ6gyrkpipDezqNcuwkUasKnw+bsUuSlL8Jz7K3zBqCRb9tVMEo\n"
+"Q3ekIcdi4H5y8bYJ15fVMfJa9k125DLFVzI8rvOpngIZiPTiLxQSvNerl6W5fPpp\n"
+"AG9C2Td2JGr0kC5mIC3GzzFk5rWbXs3XgkARzUwtuZFllr+GcNdMpPOl740CO+PT\n"
+"WUqYAfsN+lpZvxmPwT8emANhxsZbD5E6EYO0CH2SHCboquP25tcnptiEhEJxRsI+\n"
+"LtJvYcyFfD8J2av56lESobi/JY8EpFxavTm/VOs+qIKULsWmCjNybqi5NOLP+t+X\n"
+"fDQeDALh1hkcNStj5eF8Cei+eLgCIOuXci3bzqNG90i+9pdfawSShiwwgyvqFmGn\n"
+"ZayUGDQavhXs859j1Kc6GV3/8pX2ka75T1tz3at8IDpkAX/3O0vBfv4UR7oa3a3T\n"
+"vyBiHfRAfl5RP/Hzn7kYlkZMhJ2uVKN21XULftgpgqCtoHpniVdbAN1kGXiGX4OH\n"
+"5TsbtD6RBPfjv8VMdKi+Thh65Kq5InWBz1fz31mcBnG71Wohqdpf/wj6M8Y4Ysrq\n"
+"4Zx75+E3fq+SvpE04Z81R2UYRIX8N3NUuucOxbGfo1DJmQdcfSKfdcVqVk4Wxsrx\n"
+"cV+BOpoUkFplJwuifoxeDKF6Q+KqGtntWX67rsZEtoMJOMuwmHaX3HC1OeIp22z7\n"
+"HMaW0CA9pcai9LF6AclHGtkJZoAeKpv2ejGA5gusrhfVSHVdZEBSg7GW5onIoMxH\n"
+"FywU38VGRj6GhvICjppBVhA0hj69w0v0RdQwxdmXQErdaklXi4AmOxCZqG1XgMu4\n"
+"eH+Ttqm77EfIDWP3vL7mKXTdnip6uCGDJir7ORYxMPOnfRwG8e8IAUOU+Y/Qk0T8\n"
+"/CF8xFoihOAtl1yL0HOthMgajgJJ8MdhdXZhdQs02hy2q8OtwWDh+9MGJYL/NEx3\n"
+"+bFNqM/H6APtX0QkkzaKSvgswSuHOZgEZuwhByBuIcXqX5plCugadwkJm//60zid\n"
+"p8zsebb/qPy0EHy9zCK8ANaWPcLmIYv5FGRL/5wr7xEUSVJzz/2lWTlRc/jFA4IO\n"
+"1usdUA/c4LwbwxFFLtGK9T/TlLaS2I0V8mOqFoBPlmHSwSfArxsEgfeSMsvUx8zE\n"
+"oF44SlB8YCQ8/n5rif437idcfyoNWsYvOOG9K0KS34Ez92R0aGy2sPoOD4/izBcv\n"
+"feLkEShsndsw4dJqvIgI3iNkCLezTDYOCrZdTzbXMk9iB4AR20PvlW7o1N1kHBJ9\n"
+"Ad6zZkwiI3SrhKKJ6EYbbbsx6EaVnSxreX0clpHo83dzQH7iS6Gv9Wo7G6nTbBph\n"
+"ZUBfeO1naOnKZgZVYWF2dwtJk0K9Wku9Pecklq0FA3BaaMi269RAUTpGwQ8dVRha\n"
+"FlMPGTZ9yvu4ujs/tygsKhkAiE7ST8PW+sy4o40/rKTBTHRCI2Yd8IWLhNuRsKdq\n"
+"I05lJkTaUcOj5Med9Xx1EM76q8KhbnEicxk4vlJgOIn8upLQGEbfDXDnKUHogoFi\n"
+"JpxANmZnpaA5ngJzC0HVuC+qPLsnpPR30lU4vYeSQkiGpiK/akzwZ2WjUrNsd8xr\n"
+"GR5TjD4L3ZcB8FUud5xK4RAYmA0EqDxl8AlcsfFycqQPla26o60eoUHbn1Zw0bl5\n"
+"mAD5j1jQIx9GZ7kLS0tka1jT8KHzZvWZs8aadGPysQPuqDseHO7oUApBgEAO+0sV\n"
+"sm9qriKdk3MQd6pfIFrzgk0OfoD08I7mWzgZhNSpez9NtbqUwZJFGYA4wPuLoVZa\n"
+"bO3NxnhHVSH0jj0IbeGDcMpgmDd4vCwsnaoAANoVdI7HZxjUYHR5lW2KBZbXyOBW\n"
+"aDknpH3e9pSufqwSuSLEHgYgTc/E59yXke7pnFZpTUo0dNewOKF76KZsEB63TcbT\n"
+"YOVSImJ+pgCueCaABdt8WqNjMGEwHQYDVR0OBBYEFMqHQbYKyHbMbnoXU2Q8yK1K\n"
+"pzI9MB8GA1UdIwQYMBaAFMqHQbYKyHbMbnoXU2Q8yK1KpzI9MA8GA1UdEwEB/wQF\n"
+"MAMBAf8wDgYDVR0PAQH/BAQDAgGGMAsGCWCGSAFlAwQDEgOCDO4A73IWj4X0QoNC\n"
+"KVk+HOUS/vCArSFboURL0jj280N5BLTaE20LE7cZQXM1Hlvp+KdZt6XY0F4NOkRJ\n"
+"tjMDa4pwWmUEkZIwsCwpfVOeOTwH+sWKOXaEIWDFZk4dM43rBX6TobSHN4eWxPOu\n"
+"43pc8Gt6FMID127neVSHusCS+scI5OSf6a/H1N47rZ+tbVTVXkdXBO2E1MNP4odK\n"
+"Fw8/Mn1AlF1XHmmF6JdMm3KnUYuXqckHj9lo/NWSOQd/OEDxf6glXTXd1W9iURDD\n"
+"o6XeJKosYswc9KiICa2aOncsHWQuALG0SK6FuEqmM+sMWcCkAuCwBepe2vUXlgDR\n"
+"8uS2uYEqxil+zPusGE6EZUfZCa9p/fjeNuzv691kNzfuNRNk6nTERBv6iM7ms+AZ\n"
+"edHY470uOhg5U9qwxBIv9fBV4Kgba+1X8P6etBE7+npj7cwgeGJryAgpku2/kZtN\n"
+"GQVk+ZDwVZ8khCjfUadK+P6L1HGF64ApBx9PNbVoA97UjTGT8Jj2xa3j0fv3+Vos\n"
+"0XDz/y5lTdfi3bEqBrmHHxb1mKnOkiML8ogqMwWH/IX/muapH0hpnVrSr6ufDGoI\n"
+"1vHLXYb3ZXPI3vHhNaDS/6LtmtE52BoZEL+P0IdygpMvGJAM9u6pLCZ1Bh+S1wzp\n"
+"0ClJrvAqpBmrJGLp5XsGKtG7M/p8lWBKgmbY3S6+dv/Bz27jfx47vCUFetJpfp2V\n"
+"9kv5nVBXzTnvfhRqDvYGjRKuWHpycisqgxFs2vD/GQM5HXLbdCJQMp0I6e/No1Cy\n"
+"wHW+n6P5nEuTNHbmpqppjkQEmtRGcoPj9xHaRqGS2v8lgDoo0DBLp10Rhhcox+hv\n"
+"1Z8ssMA1wA+q+QacVNG4nN4Fl+iF1MURiSQ+JlF4kjElXZseU52CB1rcnXZecxfC\n"
+"BPzx150GI7T/hUIxb31ivhntcfolQUxB4YIsYtZmpiUSBvjpDOGZ3Zm9BmcFaXCv\n"
+"kI+LWjzwI1g7NWIT9tOqkXIP88HNiDGokHeHuMDB9V0M9YUTkAZC5SASSHGkbknO\n"
+"Q1SGO5iiiGJApjBSDb/UVaJdVfTqXYVrmdP0BjTZXoKUFNLp7XD38dpQRG6s+7KW\n"
+"qRQuYveVKR4qX+eX75DHMDrosab0swiety8IJ4Nlo8Hrx+jVBNe+c9fexY/3c+R+\n"
+"nuRcgr8+IUQD1KoHHv94uX+ug5XPoX6fKBni6qU88xX7be7bWAY78zuwgFHDL80g\n"
+"CKTNdB/UeCO4V7TflTuzh740d8qnQVXYI/qlzaNQ22zvqqKtXERjS/MbmXcGCcbp\n"
+"8hBtvUTSkBFsv+ArqIGAGC9vkZ+hqoXwo4QX5n/I6rOxQu2q59X0B5xwiHFhm5rE\n"
+"Sp+E7YguzjSws7dbttjIoi+9jd3qy0kYTR40xT4pB/3d95CzTAzn/bo32gfTxuKy\n"
+"IZDqgfYvn48D99drUMwmsRpTK/+0CHWOZodmVyQX622wUX7ib9oaSwB4jLeS706W\n"
+"O6xVc0gwrCz092/f1SEeqAnCyP0alSVVcRQ5mYI7ZvS4OMC3w1C/MqARGanGjlwB\n"
+"cFeGdyqYdr4Wr0GNcIMixQ8dv87w/+FHHARFN0cPR9Hpl0d+59NDKl8JJcew8IZ+\n"
+"ln3FFpcvk7Q+x/XlouSoYGVz2LjQZi1RkrlXnJDTE/RMkRkJ8IDhgm4JKoMMIdxy\n"
+"AFQVoJ2lK500Yu/+FhFnmsPDg5qkcXsb8iOrD9g6O8MnxWGKc7KguwcSxERKwM1b\n"
+"p+o4N1KdEk6oOjgbLskUzY+QRYje1WK/HBAMtlAyUmU5k7zIKwB9gto54AuvT9FK\n"
+"e449fqoZa1AjcVb4UmhqqfQLnLtmjgunEsvoQaBLecjG230nw7a/rY57OW7DqHFC\n"
+"VBDtmXIhn0GEcMCOfEt/Vbtm8bOFhzZJ1uTOy7I+YmPuvQ27BmdlbhaZi1TzOX6i\n"
+"mNxNjdSSPdk1p/VyD7A/BFf4H722BfotC3PmgWVUPGNYlpRi+tK0quJiYTMdyb7N\n"
+"DYi0SBEAGRchoiIiJPBIIX+hXh8XLqD1Jf+MSttyUqRLlnTVaCpyn6O03zunUa36\n"
+"AFkN3T5StTGskgDidNHMtcieFc0zbkc/7HIhgITrpQcSDblcVpejZNrVpYQZSQyB\n"
+"bZWeSsrwR0eaUA+W7k7Xw52j1TiSwHF4TLV1rWJr+Am1Zggz4ZBKHI99tZZjIly5\n"
+"ARYPmbh6ps8ORSj6YYxCSmRW/vHYBB/dEi2sa1xINsopiuER0Uo9lhYz3kpjhJDg\n"
+"ALAgJ1YF+89415CI/AIW2cpwnNvcHuFwT6lq5vODtwunf0buwxL+lU9kl6A7kdS4\n"
+"c9IsnULuNYQDZbNwCZfIi9+H8BVO6Fm88voILTDYmkddL1nCrND4B/VqEkalIdJH\n"
+"1tUwkagtckLzB9OiuUd4ZU/jTv0az9/gbYxs/MZUlkGWjwJKZnDSgs5temc/uj2t\n"
+"Hca/qAHbNcQFJoEecWJD7rw6DfD13yaoB9CH+0PVQRdCk4m/J3PW0X9F2fMuagR0\n"
+"xt4ALxEa9LSmGEO6q7nxIxVVOUetQI5RYjpzEsaGx9yMPg+VdDK9643habdGPiVQ\n"
+"ZblJdx5376WGbRWltCTrFtX7lXa9kYaUnvMano30GDLHWxZOksXxIRpqBIIxXZ4+\n"
+"sHgOMGYSdIPT4VyqKN50Uucv01ibMkq1oBIzVMEJ7H/X5guRmH6t5bTf55UmSby3\n"
+"I3eqKXcTHru2vR4kRSdpfIgNrnE8lelAxuItfpuNGMCBElBcEu5Whdo56c+LWgT0\n"
+"oxBa190d29cGvczv4psIJ1ROfgE9O7NbpBnbmlIANuWSeOXeq0Idg5kUnwq+toPm\n"
+"0CPmxQdSDlkTZ+yNK2vygO+zbEQk7bBscPM5fmO5ty4vcm1B6s026bTx8iGD8rnT\n"
+"2eEiXVz6pfZ+ERJrKEpozZf/pEkIXUsxX6/I5C6epM9dcbAb1XN3vBRYqpNIHpr6\n"
+"xfPV+izIW208sdAsgEIJvvLD5sbLQuja51RVVD+e52XFwstPIDTTRQ/4Z5s2/CYI\n"
+"mMSpkYi30H6ViEN55ctNNnbOQJIljkmh/kv/nNVZyQYZTb1TmnSZ76Q/4WpEpDr+\n"
+"ir8Xb6kcoCUMXjY7Bgp+3dd+oj6kOl5ZlQqhTDOyXnL+koTXZtC6Sy9gJz6nFpg4\n"
+"f2CgOIlSVr3UssDw+VVey/H+iOsf4GovYol9X+H+BAV5yDzfkxoBUGMmGxfwT8p+\n"
+"l4GJ1UVqeLZe36UZ7+oa8tdQoNxGXL3NWKoYCJCfbmMLdbzc7vpELUaGecWXNJpA\n"
+"QFZZpqlc5CLZgk5y4/E5KGSCGPEEKy7hjpAFVKYC40oPvn7KBjIkVzTQbhg5J9s4\n"
+"wuy95kKkTHhSzd/ccO55Fiuva3WnIKwKdJLc8Sqt6S1/+t0/DIudCU2dPCE8kCzc\n"
+"Oclk9DJL61PjyHcoTpOdLAAkoKycjYF/PCzxVJ+7TVx70oZ5ECzrlDG9xLVkdYQS\n"
+"kkpQLW9/Va7qhPOp50vdOzQSXtsmm/icfdXB6o61rPV9KEa/HaSYBrC0+0AohChQ\n"
+"ThHiqUo+GK37wPZKCbXJB2cY2nffHmr9NJlSpT/5ydwsmb0B/KHtLXUuyr2CKZ6k\n"
+"Hb7mwP9wofWG5w9C0c1yf/6dD8kNTHAhmVuMJL0pakz8L4wB2nCrwdiTFK0kpcqE\n"
+"bcysEuijCOXOjOHf6cBgG735NqLlT0Bea0AwQ6blQZ/KetYWJIyNJo8gntlrFDuP\n"
+"P6hglj2pKx01IKPVIDmT/hJ+ZfhBVbos3CSONgmRvozoIFNmgdw4uUGKyxQzrz96\n"
+"k8QLTy3MPhMU7Q9t+OwCqpc3P+xI2K06ey4YRFSTyBDF2vk5y8NIe/GaNedRdv1g\n"
+"bkxFkXRvC3XBPAltSLRUVU2969ZBv9ZdVUtcRVl7+1g/yE5wqNTwOfDYoLSPD3v2\n"
+"XhWLF5LhVRXv1v8FebrhO7rbATqeV/gLCbD10ePiHlmMCEMmA8SkMfljh/6FyEJr\n"
+"L3o+X+dSsqCpNpmHLza2V1jxmy8QRvZMTA96ITSxwJ8p2dn1nNd6HAkLRiWzr4Ct\n"
+"TW9F5+Hoo9K1gIi0M+1Rm/nVgWdP5MQHym+ddamgLt9EjRNl4PfyreTc3xF1E/GG\n"
+"a3CELmbXU9qIMUBuiKfZn2atrER0B9hJOdffsUAAarDZm55V3OBRtHYfILyLnfyi\n"
+"ATcPsLq79o0xWcXmAGPfWwJ4Eo/AXJ8YpblNzdO/KG7SLLsujOkfOJOsMS+kPdQw\n"
+"VgEGH6XZS2UZrA8tuIasJERsozgdyGI2YgH1pOp7R1fla6sEsJChRPvUrT7HJAL6\n"
+"BAQT3Nj4qsullNAKk78J8oB2l/49xVIcSY2uzAJobnKRuczc4fE5scHrFCosU3F3\n"
+"6T5VaYCWpLK1tiFZgb7I5gAAAAAAAAAAAAAAAAAABQ8TGiMp\n"
+"-----END CERTIFICATE-----\n";
+
+static const char * im_ca_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIVyDCCCMWgAwIBAgICEAAwCwYJYIZIAWUDBAMSMFMxCzAJBgNVBAYTAkJFMQww\n"
+"CgYDVQQIDANPVkwxDjAMBgNVBAcMBUdoZW50MQwwCgYDVQQKDANvN3MxGDAWBgNV\n"
+"BAMMD2NhLnVuaXR0ZXN0Lm83czAeFw0yNjAxMDUxOTE1MjJaFw0zNjAxMDMxOTE1\n"
+"MjJaMBoxGDAWBgNVBAMMD2ltLnVuaXR0ZXN0Lm83czCCB7IwCwYJYIZIAWUDBAMS\n"
+"A4IHoQA5Wkc2Mz19rGlaczLiVz+WqRHexHpdzgKP/wXNiduzFZtYxbXVPJHPybkX\n"
+"Jx8p18vxsTHBsUz1ST+nOqLKH0RAqqEQWBLmf2bg0To7aLkN53lg40xcZ8XszXBg\n"
+"H0cjl/agLFSvPoSpLv5R25FeR5mK5pkpaCzY29Wg2xppeAFeMPL1YBUtsxiDoLWG\n"
+"xnquhrDRWrFCHDcpcG+qFCrwSk30EJgIEFz8zAucZk++MWXINmm0rKzqAZHkh9zo\n"
+"IXHTulI6zFGxFDP19eRoke4AzmC6xJyw9W+WIU84TyU9o4bSfQtJjLFn7qk410a2\n"
+"/zm3mXS9fQiqqZm8M6mAk6UTFVHHr6/SzzwFutn652qz8xhSDkOlsd8jz9XixEpk\n"
+"wXQiZZMaai9MTW9m5Hzi+8mFMcMmGwsIgTWo/G0os8d8hxPWcRXO4lXfuHa83dUP\n"
+"KWHEcroEBjdN67R3qepCONhvq8VSHbw1/tkm8VjzTz2VCQUjFSkn/rxqJAi1cIoh\n"
+"ZEBuDuJ2Ilx3DlFAQTrMl6L6HwIqetYYyYoMUSL09MtAA1EZTIJm6CHM+gxnef20\n"
+"mLIvWDaXicm01FpBaxJwGqWD0fS+2Ii4+XwnxYh3kKLD1x1O4guIqPDPG3NVBdu5\n"
+"pKxYLAgcE9OJc3I7SfgimQCweGuEUGwHSqMjcYKYWiAodlo2z4+GtAN08Np+Av0/\n"
+"fVTOHW6aFMnvxfcpsZv0fBcwgOm+vvuxmKdIOTjJiQnLPqWWAykt3LM5AaoBT0v5\n"
+"Pd3OU7z4p/XXwkYgQXFV58nOttFHHE0ANKiLUF7iBmu2Vz3+n8ewTW/nYABUEZlC\n"
+"W+VfnhHwYhgmtg2krCBW9KNI1Vvfw9WutJxT/8+lAN3P/ZqyvF6vS9rECfn1zFIr\n"
+"mbkYlopTV6H/pD2GQmYsrEJOK8nW4SpXU0XDa+545TWDbuj04TD80B8Ao193gndY\n"
+"F97SzXgxLG4koxQzu7l2i8Fa3yr7sbjnh3xvW3abL4D7x1W2I81xVPyTfopRRUq7\n"
+"/mJE621LWpbSlLAxsnlWpt8JGw4Q3vnkxOAox5T1Mh8LyZYi7f8qx7uCMxZyBqeP\n"
+"EYu2/bHJCXv0EOZnKbr2gVy+5rnSS1hxOvRhiT3lE32/ab4Z/iNkG8oHnqkWPrdC\n"
+"HyyRtzMuCXK/4T9P/gDi7DyXLl2uklBm6YE+zrn5dENmfjUOXOAkGZ2KQ2fmbob1\n"
+"QJT14ry9lbZTkCxdC9MfOJcX8gvA3/Jm2sfOjpBJpj/r/QSmpAWV8AEE/qHfJhm0\n"
+"e1qMxcCecbTyE/JoP42cBgVo8DKy4AUJsbBjtBY3/Aa6bRrkg4M3SFlPfUUzaoLK\n"
+"AfnkVk85uqP1SV7dioChdPAz/8XvDPWUjLKxO4TXFZz2Q4SwL0OPwld8mnKaVNQI\n"
+"9oO/WIXgXS3sKLRdnfwoI6HsslhyyM/tHwjR4kIt6UomMy2GemXwFk8VGU+dcezy\n"
+"QnNHVbtw9xyjWV93JMYx+rSIAL0TVwDO/rIVLeIbp+nPDyh19TSwWG94JSTv0LZl\n"
+"HEJp36/QZ6gtRu5ezHzqJnTEC79ovO34UIC1v8zGpL5Z05+4eRqSRZ7iE/PLJ7Jb\n"
+"9+KNRvDJnwdOecjzkjKu+CJ04x8UTs0VixqXuU3CFQJ8PfT3Ed9lt5HyiT9Wlv5G\n"
+"MQJBsZBanmafEhclzN3eeBptNcQKWyxJ2bJMDxjDGncIeQ+NbYA5M7f+RXv/PJ8/\n"
+"ArZpf/OHyGQFKA81m/TiQZHaQBWHkDdjw72jMjim058VpC2OBqff6aOwXT4RKxo6\n"
+"MQ7HSbbtm88YWaBrqj/U0ji90Vxzdp5X/a1KRRadsedvm05wvxpJ3AxwBi4KBy7I\n"
+"BF00dBsXQqklG9AItqE1y0BsGakXOWD8lM7CpSMKeIfFBSXwWOHJSleM0EqGSjUD\n"
+"DHU0F5mWxVzFfxg3C/5irDFpT61d02M3EEBcJLjWkHNHpziLgH7b8OrH+utce8Ve\n"
+"5Oa4MMoDz/H7k118IP3Y1WMvymniD6f6Lv4Vja05/f8K8l0OvRIJgNGeW2lgcLfo\n"
+"4EsKcYLC3h5Uaf2hVnEW4ADj/rFDBKRlCyLpl1loimDYpn7RyPUVoz3oTzkwxKbP\n"
+"sITXRL5X0j5yLZVlJA+ibF+5+2slZSzBJqyM5EkbWTqE/rCVIWg9rnoofAFAtPlB\n"
+"qk2PVCpQYipEKnRukG/DjxIStfKitMy5y6baKiVRIdebX+vRMVs9csPDIBMg20KA\n"
+"wOuTvU4sxoPx2/2QGb9jXSoLLtjTyJEwOiGAB2O33XYkU87SPY0nLfIjyzcef3ni\n"
+"lxu49W5iGCfAcDl4ZLgU8rQgQxk0wWNa8kCSjMkvm9UuXerurZgWHaoG25lE43r/\n"
+"GxpqiUKfqW78BWOHd2MqFU4UTkzZmQdvvQ8/hSVQr8UGAo7j7KLpnSKkKVSIylwR\n"
+"qvfVnxp4vrrMNh1EHJnWvHklxnYN+Wm72b/93tB3IyijQJuhrNQXKARbimy3o3SF\n"
+"yHyOVQ5EwlQbE0w5Au6x/Q4jelSVutv4I9PRH3ahC+1Auh3VklsiFVuCfHUESUoc\n"
+"gL7HwPEJpMRrSMt6siLSarrQyVbJl7Ci/9GJjQ+ujb44a+bXuqNmMGQwHQYDVR0O\n"
+"BBYEFN227zIe99jSqqfjEh88cm1lhnstMB8GA1UdIwQYMBaAFMqHQbYKyHbMbnoX\n"
+"U2Q8yK1KpzI9MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMAsG\n"
+"CWCGSAFlAwQDEgOCDO4Azm59ev1Sf7AxeEOX/uW5iWvxURQZ7mFrOjnEa3DTdOke\n"
+"47qObh0Guh5x4r1QFGPmNNG68XOAe9JqDQIJjgoeuXFKAYqcfIAJwQOiX+rQjuUE\n"
+"Wm1dyejrj3N152c17Yxl/DLjxwA9cJ1XxOU57jNaqqjoUSgr3k5D2aH356cpIi+S\n"
+"xXWKwZcSTD97T3/bJOyCKQWxSg0qmPYwAVIWX2jMLcPaIkDTtBFijuvgfjnCJ9yC\n"
+"sBmSHEhOZmymiXocMvycYZtJTsNeTmOuxLhhcNIFzDr5+PZ4SgViSFkpmxKsPnYK\n"
+"TLo+2RTYAys/G4e/BEgwH++XC9WXidSgdHbguD+86mh39jKcnV9YaZqMhGtPQNlx\n"
+"Dv0vjrW+Ff1+HrwehpULU7Tq+DSRpIPtz3QpoKgQaFYZKiX6BG8eK5p9UQe8AZAK\n"
+"X0v/zTWznAqevmm5t97QyrcHY8aZuu0ix5NWK+0SefqySaR6hew6E9E2EUSYFQJt\n"
+"NcPQMXDr7EvGcKnNOZED5EEdxctC2VLC6jIxoIKkzgwG6iOh/mTrgvYfdzwdb/8d\n"
+"K1x8NoPblRKKztDA5kcVisFDIvESe14V2FuIZPUv8MBITt5pDCR3oyyZEDP3odYc\n"
+"f42ONFHx2rCCCfOV59Bgo3EadvGK+dzONf+j1Gv4x0lSJeTo8XcXNiSl1wa5ppvh\n"
+"mVJCnWdxkCwEiCGIzMHogt9L36cQcR6LpnevrLEPx83yQU06isDjiumpEOnqmOrX\n"
+"Gu6nSr60hPJdyurmphX+WXNONutzRJoRhwdEjZporYeg7HxQpIqO5QWeOuL4fbO5\n"
+"hFCc+Myd1RBJoq8o74Yia2fkYXxQj36AVSvPXwH62hmjQwUxaCNAeYLht/eOnv4H\n"
+"kBbP1RQKm34qRxkpG+PnXXganJsq4tyq8QVcV7htzmi1NbwMOjPR+ue/cjRfxm6s\n"
+"/IffD+vGF3Uxq02t7n4ORtehg3ICq5nmsd8nQCZ6WOdvTUgrX4Lfm9WPOXOv55s9\n"
+"O385JPT5qxWc8KNNUIzNam8VbRNZXCkSHddwDip1Nskbvbl6dSNp9X8ctEjwFI+k\n"
+"CORFh0/DdwFAV+QQf9gfOJ/qq2ZnOhC8U5t+M5SR7+s2bB4pZQg3dAYk3rctTRwi\n"
+"HKvnh6JbvELsXq78nImeJsCjHhhdnxLkEWAvT60fKnaxMpFYHVKgYY/PLGrvJ6On\n"
+"eJWYdvjSawenSSkciNyP2TGcfQObBujB/3pcZ4Si2GlXf78LCHhz2E4cJ9XdNnxi\n"
+"55wCwAHLmdxWjuxVRX58F7pT2dpGl2cVEiORv1z08zYe1X3rRa+mB+wtKui6FTOX\n"
+"fRaUBT4ak8zOEg3L1RjNaXF67ieJDWCCCaPFeGBmEo8y4fTHHxoFncINA1LHLEwo\n"
+"BnGCtoTV83C09my/x0PAdgFzISyz7yMJKtDbCV64Ru6KFsSIBc4MsEHhnl3C+npd\n"
+"OaSOLmaEUUf/wtvTO2vzIs/DMW1JRy2x+VV9SwN7Nadjy7yKqbqF2w2GbG2uRhEh\n"
+"pbDoekg/baH9sjfICetgVsDG4rpcrvJPHnWY1CvlJZ7T7KI6U6xSuhP663VJNe3a\n"
+"YOul2eqKxas2UGQVIj0o5qEXaTIIQJh79giZuCOLTjCi6cLhEgMf0arazFrt+Iez\n"
+"AcO3eWwIojjjan0WQ6dUK8ZV139XfQrGPrFOp7BWSDqdrr6jJyzPKMAJkQVBP0Fr\n"
+"FFHLpvr1cJ+Usgew4ciosNqjsb3uVfhti4eKxAM6jo5M4o++on8FB0yp8sNAoG3u\n"
+"HoWgxKAulZA/vcNLzEPKVo6fihxRQxmT8CjxAZe17actqC4CS+NBS6eBrKhJtJ/M\n"
+"rHqEDOVhni8YTP90gxKn/h9qdS+eh6Haz4X+1Z3KeKgwqMVZmpfuRgWe1nR8Ue2d\n"
+"ZD3XODV7K/3YLHEaJ/Pj3pxyA1nMLzFQAqKKJMsXvLKXlpLVWoKsbIiQ3t4yh1uE\n"
+"IC723nyoSPdGqwgPMTSgxfve7O0XW26KSaSfscqg1zYwBJbkgDKLlR/tTnWmzll5\n"
+"VBgoNth1mZ8+NyI5DboJqQUEgyDws4t6LC0pUx73ddMxaNkqpmujF0iIkuNwZPHm\n"
+"yWYMfDSXkJDUx3cXWiOpS4hpt98H5NGCweZ7y0eV0xolFn5E1o3U6qAl6kMZQYi2\n"
+"JktmQkPMkDXjvwE8ztVdfeGH0WEhXGB0L0RZIuLt+mpnYHU6xnhFOBxleAwfIawS\n"
+"LzVZ0uaNlXNwwCM0eVGh9v8c5vuxzJOf8x5vj6SfFvxTF3RGd97bThOAbh2lrqza\n"
+"3KiqSTxokRBLLubjMB2sUkYEf2OZIs7jOIf3tMRPafyVW5hlEDHd8IWAYMznKBvT\n"
+"xpTJTis5ns+2y8aVRAet3Qiq7jfAexlNr3dx/MAY3zADneQ4PG6949NrP0eYqGKn\n"
+"h0hHmAX2wBHc8CUByRfsDzsAkU6+3F68mJ37ACnobHPO9kBxHICX+RPspzTgTcZn\n"
+"XDJBHp5Th7hmzlxCYn07qjE66jAo39Ity3YeZeXOMe2qXn5b59tgrBHIqQR0lmfM\n"
+"1NbO4PxECUDhn2yKXLvj7tGS2St/J2e8j91jOACvPoq1C6rmLGFxvobg0QM83Bwg\n"
+"V88VW5XJoCeTskqdR4+2v2YsdHAcZChSkglbhiuVWqmUDjZA1xOn4osLn1dJdmk/\n"
+"pf/4ftoSytDJaFwhfJhjB5mwpAM3ftJfHSSqrGGOydvAxtcLKcBWMYPlUThRWKAI\n"
+"yYwyapSb57iMAKnguGq0AWu9lqW9KvQXhzVN9LpJATYSkNddRiWCUyUF3OJnz+UK\n"
+"DfNuWQbFXrfVsJtA4nFIqt9FnT1yX9f1OF4FshhmBG6Eh+PqfaASFzeUxatNlQad\n"
+"Ryq6TVNESSvtNGJSRsRewM7Hpm9zmNEDcbUwQAhMHG8OHsXGUpmVR5fmwwpfwznB\n"
+"M7mdvDf34DDd1nJAxiVExb/ZJIyK1+BCXH+T834iW0Aljo1bQ14Tz8nSu/S/0Fvo\n"
+"Uy92oDYi4plVbB+Z2SmAiqh4fQfW3z/5lLiltZWlEUNeyzoPsdMBYGRAoaqFgSW7\n"
+"a+7Yjqy7NbTUHzO+slS8rJNQe7b8MeTJpKtll488lE5+8/ikPv5G6aK3Q9g+4Vxi\n"
+"cPvosUyVMxA++3BVjHI//1y3EITTQOqgwk4DBnYTy7dfNvCqZr/6F8EZCIarjKsi\n"
+"XFP7kShZpgKz9DGbhP31Cz1ghr/BrRgTQgECd80OowAODslVLCCqCnu4oizmCRl5\n"
+"s1tAOJAaW/4XKvK+PTrSv2EBt5t5IRzrjkxtYcEvgFUkC/5WuGlmGd/xW12QqNyu\n"
+"xPkjBcS8UQY0/2ooQ+/Wk4/t09x0HqwkgF773VhDsOiwnrAuRWYATCyQnxz+quEQ\n"
+"ROilgS4dKV7Byh2NZI8EFUz9RcY41yXp1QSIxwwqgvExNvSi86FQXZT444yssvcd\n"
+"eTdOBIQNhhCK2MRGoZBRBBOJCydyM8UO064d4/jRd5hnuM8Zedki5lQk3hnEmqzo\n"
+"Crx2xDfsvTEXir/3n67p3R/I7JyjZUYeWD5s00RutDAOcuqZ0+WoAtZSthTRwot9\n"
+"23nH6LOyfXQKfRiVV7g1W8rQllREtbBNV380CY/yz80WcPXVFT62VtjKB3Gsqk7v\n"
+"DcSwT6CTl6wtwJhBc3sdD6Z/KKTvhLZZFWxwhlNweBqkbBewW+/s6dB5+zYpOURv\n"
+"LMmbtW2OHSig5MPx0FHB2/EeQHAqVOP43iBZ59nrRBIIJ/71pIqZHtcyOGn1uM81\n"
+"Rie/3em6AF77+BNxcp85fmEXDm/DY4qbUszpmr5ZNqA+6OrCyj/DdRg1CEOVD/x5\n"
+"SYFWwJg70njXa6I/xJeoK8lW8WZNod//brn4OfvgweF4qetZA5tVeIH4oT3TyGiv\n"
+"9+QAitHefGtkiJe0AMYM4Ws3iD7Vb8HhHKwLGxWkln60cXiB9rPbSZJav9j3kwIf\n"
+"45jCldkR3kgfx7TmG77ycXE1Eim0PaBBjANaYjkvpMlrl7P1RehcUl5jMy4mrGL3\n"
+"Xx8rNJizus4SJw3JRcR32BkjYn/KhdbJaZxFIor62vevdcRS//uiVP1HZjgySiqT\n"
+"2Sq/EydG6AhtmfVoUvLfDgSh3cvNWobpqbg3AE0VDJCKifahvyrYtvxJ1Y28dat4\n"
+"Ame8w8rZh5kpI7rl/95nXj0v+hhk/YY9uC3yH6thPkAyOu90kKAx2O7oRNrJmB3q\n"
+"FlLPE4oaNuw0q3hyYVRimC0G9R3nK7HUu/EYpvg/OGbH9C43pCYrcEBcW2fSnsjL\n"
+"wqMknqM50KNLNh6elXXpM0Wi2Ny23uF/Ng8jT8V7n/1tKJAxmBO06GJ4vnVon887\n"
+"P0d3/VZdy87yLnDCzvn+GBw0TleLpVl2f7O3vMbR7/f9DCwuNLbjAAAAAAAAAAAA\n"
+"AAAAAAAABQoQFyIo\n"
+"-----END CERTIFICATE-----\n";
+
+static const char * server_pkp_ml = \
+"-----BEGIN PRIVATE KEY-----\n"
+"MIIP/gIBADALBglghkgBZQMEAxIEgg/qMIIP5gQgfG01K07SPreWSppSwB0rViz8\n"
+"PAwX+8A4M5IcbJ1doNcEgg/A66bg0kv9lur7IwLlhsb9wt5LrVFkszDStneYGR1t\n"
+"SW7H1AX6YZmXRK7IiXkogPanLY8+ob85RKs7R2s/ac1LElnYPONePEQ1sgAUtlLV\n"
+"D+fPWi2TbyeI4sdPYA9h139/xAEmIAWNnkgiz51GFFLaZVBJtlhV0SaDCsrVIjDn\n"
+"N18mUIR1hVcFSBQjYmNlOGSBgQNUgieGBThUJTcEF4JTNidGMnBSZkCCR0dmMRUF\n"
+"g1WAeFRSdIAWVjF3JYUhhicDUoQ4NUAnBSQGJUVmOCiGVBSEN3UCVSAxgxI2NUUG\n"
+"UINBeBM2CGYlQRUjZRZUAYBAQhMmAQFTQoCDYWIhOFdodHhVdYRYJVNzRXh2KDh0\n"
+"WBd3VTVQBHIoAxVlCCgoVYMWQAY4IzMGQFeFRRN3BFUFIIeCNXVEFjiEiCBQRFBV\n"
+"ASgTMDIjFxEAZSeCFThAAFRDM3ckhHdwNEYBQCMEBgdVJAeDA3NSEVYCZHU3QxMT\n"
+"NgYmRjAmFEBWCFUjgxWBF4KGZwRXFGdhQhEwVWI4QHcBchVyUyQ1RYZoaAJhd3dl\n"
+"ZEdlVQCACFUIISgIJ0RlZ2UChRIBRjYEgkAkEnImYTSEgQZ2JTVVGCglGISAKFJ2\n"
+"I1UWOAY3VxWGZncVgDQ0Y3BkFygXEwMmh1cHhjRCaEZYKDJFGDRSQXckVIWFgQUh\n"
+"BlRDYyECZmQhVWIidBM3SAQYADMFcFJ3IwOHYUAmJDdmeDJ4hycQVUZDhXVAgYdY\n"
+"Igc2FnVHByAwAiNhWFRCY0ZgIoKBRHgjRgBoghWAhnZgUkcANmiHeIcXZWUiQFN4\n"
+"OHglcRAlUSBRFndldEdxInB3IYQydER0MnBlh0ISd3gnZxhxNwZ1VygiVCFENjeE\n"
+"WBE1AYBWJwUQYUdIJCZFEHQ0VWJDQlUSCHglBVcjByQHQoZVRxIFNBGHSGByYWdR\n"
+"VQdWgWMiAXQwUgg3UVYTMzFmBQaFQQd1CAIDMjARVGASdIGHNzE2eDQhFnNWJgFR\n"
+"B3NwIiMIEWWHOIiIR0FlVxJ4QFFgQoBwEYAiE2JzcShnQiSHRBGGCHAodhAnB1Ng\n"
+"QjQhF1ZIVUInRnRReCQSYgQzFBF0diJXY1QnQwSEZGcBQRRCZTdgFyN3RCBDcxFw\n"
+"hVBxZSZHNTVwElISJGJiV2ZFBUZBhxRBJBYhBCMmI3BCN4ATN1EUYAODCBEwA3OI\n"
+"SCJRJIAHEiQwQ2RxGFGEVkUgOCJBgkV3IiU4CABGQYhFJTdoUyd0M0OGRoNEIFgw\n"
+"aFdRIHESBAACA2M2ZmdhEQBVERYQV1hoRGRTRQJVJWFVMVAnEoMxVVd4EIVWYTQT\n"
+"FVUyhFJ3QgZSRANkBFJjUzQYJFUhMQUoAmNUBBWIMRcFJBV2MUIERUIRABdhBHGH\n"
+"hCRAFRZIiBdxhoclNHVUhTQzaCRXEnAAMUiCVyRghWQhB0hTM1EnI0gBgIVRdRYn\n"
+"ATCAYjgmZlAweAAyJUBmhXdmiDdxUgUgdoMAEjaAQUiBMBUld4JXQgWHRQIhCHCD\n"
+"ZwdgIBFWBYNygxZhRnNoYoFYZEEXc2hSRldIJYFGZYYwiBQjAkQ2RHNRcHMVFGYT\n"
+"iIMEAoVUAiQScHJ0N1IHJxAmFoKHdIBnY0JWeDEwAiURF3FHU0A4FwaBRzdYUDE2\n"
+"IidICBaCImAnCHNhAFBnYxY3I0B0Z2cDSEEFRAcHGBIIBIFxVhUmEWWCYhhiJwhQ\n"
+"hndDN1FSAhZziDZ1N1VHc3KEdwAUQUSICCdhBQRXMwZ1dHaAURV4QRRAcCRjNAhE\n"
+"KHRIU1QxdCUSEzcWgHcyFgYIeDVleIESgDKFB1CEd2U4AFB4QiZxIYRyEXUFVhgB\n"
+"AIVgAWcBIkchODEBVYcVQzB1QCNBhmE3ZBRxMChnATd2ZIBCOEN0hodWA4NGJiMQ\n"
+"ZTc2MmYXhlAxZkEWhAEmEGY4MCWFWDNUcFFxdGFXRIQFATdwFnciBXIEFgM0eAIy\n"
+"MEhWFjBghWJVVwR3Y2cwMIWAdVFTdUE2N0YxIggxVkYASDJQUUIngEJzNUSGRkJk\n"
+"FBQRhoMoFYVFBIhEd3AmeIZmYr5AgUPbAY0dBsq38J2IhecY7RWuxAblOuGnxdKA\n"
+"qrHC5mxVnFTG9dnonRbX6H2gtN8rC0DJlOLbR2/JAxLcK12E+aUfrOM4LHF1Iqo0\n"
+"NV4a7RjmCjm8VCNQDBPxGxa+9ChPZaw34dTul/KpUY3rrCmvrrWedvAwn7kyigXm\n"
+"1mq/9SoxI9QdqnhGOcmqS9obFrb0lWjk8dXMoU0QGJ/jM+zf2G9tv2qenI3h7hub\n"
+"fcOQO0MW9EIgFhmw1smPWS3ruZHyesVwQ54rcaWFkVXWqwVrXcGf28fiOTFu8lFL\n"
+"picqRDkhEJhMiq7RdfTcPr9XzY9wOqC7CoE4CRqC3URaTUspy6HFMNQYQIzoXjRX\n"
+"9Hf/TR7O9rJ/HvQoeCi+CHXQ8QFfER31FkpWYawnY21m1ZV96w5HkhjsGDASJ2qp\n"
+"3sPZVtJW0456Endn903DiYq3t80ghNlw6igMTyhYqO33LJ3ODghePzylLmiqBLxf\n"
+"4V/nFT1ZJAdL+vVvde0HIAIBnXMKo1ecxBOXt+NEMqjnkcNo7rMcPcD8MtJ7/2xC\n"
+"ppd58b6ihtCTnBorSJVHrXXulsh6BFL9Ryry4In/NsatiEcmFQdM8NF/HkBsQDqc\n"
+"tAyPd3rPhmfcLK508xtpW8qTwbICDfsykw1H1bpON/rvFgeiUzD+He7mThz3Mb3j\n"
+"huWT0xhMnMLBCDKZPE2K5+eU2gCzWPErBnOCUfCXw75SWTFRtnMSFj0vx8H1cLvo\n"
+"uUwA9RNqkcwvqcWDGHu/vVEePHQpqsTePTj/ALvNuNfdGAUOPm2YI3LP952mVga3\n"
+"0HApI7wb2sVYLxxierv+iiYK9dSXqkpt/bH//jm2vIPItdUrfEnqX+Y+20EY7qww\n"
+"eOJSIsDAG1BrUrOmb685yhMGA1nOtX13j8SUo607aYQ5ipGkSVuH1lQh60dWsOL6\n"
+"gwodsrvAcLNLndHyaOXtGls5GaMOTzLj/jF0bDfVin8RnAmNwwQc/mD0ypUi2c8p\n"
+"KugFs+4XKSKVF4D5OA7BNhUHPnvZ8G6li2KqoWwJXR7yPjSwVchJ+wDJ0n6Oeh1j\n"
+"s3iTlXqQbGSuckbJx+cWEwX7xK2ZIwL+6zhRhX3jiiOkEgkgh+pIp53rZ091rYbR\n"
+"4dugwK34jjPVU/eLUepG/WFh3vnogXjetBJpeRwz4Dgv2xr0jIg8elrCSuugFJY9\n"
+"KSMN3VXnvgIxkJCQNv84RsvXZykCbAQtsjN/uYonQT/oxPW5r28R03cXxjvdXORt\n"
+"72cRsRxc87rugvVFqzC97tm2IiegIM5CZlNRPVdB4udG2CSh3rvfvJYEkaGlnRJA\n"
+"kIfaZEN6S6WfF3w9VpPvREb/e47ELsIt7a5gvoLSqy/jmTi4ezweLH7FE1JZO7ts\n"
+"9plvPxcKfl2Lk/OCBf2Q1BJ/QZgiWC4bAXfhYjRluBC54zzAAZ+qeoiyGVh3OAuH\n"
+"qflFwx2zvHecVmyzGkuoX8ZXWKqEyqdvvgDEwiWIx5RNFWDknNAAqmewei7BqDU2\n"
+"17fB7HDWWJz/DSJdav8sQCrHrqSdOH/YJLlkaBUb8rcMZosEsR01n+NdRbavdVQz\n"
+"Ww41vkd5drkdpb6OIewPkiCjCQHZjbVI8DGtMoiR3b3hA74JStFQRiMqo/jeWcvi\n"
+"1fowInbimIKgletnd0/5J0c0m2N+qI0zDFhoP4sROh+U85icSaJQBIgMqsHO56SV\n"
+"nbKOSWgcQB88t5DwiBW7Fja7dWgfHeqGV9hb08R7thxWTuRqgBdBHM+c4CV+dL/f\n"
+"GruGulr52xClzsbxGgCovXL9UzZxXvDYOoJzcMpEI9aOGyjm4aP1eoOLa/DLR9qn\n"
+"ktqY/i3KUrzqo3f2jIO2wp1cbTMItjYXqZzeVrgVfM7QNG0mTPEWKXr8Yoypa9N/\n"
+"XHVqnP9MKKkGDqBX9C3B8NpAbJIxwbNG4n2UjkjxAPG/YXwGndn6inhwLRHgwmdc\n"
+"/quK/ott2wcrzFZYDPb6TUe3RBJUUFxgvlOfCc3fKNAC80inrxGHCSd+DCMASeZN\n"
+"s3XFu7egjjae1nliFZAtCSd/NTsucuh03kesA3hKAV5/7GGLirq/OwjSAkJOgYsQ\n"
+"jD2P86lFEjBflgnMxo1zUDRGTzE2wBlE5WX5F3oeFOoGY2avGEpJlB0lXK406MC2\n"
+"4iYRv3+kcN3D8cxjLAhHEgG9LawZRRqhZNAIuauFMvaoP6oSvUyzH2FMrTPpVTap\n"
+"H4Efa6pNfH8hfhsUw11pga9OJ4rzxHTt969QTUHK4axcE7xHmfYO7zvMMJJPip+w\n"
+"9aA6WDfpnmy4IEqYMDOJdT+J2UnlesKevgl8/PHQ3flrkq0swJqKUB8/NzFZbzwU\n"
+"YggWPvHkwgd9Yu4huWHeGzcMpUPUkQtv5bbshdLidlVbrWiJ84NcTYDrrjYJp2U1\n"
+"nHEmkJaDoKqUwF5PGeocEymeY5i+rt4q17qVYQroNZHKKUwdexuQUGjehQk2mOLC\n"
+"87Eu1aghe53KxXNnUXgxv5r89DYn4rjNUW2zSacUASNAGwUWbFFv3sCJeyXK7v5N\n"
+"8Gld8lTgH2cgSdUhbOopSXOgFjPtj0LhJqpXueywHVa5ggZyw4JfdadjMyngf+50\n"
+"ragPVL4sTKuwmVs0mE5eTgV8MvidHjk1Cyd6TVRZAAiUPM9zqNLvxSaDWRJGRTQ5\n"
+"eD0/fYDaZwFcBi6MSYid55DAo7KUDxuMQ5R7xsi7PWzGIgBPV8ZG+4WO23g3kjNA\n"
+"xVSZSM/QtGGuVu9YZLOtamK8YYgnPIMWipxCWiSl6YKhf/BT+iN539DQvvrGPNft\n"
+"/zEIkCuy2kCrL90sh1zrINw40dWJW862h3TwyhHC7kQPCGhpBecKaA+xisac9bJ0\n"
+"XVe9kEzEyjF3zJ1tsKL5cx9KAcLsMS2NIxT3ailMLliMJgsKcI7do117JBFx2vlB\n"
+"dT1Rr6sibZazdvqPLROHm1tqVnYIuUGf0bcDsXO+V57k3jybqooMjFOgqhAL3aoy\n"
+"wybFBkcDobRJ45AncGilwKhCap1eYkOikQsrhGEXFkc8anA9AN9cE5t82WfHexhb\n"
+"VUIPtgpuupnDU8Zuo8aKaTxlI+Q+p98+ityvCCSIIlRxzwtQwTXX6DA5IQRqUjh0\n"
+"Dcwy1o/j36PKNUWEWBorbxssML7FWEdZvwqoJ7F2u+bno5lCpPHumrfULdzKinH4\n"
+"/JpMpTft6CCOL/zOX8TYOo3Uns4E+ziEH3E5kS2IC6LZwia2uqEOD738DEgHmUrI\n"
+"xcdPXb2KiHTCSFOzTLjOOWzsK/QLhauYTh4wdaRwI6XqMzN3c0xG80Le0fPBROtd\n"
+"DngTH/g89SEnGxqKXe7RxVof\n"
+"-----END PRIVATE KEY-----\n";
+
+static __attribute__((unused)) const char * server_pk_ml = \
+"-----BEGIN PUBLIC KEY-----\n"
+"MIIHsjALBglghkgBZQMEAxIDggehAOum4NJL/Zbq+yMC5YbG/cLeS61RZLMw0rZ3\n"
+"mBkdbUluk3cm3nnBOZHlKiM36m040cgrYqE/h7VqzgkeuupRrOYjq2OiI+Qb9hnX\n"
+"rDM+++8tHNfHxbFEU8qMokOrilVlqAXVaUW/R92x5w3I10vnJVRHkVmpMOh68s4L\n"
+"+rQtSZM4AtnYToJd4DBI0tzV6Id6Synvp7fPPbkU8EpUHbuAegjoEmLLQdDWF4H+\n"
+"CAU0yzFtPT5yTl7S/Kz/XC8dZ2ie56jTTj10K9c1pfjqMdkfz+PGgL6Sprq5/G84\n"
+"upK4DZqUobS/dVcYraOBcWXw5Rpiyjbrsd1zCiV+WQo5oIHBNjQ8dwaMARM2gfOp\n"
+"PbSpIScrabla0w6aklyRTLrjkY4EoByJK964PFBrqvh3FTU+rrYogSo85EeZYKgR\n"
+"MF6bbtmNPoFoaBhEW19S6HYnXbZ4HTfGe6ceGF5i/lmBkbWWDOH2i+TLsRoGubi4\n"
+"hc3Fj7DFMK0TlNjB2z0yOnwLC1MZKxTktvZ3DDAW696Ke6YIg+cvchJy3G61kpKD\n"
+"0plsaK23todSIGpZDAiH9Ohxep2Jv/LJs9aE+EJ9fNYkrolnDSKCqXU/FVuvup0n\n"
+"k9tiWq5gEYGWAUt1Qaqv7I8PaMm7hlhVwmmjZXxElLtwKvlg22Krl6sXHIBmTQIx\n"
+"DS/fFJbrHPkKOFHBhvrfmCFr+h2ihVXvZbq9fvCAik2V3Qtvmot552N5jq48WgXq\n"
+"mnHs0BPscilVieg4I4/VBiCzff1uexLknLD/5Xoak60uw37djDUJe5GR+/REHHNu\n"
+"vTcGjnBmWFKKAaDpnaje1IP7rLcJAC4PRdbuZ4hbjrxUEDR40MMlT2W3BBr7avE5\n"
+"8dgjmtPrVhPqOV4jfzp5ySOMDdeN4iJJQS5giwzI5p6PRi32J1IMsQKHH8nJHZys\n"
+"BxW9TjNomt8K6jU/bP0iH4Xvjz3FdV9M44Ppar8QyTJIlFC9mRw2CjbU7B7Lx9oE\n"
+"2j92zCN6D5/cpM6WjgVNcDihbcYtO9RvhuBlr1RZ7KAhCGrq0Gtggd4pbbJmHoJk\n"
+"7OJVWpocATKfWemo+3e704cYYXiYzQXCmKeZofFFhxsPrxfdWoS03lXfK/+6RR9W\n"
+"tpZgK75OyG40S102UDvjArICbITkMad/hkQOrDixkuAQdE9ZzD2L6MOEjnIF+WUK\n"
+"pUq+JvHbZYKT16RwN33jQ6V7RHtE7qNgziDbluH5RsGmEdO2QFfbAFRanY3CeIlW\n"
+"G/AyQIJtxm5qDkQVNF72eK9jq/NzkA5eEt3uRcJR1B+DwimVD15Jn/22gLF8ETEi\n"
+"E3zp3CxoCeI7paC3V5v0wRsVXlX/hOBWS3JJyBHgT7UXHIA19UT9vnZg9G53XRie\n"
+"T6a1pUav4JFFYBz+6prcoz648uxbcVlUQMiQencVBnQy+yFP67uMhR9hjz2oQOiP\n"
+"2tITIT6B1HNbqmXTvokoCR4eFNtranA3ARAmNW58zICPts3PrvZzZRQ/Wgi14Ao8\n"
+"lvPx8MRDWR0HGchjukbqvS7RBPMsIWnbR+qAOySfBR5T7K/0nIScQ/crP/79JCG+\n"
+"NmzEuJPOhIZDySK7cWoSHedHIx/6I6tTBceMumZVQXmCganZC+xz7MlKnUFeutxE\n"
+"4j8RWeBxCjSB+W4a1XXy+J0WMebVgDyc/b2eQ/MlNL0jnEjpD9T7YPfYGGZd6qbl\n"
+"+o0OqoTzxbJDXGmsoko0l0VYXFxCsoFfNYLWBkTvYS2lX59YCpUrtiftxlaccEYG\n"
+"6ZTf/lkmveMUClBvLyFzjD6sHw0Wis2j1ySNbq72I83nP7QrL/d6xReR82dxrymU\n"
+"zbg8Xuc6b+ZwrXhbO0CmXnyNuiX1Kq+JIv96l08CqY57MMOon7MahkXUJq+8hEQ9\n"
+"AVelRhR851+15uNrtftaTNipWGRlqDxlJaBWOgj/wtuLzSDLcKMsytaprVrf+Ez/\n"
+"EBaaKRhtZsAumtE+HUc7PwS+slgDsCCg0HD75h7g1UDtlYgGEiLHW9qqCgNBJ9xx\n"
+"1ORDKvzVMJWnerECKk1afTNt2NaGXDHE6fe/AokP0lhHHIw90bplQbZn+LFFYohv\n"
+"GbEnBoPIdRPUse1drJr0Lc5b7Jp+n7lDS9uQQg0Sys8o6gfNd7Nme1MlDh9O7mpW\n"
+"0veeNg4LI+GVny7Zk7dSH6GtYZjl79DzhnHQ+n6tTl3I7dCMOgKtyZN51/LVyRZ+\n"
+"8/3WwgkUDG1DMIH6D7beW7+4FlC4OZR9a3uFclPX+4X1b6Jl9VuN1zaXeIyC5lBY\n"
+"kXcfXRT0yF2PauGQL+Qzv0J+kM9HqBO0U+p0uqkzbblIPMf1Gkff6MeM4I1RAiB7\n"
+"RqsoHekxIWkNyhLenxaKJRDCO4BOoa7QKO2FGYaUdQSKTUKO9PWFRJFmXk4YlYmi\n"
+"pFCS69VCns4mElOXm4ep/+E2tAxlxNyx2LyUVJkwySbTnSOLJuNplUf4j4EwAxid\n"
+"3gGoznQMipLhYyemI/iQQ+TtaGSSz9STTjky3ajrNUI92Rr1n3M66z7hx8Zhh3dp\n"
+"0WfJ47y3KBtFjN4pVHDs+RAhcF5f58vy1iFWdHqB1clV1SU6g40D0YXAlcdPN2cV\n"
+"k1M8ksQo\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char * signed_server_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIV/jCCCPugAwIBAgICEAAwCwYJYIZIAWUDBAMSMBoxGDAWBgNVBAMMD2ltLnVu\n"
+"aXR0ZXN0Lm83czAeFw0yNjAxMDUxOTE1MjlaFw0yNzAxMDUxOTE1MjlaMB4xHDAa\n"
+"BgNVBAMME3Rlc3QtMS51bml0dGVzdC5vN3MwggeyMAsGCWCGSAFlAwQDEgOCB6EA\n"
+"66bg0kv9lur7IwLlhsb9wt5LrVFkszDStneYGR1tSW6TdybeecE5keUqIzfqbTjR\n"
+"yCtioT+HtWrOCR666lGs5iOrY6Ij5Bv2GdesMz777y0c18fFsURTyoyiQ6uKVWWo\n"
+"BdVpRb9H3bHnDcjXS+clVEeRWakw6Hryzgv6tC1JkzgC2dhOgl3gMEjS3NXoh3pL\n"
+"Ke+nt889uRTwSlQdu4B6COgSYstB0NYXgf4IBTTLMW09PnJOXtL8rP9cLx1naJ7n\n"
+"qNNOPXQr1zWl+Oox2R/P48aAvpKmurn8bzi6krgNmpShtL91Vxito4FxZfDlGmLK\n"
+"Nuux3XMKJX5ZCjmggcE2NDx3BowBEzaB86k9tKkhJytpuVrTDpqSXJFMuuORjgSg\n"
+"HIkr3rg8UGuq+HcVNT6utiiBKjzkR5lgqBEwXptu2Y0+gWhoGERbX1Lodiddtngd\n"
+"N8Z7px4YXmL+WYGRtZYM4faL5MuxGga5uLiFzcWPsMUwrROU2MHbPTI6fAsLUxkr\n"
+"FOS29ncMMBbr3op7pgiD5y9yEnLcbrWSkoPSmWxorbe2h1IgalkMCIf06HF6nYm/\n"
+"8smz1oT4Qn181iSuiWcNIoKpdT8VW6+6nSeT22JarmARgZYBS3VBqq/sjw9oybuG\n"
+"WFXCaaNlfESUu3Aq+WDbYquXqxccgGZNAjENL98Ulusc+Qo4UcGG+t+YIWv6HaKF\n"
+"Ve9lur1+8ICKTZXdC2+ai3nnY3mOrjxaBeqacezQE+xyKVWJ6Dgjj9UGILN9/W57\n"
+"EuScsP/lehqTrS7Dft2MNQl7kZH79EQcc269NwaOcGZYUooBoOmdqN7Ug/ustwkA\n"
+"Lg9F1u5niFuOvFQQNHjQwyVPZbcEGvtq8Tnx2COa0+tWE+o5XiN/OnnJI4wN143i\n"
+"IklBLmCLDMjmno9GLfYnUgyxAocfyckdnKwHFb1OM2ia3wrqNT9s/SIfhe+PPcV1\n"
+"X0zjg+lqvxDJMkiUUL2ZHDYKNtTsHsvH2gTaP3bMI3oPn9ykzpaOBU1wOKFtxi07\n"
+"1G+G4GWvVFnsoCEIaurQa2CB3iltsmYegmTs4lVamhwBMp9Z6aj7d7vThxhheJjN\n"
+"BcKYp5mh8UWHGw+vF91ahLTeVd8r/7pFH1a2lmArvk7IbjRLXTZQO+MCsgJshOQx\n"
+"p3+GRA6sOLGS4BB0T1nMPYvow4SOcgX5ZQqlSr4m8dtlgpPXpHA3feNDpXtEe0Tu\n"
+"o2DOINuW4flGwaYR07ZAV9sAVFqdjcJ4iVYb8DJAgm3GbmoORBU0XvZ4r2Or83OQ\n"
+"Dl4S3e5FwlHUH4PCKZUPXkmf/baAsXwRMSITfOncLGgJ4juloLdXm/TBGxVeVf+E\n"
+"4FZLcknIEeBPtRccgDX1RP2+dmD0bnddGJ5PprWlRq/gkUVgHP7qmtyjPrjy7Ftx\n"
+"WVRAyJB6dxUGdDL7IU/ru4yFH2GPPahA6I/a0hMhPoHUc1uqZdO+iSgJHh4U22tq\n"
+"cDcBECY1bnzMgI+2zc+u9nNlFD9aCLXgCjyW8/HwxENZHQcZyGO6Ruq9LtEE8ywh\n"
+"adtH6oA7JJ8FHlPsr/SchJxD9ys//v0kIb42bMS4k86EhkPJIrtxahId50cjH/oj\n"
+"q1MFx4y6ZlVBeYKBqdkL7HPsyUqdQV663ETiPxFZ4HEKNIH5bhrVdfL4nRYx5tWA\n"
+"PJz9vZ5D8yU0vSOcSOkP1Ptg99gYZl3qpuX6jQ6qhPPFskNcaayiSjSXRVhcXEKy\n"
+"gV81gtYGRO9hLaVfn1gKlSu2J+3GVpxwRgbplN/+WSa94xQKUG8vIXOMPqwfDRaK\n"
+"zaPXJI1urvYjzec/tCsv93rFF5HzZ3GvKZTNuDxe5zpv5nCteFs7QKZefI26JfUq\n"
+"r4ki/3qXTwKpjnsww6ifsxqGRdQmr7yERD0BV6VGFHznX7Xm42u1+1pM2KlYZGWo\n"
+"PGUloFY6CP/C24vNIMtwoyzK1qmtWt/4TP8QFpopGG1mwC6a0T4dRzs/BL6yWAOw\n"
+"IKDQcPvmHuDVQO2ViAYSIsdb2qoKA0En3HHU5EMq/NUwlad6sQIqTVp9M23Y1oZc\n"
+"McTp978CiQ/SWEccjD3RumVBtmf4sUViiG8ZsScGg8h1E9Sx7V2smvQtzlvsmn6f\n"
+"uUNL25BCDRLKzyjqB813s2Z7UyUOH07ualbS9542Dgsj4ZWfLtmTt1Ifoa1hmOXv\n"
+"0POGcdD6fq1OXcjt0Iw6Aq3Jk3nX8tXJFn7z/dbCCRQMbUMwgfoPtt5bv7gWULg5\n"
+"lH1re4VyU9f7hfVvomX1W43XNpd4jILmUFiRdx9dFPTIXY9q4ZAv5DO/Qn6Qz0eo\n"
+"E7RT6nS6qTNtuUg8x/UaR9/ox4zgjVECIHtGqygd6TEhaQ3KEt6fFoolEMI7gE6h\n"
+"rtAo7YUZhpR1BIpNQo709YVEkWZeThiViaKkUJLr1UKeziYSU5ebh6n/4Ta0DGXE\n"
+"3LHYvJRUmTDJJtOdI4sm42mVR/iPgTADGJ3eAajOdAyKkuFjJ6Yj+JBD5O1oZJLP\n"
+"1JNOOTLdqOs1Qj3ZGvWfczrrPuHHxmGHd2nRZ8njvLcoG0WM3ilUcOz5ECFwXl/n\n"
+"y/LWIVZ0eoHVyVXVJTqDjQPRhcCVx083ZxWTUzySxCijgdAwgc0wCQYDVR0TBAIw\n"
+"ADAdBgNVHQ4EFgQUJPWgYA8LLq4MCf1piahVdRM2L1kwfAYDVR0jBHUwc4AU3bbv\n"
+"Mh732NKqp+MSHzxybWWGey2hV6RVMFMxCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANP\n"
+"VkwxDjAMBgNVBAcMBUdoZW50MQwwCgYDVQQKDANvN3MxGDAWBgNVBAMMD2NhLnVu\n"
+"aXR0ZXN0Lm83c4ICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF\n"
+"BwMBMAsGCWCGSAFlAwQDEgOCDO4AL+NWTFvdEUNLN5IUivvP05NoYbA0pmOm1VzU\n"
+"Ll3zCMHepauAJQsjw5BHYQ0H1Wr1NoO4Ji4nPLyEvUY1TiSfTnlQDPHRu2LBKNd7\n"
+"2pHBgYldTKASweMBwYeozgiaT4qn62nscsIrUU1iZiHX3diqwUxoF0mdRW6Zi1PF\n"
+"/vGH8t1rCzGD90EJV1UgSUEmx0A9MCy3b64VQLsuLkw2fOtqZM6sLRWrcE6d7use\n"
+"V55jqo1zV+VZETuevQsG+DJp7ApzhF6iDomyXvdxE6njcRZ1yW/hCt++bmRglFoa\n"
+"TCyYr8EiKPi0RhHFNyrVUtWulfOqiWEdOyRS2dLpxYmJB8Z9S9Vc32RZMmBxtx9K\n"
+"o3Dx36q3rUNJxrZjSxOgMpyeuzDp39M6pbbKwj163EKumjQfnYpVYYtKuSplqtyw\n"
+"rQzPg4pxhjyMJS6TlQg0PdQJKIdLjaqeJNyGgol42IR+TZQe7SHWj9ipwI4kCK4t\n"
+"1eapxcYDkmWAxv070RBmsJC/lwuHSl0tl5v31noTC8aXp9feSE7eGpv2UerJ/aIw\n"
+"U8O8Y7n/301f5xrRRo0FJk6jg4UkQWrhuo92ztP1fnQ2NKAfcpp+nfGjKonUdBKb\n"
+"XJ0DrzOK/CkvADe7i2K+KROsJphWH9EswPhJpL3exW69UawtpW0JCOC2faKJJ/dL\n"
+"fnmUAd2cvA4xigZI+IO13gITs6edwwpY/Z1nYcVDWdAWLluoaZoOxa3cUx1vjQhM\n"
+"++tHqUhDdFhD8rfhEiRHTypaz6vAVC3BbrOb64eXutzWnUUDlyfGTHFseF4pFrQO\n"
+"02Zn6GE6vUc59n7v7qbLzHkkeFM3v3Ut143k8GhPtoCdnzbRNg/NbDZGR30+EGUQ\n"
+"yaqqVuEveLH4IjaHiJX73Gnbck4PZBRavjLWuIs+9O/r1RKOagaG9UYjBtd8uSPd\n"
+"t33Te3B1uKoJ6O280CVc2em8QWOBhNKSK4d6TfVt2+X4UOjFrQpDgfNaHBKCUpFo\n"
+"K8+eWk+uBmjoa64h2oWt3WtjoS7pZyKrw2HvcMNM9onl6c8w3r4syxUS/K5vAaoV\n"
+"S6DrGJxi8aZS1LvM2ahxeHOdJD4DmHY0cb0f/OsDaFIN/SCpceBcxZSOMbPMAKPO\n"
+"DSF9ZAtDkm2zTynn1NtHUbof1TFKwBSRV5iVbxBzZc0V3sBz+Bi3erg2/nW8IwBD\n"
+"DTc8yNz5OnzJgdOPIHdN0acvutb1Bx0PRjc+gdvj8IH4qt5qKZ97ZDR6Himq+qRG\n"
+"+wLAnucVqQwnA9UGBEmItt1WMFMIG/IjkgQSiZhOYXu1fSa6H7S7vIqGCmM1K2M3\n"
+"UbqCcrnr3X3wBTKijshvo0LLta7u+jXesKrjzMfSOydDy5aPl7bmkDMtjTcxqmuv\n"
+"GdzdFCFpSE4RnG+J6Gjv+E+grC/mfgLEefoeM1H5m7Y5QslB3R5Y7HK9bDl5UmjW\n"
+"FNe4pwynele3/+A4jsHX88bsI08sfh8Ms1XTyxGj+2eqW1kLK6c3U0QQIudDSPnX\n"
+"Q7ZmuqgnfYkN4CyWNocDSAvjbQs7ZjNlE/PVLY7N/cQY4tK+w7OUGKXDXTPtZa0o\n"
+"uwOhj37QQvcigDtiCpPc4qpSK9FrBDnXUkrAlie4DOVaVrTH77GdmFAtZmdB1Vll\n"
+"2TH2L8SskFiOnIPFLKs0RbzIP+SPNIy+etXBVm+dsMYeogw25Ka0GJuv+tifERxX\n"
+"QOZQ3tA4RzD1XGkHtmvrBleeNQZ1m2ox64VIO41O0/08mxpfNehCy3RakfP4taiL\n"
+"8Z3I0FHtV0EsApnxZsv1juTpQaxIli83Vk4TbVKEn05p1N20zkJNncVSSHk9FKis\n"
+"x5zx1bfUAL2ITVuxPLnUpUtS84QWit16uYiTNqp1vm1y7S4aV+pPzsqgGh5opIhE\n"
+"CW3jRvvXL8ftec5My8uSg/rkO+v7qbsvnpcRaZ6DHD70yWTvOj//94tQQ/0Xlt0K\n"
+"LZLTJm6Sd1XG7RyApw+YTXrRDro707e49n6TcL4cTsgaNTxTutz0XK4OHf3AYVrH\n"
+"rXXoRAmcBQ3fF7qI8ZaA1XnPN8F4rG0Z2tGhzjSHjcvKJnDOHd6KcdYW9hrCmmdk\n"
+"O0SJZb42/BUqrwa63DSMQyOQnmuUjMAhHmNAgrCHwLyi/Gwr8xQOxmvGrm4aAfgt\n"
+"G/rCqLRZJUITcYGUJSUwwjFQUn2jQb46OPvjGjfKo1MpDEmQFZ46o7Cjja3lQaoZ\n"
+"h/+NzfqL7nzWSNYGt2Z4a6aQJG7BfhRzBWSyD9nwH4SxHfzp8fdEj7R9vqSX2GS3\n"
+"y3/pddgL4zVthXy+FGNzO3oVXt1wVsTUyTGjLxsNjrwud90NUxxKzdIi/Ta+kMOU\n"
+"c7iYHj4q1jotP1QYLAnKMdk9Q60Y3gVSbxFSWqNAGos0Pp7HI1cu69or82t4bzmF\n"
+"YHi8kBHwhNgq5SZaiqqJ344az/7Nbzy737cLVH5kWpwY7Vcssw8us3DMxCXjgOLp\n"
+"F0pAdv3DWjDXH3XA1lqZ9zun7yQa9c791U+cOWjWL5lImvDDBuETheUIFwhBiB/I\n"
+"1GipLGl6dUsO84ME5SqqrBsyVASpxlqYU4K65KGST6hCbZ9rKtuFiKC/YFttktSD\n"
+"7/g0hTECk+pMm3AftEM85dTYUpgbTgq+6a5DNyvWfrRAjJUY0E+BQqfN6EGNoFrn\n"
+"eNqQEPMcGt3gqqOb8K+CTmOWbhzvrBRzm1708cdP4D6iegb5F3e9hH+jPakpiTdf\n"
+"GybQCzq9ix+yyc+0wTotgU9rDtms/Etg6s4ldl5ZsVnTuIh39Znab1EvMiZiFmDV\n"
+"zATnmdcFjXDwF2smHvTHg5o27GpwVlAso4iZC3X9ZkadgO+UGh8dWzMxiAAlmPAl\n"
+"AMZ+3RtCp0sexUPjItyQMw998hsphO3jgaZsUAtis3c0lvfAgngfhCfEiqyOlMCM\n"
+"aEgSHO+ai6cOnFQojbiVogKiwLlQXZljGFGhQKVJiVa3koz300e5XEZmH9rqEyNw\n"
+"xImm33ti39Ovj2Z1m0VCigMUh0r6FiY87KUVKrz9/3slON8qiH5rtsAF0ZGCNuI6\n"
+"AoW8WlqCtLcgX3tMg/60mC6pLqkvhGXJQkgWciZRXyP2/nrIhhZCHsm838HaHaQ1\n"
+"3C+cmiG2sUHZvphzOFRnIWSetBoiDWLUYQyzEO9N7HE7dXxVCXa42RXucDWs2kab\n"
+"G1lJGtNGJc88qppGyX1+nyKAG3yga9g/neWaO+Zd8Y8JPJqPN7iICYM3ZHuvcOrW\n"
+"MZ+mQ5OHTd1n46OadtUOKMhOmeWEhXZhDExZop0fV2X5mrEUVyFhxnKahh4Djnip\n"
+"KH5cO2XDiXRjpVcIFhA40/LGSBUta0Bdla4jNpwfH6fUgPnwOCQN3qMI977bjyJC\n"
+"Ud2quI3tEKiRT3iT21EcmCuoKv2G266ku7KnH0exX9Fgv60duEGgFp15Z0MW4FH6\n"
+"FxpfAUddEyLs5HbawaDCTCe714ycx/KknGZQWJzuUsqSzzCYK5HtKulOzGmaIgpn\n"
+"RFtXpar+FXF1ZHh62nHV6ia7z66DpqtB022Z6dBtfNSkTU+MLKljjS35JeVfB2Rs\n"
+"y75GcvruPo8Dr0j1aLolqOECJfNBbm83UWPvywkcRQGC4jvRZMwGyJzFjglf8a1y\n"
+"SCi1GpUaRhnM72Ub0J8IreoeZB0mE+EFmXW0ZD/AKwD4r3jJmZKRZEFzwzp1OFOg\n"
+"ljLUR7LNTV/M6nfYXihU10D9UpJYnXf32Jjtntiw6/mfCKs36gvKjt8MLzeZsxEZ\n"
+"KDwXTmsPnHoPQfk9sy5vuzb9PJl3Z6xR+vsaTfQwMzXPiCVEcH85k0VlkTNKNMfF\n"
+"iyeh0f0hBd8+gM3ah3AWnTfKNCI55p6KwOOPu7YtfUExlw3QIXO55vBdvcK0PPH4\n"
+"w6T+9sptOqQQ596Q/p6oGuHSlf+FJ5DTM3CTIxz6TjWmQ8wABkheyowPCgQ8dRtt\n"
+"heaWakbbOSUFWO24Dv+WFdaGXu6SXwRffwoCzIM+geCGz1KY4WxAWnXwySQ1fx16\n"
+"wy6wTd+Fnd02DcC0gpxnGycE13ZjvSWFB4BSs/2YMgHfGO9m/ve+32kEU1Aw7RYA\n"
+"WVJGvgaHG0/ECFV8h0nGaIxpUeln6u+VTd5fuFTblp4GMH48gWOoxpwtQepIOxS7\n"
+"TDADutYr+VHgCFRPqLht+Q6SovbQT9ddUdOEJhWJs721i3z5nvs8xpIRVoOZEhhU\n"
+"Qzz/EB/uLOkg4q8me2UNN4mpjQNbrwKsw+0u7WEaRiRD7eQP0pGz7podTRe/HRaM\n"
+"xrsddxHhj/h8d8+9EFsyNHTDgAcIN9aC6h512LOlGLWKlW6q96ZiQrQ+gXJldP7z\n"
+"XM0jvOlAlbrQ5/4ahYgKqbT7/QE0R7f4BwxidI9AaXzGAAAAAAAAAAAAAAAAAAAA\n"
+"AAAAAAAAAAAAAAAABgkOExgc\n"
+"-----END CERTIFICATE-----\n";
+
+static __attribute__((unused)) const char * server_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIVtjCCCLOgAwIBAgIUS/okpA0M+TarUc6SknL4NJuAwKAwCwYJYIZIAWUDBAMS\n"
+"MB4xHDAaBgNVBAMME3Rlc3QtMS51bml0dGVzdC5vN3MwHhcNMjYwMTA1MTkyMDMz\n"
+"WhcNNDUxMjMxMTkyMDMzWjAeMRwwGgYDVQQDDBN0ZXN0LTEudW5pdHRlc3Qubzdz\n"
+"MIIHsjALBglghkgBZQMEAxIDggehAOum4NJL/Zbq+yMC5YbG/cLeS61RZLMw0rZ3\n"
+"mBkdbUluk3cm3nnBOZHlKiM36m040cgrYqE/h7VqzgkeuupRrOYjq2OiI+Qb9hnX\n"
+"rDM+++8tHNfHxbFEU8qMokOrilVlqAXVaUW/R92x5w3I10vnJVRHkVmpMOh68s4L\n"
+"+rQtSZM4AtnYToJd4DBI0tzV6Id6Synvp7fPPbkU8EpUHbuAegjoEmLLQdDWF4H+\n"
+"CAU0yzFtPT5yTl7S/Kz/XC8dZ2ie56jTTj10K9c1pfjqMdkfz+PGgL6Sprq5/G84\n"
+"upK4DZqUobS/dVcYraOBcWXw5Rpiyjbrsd1zCiV+WQo5oIHBNjQ8dwaMARM2gfOp\n"
+"PbSpIScrabla0w6aklyRTLrjkY4EoByJK964PFBrqvh3FTU+rrYogSo85EeZYKgR\n"
+"MF6bbtmNPoFoaBhEW19S6HYnXbZ4HTfGe6ceGF5i/lmBkbWWDOH2i+TLsRoGubi4\n"
+"hc3Fj7DFMK0TlNjB2z0yOnwLC1MZKxTktvZ3DDAW696Ke6YIg+cvchJy3G61kpKD\n"
+"0plsaK23todSIGpZDAiH9Ohxep2Jv/LJs9aE+EJ9fNYkrolnDSKCqXU/FVuvup0n\n"
+"k9tiWq5gEYGWAUt1Qaqv7I8PaMm7hlhVwmmjZXxElLtwKvlg22Krl6sXHIBmTQIx\n"
+"DS/fFJbrHPkKOFHBhvrfmCFr+h2ihVXvZbq9fvCAik2V3Qtvmot552N5jq48WgXq\n"
+"mnHs0BPscilVieg4I4/VBiCzff1uexLknLD/5Xoak60uw37djDUJe5GR+/REHHNu\n"
+"vTcGjnBmWFKKAaDpnaje1IP7rLcJAC4PRdbuZ4hbjrxUEDR40MMlT2W3BBr7avE5\n"
+"8dgjmtPrVhPqOV4jfzp5ySOMDdeN4iJJQS5giwzI5p6PRi32J1IMsQKHH8nJHZys\n"
+"BxW9TjNomt8K6jU/bP0iH4Xvjz3FdV9M44Ppar8QyTJIlFC9mRw2CjbU7B7Lx9oE\n"
+"2j92zCN6D5/cpM6WjgVNcDihbcYtO9RvhuBlr1RZ7KAhCGrq0Gtggd4pbbJmHoJk\n"
+"7OJVWpocATKfWemo+3e704cYYXiYzQXCmKeZofFFhxsPrxfdWoS03lXfK/+6RR9W\n"
+"tpZgK75OyG40S102UDvjArICbITkMad/hkQOrDixkuAQdE9ZzD2L6MOEjnIF+WUK\n"
+"pUq+JvHbZYKT16RwN33jQ6V7RHtE7qNgziDbluH5RsGmEdO2QFfbAFRanY3CeIlW\n"
+"G/AyQIJtxm5qDkQVNF72eK9jq/NzkA5eEt3uRcJR1B+DwimVD15Jn/22gLF8ETEi\n"
+"E3zp3CxoCeI7paC3V5v0wRsVXlX/hOBWS3JJyBHgT7UXHIA19UT9vnZg9G53XRie\n"
+"T6a1pUav4JFFYBz+6prcoz648uxbcVlUQMiQencVBnQy+yFP67uMhR9hjz2oQOiP\n"
+"2tITIT6B1HNbqmXTvokoCR4eFNtranA3ARAmNW58zICPts3PrvZzZRQ/Wgi14Ao8\n"
+"lvPx8MRDWR0HGchjukbqvS7RBPMsIWnbR+qAOySfBR5T7K/0nIScQ/crP/79JCG+\n"
+"NmzEuJPOhIZDySK7cWoSHedHIx/6I6tTBceMumZVQXmCganZC+xz7MlKnUFeutxE\n"
+"4j8RWeBxCjSB+W4a1XXy+J0WMebVgDyc/b2eQ/MlNL0jnEjpD9T7YPfYGGZd6qbl\n"
+"+o0OqoTzxbJDXGmsoko0l0VYXFxCsoFfNYLWBkTvYS2lX59YCpUrtiftxlaccEYG\n"
+"6ZTf/lkmveMUClBvLyFzjD6sHw0Wis2j1ySNbq72I83nP7QrL/d6xReR82dxrymU\n"
+"zbg8Xuc6b+ZwrXhbO0CmXnyNuiX1Kq+JIv96l08CqY57MMOon7MahkXUJq+8hEQ9\n"
+"AVelRhR851+15uNrtftaTNipWGRlqDxlJaBWOgj/wtuLzSDLcKMsytaprVrf+Ez/\n"
+"EBaaKRhtZsAumtE+HUc7PwS+slgDsCCg0HD75h7g1UDtlYgGEiLHW9qqCgNBJ9xx\n"
+"1ORDKvzVMJWnerECKk1afTNt2NaGXDHE6fe/AokP0lhHHIw90bplQbZn+LFFYohv\n"
+"GbEnBoPIdRPUse1drJr0Lc5b7Jp+n7lDS9uQQg0Sys8o6gfNd7Nme1MlDh9O7mpW\n"
+"0veeNg4LI+GVny7Zk7dSH6GtYZjl79DzhnHQ+n6tTl3I7dCMOgKtyZN51/LVyRZ+\n"
+"8/3WwgkUDG1DMIH6D7beW7+4FlC4OZR9a3uFclPX+4X1b6Jl9VuN1zaXeIyC5lBY\n"
+"kXcfXRT0yF2PauGQL+Qzv0J+kM9HqBO0U+p0uqkzbblIPMf1Gkff6MeM4I1RAiB7\n"
+"RqsoHekxIWkNyhLenxaKJRDCO4BOoa7QKO2FGYaUdQSKTUKO9PWFRJFmXk4YlYmi\n"
+"pFCS69VCns4mElOXm4ep/+E2tAxlxNyx2LyUVJkwySbTnSOLJuNplUf4j4EwAxid\n"
+"3gGoznQMipLhYyemI/iQQ+TtaGSSz9STTjky3ajrNUI92Rr1n3M66z7hx8Zhh3dp\n"
+"0WfJ47y3KBtFjN4pVHDs+RAhcF5f58vy1iFWdHqB1clV1SU6g40D0YXAlcdPN2cV\n"
+"k1M8ksQoo3MwcTAdBgNVHQ4EFgQUJPWgYA8LLq4MCf1piahVdRM2L1kwHwYDVR0j\n"
+"BBgwFoAUJPWgYA8LLq4MCf1piahVdRM2L1kwDwYDVR0TAQH/BAUwAwEB/zAeBgNV\n"
+"HREEFzAVghN0ZXN0LTEudW5pdHRlc3QubzdzMAsGCWCGSAFlAwQDEgOCDO4AqzJ/\n"
+"0OXl6WB18z5y1YM/8A7SM8JozXzq7Jdngg2GmFNiWLGW3TR3FI7KCVw1AT88gpK7\n"
+"Fu7FMaZZ9gIGGzO+aUH0NcKpfXTTCjDISabzXc4+lAc/9jCNO3uL5lYbWuSJOCBf\n"
+"5eAbvdv4y2kAnnP1uw2YYKFeBJLo/pZAm1jvXniOEIwan+Z1KUDNU3qFBG7I/zQW\n"
+"rqzDFkdqyI+bHgMgWsOAvRu0rtoM4qOlbvdLlpULT+Nj25jCf2p18W19SN8MoQLZ\n"
+"bQH+Y/F6i6yXv9CpzAT9RWRSJyI8mdh3n5F6QlNYrJt7xzeDxXiTgICxNpxWtTx9\n"
+"+D+YEMhxGs2R6UAk+hn1P2nZgZ/HPIBgqGg8rXyDfAecRNbDUMJxRLhPGRdvidFy\n"
+"UqfjWoasltAbEricq5wPWObNrOPHWI43JWQ5Efk4CqZORhIiiash1K2kMyYeiY3E\n"
+"hteoCOUeOKMSu06pFtR6f7khGJUfiXIe9HPAJL4ZwnnisGvI7cnmM7JCMXSC/H86\n"
+"h1z7d+XMvFTNnMBsOpAm1nBV+ZoIHzyQ2n770I31CBq+ZmS/bSAqjAlC4CJ8wjNH\n"
+"LD7sB2Rek22wB518Jy8MJSShAeF8vHk7EGijukOdre0tVLDDPOcGjZL1bgi91XVW\n"
+"VQcO7EsLIf/kdyfAUZQGc/6LMwRCp+1BeDFp0wT74rYUpn3+MOXjWoTou4N97xnd\n"
+"w862gnU/e70wsBGgPuDgVlB9gnAnkoQB0C1s3ecBo7HrWRIeYDHr6JTUMrdCAYJx\n"
+"aw88edY1rWAdUGgk0UlT7lm+MBBJIyBtVY6mYWZkL7IU3DSk0Z9yrXciHC1H4sOw\n"
+"h9ahd1seUT4G4GobMM19t7q6ibAlgvVWWlVDi7TG/LPoySlIwdkmGt2PQn0pjmPj\n"
+"jXfg4He4bTKbu8SPuH9r22nPKz/0ffw/SgP7F/F6nb6KVAZnxoyo9SWLQVC8EVDP\n"
+"H/mhQ6GLfZEDfr0dGtFxCJgYF1F7E6+qUbEKC7BtqMvUBCkiLUO/2Cwa+xFiz8Py\n"
+"SKrumacBRG+PD128wZDBhStM+U52cCKeh7h1ENDejL9IK8YsbQn514h03EhlrwRw\n"
+"3xHKFGQKk/iv5tgU9nZ+tPj5J7b3nM8jdGYyFw9Dl4h+pf27DXAVzkiFONCbbedZ\n"
+"zCazjwX8fXOOgEvI82y/60/SlX+679/HoYGjyJVctyLfBlGEavx6SerTZZsNaWe3\n"
+"jIVDf0OWvF6fzK4/VWEr/iULJquQlxnTOvhtxFVuvK0DoTFSzPCgF7U+inrJ5i+3\n"
+"31JHuoBYtfEa6SS7dQVrRG74L7aqmbzPLkAz/k07hLGW+6WhLtPHCqQVAt4v0DHe\n"
+"iWVkMEjXdqEQqYyu0ObSenZLPDkcvJNNaFH4A7wvJJXWXtm5UfBq9RR1IR+VErf/\n"
+"Da9hEOJP1n0wcKyE6sfLwNTn/wAWCc4BF+XRQFhdUcmMNzoMd3YNlRcLWBlFxvVI\n"
+"FFd4/nbuwCplrD/H6Loq3McgOoDY6L1nXjfgnFSmzN/83yzaUJdp6dVaXMsrDGiT\n"
+"Zda57l4oUcTJgoNtU6DGyvQoZlF11gBZVi7M+3YAPnnHmy8w4W6dN1tyCaxOcaht\n"
+"I8eapuA+Gl0mYYcf8FGLXmFt8s8GvC/vP6snlxN0wxzEwzwEUS9zi+KNKokvahRn\n"
+"M8wYMXMuKBgOGVv3nzicXLNOAoLe56Y5Hnm7a9FGNrRenUQeRQ7yA0/iWg1v2hNi\n"
+"wJX1DQw89awbiv+d920Te0VJFrV39jdNE8pQItWKoBlPMvBpXOks6rBGo6wl1+lE\n"
+"RXqZVgdxC9/b7gCK/DrRQPoixTZWyX/Gyr+r+cnduMAECDeAcoyghK6/OfAo0JF+\n"
+"5kjb0xcMYwbRNoPEWVRwmO6Y/FMVVj4yURow2Pkdm/Ng9J19QpgJa5zpih+xawVb\n"
+"U3Jz3SMvIIfoi1eUdwSklH2XPIS0qxz/aEtqaNX6FGUwbK8j2fVbRmxiy7buoJ0i\n"
+"ya/EfXW+enc1z1mjKHKESy66JZwhaEXpDpllw6U3rNDGXDBIB3AlA1Zm7FbiDB2h\n"
+"eq1jhee8ldHC1X/wMu6TTaP3JNPkJ2/BNVqUvtjLLVAIzJ2GJZEiWA5Fni415gWX\n"
+"9IGq9HqG29LM86RMq0ElP9U0YGUsQrFmrQnB7m0jP+bcQLLJNI5A193GdT9GyGiq\n"
+"oyoUptuAZ7e9gqbc8m9LCwhamZaRDLdknxYD7F3tDmOoawApE7wk5PVgSK1tKNUB\n"
+"QkdILE6sxkglv10JhkwI5nrQTDJqDNW2gmpQlFOpJm35iJpaJtyz2iqS/mmGfNiZ\n"
+"vXuxdDIHOraA7NRVuJadB+UX1+/w7CBzyPm3bAPRMgvfOLoU5ARcy1S0VFwjP72o\n"
+"Rx/ccStVtStLFV4yEPa2+GIukRjYrKf4TQbQcHXdRfb7vh1mk8+33GHd6cYLYYIb\n"
+"6gPBEdyDOvFliD5AZsib8/0K92ldD2uZoJuj1SgBP4T3FDlFlmxdzTydM+fzmvh8\n"
+"PRYwY8ZsGfMbYkDzcqPrP6YY5m49toA2/PFscxP3ThJEmBh9GfU/4N1oY2cRqhnX\n"
+"9BbWTMz/aHyj2t3C/CkcavOIH0R80w6dN5bumylsI0iK2w60DHfOIWG44rakc0qr\n"
+"TR2Zx+sMWwlfgYOMGEc+XE5wjqLJqywLbobfE3bMjLjBTKNjSCyOaooh3UFOJO5k\n"
+"iY7MxCtCASSMeU0lsvtWfHH9c5bMEvFYnF6WgJ05kUvUmHRJFTlMb3uhyXae8mDh\n"
+"qrSyp8vMYQmZEZzVaG1D/cDlnNuG+kCPtxoqtnEnFNGH9zNO2NTOV70tNl9tS/7G\n"
+"/abEGKZnrk0BZ+6ViE2V8F7bm0JpUpwdNOpohjiVp06PIKVHamlwa9TcAGVNo+vK\n"
+"YD0MK4HMaFget5OsvqRCOv3J3d+Ukih6/LLYyAmgW/+04G567SSqhB5HL6qrV99G\n"
+"lmXGeCNtZ4Ypdu1xzsfQRiDSJWT2a5GZdNVGFgoiZyI0LNI8hKudemYcUk0R61eO\n"
+"vBRiny8U59ZMx3JwHZuJ88Wu2MXtzQxuIAfRgh1gXVnZ3YSRxa0xviWkLq4Z/MH4\n"
+"o0VhJ7LqdfJNP/PvFapddi53pMbANvuou0OoKhDn0otwCCzgU/Ye2DMhU/2o1oRT\n"
+"x5gRZPxp80r8tO1iNW9789dxw+Q6icof1vQJ6zeJVdQDQND2jtP15IZGOrG71Ohz\n"
+"54mOu+eHMif4FJvPodYPgqBPFsn8wjow81lhU+nBPYzVpWjaQ1ZpbGwymLkqXR36\n"
+"5Xol+wA8Xr1znB7wmhJVNOvhMbn/lHekzXuoST8HGy+RPUhZ+DPIkihWmEL1LSDB\n"
+"IwdYM9TO1fRMXzCXQRL4KSoYObVWy8gE/GIKevivvlM/FNr1fNh/xyPE6OqGOchx\n"
+"w2NAP6uBxnwPwk2dA7uqFQi18klen/b1JA5hKdhEttK/uOmcb+Scy4wjyw39YCSW\n"
+"Ybf2r8lvqW55SfYBIfk59B7vSCttQgqLqs8CR6ynTybUsleb2EtfU/iCOqkKUFVu\n"
+"CzrWzZEC54QrzE074nKQ/VVidrMXbFe/bWLd84a1CF+Lc2jlh3r2Vqp6K4QJOQS/\n"
+"1nXWu+DolDhWubBjTHlpN0LuyvIRSaFaBKrHckYGTUi24YHHuk5PrVb+LNm14H2z\n"
+"Slv/m+BWsB94Gs4EXOO0yxDWc2dY8s0+v7j40f4pxavE24ehFceD7SxouyegFA/x\n"
+"OPGGYWYVi1A73X0B4SbUJuazhZa3J03iGNxdPQ3tW6GPT9Z2ewJOkS91e5iABbw9\n"
+"DB0kJoZMY05a9X0SlrWDF/2MPRr7XasHzAaOiO8DO2TFp6gpCeSLzzbjw6dBa5Yh\n"
+"86oKLxfSZPIQnF5jS84DhAHvQ6K7P9v7bqk5SD2kIOronCwEAQYulWADvByijGQy\n"
+"+cx4CULZ2eO/Bsf0+d1EgsfpTG/7s/hCY3U/+1Gr2iKXQfiNIFUo8l0V4YKBon+0\n"
+"3Eits0tHG1+WS/7QvgoB5+43md6ANnyyhH64WAR3wMFiG3/ThLRfND3DvkuFwdkJ\n"
+"kPiJw1k6RwuCp7Pg4iaWpvNymeMnEl28ewB9yIjO8Cnp/olo8FuTCjrJtMIouxTY\n"
+"2kyjB35id4d/NjxHYl257Fxh4XFRbfyG+U5g7f/fTB17OzzoY39kIOdINMulQB8y\n"
+"jn2Fd9Kw3rWMUWgwqiEsVU2EoO/YWY1EmKCU5WF3bHXwXgGZLEZP3zIfmy/h2bAt\n"
+"+EIJO/yec956WkxuFzMAu2GcMxya2I2ZQsR1lyYqe9uXKOIMu18OEQ+0I90haNSb\n"
+"1mXUsGxiKDsXHBwkCX/eeJmVIQDPSJfj1D5nQ4gAO01RVVhdYXCH/Q4PLX6eyesH\n"
+"b3t9ma4mb36DrMjKTFFWwtgya56fo+f6AAAAAAAAAAAAAAAACxIYHyQr\n"
+"-----END CERTIFICATE-----\n";
+
+#endif /* TEST_CERTS_PQC_H */
+
diff --git a/include/ouroboros/test.h b/include/test/test.h
index 096e145c..306e737a 100644
--- a/include/ouroboros/test.h
+++ b/include/test/test.h
@@ -28,21 +28,43 @@
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/resource.h>
-#define TEST_START() \
+#define TEST_RC_SUCCESS 0
+#define TEST_RC_SKIP 1
+#define TEST_RC_FAIL -1
+
+#define TEST_START(...) \
+ do { \
+ printf("%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" started.\n"); \
+ fflush(stdout); \
+ } while (0)
+
+#define TEST_SUCCESS(...) \
do { \
- printf("%s started.\n", __func__); \
+ printf("\x1b[32m%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" succeeded.\x1b[0m\n"); \
fflush(stdout); \
} while (0)
-#define TEST_SUCCESS() \
+
+#define TEST_SKIPPED() \
do { \
- printf("%s succeeded.\n", __func__); \
+ printf("\x1b[33m%s skipped.\x1b[0m\n", __func__); \
fflush(stdout); \
} while (0)
-#define TEST_FAIL() \
+#define TEST_FAIL(...) \
do { \
- printf("%s failed.\n", __func__); \
+ printf("\x1b[31m%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" failed.\x1b[0m\n"); \
fflush(stdout); \
} while (0)
@@ -57,26 +79,31 @@ static int __attribute__((unused)) test_assert_fail(int(* testfunc)(void))
pid = fork();
if (pid == -1) {
printf("Failed to fork: %s.\n", strerror(errno));
- return -1;
+ return TEST_RC_FAIL;
}
- if (pid == 0)
+ if (pid == 0) {
+#ifdef DISABLE_TESTS_CORE_DUMPS
+ struct rlimit rl = { .rlim_cur = 0, .rlim_max = 0 };
+ setrlimit(RLIMIT_CORE, &rl);
+#endif
return testfunc(); /* should abort */
+ }
waitpid(pid, &wstatus, 0);
#ifdef CONFIG_OUROBOROS_DEBUG
if (WIFSIGNALED(wstatus) && (wstatus == 134 || wstatus == 6))
- return 0;
+ return TEST_RC_SUCCESS;
printf("Process did not abort, status: %d.\n", wstatus);
#else
if (WIFEXITED(wstatus) && wstatus == 0)
- return 0;
+ return TEST_RC_SUCCESS;
printf("Process did not exit, status: %d.\n", wstatus);
#endif
- return -1;
+ return TEST_RC_FAIL;
}
#endif /* OUROBOROS_LIB_TEST_H */
diff --git a/irmd.conf.in b/irmd.conf.in
index 4fb01c93..dee88392 100644
--- a/irmd.conf.in
+++ b/irmd.conf.in
@@ -1,20 +1,16 @@
### Example Ouroboros configuration file
#
-# This file contains a summary of current machine configuration
-# options for the O7s prototype. The IRMd will attempt to load its
-# configuration file during startup from the file
-# @OUROBOROS_CONFIG_DIR@@OUROBOROS_CONFIG_FILE@.
+# This file contains a summary of current machine configuration options
+# for the O7s prototype.
#
-# At the top level, it accepts configuration of
-# services via the "name" directive, and configuration of the network
-# specifying different IPCPs in the system. So, the list of accepted
-# top level stanza is:
+# The list of accepted top level stanza is:
#
# [name.<name of service>] add a new name to the system.
# [local.<name for IPCP>] add a new local IPCP to the system.
# [eth-llc.<name for IPCP>] add a new IPCP over Ethernet (LLC) to the system.
# [eth-dix.<name for IPCP>] add a new IPCP over Ethernet (DIX) to the system.
-# [eth-udp.<name for IPCP>] add a new IPCP over UDP/IPv4 to the system.
+# [udp4.<name for IPCP>] add a new IPCP over UDP/IPv4 to the system.
+# [udp6.<name for IPCP>] add a new IPCP over UDP/IPv6 to the system.
# [broadcast.<name of IPCP>] add a new broadcast IPCP to the system.
# [unicast.<name of IPCP>] add a new unicast IPCP to the system.
#
@@ -56,6 +52,17 @@ prog=["@INSTALL_DIR@/ocbr"] # Defaults to [].
[name.ovpn]
prog=["@INSTALL_DIR@/ovpn"] # Defaults to [].
+[name."oping.secure"]
+prog=["@INSTALL_DIR@/oping"] # Defaults to [].
+args=["--listen"] # Defaults to disabled. Autostart server with these args.
+lb="round-robin" # Defaults to spill (load-balancing options: spill, round-robin).
+# server_enc_file=/path/to/enc.conf Default: @OUROBOROS_SRV_CRT_DIR@/<name>/enc.conf
+# server_crt_file=/path/to/crt.pem Default: @OUROBOROS_SRV_CRT_DIR@/<name>/crt.pem
+# server_key_file=/path/to/key.pem Default: @OUROBOROS_SRV_CRT_DIR@/<name>/key.pem
+# client_enc_file=/path/to/enc.conf Default: @OUROBOROS_CLI_CRT_DIR@/<name>/enc.conf
+# client_crt_file=/path/to/crt.pem Default: @OUROBOROS_CLI_CRT_DIR@/<name>/crt.pem
+# client_key_file=/path/to/key.pem Default: @OUROBOROS_CLI_CRT_DIR@/<name>/key.pem
+
[local.local1]
bootstrap="local1" # Defaults to not set.
# BOOTSTRAP CONFIGURATION
@@ -77,16 +84,23 @@ dev="lo"
# hash="SHA3_224" # Defaults to SHA3_256.
reg=["lan1"]
-[udp.udp1]
+[udp4.udp1]
bootstrap="udp" # Defaults to not set.
# BOOTSTRAP CONFIGURATION
ip="127.0.0.1"
# port=9000 # Defaults to 3435.
# dns="127.0.0.1" # Requires a DDNS server. Disables DDNS support if not set.
+[udp6.udp2]
+bootstrap="udp2" # Defaults to not set.
+ # BOOTSTRAP CONFIGURATION
+ip="::1"
+# port=9000 # Defaults to 3435.
+# dns="::1" # Requires a DDNS server. Disables DDNS support if not set.
+
[broadcast.bc1]
bootstrap="broadcast" # Defaults to not set.
-autobind=true # Defaults to false.
+# autobind=true # Defaults to false.
[broadcast.bc2]
enrol="bc1"
@@ -96,13 +110,22 @@ enrol="bc1"
bootstrap="LAN" # Defaults to not set.
autobind=true # Defaults to false.
# BOOTSTRAP CONFIGURATION
-# hash="SHA3_224" # Defaults to SHA3_256.
# addr_size=4 # Defaults to 4 (32-bit addresses).
# eid_size=8 # Defaults to 8 (64-bit endpoint IDs, only accepted option).
# max_ttl=60 # Defaults to 60 (max 255).
# addr-auth="flat" # Defaults to flat (currently only option).
# routing="lfa" # Defaults to link-state (options: link-state, lfa, ecmp).
+# ls_t_recalc=4 # Forwarding Function update interval (s) (Default 4).
+# ls_t_update=15 # Link State Advertisement update interval (s) (Default 15).
+# ls_t_timeo=60 # Link Timeout (s) (Default 60).
# congestion="none" # Defaults to mb-ecn (options: none, mb-ecn).
+# directory=DHT # Defaults to DHT (options: DHT)
+# hash="SHA3_224" # Defaults to SHA3_256.
+# dht_alpha=3 # DHT parallel search factor (Default 3, optimal)
+# dht_k=8 # DHT replication factor (Default: 8, same as Mainline DHT)
+# dht_t_expiry=3600 # DHT entry expiry time (s) (Default: 86400 s)
+# dht_t_refresh=900 # DHT contact refresh interval (s) (Default: 900 s)
+# dht_t_replicate=900 # DHT replication interval (s) (Default: 900 s)
# NAMES KNOWN
reg=["oping"] # Defaults to [].
diff --git a/ouroboros-dev.pc.in b/ouroboros-dev.pc.in
index 8895a76b..b92c2741 100644
--- a/ouroboros-dev.pc.in
+++ b/ouroboros-dev.pc.in
@@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
-libdir=${exec_prefix}/usr/lib
-includedir=${prefix}/usr/include
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: ouroboros-dev
Description: Development library for Ouroboros
diff --git a/ouroboros-irm.pc.in b/ouroboros-irm.pc.in
index 5e5841c2..6acfe6d6 100644
--- a/ouroboros-irm.pc.in
+++ b/ouroboros-irm.pc.in
@@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
-libdir=${exec_prefix}/usr/lib
-includedir=${prefix}/usr/include
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: ouroboros-irm
Description: Management library for Ouroboros
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index 54fdd8ab..00000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-add_subdirectory(lib)
-add_subdirectory(ipcpd)
-add_subdirectory(irmd)
-add_subdirectory(tools)
diff --git a/src/ipcpd/CMakeLists.txt b/src/ipcpd/CMakeLists.txt
index 54294f11..609da54a 100644
--- a/src/ipcpd/CMakeLists.txt
+++ b/src/ipcpd/CMakeLists.txt
@@ -1,62 +1,30 @@
-set(CONNMGR_RCV_TIMEOUT 1000 CACHE STRING
- "Timeout for the connection manager to wait for OCEP info (ms).")
-set(IPCP_DEBUG_LOCAL FALSE CACHE BOOL
- "Use PID as address for local debugging")
-set(IPCP_QOS_CUBE_BE_PRIO 50 CACHE STRING
- "Priority for best effort QoS cube (0-99)")
-set(IPCP_QOS_CUBE_VIDEO_PRIO 90 CACHE STRING
- "Priority for video QoS cube (0-99)")
-set(IPCP_QOS_CUBE_VOICE_PRIO 99 CACHE STRING
- "Priority for voice QoS cube (0-99)")
-set(IPCP_MIN_THREADS 4 CACHE STRING
- "Minimum number of worker threads in the IPCP")
-set(IPCP_ADD_THREADS 4 CACHE STRING
- "Number of extra threads to start when an IPCP faces thread starvation")
-set(IPCP_SCHED_THR_MUL 2 CACHE STRING
- "Number of scheduler threads per QoS cube")
-set(DISABLE_CORE_LOCK TRUE CACHE BOOL
- "Disable locking performance threads to a core")
-set(IPCP_CONN_WAIT_DIR TRUE CACHE BOOL
- "Check the running state of the directory when adding a dt connection")
-set(DHT_ENROLL_SLACK 50 CACHE STRING
- "DHT enrollment waiting time (0-999, ms)")
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- set(IPCP_LINUX_TIMERSLACK_NS 1000 CACHE STRING
- "Slack value for high resolution timers on Linux systems.")
-endif ()
-
-if ((IPCP_QOS_CUBE_BE_PRIO LESS 0) OR (IPCP_QOS_CUBE_BE_PRIO GREATER 99))
- message(FATAL_ERROR "Invalid priority for best effort QoS cube")
-endif ()
-
-if ((IPCP_QOS_CUBE_VIDEO_PRIO LESS 0) OR (IPCP_QOS_CUBE_VIDEO_PRIO GREATER 99))
- message(FATAL_ERROR "Invalid priority for video QoS cube")
-endif ()
-
-if ((IPCP_QOS_CUBE_VOICE_PRIO LESS 0) OR (IPCP_QOS_CUBE_VOICE_PRIO GREATER 99))
- message(FATAL_ERROR "Invalid priority for voice QoS cube")
-endif ()
-
-if ((DHT_ENROLL_SLACK LESS 0) OR (DHT_ENROLL_SLACK GREATER 999))
- message(FATAL_ERROR "Invalid DHT slack value")
-endif ()
-
+# IPCP (IPC Process) daemons build configuration
+# Configuration options and validation are in cmake/config/ipcp/*.cmake
+# Common sources shared by all IPCPs (absolute paths for subdirectories)
set(IPCP_SOURCES
- # Add source files here
${CMAKE_CURRENT_SOURCE_DIR}/ipcp.c
${CMAKE_CURRENT_SOURCE_DIR}/shim-data.c
- )
+)
-set (COMMON_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/common/enroll.c
- )
+set(COMMON_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/common/enroll.c
+)
-add_subdirectory(local)
-add_subdirectory(eth)
-add_subdirectory(udp)
-add_subdirectory(unicast)
-add_subdirectory(broadcast)
+set(IPCP_INCLUDE_DIRS
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
+
+add_subdirectory(local)
+add_subdirectory(broadcast)
+add_subdirectory(unicast)
+if(HAVE_ETH)
+ add_subdirectory(eth)
+endif()
+add_subdirectory(udp)
diff --git a/src/ipcpd/broadcast/CMakeLists.txt b/src/ipcpd/broadcast/CMakeLists.txt
index d85f335e..433d9979 100644
--- a/src/ipcpd/broadcast/CMakeLists.txt
+++ b/src/ipcpd/broadcast/CMakeLists.txt
@@ -1,35 +1,22 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+# Broadcast IPCP build configuration
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+set(IPCP_BROADCAST_TARGET ipcpd-broadcast)
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(IPCP_BROADCAST_TARGET ipcpd-broadcast CACHE INTERNAL "")
-set(IPCP_BROADCAST_MPL 60 CACHE STRING
- "Default maximum packet lifetime for the broadcast IPCP, in seconds")
-
-set(SOURCE_FILES
- # Add source files here
+set(BROADCAST_SOURCES
connmgr.c
dt.c
main.c
- )
+)
+
+add_executable(${IPCP_BROADCAST_TARGET}
+ ${BROADCAST_SOURCES}
+ ${IPCP_SOURCES}
+ ${COMMON_SOURCES}
+)
-add_executable(ipcpd-broadcast ${SOURCE_FILES} ${IPCP_SOURCES} ${COMMON_SOURCES}
- ${LAYER_CONFIG_PROTO_SRCS})
-target_link_libraries(ipcpd-broadcast LINK_PUBLIC ouroboros-dev)
+target_include_directories(${IPCP_BROADCAST_TARGET} PRIVATE ${IPCP_INCLUDE_DIRS})
+target_link_libraries(${IPCP_BROADCAST_TARGET} PRIVATE ouroboros-dev)
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-broadcast -DCONFIG_OUROBOROS_DEBUG)
-endif ()
+ouroboros_target_debug_definitions(${IPCP_BROADCAST_TARGET})
-install(TARGETS ipcpd-broadcast RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+install(TARGETS ${IPCP_BROADCAST_TARGET} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/broadcast/main.c b/src/ipcpd/broadcast/main.c
index f51fc629..151b38c8 100644
--- a/src/ipcpd/broadcast/main.c
+++ b/src/ipcpd/broadcast/main.c
@@ -36,6 +36,7 @@
#include <ouroboros/ipcp-dev.h>
#include <ouroboros/logs.h>
#include <ouroboros/notifier.h>
+#include <ouroboros/np1_flow.h>
#include <ouroboros/random.h>
#include <ouroboros/rib.h>
#include <ouroboros/time.h>
@@ -52,13 +53,8 @@
#include <assert.h>
#include <inttypes.h>
-struct ipcp ipcpi;
-
-static int initialize_components(const struct ipcp_config * conf)
+static int initialize_components(void)
{
- strcpy(ipcpi.layer_name, conf->layer_info.name);
- ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo;
-
assert(ipcp_dir_hash_len() != 0);
if (dt_init() < 0) {
@@ -107,6 +103,7 @@ static void stop_components(void)
static int broadcast_ipcp_enroll(const char * dst,
struct layer_info * info)
{
+ struct ipcp_config * conf;
struct conn conn;
uint8_t id[ENROLL_ID_LEN];
@@ -128,7 +125,10 @@ static int broadcast_ipcp_enroll(const char * dst,
goto fail_enroll_boot;
}
- if (initialize_components(enroll_get_conf()) < 0) {
+ conf = enroll_get_conf();
+ *info = conf->layer_info;
+
+ if (initialize_components() < 0) {
log_err_id(id, "Failed to initialize components.");
goto fail_enroll_boot;
}
@@ -146,9 +146,6 @@ static int broadcast_ipcp_enroll(const char * dst,
log_info_id(id, "Enrolled with %s.", dst);
- info->dir_hash_algo = (enum pol_dir_hash) ipcpi.dir_hash_algo;
- strcpy(info->name, ipcpi.layer_name);
-
return 0;
fail_start_comp:
@@ -159,16 +156,15 @@ static int broadcast_ipcp_enroll(const char * dst,
return -1;
}
-static int broadcast_ipcp_bootstrap(const struct ipcp_config * conf)
+static int broadcast_ipcp_bootstrap(struct ipcp_config * conf)
{
assert(conf);
assert(conf->type == THIS_TYPE);
- ((struct ipcp_config *) conf)->layer_info.dir_hash_algo =
- DIR_HASH_SHA3_256;
+ assert(conf->layer_info.dir_hash_algo == DIR_HASH_SHA3_256);
enroll_bootstrap(conf);
- if (initialize_components(conf)) {
+ if (initialize_components()) {
log_err("Failed to init IPCP components.");
goto fail_init;
}
@@ -190,39 +186,58 @@ static int name_check(const uint8_t * dst)
{
uint8_t * buf;
size_t len;
- int ret;
+ int err;
+ char layer[LAYER_NAME_SIZE + 1];
- len = hash_len(ipcpi.dir_hash_algo);
+ len = ipcp_dir_hash_len();
buf = malloc(len);
- if (buf == NULL)
- return -ENOMEM;
+ if (buf == NULL) {
+ log_err("Failed to malloc buffer.");
+ err = -ENOMEM;
+ goto fail_buf;
+ }
- str_hash(ipcpi.dir_hash_algo, buf, ipcpi.layer_name);
+ err = ipcp_get_layer_name(layer);
+ if (err < 0) {
+ log_err("Failed to get layer name.");
+ goto fail_layer;
+ }
- ret = memcmp(buf, dst, len);
+ str_hash(HASH_SHA3_256, buf, layer);
+
+ if (memcmp(buf, dst, len) < 0) {
+ log_err("Hash mismatch for layer %s.", layer);
+ err = -ENAME;
+ goto fail_layer;
+ }
free(buf);
- return ret;
+ return 0;
+
+ fail_layer:
+ free(buf);
+ fail_buf:
+ return err;
}
static int broadcast_ipcp_join(int fd,
- const uint8_t * dst,
- qosspec_t qs)
+ const uint8_t * dst)
{
+ int err;
struct conn conn;
time_t mpl = IPCP_BROADCAST_MPL;
- buffer_t data = {NULL, 0};
-
- (void) qs;
+ buffer_t data = BUF_INIT;
memset(&conn, 0, sizeof(conn));
conn.flow_info.fd = fd;
+ conn.flow_info.qs = qos_np1;
- if (name_check(dst) != 0) {
+ err = name_check(dst);
+ if (err < 0) {
log_err("Failed to check name.");
- return -1;
+ return err;
}
notifier_event(NOTIFY_DT_CONN_ADD, &conn);
diff --git a/src/ipcpd/common/connmgr.c b/src/ipcpd/common/connmgr.c
index 4b5fd420..1bb8c932 100644
--- a/src/ipcpd/common/connmgr.c
+++ b/src/ipcpd/common/connmgr.c
@@ -38,12 +38,6 @@
#include <stdlib.h>
#include <string.h>
-enum connmgr_state {
- CONNMGR_NULL = 0,
- CONNMGR_INIT,
- CONNMGR_RUNNING
-};
-
struct conn_el {
struct list_head next;
struct conn conn;
@@ -61,7 +55,6 @@ struct comp {
struct {
struct comp comps[COMPID_MAX];
- enum connmgr_state state;
pthread_t acceptor;
} connmgr;
@@ -179,6 +172,8 @@ static void * flow_acceptor(void * o)
continue;
}
+ fccntl(fd, FLOWSRCVTIMEO, NULL);
+
err = add_comp_conn(id, fd, qs, &rcv_info);
if (err < 0) {
log_err("Failed to add new connection: %d.", err);
@@ -226,8 +221,6 @@ static void handle_event(void * self,
int connmgr_init(void)
{
- connmgr.state = CONNMGR_INIT;
-
if (notifier_reg(handle_event, NULL)) {
log_err("Failed to register notifier.");
return -1;
@@ -240,13 +233,10 @@ void connmgr_fini(void)
{
int i;
- notifier_unreg(handle_event);
-
- if (connmgr.state == CONNMGR_RUNNING)
- pthread_join(connmgr.acceptor, NULL);
-
for (i = 0; i < COMPID_MAX; ++i)
connmgr_comp_fini(i);
+
+ notifier_unreg(handle_event);
}
int connmgr_start(void)
@@ -256,15 +246,13 @@ int connmgr_start(void)
return -1;
}
- connmgr.state = CONNMGR_RUNNING;
-
return 0;
}
void connmgr_stop(void)
{
- if (connmgr.state == CONNMGR_RUNNING)
- pthread_cancel(connmgr.acceptor);
+ pthread_cancel(connmgr.acceptor);
+ pthread_join(connmgr.acceptor, NULL);
}
int connmgr_comp_init(enum comp_id id,
@@ -487,9 +475,6 @@ int connmgr_alloc(enum comp_id id,
switch (id) {
case COMPID_DT:
notifier_event(NOTIFY_DT_CONN_ADD, conn);
-#if defined(BUILD_IPCP_UNICAST) && defined(IPCP_CONN_WAIT_DIR)
- dir_wait_running();
-#endif
break;
case COMPID_MGMT:
notifier_event(NOTIFY_MGMT_CONN_ADD, conn);
@@ -513,7 +498,7 @@ int connmgr_dealloc(enum comp_id id,
case COMPID_DT:
notifier_event(NOTIFY_DT_CONN_DEL, conn);
break;
-#if defined(BUILD_IPCP_UNICAST) && defined(IPCP_CONN_WAIT_DIR)
+#if defined(BUILD_IPCP_UNICAST)
case COMPID_MGMT:
notifier_event(NOTIFY_MGMT_CONN_DEL, conn);
break;
diff --git a/src/ipcpd/common/enroll.c b/src/ipcpd/common/enroll.c
index 5e35ce37..8e5384a5 100644
--- a/src/ipcpd/common/enroll.c
+++ b/src/ipcpd/common/enroll.c
@@ -43,20 +43,19 @@
#include <string.h>
#include <pthread.h>
+#ifdef __APPLE__
+#define llabs labs
+#endif
+
#define ENROLL_COMP "Enrollment"
#define ENROLL_PROTO "OEP" /* Ouroboros enrollment protocol */
#define ENROLL_WARN_TIME_OFFSET 20
#define ENROLL_BUF_LEN 1024
-enum enroll_state {
- ENROLL_NULL = 0,
- ENROLL_INIT,
- ENROLL_RUNNING
-};
struct {
struct ipcp_config conf;
- enum enroll_state state;
+
pthread_t listener;
} enroll;
@@ -107,8 +106,6 @@ static void * enroll_handle(void * o)
log_info_id(req.id, "Handling incoming enrollment.");
- /* TODO: authentication, timezone handling (UTC). */
-
ack.result = -100;
clock_gettime(CLOCK_REALTIME, &resp.t);
@@ -227,12 +224,14 @@ int enroll_boot(struct conn * conn,
return -1;
}
- if (resp.conf.type != ipcpi.type) {
+ if (resp.conf.type != ipcp_get_type()) {
log_err_id(id, "Wrong type in enrollment response %d (%d).",
- resp.conf.type, ipcpi.type);
+ resp.conf.type, ipcp_get_type());
return -1;
}
+ enroll.conf = resp.conf;
+
clock_gettime(CLOCK_REALTIME, &rtt);
delta_t = ts_diff_ms(&t0, &rtt);
@@ -240,11 +239,9 @@ int enroll_boot(struct conn * conn,
rtt.tv_sec = resp.t.tv_sec;
rtt.tv_nsec = resp.t.tv_nsec;
- if (labs(ts_diff_ms(&t0, &rtt)) - delta_t > ENROLL_WARN_TIME_OFFSET)
+ if (llabs(ts_diff_ms(&t0, &rtt)) - delta_t > ENROLL_WARN_TIME_OFFSET)
log_warn_id(id, "Clock offset above threshold.");
- enroll.conf = resp.conf;
-
return 0;
}
@@ -307,16 +304,11 @@ int enroll_init(void)
return -1;
}
- enroll.state = ENROLL_INIT;
-
return 0;
}
void enroll_fini(void)
{
- if (enroll.state == ENROLL_RUNNING)
- pthread_join(enroll.listener, NULL);
-
connmgr_comp_fini(COMPID_ENROLL);
}
@@ -325,13 +317,11 @@ int enroll_start(void)
if (pthread_create(&enroll.listener, NULL, enroll_handle, NULL))
return -1;
- enroll.state = ENROLL_RUNNING;
-
return 0;
}
void enroll_stop(void)
{
- if (enroll.state == ENROLL_RUNNING)
- pthread_cancel(enroll.listener);
+ pthread_cancel(enroll.listener);
+ pthread_join(enroll.listener, NULL);
}
diff --git a/src/ipcpd/config.h.in b/src/ipcpd/config.h.in
index fe4f5fd2..806de2b6 100644
--- a/src/ipcpd/config.h.in
+++ b/src/ipcpd/config.h.in
@@ -29,11 +29,9 @@
#define SOCKET_TIMEOUT @SOCKET_TIMEOUT@
#define CONNECT_TIMEOUT @CONNECT_TIMEOUT@
-#define SHM_BUFFER_SIZE @SHM_BUFFER_SIZE@
-#define SHM_RDRB_BLOCK_SIZE @SHM_RDRB_BLOCK_SIZE@
+#define SSM_POOL_BLOCK_SIZE @SSM_POOL_BLOCK_SIZE@
#define DU_BUFF_HEADSPACE @DU_BUFF_HEADSPACE@
#define DU_BUFF_TAILSPACE @DU_BUFF_TAILSPACE@
-#cmakedefine SHM_RDRB_MULTI_BLOCK
#define IPCP_MIN_THREADS @IPCP_MIN_THREADS@
#define IPCP_ADD_THREADS @IPCP_ADD_THREADS@
@@ -41,21 +39,24 @@
#define IPCP_LINUX_SLACK_NS @IPCP_LINUX_TIMERSLACK_NS@
-#cmakedefine IPCP_DEBUG_LOCAL
-
/* unicast IPCP */
#define QOS_PRIO_BE @IPCP_QOS_CUBE_BE_PRIO@
#define QOS_PRIO_VIDEO @IPCP_QOS_CUBE_VIDEO_PRIO@
#define QOS_PRIO_VOICE @IPCP_QOS_CUBE_VOICE_PRIO@
#define IPCP_SCHED_THR_MUL @IPCP_SCHED_THR_MUL@
#define PFT_SIZE @PFT_SIZE@
-#define DHT_ENROLL_SLACK @DHT_ENROLL_SLACK@
#define IPCP_UNICAST_MPL @IPCP_UNICAST_MPL@
#define CONNMGR_RCV_TIMEOUT @CONNMGR_RCV_TIMEOUT@
-#cmakedefine IPCP_CONN_WAIT_DIR
#cmakedefine DISABLE_CORE_LOCK
+#cmakedefine BUILD_CONTAINER
#cmakedefine IPCP_FLOW_STATS
+#cmakedefine IPCP_DEBUG_LOCAL
+#ifdef CONFIG_OUROBOROS_DEBUG
+#cmakedefine DEBUG_PROTO_DHT
+#cmakedefine DEBUG_PROTO_OEP
+#cmakedefine DEBUG_PROTO_LS
+#endif
/* udp */
#cmakedefine HAVE_DDNS
@@ -70,10 +71,11 @@
#cmakedefine HAVE_BPF
#cmakedefine HAVE_RAW_SOCKETS
#cmakedefine IPCP_ETH_QDISC_BYPASS
-#define IPCP_ETH_RD_THR @IPCP_ETH_RD_THR@
-#define IPCP_ETH_WR_THR @IPCP_ETH_WR_THR@
-#define IPCP_ETH_LO_MTU @IPCP_ETH_LO_MTU@
-#define IPCP_ETH_MPL @IPCP_ETH_MPL@
+#define IPCP_ETH_RD_THR @IPCP_ETH_RD_THR@
+#define IPCP_ETH_WR_THR @IPCP_ETH_WR_THR@
+#define IPCP_ETH_LO_MTU @IPCP_ETH_LO_MTU@
+#define IPCP_ETH_MGMT_FRAME_SIZE @IPCP_ETH_MGMT_FRAME_SIZE@
+#define IPCP_ETH_MPL @IPCP_ETH_MPL@
/* local */
#define IPCP_LOCAL_MPL @IPCP_LOCAL_MPL@
diff --git a/src/ipcpd/eth/CMakeLists.txt b/src/ipcpd/eth/CMakeLists.txt
index d57e1848..e6cc8224 100644
--- a/src/ipcpd/eth/CMakeLists.txt
+++ b/src/ipcpd/eth/CMakeLists.txt
@@ -1,134 +1,24 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-find_path(NETMAP_C_INCLUDE_DIR
- net/netmap_user.h
- HINTS /usr/include /usr/local/include)
-
-mark_as_advanced(NETMAP_C_INCLUDE_DIR)
-
-# Check for raw sockets
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- set(DISABLE_RAW_SOCKETS FALSE CACHE BOOL
- "Disable raw socket support for Ethernet IPCPs")
- if (NOT DISABLE_RAW_SOCKETS)
- message(STATUS "Raw socket support for Ethernet IPCPs enabled")
- set(HAVE_RAW_SOCKETS TRUE PARENT_SCOPE)
- set(HAVE_RAW_SOCKETS TRUE)
- set(HAVE_ETH TRUE)
- else ()
- message(STATUS "Raw socket support for Ethernet IPCPs disabled by user")
- unset(HAVE_RAW_SOCKETS PARENT_SCOPE)
- unset(HAVE_RAW_SOCKETS)
- endif ()
-endif ()
-
-# Check for BPF
-if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
- find_path(BPF_C_INCLUDE_DIR
- net/bpf.h
- HINTS /usr/include /usr/local/include)
-
- mark_as_advanced(BPF_C_INCLUDE_DIR)
-
- if (BPF_C_INCLUDE_DIR)
- set(DISABLE_BPF FALSE CACHE BOOL
- "Disable Berkeley Packet Filter support for Ethernet IPCPs")
- if (NOT DISABLE_BPF)
- message(STATUS "Berkeley Packet Filter support "
- "for Ethernet IPCPs enabled")
- set(HAVE_BPF TRUE PARENT_SCOPE)
- set(HAVE_BPF TRUE)
- set(HAVE_ETH TRUE)
- else ()
- message(STATUS "Berkeley Packet Filter support "
- "for Ethernet IPCPs disabled by user")
- unset(HAVE_BPF PARENT_SCOPE)
- unset(HAVE_BPF)
- endif ()
- endif ()
-endif ()
-
-# Check for netmap exclusively
-if (NOT HAVE_RAW_SOCKETS AND NOT HAVE_BPF AND NETMAP_C_INCLUDE_DIR)
- set(DISABLE_NETMAP FALSE CACHE BOOL
- "Disable netmap support for ETH IPCPs")
- if (NOT DISABLE_NETMAP)
- message(STATUS "Netmap support for Ethernet IPCPs enabled")
- set(HAVE_NETMAP TRUE PARENT_SCOPE)
- set(HAVE_ETH TRUE)
- else ()
- message(STATUS "Netmap support for Ethernet IPCPs disabled by user")
- unset(HAVE_NETMAP PARENT_SCOPE)
- endif ()
-endif ()
-
-if (HAVE_ETH)
- message(STATUS "Supported raw packet API found, building eth-llc and eth-dix")
-
- set(IPCP_ETH_RD_THR 1 CACHE STRING
- "Number of reader threads in Ethernet IPCP")
- set(IPCP_ETH_WR_THR 1 CACHE STRING
- "Number of writer threads in Ethernet IPCP")
- set(IPCP_ETH_QDISC_BYPASS false CACHE BOOL
- "Bypass the Qdisc in the kernel when using raw sockets")
- set(IPCP_ETH_LO_MTU 1500 CACHE STRING
- "Restrict Ethernet MTU over loopback interfaces")
- set(IPCP_ETH_MPL 5 CACHE STRING
- "Default maximum packet lifetime for the Ethernet IPCPs, in seconds")
-
- set(ETH_LLC_SOURCES
- # Add source files here
- ${CMAKE_CURRENT_SOURCE_DIR}/llc.c
- )
-
- set(ETH_DIX_SOURCES
- # Add source files here
- ${CMAKE_CURRENT_SOURCE_DIR}/dix.c
- )
-
- set(IPCP_ETH_LLC_TARGET ipcpd-eth-llc CACHE INTERNAL "")
- set(IPCP_ETH_DIX_TARGET ipcpd-eth-dix CACHE INTERNAL "")
-
- add_executable(ipcpd-eth-llc ${ETH_LLC_SOURCES} ${IPCP_SOURCES})
- add_executable(ipcpd-eth-dix ${ETH_DIX_SOURCES} ${IPCP_SOURCES})
-
- if (HAVE_BPF AND NOT APPLE)
- target_include_directories(ipcpd-eth-llc PUBLIC ${BPF_C_INCLUDE_DIR})
- target_include_directories(ipcpd-eth-dix PUBLIC ${BPF_C_INCLUDE_DIR})
- endif ()
-
- if (HAVE_NETMAP AND NOT APPLE)
- set_target_properties(ipcpd-eth-llc PROPERTIES
- COMPILE_FLAGS "${CMAKE_C_FLAGS} -std=c99")
- set_target_properties(ipcpd-eth-dix PROPERTIES
- COMPILE_FLAGS "${CMAKE_C_FLAGS} -std=c99")
- target_include_directories(ipcpd-eth-llc PUBLIC
- ${NETMAP_C_INCLUDE_DIR})
- target_include_directories(ipcpd-eth-dix PUBLIC
- ${NETMAP_C_INCLUDE_DIR})
- endif ()
-
- target_link_libraries(ipcpd-eth-llc LINK_PUBLIC ouroboros-dev)
- target_link_libraries(ipcpd-eth-dix LINK_PUBLIC ouroboros-dev)
-
- include(AddCompileFlags)
- if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-eth-llc -DCONFIG_OUROBOROS_DEBUG)
- add_compile_flags(ipcpd-eth-dix -DCONFIG_OUROBOROS_DEBUG)
- endif ()
-
- install(TARGETS ipcpd-eth-llc ipcpd-eth-dix RUNTIME DESTINATION
- ${CMAKE_INSTALL_SBINDIR})
-endif ()
+# Ethernet IPCPs build configuration (LLC and DIX)
+# HAVE_ETH detection is in cmake/dependencies.cmake
+
+set(IPCP_ETH_LLC_TARGET ipcpd-eth-llc)
+set(IPCP_ETH_DIX_TARGET ipcpd-eth-dix)
+
+add_executable(${IPCP_ETH_LLC_TARGET} llc.c ${IPCP_SOURCES})
+add_executable(${IPCP_ETH_DIX_TARGET} dix.c ${IPCP_SOURCES})
+
+foreach(target ${IPCP_ETH_LLC_TARGET} ${IPCP_ETH_DIX_TARGET})
+ target_include_directories(${target} PRIVATE ${IPCP_INCLUDE_DIRS})
+ if(HAVE_BPF AND NOT APPLE)
+ target_include_directories(${target} PRIVATE ${BPF_C_INCLUDE_DIR})
+ endif()
+ if(HAVE_NETMAP AND NOT APPLE)
+ target_compile_options(${target} PRIVATE -std=c99)
+ target_include_directories(${target} PRIVATE ${NETMAP_C_INCLUDE_DIR})
+ endif()
+ target_link_libraries(${target} PRIVATE ouroboros-dev)
+ ouroboros_target_debug_definitions(${target})
+endforeach()
+
+install(TARGETS ${IPCP_ETH_LLC_TARGET} ${IPCP_ETH_DIX_TARGET}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c
index ea6e0f1c..9d281297 100644
--- a/src/ipcpd/eth/eth.c
+++ b/src/ipcpd/eth/eth.c
@@ -52,6 +52,7 @@
#include <ouroboros/pthread.h>
#include "ipcp.h"
+#include "np1.h"
#include "shim-data.h"
#include <signal.h>
@@ -88,31 +89,31 @@
#include <sys/mman.h>
#if defined(HAVE_NETMAP)
-#define NETMAP_WITH_LIBS
-#include <net/netmap_user.h>
+ #define NETMAP_WITH_LIBS
+ #include <net/netmap_user.h>
#elif defined(HAVE_BPF)
-#define BPF_DEV_MAX 256
-#define BPF_BLEN sysconf(_SC_PAGESIZE)
-#include <net/bpf.h>
+ #define BPF_DEV_MAX 256
+ #define BPF_BLEN sysconf(_SC_PAGESIZE)
+ #include <net/bpf.h>
#endif
-#ifdef __linux__
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_VAL(a) \
+ (uint8_t)(a)[0], (uint8_t)(a)[1], (uint8_t)(a)[2], \
+ (uint8_t)(a)[3], (uint8_t)(a)[4], (uint8_t)(a)[5]
+
+
#ifndef ETH_MAX_MTU /* In if_ether.h as of Linux 4.10. */
-#define ETH_MAX_MTU 0xFFFFU
+ #define ETH_MAX_MTU 0xFFFFU
#endif /* ETH_MAX_MTU */
#ifdef BUILD_ETH_DIX
-#define ETH_MTU eth_data.mtu
-#define ETH_MTU_MAX ETH_MAX_MTU
+ #define ETH_MTU eth_data.mtu
+ #define ETH_MTU_MAX ETH_MAX_MTU
#else
-#define ETH_MTU eth_data.mtu
-#define ETH_MTU_MAX 1500
+ #define ETH_MTU eth_data.mtu
+ #define ETH_MTU_MAX 1500
#endif /* BUILD_ETH_DIX */
-#else /* __linux__ */
-#define ETH_MTU 1500
-#define ETH_MTU_MAX ETH_MTU
-#endif /* __linux__ */
-#define MAC_SIZE 6
#define ETH_TYPE_LENGTH_SIZE sizeof(uint16_t)
#define ETH_HEADER_SIZE (2 * MAC_SIZE + ETH_TYPE_LENGTH_SIZE)
@@ -138,15 +139,13 @@
#define NAME_QUERY_TIMEO 2000 /* ms */
#define MGMT_TIMEO 100 /* ms */
-#define MGMT_FRAME_SIZE 2048
+#define MGMT_FRAME_SIZE IPCP_ETH_MGMT_FRAME_SIZE
#define FLOW_REQ 0
#define FLOW_REPLY 1
#define NAME_QUERY_REQ 2
#define NAME_QUERY_REPLY 3
-struct ipcp ipcpi;
-
struct mgmt_msg {
#if defined(BUILD_ETH_DIX)
uint16_t seid;
@@ -165,13 +164,12 @@ struct mgmt_msg {
uint32_t max_gap;
uint32_t delay;
uint32_t timeout;
- uint16_t cypher_s;
+ int32_t response;
uint8_t in_order;
#if defined (BUILD_ETH_DIX)
uint8_t code;
uint8_t availability;
#endif
- int8_t response;
} __attribute__((packed));
struct eth_frame {
@@ -209,8 +207,9 @@ struct mgmt_frame {
struct {
struct shim_data * shim_data;
-#ifdef __linux__
+
int mtu;
+#ifdef __linux__
int if_idx;
#endif
#if defined(HAVE_NETMAP)
@@ -476,6 +475,8 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr,
if (buf == NULL)
return -1;
+ memset(buf, 0, len + ETH_HEADER_TOT_SIZE + data->len);
+
msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE);
msg->code = FLOW_REQ;
#if defined(BUILD_ETH_DIX)
@@ -491,7 +492,6 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
- msg->cypher_s = hton16(qs.cypher_s);
msg->timeout = hton32(qs.timeout);
memcpy(msg + 1, hash, ipcp_dir_hash_len());
@@ -529,6 +529,8 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr,
if (buf == NULL)
return -1;
+ memset(buf, 0, sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len);
+
msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE);
msg->code = FLOW_REPLY;
@@ -539,7 +541,7 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr,
msg->ssap = ssap;
msg->dsap = dsap;
#endif
- msg->response = response;
+ msg->response = hton32(response);
if (data->len > 0)
memcpy(msg + 1, data->data, data->len);
@@ -667,6 +669,8 @@ static int eth_ipcp_name_query_req(const uint8_t * hash,
if (buf == NULL)
return -1;
+ memset(buf, 0, len + ETH_HEADER_TOT_SIZE);
+
msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE);
msg->code = NAME_QUERY_REPLY;
@@ -694,11 +698,11 @@ static int eth_ipcp_name_query_req(const uint8_t * hash,
static int eth_ipcp_name_query_reply(const uint8_t * hash,
uint8_t * r_addr)
{
- uint64_t address = 0;
+ struct addr addr;
- memcpy(&address, r_addr, MAC_SIZE);
+ memcpy(&addr.mac, r_addr, MAC_SIZE);
- shim_data_dir_add_entry(eth_data.shim_data, hash, address);
+ shim_data_dir_add_entry(eth_data.shim_data, hash, addr);
shim_data_dir_query_respond(eth_data.shim_data, hash);
@@ -729,7 +733,6 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf,
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
- qs.cypher_s = ntoh16(msg->cypher_s);
qs.timeout = ntoh32(msg->timeout);
data.data = (uint8_t *) buf + msg_len;
@@ -762,7 +765,7 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf,
msg->ssap,
msg->dsap,
#endif
- msg->response,
+ ntoh32(msg->response),
&data);
break;
case NAME_QUERY_REQ:
@@ -837,7 +840,7 @@ static void * eth_ipcp_packet_reader(void * o)
#if defined(HAVE_NETMAP)
struct nm_pkthdr hdr;
#else
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
fd_set fds;
int frame_len;
#endif
@@ -869,21 +872,21 @@ static void * eth_ipcp_packet_reader(void * o)
if (select(eth_data.bpf + 1, &fds, NULL, NULL, NULL))
continue;
assert(FD_ISSET(eth_data.bpf, &fds));
- if (ipcp_sdb_reserve(&sdb, BPF_LEN))
+ if (ipcp_spb_reserve(&spb, BPF_LEN))
continue;
- buf = shm_du_buff_head(sdb);
+ buf = ssm_pk_buff_head(spb);
frame_len = read(eth_data.bpf, buf, BPF_BLEN);
#elif defined(HAVE_RAW_SOCKETS)
FD_SET(eth_data.s_fd, &fds);
if (select(eth_data.s_fd + 1, &fds, NULL, NULL, NULL) < 0)
continue;
assert(FD_ISSET(eth_data.s_fd, &fds));
- if (ipcp_sdb_reserve(&sdb, ETH_MTU))
+ if (ipcp_spb_reserve(&spb, ETH_MTU))
continue;
- buf = shm_du_buff_head_alloc(sdb, ETH_HEADER_TOT_SIZE);
+ buf = ssm_pk_buff_head_alloc(spb, ETH_HEADER_TOT_SIZE);
if (buf == NULL) {
log_dbg("Failed to allocate header.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
continue;
}
frame_len = recv(eth_data.s_fd, buf,
@@ -891,7 +894,7 @@ static void * eth_ipcp_packet_reader(void * o)
#endif
if (frame_len <= 0) {
log_dbg("Failed to receive frame.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
continue;
}
#endif
@@ -933,7 +936,13 @@ static void * eth_ipcp_packet_reader(void * o)
if (ssap == MGMT_SAP && dsap == MGMT_SAP) {
#endif
- ipcp_sdb_release(sdb); /* No need for the N+1 buffer. */
+ ipcp_spb_release(spb); /* No need for the N+1 buffer. */
+
+ if (length > MGMT_FRAME_SIZE) {
+ log_warn("Management frame size %u exceeds %u.",
+ length, MGMT_FRAME_SIZE);
+ goto fail_frame;
+ }
frame = malloc(sizeof(*frame));
if (frame == NULL) {
@@ -973,22 +982,22 @@ static void * eth_ipcp_packet_reader(void * o)
pthread_rwlock_unlock(&eth_data.flows_lock);
#ifndef HAVE_NETMAP
- shm_du_buff_head_release(sdb, ETH_HEADER_TOT_SIZE);
- shm_du_buff_truncate(sdb, length);
+ ssm_pk_buff_head_release(spb, ETH_HEADER_TOT_SIZE);
+ ssm_pk_buff_truncate(spb, length);
#else
- if (ipcp_sdb_reserve(&sdb, length))
+ if (ipcp_spb_reserve(&spb, length))
continue;
- buf = shm_du_buff_head(sdb);
+ buf = ssm_pk_buff_head(spb);
memcpy(buf, &e_frame->payload, length);
#endif
- if (np1_flow_write(fd, sdb) < 0)
- ipcp_sdb_release(sdb);
+ if (np1_flow_write(fd, spb, NP1_GET_POOL(fd)) < 0)
+ ipcp_spb_release(spb);
continue;
fail_frame:
#ifndef HAVE_NETMAP
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
#endif
}
}
@@ -1004,7 +1013,7 @@ static void cleanup_writer(void * o)
static void * eth_ipcp_packet_writer(void * o)
{
int fd;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
size_t len;
#if defined(BUILD_ETH_DIX)
uint16_t deid;
@@ -1032,17 +1041,17 @@ static void * eth_ipcp_packet_writer(void * o)
if (fqueue_type(fq) != FLOW_PKT)
continue;
- if (np1_flow_read(fd, &sdb)) {
+ if (np1_flow_read(fd, &spb, NP1_GET_POOL(fd))) {
log_dbg("Bad read from fd %d.", fd);
continue;
}
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
- if (shm_du_buff_head_alloc(sdb, ETH_HEADER_TOT_SIZE)
+ if (ssm_pk_buff_head_alloc(spb, ETH_HEADER_TOT_SIZE)
== NULL) {
log_dbg("Failed to allocate header.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
continue;
}
@@ -1065,10 +1074,10 @@ static void * eth_ipcp_packet_writer(void * o)
#elif defined(BUILD_ETH_LLC)
dsap, ssap,
#endif
- shm_du_buff_head(sdb),
+ ssm_pk_buff_head(spb),
len))
log_dbg("Failed to send frame.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
}
}
@@ -1217,140 +1226,138 @@ static int open_bpf_device(void)
}
#endif
-static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
-{
- int idx;
- struct ifreq ifr;
-#if defined(HAVE_NETMAP)
- char ifn[IFNAMSIZ];
-#elif defined(HAVE_BPF)
- int enable = 1;
- int disable = 0;
- int blen;
-#endif /* HAVE_NETMAP */
-
#if defined(__FreeBSD__) || defined(__APPLE__)
+static int ifr_hwaddr_from_ifaddrs(struct ifreq * ifr)
+{
struct ifaddrs * ifaddr;
struct ifaddrs * ifa;
-#elif defined(__linux__)
- int skfd;
-#endif
-#ifndef SHM_RDRB_MULTI_BLOCK
- size_t maxsz;
-#endif
-#if defined(HAVE_RAW_SOCKETS)
- #if defined(IPCP_ETH_QDISC_BYPASS)
- int qdisc_bypass = 1;
- #endif /* ENABLE_QDISC_BYPASS */
- int flags;
-#endif
- assert(conf);
- assert(conf->type == THIS_TYPE);
-
- ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo;
- strcpy(ipcpi.layer_name, conf->layer_info.name);
-
- if (strlen(conf->eth.dev) >= IFNAMSIZ) {
- log_err("Invalid device name: %s.", conf->eth.dev);
- return -1;
- }
-
- memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, conf->eth.dev);
-
-#ifdef BUILD_ETH_DIX
- if (conf->eth.ethertype < 0x0600 || conf->eth.ethertype == 0xFFFF) {
- log_err("Invalid Ethertype: %d.", conf->eth.ethertype);
- return -1;
- }
- eth_data.ethertype = htons(conf->eth.ethertype);
-#endif
+ int idx;
-#if defined(__FreeBSD__) || defined(__APPLE__)
if (getifaddrs(&ifaddr) < 0) {
log_err("Could not get interfaces.");
- return -1;
+ goto fail_ifaddrs;
}
for (ifa = ifaddr, idx = 0; ifa != NULL; ifa = ifa->ifa_next, ++idx) {
- if (strcmp(ifa->ifa_name, conf->eth.dev))
- continue;
- log_dbg("Interface %s found.", conf->eth.dev);
-
- #if defined(HAVE_NETMAP) || defined(HAVE_BPF)
- memcpy(eth_data.hw_addr,
- LLADDR((struct sockaddr_dl *) (ifa)->ifa_addr),
- MAC_SIZE);
- #elif defined (HAVE_RAW_SOCKETS)
- memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr));
- #endif
- break;
+ if (strcmp(ifa->ifa_name, ifr->ifr_name) == 0)
+ break;
}
- freeifaddrs(ifaddr);
-
if (ifa == NULL) {
log_err("Interface not found.");
- return -1;
+ goto fail_ifa;
}
+ memcpy(&ifr->ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr));
+
+ log_dbg("Interface %s hwaddr " MAC_FMT ".", ifr->ifr_name,
+ MAC_VAL(ifr->ifr_addr.sa_data));
+
+ freeifaddrs(ifaddr);
+
+ return 0;
+ fail_ifa:
+ freeifaddrs(ifaddr);
+ fail_ifaddrs:
+ return -1;
+
+}
#elif defined(__linux__)
+static int ifr_hwaddr_from_socket(struct ifreq * ifr)
+{
+ int skfd;
+
skfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (skfd < 0) {
log_err("Failed to open socket.");
- return -1;
+ goto fail_socket;
}
- if (ioctl(skfd, SIOCGIFMTU, &ifr)) {
- log_err("Failed to get MTU.");
- close(skfd);
- return -1;
+ if (ioctl(skfd, SIOCGIFHWADDR, ifr)) {
+ log_err("Failed to get hwaddr.");
+ goto fail_ifr;
}
- log_dbg("Device MTU is %d.", ifr.ifr_mtu);
+ log_dbg("Interface %s hwaddr " MAC_FMT ".", ifr->ifr_name,
+ MAC_VAL(ifr->ifr_hwaddr.sa_data));
- eth_data.mtu = MIN((int) ETH_MTU_MAX, ifr.ifr_mtu);
- if (memcmp(conf->eth.dev, "lo", 2) == 0 &&
- eth_data.mtu > IPCP_ETH_LO_MTU) {
- log_dbg("Using loopback interface. MTU restricted to %d.",
- IPCP_ETH_LO_MTU);
- eth_data.mtu = IPCP_ETH_LO_MTU;
- }
+ close(skfd);
-#ifndef SHM_RDRB_MULTI_BLOCK
- maxsz = SHM_RDRB_BLOCK_SIZE - 5 * sizeof(size_t) -
- (DU_BUFF_HEADSPACE + DU_BUFF_TAILSPACE);
- if ((size_t) eth_data.mtu > maxsz ) {
- log_dbg("Layer MTU truncated to shm block size.");
- eth_data.mtu = maxsz;
- }
+ return 0;
+
+ fail_ifr:
+ close(skfd);
+ fail_socket:
+ return -1;
+}
#endif
- log_dbg("Layer MTU is %d.", eth_data.mtu);
- if (ioctl(skfd, SIOCGIFHWADDR, &ifr)) {
- log_err("Failed to get hwaddr.");
- close(skfd);
- return -1;
+static int eth_ifr_hwaddr(struct ifreq * ifr)
+{
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ return ifr_hwaddr_from_ifaddrs(ifr);
+#elif defined(__linux__)
+ return ifr_hwaddr_from_socket(ifr);
+#else
+ return -1;
+#endif
+}
+
+static int eth_ifr_mtu(struct ifreq * ifr)
+{
+ int skfd;
+
+ skfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (skfd < 0) {
+ log_err("Failed to open socket.");
+ goto fail_socket;
}
+ if (ioctl(skfd, SIOCGIFMTU, ifr) < 0) {
+ log_err("Failed to get MTU.");
+ goto fail_mtu;
+ }
close(skfd);
- idx = if_nametoindex(conf->eth.dev);
- if (idx == 0) {
- log_err("Failed to retrieve interface index.");
+ return 0;
+
+ fail_mtu:
+ close(skfd);
+ fail_socket:
+ return -1;
+}
+
+static int eth_set_mtu(struct ifreq * ifr)
+{
+ if (eth_ifr_mtu(ifr) < 0) {
+ log_err("Failed to get interface MTU.");
return -1;
}
- eth_data.if_idx = idx;
-#endif /* __FreeBSD__ */
+ log_dbg("Device MTU is %d.", ifr->ifr_mtu);
+
+ eth_data.mtu = MIN((int) ETH_MTU_MAX, ifr->ifr_mtu);
+ if (memcmp(ifr->ifr_name, "lo", 2) == 0 &&
+ eth_data.mtu > IPCP_ETH_LO_MTU) {
+ log_dbg("Using loopback interface. MTU restricted to %d.",
+ IPCP_ETH_LO_MTU);
+ eth_data.mtu = IPCP_ETH_LO_MTU;
+ }
+
+ log_dbg("Layer MTU is %d.", eth_data.mtu);
+
+ return 0;
+}
#if defined(HAVE_NETMAP)
+static int eth_init_nmd(struct ifreq * ifr)
+{
strcpy(ifn, "netmap:");
- strcat(ifn, conf->eth.dev);
+ strcat(ifn, ifr->ifr_name);
eth_data.nmd = nm_open(ifn, NULL, 0, NULL);
if (eth_data.nmd == NULL) {
log_err("Failed to open netmap device.");
- return -1;
+ goto fail_nmd;
}
memset(&eth_data.poll_in, 0, sizeof(eth_data.poll_in));
@@ -1362,11 +1369,22 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
eth_data.poll_out.events = POLLOUT;
log_info("Using netmap device.");
-#elif defined(HAVE_BPF) /* !HAVE_NETMAP */
+
+ return 0;
+ fail_nmd:
+ return -1;
+}
+#elif defined (HAVE_BPF)
+static int eth_init_bpf(struct ifreq * ifr)
+{
+ int enable = 1;
+ int disable = 0;
+ int blen;
+
eth_data.bpf = open_bpf_device();
if (eth_data.bpf < 0) {
log_err("Failed to open bpf device.");
- return -1;
+ goto fail_bpf;
}
ioctl(eth_data.bpf, BIOCGBLEN, &blen);
@@ -1376,7 +1394,7 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
goto fail_device;
}
- if (ioctl(eth_data.bpf, BIOCSETIF, &ifr) < 0) {
+ if (ioctl(eth_data.bpf, BIOCSETIF, ifr) < 0) {
log_err("Failed to set interface.");
goto fail_device;
}
@@ -1397,22 +1415,39 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
}
log_info("Using Berkeley Packet Filter.");
+
+ return 0;
+
+ fail_device:
+ close(eth_data.bpf);
+ fail_bpf:
+ return -1;
+}
#elif defined(HAVE_RAW_SOCKETS)
+static int eth_init_raw_socket(struct ifreq * ifr)
+{
+ int idx;
+ int flags;
+#if defined(IPCP_ETH_QDISC_BYPASS)
+ int qdisc_bypass = 1;
+#endif /* ENABLE_QDISC_BYPASS */
+
+ idx = if_nametoindex(ifr->ifr_name);
+ if (idx == 0) {
+ log_err("Failed to retrieve interface index.");
+ return -1;
+ }
memset(&(eth_data.device), 0, sizeof(eth_data.device));
eth_data.device.sll_ifindex = idx;
eth_data.device.sll_family = AF_PACKET;
- memcpy(eth_data.device.sll_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE);
+ memcpy(eth_data.device.sll_addr, ifr->ifr_hwaddr.sa_data, MAC_SIZE);
eth_data.device.sll_halen = MAC_SIZE;
eth_data.device.sll_protocol = htons(ETH_P_ALL);
-
- #if defined (BUILD_ETH_DIX)
+#if defined (BUILD_ETH_DIX)
eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, eth_data.ethertype);
- #elif defined (BUILD_ETH_LLC)
+#elif defined (BUILD_ETH_LLC)
eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2));
- #endif
-
- log_info("Using raw socket device.");
-
+#endif
if (eth_data.s_fd < 0) {
log_err("Failed to create socket.");
goto fail_socket;
@@ -1429,28 +1464,93 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
goto fail_device;
}
- #if defined(IPCP_ETH_QDISC_BYPASS)
+#if defined(IPCP_ETH_QDISC_BYPASS)
if (setsockopt(eth_data.s_fd, SOL_PACKET, PACKET_QDISC_BYPASS,
&qdisc_bypass, sizeof(qdisc_bypass))) {
log_info("Qdisc bypass not supported.");
}
- #endif
+#endif
if (bind(eth_data.s_fd, (struct sockaddr *) &eth_data.device,
sizeof(eth_data.device)) < 0) {
log_err("Failed to bind socket to interface.");
goto fail_device;
}
+#ifdef __linux__
+ eth_data.if_idx = idx;
+#endif
+ log_info("Using raw socket device.");
+
+ return 0;
+ fail_device:
+ close(eth_data.s_fd);
+ fail_socket:
+ return -1;
+}
+#endif
+
+static int eth_ipcp_bootstrap(struct ipcp_config * conf)
+{
+ struct ifreq ifr;
+ int i;
+#if defined(HAVE_NETMAP)
+ char ifn[IFNAMSIZ];
+#endif /* HAVE_NETMAP */
+
+ assert(conf);
+ assert(conf->type == THIS_TYPE);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, conf->eth.dev);
+
+ if (strlen(conf->eth.dev) >= IFNAMSIZ) {
+ log_err("Invalid device name: %s.", conf->eth.dev);
+ return -1;
+ }
+#ifdef BUILD_ETH_DIX
+ if (conf->eth.ethertype < 0x0600 || conf->eth.ethertype == 0xFFFF) {
+ log_err("Invalid Ethertype: %d.", conf->eth.ethertype);
+ return -1;
+ }
+ eth_data.ethertype = htons(conf->eth.ethertype);
+#endif
+ if (eth_set_mtu(&ifr) < 0) {
+ log_err("Failed to set MTU.");
+ return -1;
+ }
+
+ if (eth_ifr_hwaddr(&ifr) < 0) {
+ log_err("Failed to get hardware addr.");
+ return -1;
+ }
+#if defined(HAVE_NETMAP) || defined(HAVE_BPF)
+ memcpy(eth_data.hw_addr, LLADDR((struct sockaddr_dl *) &ifr.ifr_addr),
+ MAC_SIZE);
+#endif
+#if defined(HAVE_NETMAP)
+ if (eth_init_nmd(&ifr) < 0) {
+ log_err("Failed to initialize netmap device.");
+ return -1;
+ }
+#elif defined(HAVE_BPF) /* !HAVE_NETMAP */
+ if (eth_init_bpf(&ifr) < 0) {
+ log_err("Failed to initialize BPF device.");
+ return -1;
+ }
+#elif defined(HAVE_RAW_SOCKETS)
+ if (eth_init_raw_socket(&ifr) < 0) {
+ log_err("Failed to initialize raw socket device.");
+ return -1;
+ }
#endif /* HAVE_NETMAP */
#if defined(__linux__)
if (pthread_create(&eth_data.if_monitor, NULL,
eth_ipcp_if_monitor, NULL)) {
log_err("Failed to create monitor thread: %s.",
strerror(errno));
- goto fail_device;
+ goto fail_monitor;
}
#endif
-
if (pthread_create(&eth_data.mgmt_handler, NULL,
eth_ipcp_mgmt_handler, NULL)) {
log_err("Failed to create mgmt handler thread: %s.",
@@ -1458,8 +1558,8 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
goto fail_mgmt_handler;
}
- for (idx = 0; idx < IPCP_ETH_RD_THR; ++idx) {
- if (pthread_create(&eth_data.packet_reader[idx], NULL,
+ for (i = 0; i < IPCP_ETH_RD_THR; i++) {
+ if (pthread_create(&eth_data.packet_reader[i], NULL,
eth_ipcp_packet_reader, NULL)) {
log_err("Failed to create packet reader thread: %s",
strerror(errno));
@@ -1467,8 +1567,8 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
}
}
- for (idx = 0; idx < IPCP_ETH_WR_THR; ++idx) {
- if (pthread_create(&eth_data.packet_writer[idx], NULL,
+ for (i = 0; i < IPCP_ETH_WR_THR; i++) {
+ if (pthread_create(&eth_data.packet_writer[i], NULL,
eth_ipcp_packet_writer, NULL)) {
log_err("Failed to create packet writer thread: %s",
strerror(errno));
@@ -1486,15 +1586,15 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
return 0;
fail_packet_writer:
- while (idx > 0) {
- pthread_cancel(eth_data.packet_writer[--idx]);
- pthread_join(eth_data.packet_writer[idx], NULL);
+ while (i-- > 0) {
+ pthread_cancel(eth_data.packet_writer[i]);
+ pthread_join(eth_data.packet_writer[i], NULL);
}
- idx = IPCP_ETH_RD_THR;
+ i = IPCP_ETH_RD_THR;
fail_packet_reader:
- while (idx > 0) {
- pthread_cancel(eth_data.packet_reader[--idx]);
- pthread_join(eth_data.packet_reader[idx], NULL);
+ while (i-- > 0) {
+ pthread_cancel(eth_data.packet_reader[i]);
+ pthread_join(eth_data.packet_reader[i], NULL);
}
pthread_cancel(eth_data.mgmt_handler);
pthread_join(eth_data.mgmt_handler, NULL);
@@ -1503,8 +1603,8 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
pthread_cancel(eth_data.if_monitor);
pthread_join(eth_data.if_monitor, NULL);
#endif
-#if defined(__linux__) || !defined(HAVE_NETMAP)
- fail_device:
+#if defined(__linux__)
+ fail_monitor:
#endif
#if defined(HAVE_NETMAP)
nm_close(eth_data.nmd);
@@ -1513,7 +1613,6 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf)
#elif defined(HAVE_RAW_SOCKETS)
close(eth_data.s_fd);
#endif
- fail_socket:
return -1;
}
@@ -1554,6 +1653,8 @@ static int eth_ipcp_query(const uint8_t * hash)
if (buf == NULL)
return -1;
+ memset(buf, 0, len + ETH_HEADER_TOT_SIZE);
+
msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE);
msg->code = NAME_QUERY_REQ;
@@ -1599,7 +1700,7 @@ static int eth_ipcp_flow_alloc(int fd,
uint8_t ssap = 0;
#endif
uint8_t r_addr[MAC_SIZE];
- uint64_t addr = 0;
+ struct addr addr;
assert(hash);
@@ -1608,10 +1709,12 @@ static int eth_ipcp_flow_alloc(int fd,
HASH_VAL32(hash));
return -1;
}
+
addr = shim_data_dir_get_addr(eth_data.shim_data, hash);
+ memcpy(r_addr, &addr.mac, MAC_SIZE);
- pthread_rwlock_wrlock(&eth_data.flows_lock);
#ifdef BUILD_ETH_LLC
+ pthread_rwlock_wrlock(&eth_data.flows_lock);
ssap = bmp_allocate(eth_data.saps);
if (!bmp_is_id_valid(eth_data.saps, ssap)) {
pthread_rwlock_unlock(&eth_data.flows_lock);
@@ -1621,10 +1724,8 @@ static int eth_ipcp_flow_alloc(int fd,
eth_data.fd_to_ef[fd].sap = ssap;
eth_data.ef_to_fd[ssap] = fd;
-#endif
pthread_rwlock_unlock(&eth_data.flows_lock);
-
- memcpy(r_addr, &addr, MAC_SIZE);
+#endif
if (eth_ipcp_alloc(r_addr,
#if defined(BUILD_ETH_DIX)
diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c
index 966c4920..3ea77da9 100644
--- a/src/ipcpd/ipcp.c
+++ b/src/ipcpd/ipcp.c
@@ -52,6 +52,7 @@
#include <ouroboros/utils.h>
#include "ipcp.h"
+#include "np1.h"
#include <signal.h>
#include <string.h>
@@ -64,13 +65,75 @@
#endif
#endif
-char * info[LAYER_NAME_SIZE + 1] = {
- "_state",
- "_type",
- "_layer",
- NULL
+#ifndef CLOCK_REALTIME_COARSE
+#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
+#endif
+
+static char * ipcp_type_str[] = {
+ "local",
+ "unicast",
+ "broadcast",
+ "eth-llc",
+ "eth-dix",
+ "udp4",
+ "udp6"
+};
+
+static char * dir_hash_str[] = {
+ "SHA3-224",
+ "SHA3-256",
+ "SHA3-384",
+ "SHA3-512",
+ "CRC32",
+ "MD5"
};
+static char * ipcp_state_str[] = {
+ "null",
+ "init",
+ "boot",
+ "bootstrapped",
+ "enrolled",
+ "operational",
+ "shutdown"
+};
+
+struct {
+ pid_t irmd_pid;
+ char * name;
+
+ enum ipcp_type type;
+ char layer_name[LAYER_NAME_SIZE + 1];
+
+ uint64_t dt_addr;
+
+ enum hash_algo dir_hash_algo;
+
+ struct ipcp_ops * ops;
+ int irmd_fd;
+
+ enum ipcp_state state;
+ pthread_cond_t state_cond;
+ pthread_mutex_t state_mtx;
+
+ int sockfd;
+ char * sock_path;
+
+ struct list_head cmds;
+ pthread_cond_t cmd_cond;
+ pthread_mutex_t cmd_lock;
+
+ int alloc_id;
+ pthread_cond_t alloc_cond;
+ pthread_mutex_t alloc_lock;
+
+ struct tpm * tpm;
+
+ pthread_t acceptor;
+} ipcpd;
+
+struct np1_state np1;
+
struct cmd {
struct list_head next;
@@ -79,9 +142,38 @@ struct cmd {
int fd;
};
+enum ipcp_type ipcp_get_type(void)
+{
+ return ipcpd.type;
+}
+
+const char * ipcp_get_name(void)
+{
+ return ipcpd.name;
+}
+
+void ipcp_set_dir_hash_algo(enum hash_algo algo)
+{
+ ipcpd.dir_hash_algo = algo;
+}
+
+size_t ipcp_dir_hash_len(void)
+{
+ return hash_len(ipcpd.dir_hash_algo);
+}
+
+int ipcp_get_layer_name(char * layer)
+{
+ if (ipcp_get_state() < IPCP_OPERATIONAL)
+ return -EIPCPSTATE;
+
+ strcpy(layer, ipcpd.layer_name);
+ return 0;
+}
+
uint8_t * ipcp_hash_dup(const uint8_t * hash)
{
- uint8_t * dup = malloc(hash_len(ipcpi.dir_hash_algo));
+ uint8_t * dup = malloc(hash_len(ipcpd.dir_hash_algo));
if (dup == NULL)
return NULL;
@@ -105,6 +197,13 @@ void ipcp_hash_str(char * buf,
buf[2 * i] = '\0';
}
+static const char * info[] = {
+ "_state",
+ "_type",
+ "_layer",
+ NULL
+};
+
static int ipcp_rib_read(const char * path,
char * buf,
size_t len)
@@ -132,18 +231,20 @@ static int ipcp_rib_read(const char * path,
}
if (strcmp(entry, info[1]) == 0) { /* _type */
- if (ipcpi.type == IPCP_LOCAL)
+ if (ipcpd.type == IPCP_LOCAL)
strcpy(buf, "local\n");
- else if (ipcpi.type == IPCP_UNICAST)
+ else if (ipcpd.type == IPCP_UNICAST)
strcpy(buf, "unicast\n");
- else if (ipcpi.type == IPCP_BROADCAST)
+ else if (ipcpd.type == IPCP_BROADCAST)
strcpy(buf, "broadcast\n");
- else if (ipcpi.type == IPCP_ETH_LLC)
+ else if (ipcpd.type == IPCP_ETH_LLC)
strcpy(buf, "eth-llc\n");
- else if (ipcpi.type == IPCP_ETH_DIX)
+ else if (ipcpd.type == IPCP_ETH_DIX)
strcpy(buf, "eth-dix\n");
- else if (ipcpi.type == IPCP_UDP)
- strcpy(buf, "udp\n");
+ else if (ipcpd.type == IPCP_UDP4)
+ strcpy(buf, "udp4\n");
+ else if (ipcpd.type == IPCP_UDP6)
+ strcpy(buf, "udp6\n");
else
strcpy(buf, "bug\n");
}
@@ -153,7 +254,7 @@ static int ipcp_rib_read(const char * path,
if (ipcp_get_state() < IPCP_OPERATIONAL)
strcpy(buf, "(null)");
else
- strcpy(buf, ipcpi.layer_name);
+ strcpy(buf, ipcpd.layer_name);
buf[strlen(buf)] = '\n';
}
@@ -165,12 +266,11 @@ static int ipcp_rib_readdir(char *** buf)
{
int i = 0;
- while (info[i] != NULL)
- i++;
+ while (info[i++] != NULL);
*buf = malloc(sizeof(**buf) * i);
if (*buf == NULL)
- goto fail;
+ goto fail_entries;
i = 0;
@@ -183,12 +283,11 @@ static int ipcp_rib_readdir(char *** buf)
return i;
fail_dup:
- while (i > 0)
- free((*buf)[--i]);
- fail:
+ while (i-- > 0)
+ free((*buf)[i]);
free(*buf);
-
- return -1;
+ fail_entries:
+ return -ENOMEM;
}
static int ipcp_rib_getattr(const char * path,
@@ -218,10 +317,10 @@ static void * acceptloop(void * o)
(void) o;
while (ipcp_get_state() != IPCP_SHUTDOWN &&
- ipcp_get_state() != IPCP_NULL) {
+ ipcp_get_state() != IPCP_INIT) {
struct cmd * cmd;
- csockfd = accept(ipcpi.sockfd, 0, 0);
+ csockfd = accept(ipcpd.sockfd, 0, 0);
if (csockfd < 0)
continue;
@@ -249,13 +348,13 @@ static void * acceptloop(void * o)
cmd->fd = csockfd;
- pthread_mutex_lock(&ipcpi.cmd_lock);
+ pthread_mutex_lock(&ipcpd.cmd_lock);
- list_add(&cmd->next, &ipcpi.cmds);
+ list_add(&cmd->next, &ipcpd.cmds);
- pthread_cond_signal(&ipcpi.cmd_cond);
+ pthread_cond_signal(&ipcpd.cmd_cond);
- pthread_mutex_unlock(&ipcpi.cmd_lock);
+ pthread_mutex_unlock(&ipcpd.cmd_lock);
}
return (void *) 0;
@@ -276,34 +375,34 @@ int ipcp_wait_flow_req_arr(const uint8_t * dst,
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
- pthread_mutex_lock(&ipcpi.alloc_lock);
+ pthread_mutex_lock(&ipcpd.alloc_lock);
- while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) {
+ while (ipcpd.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) {
ts_add(&abstime, &ts, &abstime);
- pthread_cond_timedwait(&ipcpi.alloc_cond,
- &ipcpi.alloc_lock,
+ pthread_cond_timedwait(&ipcpd.alloc_cond,
+ &ipcpd.alloc_lock,
&abstime);
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- pthread_mutex_unlock(&ipcpi.alloc_lock);
+ pthread_mutex_unlock(&ipcpd.alloc_lock);
log_err("Won't allocate over non-operational IPCP.");
return -EIPCPSTATE;
}
- assert(ipcpi.alloc_id == -1);
+ assert(ipcpd.alloc_id == -1);
fd = ipcp_flow_req_arr(&hash, qs, mpl, data);
if (fd < 0) {
- pthread_mutex_unlock(&ipcpi.alloc_lock);
+ pthread_mutex_unlock(&ipcpd.alloc_lock);
log_err("Failed to get fd for flow.");
return fd;
}
- ipcpi.alloc_id = fd;
- pthread_cond_broadcast(&ipcpi.alloc_cond);
+ ipcpd.alloc_id = fd;
+ pthread_cond_broadcast(&ipcpd.alloc_cond);
- pthread_mutex_unlock(&ipcpi.alloc_lock);
+ pthread_mutex_unlock(&ipcpd.alloc_lock);
return fd;
@@ -311,31 +410,31 @@ int ipcp_wait_flow_req_arr(const uint8_t * dst,
int ipcp_wait_flow_resp(const int fd)
{
- struct timespec ts = TIMESPEC_INIT_MS(ALLOC_TIMEOUT);
- struct timespec abstime;
+ struct timespec ts = TIMESPEC_INIT_MS(ALLOC_TIMEOUT);
+ struct timespec abstime;
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
- pthread_mutex_lock(&ipcpi.alloc_lock);
+ pthread_mutex_lock(&ipcpd.alloc_lock);
- while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) {
+ while (ipcpd.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) {
ts_add(&abstime, &ts, &abstime);
- pthread_cond_timedwait(&ipcpi.alloc_cond,
- &ipcpi.alloc_lock,
+ pthread_cond_timedwait(&ipcpd.alloc_cond,
+ &ipcpd.alloc_lock,
&abstime);
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- pthread_mutex_unlock(&ipcpi.alloc_lock);
+ pthread_mutex_unlock(&ipcpd.alloc_lock);
return -1;
}
- assert(ipcpi.alloc_id == fd);
+ assert(ipcpd.alloc_id == fd);
- ipcpi.alloc_id = -1;
- pthread_cond_broadcast(&ipcpi.alloc_cond);
+ ipcpd.alloc_id = -1;
+ pthread_cond_broadcast(&ipcpd.alloc_cond);
- pthread_mutex_unlock(&ipcpi.alloc_lock);
+ pthread_mutex_unlock(&ipcpd.alloc_lock);
return 0;
}
@@ -349,30 +448,58 @@ static void free_msg(void * o)
static void do_bootstrap(ipcp_config_msg_t * conf_msg,
ipcp_msg_t * ret_msg)
{
- struct ipcp_config conf;
+ struct ipcp_config conf;
+ struct layer_info * info;
log_info("Bootstrapping...");
- if (ipcpi.ops->ipcp_bootstrap == NULL) {
- log_err("Bootstrap unsupported.");
+ if (ipcpd.ops->ipcp_bootstrap == NULL) {
+ log_err("Failed to Bootstrap: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- if (ipcp_get_state() != IPCP_INIT) {
- log_err("IPCP in wrong state.");
+ if (ipcp_get_state() != IPCP_BOOT) {
+
+ log_err("Failed to bootstrap: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_BOOT]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
}
conf = ipcp_config_msg_to_s(conf_msg);
- ret_msg->result = ipcpi.ops->ipcp_bootstrap(&conf);
- if (ret_msg->result == 0) {
- ret_msg->layer_info = layer_info_s_to_msg(&conf.layer_info);
- ipcp_set_state(IPCP_OPERATIONAL);
+ switch(conf.type) { /* FIXED algorithms */
+ case IPCP_UDP4:
+ /* FALLTHRU */
+ case IPCP_UDP6:
+ conf.layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5;
+ break;
+ case IPCP_BROADCAST:
+ conf.layer_info.dir_hash_algo = DIR_HASH_SHA3_256;
+ break;
+ default:
+ break;
+ }
+
+ ret_msg->result = ipcpd.ops->ipcp_bootstrap(&conf);
+ if (ret_msg->result < 0) {
+ log_err("Failed to bootstrap IPCP.");
+ return;
}
- finish:
- log_info("Finished bootstrapping: %d.", ret_msg->result);
+
+ info = &conf.layer_info;
+
+ strcpy(ipcpd.layer_name, info->name);
+ ipcpd.dir_hash_algo = (enum hash_algo) info->dir_hash_algo;
+ ret_msg->layer_info = layer_info_s_to_msg(info);
+ ipcp_set_state(IPCP_OPERATIONAL);
+
+ log_info("Finished bootstrapping in %s.", info->name);
+ log_info(" type: %s", ipcp_type_str[ipcpd.type]);
+ log_info(" hash: %s [%zd bytes]",
+ dir_hash_str[ipcpd.dir_hash_algo],
+ ipcp_dir_hash_len());
}
static void do_enroll(const char * dst,
@@ -382,25 +509,36 @@ static void do_enroll(const char * dst,
log_info("Enrolling with %s...", dst);
- if (ipcpi.ops->ipcp_enroll == NULL) {
- log_err("Enroll unsupported.");
+ if (ipcpd.ops->ipcp_enroll == NULL) {
+ log_err("Failed to enroll: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- if (ipcp_get_state() != IPCP_INIT) {
- log_err("IPCP in wrong state.");
+ if (ipcp_get_state() != IPCP_BOOT) {
+ log_err("Failed to enroll: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_BOOT]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_enroll(dst, &info);
- if (ret_msg->result == 0) {
- ret_msg->layer_info = layer_info_s_to_msg(&info);
- ipcp_set_state(IPCP_OPERATIONAL);
+ ret_msg->result = ipcpd.ops->ipcp_enroll(dst, &info);
+ if (ret_msg->result < 0) {
+ log_err("Failed to bootstrap IPCP.");
+ return;
}
- finish:
- log_info("Finished enrolling with %s: %d.", dst, ret_msg->result);
+
+ strcpy(ipcpd.layer_name, info.name);
+ ipcpd.dir_hash_algo = (enum hash_algo) info.dir_hash_algo;
+ ret_msg->layer_info = layer_info_s_to_msg(&info);
+ ipcp_set_state(IPCP_OPERATIONAL);
+
+ log_info("Finished enrolling with %s in layer %s.", dst, info.name);
+ log_info(" type: %s", ipcp_type_str[ipcpd.type]);
+ log_info(" hash: %s [%zd bytes]",
+ dir_hash_str[ipcpd.dir_hash_algo],
+ ipcp_dir_hash_len());
}
static void do_connect(const char * dst,
@@ -410,15 +548,15 @@ static void do_connect(const char * dst,
{
log_info("Connecting %s to %s...", comp, dst);
- if (ipcpi.ops->ipcp_connect == NULL) {
- log_err("Connect unsupported.");
+ if (ipcpd.ops->ipcp_connect == NULL) {
+ log_err("Failed to connect: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_connect(dst, comp, qs);
- finish:
- log_info("Finished connecting: %d.", ret_msg->result);
+ ret_msg->result = ipcpd.ops->ipcp_connect(dst, comp, qs);
+
+ log_info("Finished connecting.");
}
static void do_disconnect(const char * dst,
@@ -427,17 +565,15 @@ static void do_disconnect(const char * dst,
{
log_info("Disconnecting %s from %s...", comp, dst);
- if (ipcpi.ops->ipcp_disconnect == NULL) {
- log_err("Disconnect unsupported.");
+ if (ipcpd.ops->ipcp_disconnect == NULL) {
+ log_err("Failed to disconnect: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_disconnect(dst, comp);
+ ret_msg->result = ipcpd.ops->ipcp_disconnect(dst, comp);
- finish:
- log_info("Finished disconnecting %s from %s: %d.",
- comp, dst, ret_msg->result);
+ log_info("Finished disconnecting %s from %s.", comp, dst);
}
static void do_reg(const uint8_t * hash,
@@ -446,16 +582,15 @@ static void do_reg(const uint8_t * hash,
log_info("Registering " HASH_FMT32 "...", HASH_VAL32(hash));
- if (ipcpi.ops->ipcp_reg == NULL) {
- log_err("Registration unsupported.");
+ if (ipcpd.ops->ipcp_reg == NULL) {
+ log_err("Failed to register: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_reg(hash);
- finish:
- log_info("Finished registering " HASH_FMT32 " : %d.",
- HASH_VAL32(hash), ret_msg->result);
+ ret_msg->result = ipcpd.ops->ipcp_reg(hash);
+
+ log_info("Finished registering " HASH_FMT32 ".", HASH_VAL32(hash));
}
static void do_unreg(const uint8_t * hash,
@@ -463,16 +598,15 @@ static void do_unreg(const uint8_t * hash,
{
log_info("Unregistering " HASH_FMT32 "...", HASH_VAL32(hash));
- if (ipcpi.ops->ipcp_unreg == NULL) {
- log_err("Unregistration unsupported.");
+ if (ipcpd.ops->ipcp_unreg == NULL) {
+ log_err("Failed to unregister: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_unreg(hash);
- finish:
- log_info("Finished unregistering " HASH_FMT32 ": %d.",
- HASH_VAL32(hash), ret_msg->result);
+ ret_msg->result = ipcpd.ops->ipcp_unreg(hash);
+
+ log_info("Finished unregistering " HASH_FMT32 ".", HASH_VAL32(hash));
}
static void do_query(const uint8_t * hash,
@@ -480,19 +614,21 @@ static void do_query(const uint8_t * hash,
{
/* TODO: Log this operation when IRMd has internal caches. */
- if (ipcpi.ops->ipcp_query == NULL) {
- log_err("Directory query unsupported.");
+ if (ipcpd.ops->ipcp_query == NULL) {
+ log_err("Failed to query: operation unsupported.");
ret_msg->result = -ENOTSUP;
return;
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_err("IPCP in wrong state.");
+ log_dbg("Failed to query: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_OPERATIONAL]);
ret_msg->result = -EIPCPSTATE;
return;
}
- ret_msg->result = ipcpi.ops->ipcp_query(hash);
+ ret_msg->result = ipcpd.ops->ipcp_query(hash);
}
static void do_flow_alloc(pid_t pid,
@@ -500,23 +636,27 @@ static void do_flow_alloc(pid_t pid,
uint8_t * dst,
qosspec_t qs,
const buffer_t * data,
+ uid_t uid,
ipcp_msg_t * ret_msg)
{
- int fd;
+ int fd;
+ struct ssm_pool * pool = NULL;
log_info("Allocating flow %d for %d to " HASH_FMT32 ".",
flow_id, pid, HASH_VAL32(dst));
- if (ipcpi.ops->ipcp_flow_alloc == NULL) {
- log_err("Flow allocation unsupported.");
+ if (ipcpd.ops->ipcp_flow_alloc == NULL) {
+ log_err("Flow allocation failed: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_err("IPCP in wrong state.");
+ log_err("Failed to enroll: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_OPERATIONAL]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
}
fd = np1_flow_alloc(pid, flow_id);
@@ -524,83 +664,124 @@ static void do_flow_alloc(pid_t pid,
log_err("Failed allocating n + 1 fd on flow_id %d: %d",
flow_id, fd);
ret_msg->result = -EFLOWDOWN;
- goto finish;
+ return;
+ }
+
+ if (uid != 0) {
+ pool = ssm_pool_open(uid);
+ if (pool == NULL) {
+ log_err("Failed to open PUP for uid %d.", uid);
+ ret_msg->result = -ENOMEM;
+ return;
+ }
}
- ret_msg->result = ipcpi.ops->ipcp_flow_alloc(fd, dst, qs, data);
- finish:
- log_info("Finished allocating flow %d to " HASH_FMT32 ": %d.",
- flow_id, HASH_VAL32(dst), ret_msg->result);
+ NP1_SET_POOL(fd, pool);
+
+ ret_msg->result = ipcpd.ops->ipcp_flow_alloc(fd, dst, qs, data);
+
+ log_info("Finished allocating flow %d to " HASH_FMT32 ".",
+ flow_id, HASH_VAL32(dst));
}
static void do_flow_join(pid_t pid,
int flow_id,
const uint8_t * dst,
- qosspec_t qs,
+ uid_t uid,
ipcp_msg_t * ret_msg)
{
- int fd;
+ int fd;
+ struct ssm_pool * pool = NULL;
log_info("Joining layer " HASH_FMT32 ".", HASH_VAL32(dst));
- if (ipcpi.ops->ipcp_flow_join == NULL) {
- log_err("Broadcast unsupported.");
+ if (ipcpd.ops->ipcp_flow_join == NULL) {
+ log_err("Failed to join: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_err("IPCP in wrong state.");
+ log_err("Failed to join: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_OPERATIONAL]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
}
fd = np1_flow_alloc(pid, flow_id);
if (fd < 0) {
log_err("Failed allocating n + 1 fd on flow_id %d.", flow_id);
ret_msg->result = -1;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_flow_join(fd, dst, qs);
- finish:
+ if (uid != 0) {
+ pool = ssm_pool_open(uid);
+ if (pool == NULL) {
+ log_err("Failed to open PUP for uid %d.", uid);
+ ret_msg->result = -ENOMEM;
+ return;
+ }
+ }
+
+ NP1_SET_POOL(fd, pool);
+
+ ret_msg->result = ipcpd.ops->ipcp_flow_join(fd, dst);
+
log_info("Finished joining layer " HASH_FMT32 ".", HASH_VAL32(dst));
}
static void do_flow_alloc_resp(int resp,
int flow_id,
+ uid_t uid,
const buffer_t * data,
ipcp_msg_t * ret_msg)
{
- int fd = -1;
+ int fd = -1;
+ struct ssm_pool * pool = NULL;
log_info("Responding %d to alloc on flow_id %d.", resp, flow_id);
- if (ipcpi.ops->ipcp_flow_alloc_resp == NULL) {
- log_err("Flow_alloc_resp unsupported.");
+ if (ipcpd.ops->ipcp_flow_alloc_resp == NULL) {
+ log_err("Failed to respond on flow %d: operation unsupported.",
+ flow_id);
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_err("IPCP in wrong state.");
+ log_err("Failed to respond to flow %d:"
+ "IPCP in state <%s>, need <%s>.",
+ flow_id,
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_OPERATIONAL]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
+ }
+
+ fd = np1_flow_resp(flow_id, resp);
+ if (fd < 0) {
+ log_warn("Flow_id %d is not known.", flow_id);
+ ret_msg->result = -1;
+ return;
}
- if (resp == 0) {
- fd = np1_flow_resp(flow_id);
- if (fd < 0) {
- log_warn("Flow_id %d is not known.", flow_id);
- ret_msg->result = -1;
- goto finish;
+ if (uid != 0) {
+ pool = ssm_pool_open(uid);
+ if (pool == NULL) {
+ log_err("Failed to open PUP for uid %d.", uid);
+ ret_msg->result = -ENOMEM;
+ return;
}
}
- ret_msg->result = ipcpi.ops->ipcp_flow_alloc_resp(fd, resp, data);
- finish:
- log_info("Finished responding to allocation request: %d",
+ NP1_SET_POOL(fd, pool);
+
+ ret_msg->result = ipcpd.ops->ipcp_flow_alloc_resp(fd, resp, data);
+
+ log_info("Finished responding %d to allocation request.",
ret_msg->result);
}
@@ -612,55 +793,56 @@ static void do_flow_dealloc(int flow_id,
log_info("Deallocating flow %d.", flow_id);
- if (ipcpi.ops->ipcp_flow_dealloc == NULL) {
- log_err("Flow deallocation unsupported.");
+ if (ipcpd.ops->ipcp_flow_dealloc == NULL) {
+ log_err("Failed to dealloc: operation unsupported.");
ret_msg->result = -ENOTSUP;
- goto finish;
+ return;
}
if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_err("IPCP in wrong state.");
+ log_err("Failed to enroll: IPCP in state <%s>, need <%s>.",
+ ipcp_state_str[ipcp_get_state()],
+ ipcp_state_str[IPCP_OPERATIONAL]);
ret_msg->result = -EIPCPSTATE;
- goto finish;
+ return;
}
fd = np1_flow_dealloc(flow_id, timeo_sec);
if (fd < 0) {
log_warn("Could not deallocate flow_id %d.", flow_id);
ret_msg->result = -1;
- goto finish;
+ return;
}
- ret_msg->result = ipcpi.ops->ipcp_flow_dealloc(fd);
- finish:
- log_info("Finished deallocating flow %d: %d.",
- flow_id, ret_msg->result);
+ ret_msg->result = ipcpd.ops->ipcp_flow_dealloc(fd);
+
+ log_info("Finished deallocating flow %d.", flow_id);
}
static void * mainloop(void * o)
{
- int sfd;
- buffer_t buffer;
- ipcp_msg_t * msg;
+ int sfd;
+ buffer_t buffer;
+ ipcp_msg_t * msg;
(void) o;
while (true) {
- ipcp_msg_t ret_msg = IPCP_MSG__INIT;
- qosspec_t qs;
- struct cmd * cmd;
- buffer_t data;
+ ipcp_msg_t ret_msg = IPCP_MSG__INIT;
+ qosspec_t qs;
+ struct cmd * cmd;
+ buffer_t data;
ret_msg.code = IPCP_MSG_CODE__IPCP_REPLY;
- pthread_mutex_lock(&ipcpi.cmd_lock);
+ pthread_mutex_lock(&ipcpd.cmd_lock);
- pthread_cleanup_push(__cleanup_mutex_unlock, &ipcpi.cmd_lock);
+ pthread_cleanup_push(__cleanup_mutex_unlock, &ipcpd.cmd_lock);
- while (list_is_empty(&ipcpi.cmds))
- pthread_cond_wait(&ipcpi.cmd_cond, &ipcpi.cmd_lock);
+ while (list_is_empty(&ipcpd.cmds))
+ pthread_cond_wait(&ipcpd.cmd_cond, &ipcpd.cmd_lock);
- cmd = list_last_entry(&ipcpi.cmds, struct cmd, next);
+ cmd = list_last_entry(&ipcpd.cmds, struct cmd, next);
list_del(&cmd->next);
pthread_cleanup_pop(true);
@@ -675,7 +857,7 @@ static void * mainloop(void * o)
continue;
}
- tpm_dec(ipcpi.tpm);
+ tpm_begin_work(ipcpd.tpm);
pthread_cleanup_push(__cleanup_close_ptr, &sfd);
pthread_cleanup_push(free_msg, msg);
@@ -717,13 +899,12 @@ static void * mainloop(void * o)
qs = qos_spec_msg_to_s(msg->qosspec);
do_flow_alloc(msg->pid, msg->flow_id,
msg->hash.data, qs,
- &data, &ret_msg);
+ &data, msg->uid, &ret_msg);
break;
case IPCP_MSG_CODE__IPCP_FLOW_JOIN:
assert(msg->hash.len == ipcp_dir_hash_len());
- qs = qos_spec_msg_to_s(msg->qosspec);
do_flow_join(msg->pid, msg->flow_id,
- msg->hash.data, qs, &ret_msg);
+ msg->hash.data, msg->uid, &ret_msg);
break;
case IPCP_MSG_CODE__IPCP_FLOW_ALLOC_RESP:
assert(msg->pk.len > 0 ? msg->pk.data != NULL
@@ -731,7 +912,7 @@ static void * mainloop(void * o)
data.len = msg->pk.len;
data.data = msg->pk.data;
do_flow_alloc_resp(msg->response, msg->flow_id,
- &data, &ret_msg);
+ msg->uid, &data, &ret_msg);
break;
case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC:
do_flow_dealloc(msg->flow_id, msg->timeo_sec, &ret_msg);
@@ -749,7 +930,7 @@ static void * mainloop(void * o)
if (buffer.len == 0) {
log_err("Failed to pack reply message");
close(sfd);
- tpm_inc(ipcpi.tpm);
+ tpm_end_work(ipcpd.tpm);
continue;
}
@@ -757,7 +938,7 @@ static void * mainloop(void * o)
if (buffer.data == NULL) {
log_err("Failed to create reply buffer.");
close(sfd);
- tpm_inc(ipcpi.tpm);
+ tpm_end_work(ipcpd.tpm);
continue;
}
@@ -766,16 +947,16 @@ static void * mainloop(void * o)
if (ret_msg.layer_info != NULL)
layer_info_msg__free_unpacked(ret_msg.layer_info, NULL);
- pthread_cleanup_push(__cleanup_close_ptr, &sfd);
pthread_cleanup_push(free, buffer.data)
+ pthread_cleanup_push(__cleanup_close_ptr, &sfd);
if (write(sfd, buffer.data, buffer.len) == -1)
log_warn("Failed to send reply message");
- pthread_cleanup_pop(true);
- pthread_cleanup_pop(true);
+ pthread_cleanup_pop(true); /* close sfd */
+ pthread_cleanup_pop(true); /* free buffer.data */
- tpm_inc(ipcpi.tpm);
+ tpm_end_work(ipcpd.tpm);
}
return (void *) 0;
@@ -794,10 +975,10 @@ static int parse_args(int argc,
if (atoi(argv[1]) == 0)
return -1;
- ipcpi.irmd_pid = atoi(argv[1]);
+ ipcpd.irmd_pid = atoi(argv[1]);
/* argument 2: IPCP name */
- ipcpi.name = argv[2];
+ ipcpd.name = argv[2];
/* argument 3: syslog */
if (argv[3] != NULL)
@@ -813,71 +994,69 @@ int ipcp_init(int argc,
{
bool log;
pthread_condattr_t cattr;
- int ret = -1;
if (parse_args(argc, argv, &log))
return -1;
log_init(log);
- ipcpi.irmd_fd = -1;
- ipcpi.state = IPCP_NULL;
- ipcpi.type = type;
+ ipcpd.type = type;
#if defined (__linux__)
prctl(PR_SET_TIMERSLACK, IPCP_LINUX_SLACK_NS, 0, 0, 0);
#endif
- ipcpi.sock_path = ipcp_sock_path(getpid());
- if (ipcpi.sock_path == NULL)
+ ipcpd.sock_path = sock_path(getpid(), IPCP_SOCK_PATH_PREFIX);
+ if (ipcpd.sock_path == NULL)
goto fail_sock_path;
- ipcpi.sockfd = server_socket_open(ipcpi.sock_path);
- if (ipcpi.sockfd < 0) {
- log_err("Could not open server socket.");
+ ipcpd.sockfd = server_socket_open(ipcpd.sock_path);
+ if (ipcpd.sockfd < 0) {
+ log_err("Failed to open server socket at %s.",
+ ipcpd.sock_path);
goto fail_serv_sock;
}
- ipcpi.ops = ops;
+ ipcpd.ops = ops;
- if (pthread_mutex_init(&ipcpi.state_mtx, NULL)) {
- log_err("Could not create mutex.");
+ if (pthread_mutex_init(&ipcpd.state_mtx, NULL)) {
+ log_err("Failed to create mutex.");
goto fail_state_mtx;
}
if (pthread_condattr_init(&cattr)) {
- log_err("Could not create condattr.");
+ log_err("Failed to create condattr.");
goto fail_cond_attr;
}
#ifndef __APPLE__
pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
#endif
- if (pthread_cond_init(&ipcpi.state_cond, &cattr)) {
- log_err("Could not init condvar.");
+ if (pthread_cond_init(&ipcpd.state_cond, &cattr)) {
+ log_err("Failed to init condvar.");
goto fail_state_cond;
}
- if (pthread_mutex_init(&ipcpi.alloc_lock, NULL)) {
+ if (pthread_mutex_init(&ipcpd.alloc_lock, NULL)) {
log_err("Failed to init mutex.");
goto fail_alloc_lock;
}
- if (pthread_cond_init(&ipcpi.alloc_cond, &cattr)) {
+ if (pthread_cond_init(&ipcpd.alloc_cond, &cattr)) {
log_err("Failed to init convar.");
goto fail_alloc_cond;
}
- if (pthread_mutex_init(&ipcpi.cmd_lock, NULL)) {
+ if (pthread_mutex_init(&ipcpd.cmd_lock, NULL)) {
log_err("Failed to init mutex.");
goto fail_cmd_lock;
}
- if (pthread_cond_init(&ipcpi.cmd_cond, &cattr)) {
+ if (pthread_cond_init(&ipcpd.cmd_cond, &cattr)) {
log_err("Failed to init convar.");
goto fail_cmd_cond;
}
- if (rib_init(ipcpi.name)) {
+ if (rib_init(ipcpd.name)) {
log_err("Failed to initialize RIB.");
goto fail_rib_init;
}
@@ -887,21 +1066,26 @@ int ipcp_init(int argc,
goto fail_rib_reg;
}
- ipcpi.tpm = tpm_create(IPCP_MIN_THREADS, IPCP_ADD_THREADS,
+ list_head_init(&ipcpd.cmds);
+
+ ipcpd.tpm = tpm_create(IPCP_MIN_THREADS, IPCP_ADD_THREADS,
mainloop, NULL);
- if (ipcpi.tpm == NULL) {
+ if (ipcpd.tpm == NULL) {
log_err("Failed to create threadpool manager.");
goto fail_tpm_create;
}
- list_head_init(&ipcpi.cmds);
+ ipcpd.alloc_id = -1;
- ipcpi.alloc_id = -1;
+ memset(&np1, 0, sizeof(np1));
pthread_condattr_destroy(&cattr);
ipcp_set_state(IPCP_INIT);
+ log_info("IPCP %s %d initialized.", ipcp_type_str[ipcpd.type],
+ getpid());
+
return 0;
fail_tpm_create:
@@ -909,25 +1093,25 @@ int ipcp_init(int argc,
fail_rib_reg:
rib_fini();
fail_rib_init:
- pthread_cond_destroy(&ipcpi.cmd_cond);
+ pthread_cond_destroy(&ipcpd.cmd_cond);
fail_cmd_cond:
- pthread_mutex_destroy(&ipcpi.cmd_lock);
+ pthread_mutex_destroy(&ipcpd.cmd_lock);
fail_cmd_lock:
- pthread_cond_destroy(&ipcpi.alloc_cond);
+ pthread_cond_destroy(&ipcpd.alloc_cond);
fail_alloc_cond:
- pthread_mutex_destroy(&ipcpi.alloc_lock);
+ pthread_mutex_destroy(&ipcpd.alloc_lock);
fail_alloc_lock:
- pthread_cond_destroy(&ipcpi.state_cond);
+ pthread_cond_destroy(&ipcpd.state_cond);
fail_state_cond:
pthread_condattr_destroy(&cattr);
fail_cond_attr:
- pthread_mutex_destroy(&ipcpi.state_mtx);
+ pthread_mutex_destroy(&ipcpd.state_mtx);
fail_state_mtx:
- close(ipcpi.sockfd);
+ close(ipcpd.sockfd);
fail_serv_sock:
- free(ipcpi.sock_path);
+ free(ipcpd.sock_path);
fail_sock_path:
- return ret;
+ return -1;
}
int ipcp_start(void)
@@ -944,20 +1128,22 @@ int ipcp_start(void)
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
info.pid = getpid();
- info.type = ipcpi.type;
- strcpy(info.name, ipcpi.name);
- info.state = IPCP_OPERATIONAL;
+ info.type = ipcpd.type;
+ strcpy(info.name, ipcpd.name);
+ info.state = IPCP_BOOT;
- if (tpm_start(ipcpi.tpm))
+ ipcp_set_state(IPCP_BOOT);
+
+ if (tpm_start(ipcpd.tpm)) {
+ log_err("Failed to start threadpool manager.");
goto fail_tpm_start;
+ }
- if (pthread_create(&ipcpi.acceptor, NULL, acceptloop, NULL)) {
+ if (pthread_create(&ipcpd.acceptor, NULL, acceptloop, NULL)) {
log_err("Failed to create acceptor thread.");
goto fail_acceptor;
}
- info.state = IPCP_OPERATIONAL;
-
if (ipcp_create_r(&info)) {
log_err("Failed to notify IRMd we are initialized.");
goto fail_create_r;
@@ -966,14 +1152,13 @@ int ipcp_start(void)
return 0;
fail_create_r:
- pthread_cancel(ipcpi.acceptor);
- pthread_join(ipcpi.acceptor, NULL);
+ pthread_cancel(ipcpd.acceptor);
+ pthread_join(ipcpd.acceptor, NULL);
fail_acceptor:
- tpm_stop(ipcpi.tpm);
+ tpm_stop(ipcpd.tpm);
fail_tpm_start:
- tpm_destroy(ipcpi.tpm);
- ipcp_set_state(IPCP_NULL);
- info.state = IPCP_NULL;
+ tpm_destroy(ipcpd.tpm);
+ ipcp_set_state(IPCP_INIT);
ipcp_create_r(&info);
return -1;
}
@@ -993,7 +1178,7 @@ void ipcp_sigwait(void)
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGPIPE);
- while(ipcp_get_state() != IPCP_NULL &&
+ while(ipcp_get_state() != IPCP_INIT &&
ipcp_get_state() != IPCP_SHUTDOWN) {
#ifdef __APPLE__
if (sigwait(&sigset, &sig) < 0) {
@@ -1007,7 +1192,7 @@ void ipcp_sigwait(void)
#ifdef __APPLE__
memset(&info, 0, sizeof(info));
info.si_signo = sig;
- info.si_pid = ipcpi.irmd_pid;
+ info.si_pid = ipcpd.irmd_pid;
#endif
switch(info.si_signo) {
case SIGINT:
@@ -1017,9 +1202,9 @@ void ipcp_sigwait(void)
case SIGHUP:
/* FALLTHRU */
case SIGQUIT:
- if (info.si_pid == ipcpi.irmd_pid) {
- if (ipcp_get_state() == IPCP_INIT)
- ipcp_set_state(IPCP_NULL);
+ if (info.si_pid == ipcpd.irmd_pid) {
+ if (ipcp_get_state() == IPCP_BOOT)
+ ipcp_set_state(IPCP_INIT);
if (ipcp_get_state() == IPCP_OPERATIONAL)
ipcp_set_state(IPCP_SHUTDOWN);
@@ -1038,58 +1223,62 @@ void ipcp_stop(void)
{
log_info("IPCP %d shutting down.", getpid());
- pthread_cancel(ipcpi.acceptor);
- pthread_join(ipcpi.acceptor, NULL);
+ pthread_cancel(ipcpd.acceptor);
+ pthread_join(ipcpd.acceptor, NULL);
- tpm_stop(ipcpi.tpm);
+ tpm_stop(ipcpd.tpm);
+
+ ipcp_set_state(IPCP_INIT);
}
void ipcp_fini(void)
{
- tpm_destroy(ipcpi.tpm);
+ tpm_destroy(ipcpd.tpm);
rib_unreg(IPCP_INFO);
rib_fini();
- close(ipcpi.sockfd);
- if (unlink(ipcpi.sock_path))
- log_warn("Could not unlink %s.", ipcpi.sock_path);
+ close(ipcpd.sockfd);
+ if (unlink(ipcpd.sock_path))
+ log_warn("Could not unlink %s.", ipcpd.sock_path);
- free(ipcpi.sock_path);
+ free(ipcpd.sock_path);
- pthread_cond_destroy(&ipcpi.state_cond);
- pthread_mutex_destroy(&ipcpi.state_mtx);
- pthread_cond_destroy(&ipcpi.alloc_cond);
- pthread_mutex_destroy(&ipcpi.alloc_lock);
- pthread_cond_destroy(&ipcpi.cmd_cond);
- pthread_mutex_destroy(&ipcpi.cmd_lock);
+ pthread_cond_destroy(&ipcpd.state_cond);
+ pthread_mutex_destroy(&ipcpd.state_mtx);
+ pthread_cond_destroy(&ipcpd.alloc_cond);
+ pthread_mutex_destroy(&ipcpd.alloc_lock);
+ pthread_cond_destroy(&ipcpd.cmd_cond);
+ pthread_mutex_destroy(&ipcpd.cmd_lock);
log_info("IPCP %d out.", getpid());
log_fini();
+
+ ipcpd.state = IPCP_NULL;
}
void ipcp_set_state(enum ipcp_state state)
{
- pthread_mutex_lock(&ipcpi.state_mtx);
+ pthread_mutex_lock(&ipcpd.state_mtx);
- ipcpi.state = state;
+ ipcpd.state = state;
- pthread_cond_broadcast(&ipcpi.state_cond);
- pthread_mutex_unlock(&ipcpi.state_mtx);
+ pthread_cond_broadcast(&ipcpd.state_cond);
+ pthread_mutex_unlock(&ipcpd.state_mtx);
}
enum ipcp_state ipcp_get_state(void)
{
enum ipcp_state state;
- pthread_mutex_lock(&ipcpi.state_mtx);
+ pthread_mutex_lock(&ipcpd.state_mtx);
- state = ipcpi.state;
+ state = ipcpd.state;
- pthread_mutex_unlock(&ipcpi.state_mtx);
+ pthread_mutex_unlock(&ipcpd.state_mtx);
return state;
}
diff --git a/src/ipcpd/ipcp.h b/src/ipcpd/ipcp.h
index aab490c7..e8c31a32 100644
--- a/src/ipcpd/ipcp.h
+++ b/src/ipcpd/ipcp.h
@@ -34,8 +34,10 @@
#include <pthread.h>
#include <time.h>
+#define ipcp_dir_hash_strlen() (ipcp_dir_hash_len() * 2)
+
struct ipcp_ops {
- int (* ipcp_bootstrap)(const struct ipcp_config * conf);
+ int (* ipcp_bootstrap)(struct ipcp_config * conf);
int (* ipcp_enroll)(const char * dst,
struct layer_info * info);
@@ -59,8 +61,7 @@ struct ipcp_ops {
const buffer_t * data);
int (* ipcp_flow_join)(int fd,
- const uint8_t * dst,
- qosspec_t qs);
+ const uint8_t * dst);
int (* ipcp_flow_alloc_resp)(int fd,
int response,
@@ -69,43 +70,6 @@ struct ipcp_ops {
int (* ipcp_flow_dealloc)(int fd);
};
-#define ipcp_dir_hash_strlen() (hash_len(ipcpi.dir_hash_algo) * 2)
-#define ipcp_dir_hash_len() (hash_len(ipcpi.dir_hash_algo))
-
-extern struct ipcp {
- pid_t irmd_pid;
- char * name;
-
- enum ipcp_type type;
- char layer_name[LAYER_NAME_SIZE + 1];
-
- uint64_t dt_addr;
-
- enum hash_algo dir_hash_algo;
-
- struct ipcp_ops * ops;
- int irmd_fd;
-
- enum ipcp_state state;
- pthread_cond_t state_cond;
- pthread_mutex_t state_mtx;
-
- int sockfd;
- char * sock_path;
-
- struct list_head cmds;
- pthread_cond_t cmd_cond;
- pthread_mutex_t cmd_lock;
-
- int alloc_id;
- pthread_cond_t alloc_cond;
- pthread_mutex_t alloc_lock;
-
- struct tpm * tpm;
-
- pthread_t acceptor;
-} ipcpi;
-
int ipcp_init(int argc,
char ** argv,
struct ipcp_ops * ops,
@@ -119,13 +83,17 @@ void ipcp_stop(void);
void ipcp_fini(void);
+enum ipcp_type ipcp_get_type(void);
+
+const char * ipcp_get_name(void);
+
+/* TODO: Only specify hash algorithm in directory policy */
+void ipcp_set_dir_hash_algo(enum hash_algo algo);
+
void ipcp_set_state(enum ipcp_state state);
enum ipcp_state ipcp_get_state(void);
-int ipcp_parse_arg(int argc,
- char * argv[]);
-
/* Helper functions to handle races during flow allocation */
int ipcp_wait_flow_req_arr(const uint8_t * dst,
qosspec_t qs,
@@ -134,7 +102,12 @@ int ipcp_wait_flow_req_arr(const uint8_t * dst,
int ipcp_wait_flow_resp(const int fd);
+
/* Helper functions for directory entries, could be moved */
+size_t ipcp_dir_hash_len(void);
+
+int ipcp_get_layer_name(char * layer);
+
uint8_t * ipcp_hash_dup(const uint8_t * hash);
void ipcp_hash_str(char buf[],
diff --git a/src/ipcpd/local/CMakeLists.txt b/src/ipcpd/local/CMakeLists.txt
index 10fd0120..0da4d47a 100644
--- a/src/ipcpd/local/CMakeLists.txt
+++ b/src/ipcpd/local/CMakeLists.txt
@@ -1,34 +1,19 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+# Local IPCP build configuration
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+set(IPCP_LOCAL_TARGET ipcpd-local)
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
+add_executable(${IPCP_LOCAL_TARGET}
+ main.c
+ ${IPCP_SOURCES}
+)
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
+target_include_directories(${IPCP_LOCAL_TARGET} PRIVATE ${IPCP_INCLUDE_DIRS})
+target_link_libraries(${IPCP_LOCAL_TARGET} PRIVATE ouroboros-dev)
-set(IPCP_LOCAL_TARGET ipcpd-local CACHE INTERNAL "")
-set(IPCP_LOCAL_MPL 2 CACHE STRING
- "Default maximum packet lifetime for the Ethernet IPCPs, in seconds")
+ouroboros_target_debug_definitions(${IPCP_LOCAL_TARGET})
-set(LOCAL_SOURCES
- # Add source files here
- ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
+if(IPCP_LOCAL_POLLING)
+ target_compile_definitions(${IPCP_LOCAL_TARGET} PRIVATE CONFIG_IPCP_LOCAL_POLLING)
+endif()
-add_executable(ipcpd-local ${LOCAL_SOURCES} ${IPCP_SOURCES})
-target_link_libraries(ipcpd-local LINK_PUBLIC ouroboros-common ouroboros-dev)
-
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-local -DCONFIG_OUROBOROS_DEBUG)
-endif ()
-
-install(TARGETS ipcpd-local RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
-
-# Enable once ipcp-local has tests
-# add_subdirectory(tests)
+install(TARGETS ${IPCP_LOCAL_TARGET} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/local/main.c b/src/ipcpd/local/main.c
index 160e07e0..5372197f 100644
--- a/src/ipcpd/local/main.c
+++ b/src/ipcpd/local/main.c
@@ -40,6 +40,7 @@
#include <ouroboros/local-dev.h>
#include "ipcp.h"
+#include "np1.h"
#include "shim-data.h"
#include <string.h>
@@ -50,8 +51,6 @@
#define THIS_TYPE IPCP_LOCAL
-struct ipcp ipcpi;
-
struct {
struct shim_data * shim_data;
@@ -105,47 +104,54 @@ static void local_data_fini(void){
static void * local_ipcp_packet_loop(void * o)
{
+ int src_fd;
+ int dst_fd;
+ struct timespec * timeout;
+#ifdef CONFIG_IPCP_LOCAL_POLLING
+ struct timespec ts_poll = {0, 0};
+#endif
(void) o;
ipcp_lock_to_core();
- while (true) {
- int fd;
- ssize_t idx;
+#ifdef CONFIG_IPCP_LOCAL_POLLING
+ timeout = &ts_poll; /* Spin poll with zero timeout */
+#else
+ timeout = NULL; /* Block until event */
+#endif
- fevent(local_data.flows, local_data.fq, NULL);
+ while (true) {
+ fevent(local_data.flows, local_data.fq, timeout);
- while ((fd = fqueue_next(local_data.fq)) >= 0) {
+ while ((src_fd = fqueue_next(local_data.fq)) >= 0) {
if (fqueue_type(local_data.fq) != FLOW_PKT)
continue;
- idx = local_flow_read(fd);
- if (idx < 0)
- continue;
-
- assert(idx < (SHM_BUFFER_SIZE));
-
pthread_rwlock_rdlock(&local_data.lock);
- fd = local_data.in_out[fd];
+ dst_fd = local_data.in_out[src_fd];
pthread_rwlock_unlock(&local_data.lock);
- if (fd != -1)
- local_flow_write(fd, idx);
+ if (dst_fd == -1)
+ continue;
+
+ local_flow_transfer(src_fd, dst_fd,
+ NP1_GET_POOL(src_fd),
+ NP1_GET_POOL(dst_fd));
}
}
return (void *) 0;
}
-static int local_ipcp_bootstrap(const struct ipcp_config * conf)
+static int local_ipcp_bootstrap(struct ipcp_config * conf)
{
+
assert(conf);
assert(conf->type == THIS_TYPE);
- ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo;
- strcpy(ipcpi.layer_name,conf->layer_info.name);
+ (void) conf;
if (pthread_create(&local_data.packet_loop, NULL,
local_ipcp_packet_loop, NULL)) {
diff --git a/src/ipcpd/np1.h b/src/ipcpd/np1.h
new file mode 100644
index 00000000..a3f21200
--- /dev/null
+++ b/src/ipcpd/np1.h
@@ -0,0 +1,41 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * N+1 flow pool tracking for IPCPs
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IPCPD_NP1_H
+#define OUROBOROS_IPCPD_NP1_H
+
+#include "config.h"
+
+#include <ouroboros/ssm_pool.h>
+
+#define NP1_LOAD(ptr) (__atomic_load_n((ptr), __ATOMIC_ACQUIRE))
+#define NP1_STORE(ptr, v) (__atomic_store_n((ptr), (v), __ATOMIC_RELEASE))
+#define NP1_GET_POOL(fd) (NP1_LOAD(&np1.pool[(fd)]))
+#define NP1_SET_POOL(fd, p) (NP1_STORE(&np1.pool[(fd)], (p)))
+
+struct np1_state {
+ struct ssm_pool * pool[SYS_MAX_FLOWS];
+};
+
+extern struct np1_state np1;
+
+#endif /* OUROBOROS_IPCPD_NP1_H */
diff --git a/src/ipcpd/shim-data.c b/src/ipcpd/shim-data.c
index 1fac63ac..8801213a 100644
--- a/src/ipcpd/shim-data.c
+++ b/src/ipcpd/shim-data.c
@@ -51,7 +51,7 @@ struct reg_entry {
struct dir_entry {
struct list_head list;
uint8_t * hash;
- uint64_t addr;
+ struct addr addr;
};
static void destroy_dir_query(struct dir_query * query)
@@ -108,14 +108,12 @@ static void reg_entry_destroy(struct reg_entry * entry)
{
assert(entry);
- if (entry->hash != NULL)
- free(entry->hash);
-
+ free(entry->hash);
free(entry);
}
-static struct dir_entry * dir_entry_create(uint8_t * hash,
- uint64_t addr)
+static struct dir_entry * dir_entry_create(uint8_t * hash,
+ struct addr addr)
{
struct dir_entry * entry = malloc(sizeof(*entry));
if (entry == NULL)
@@ -133,9 +131,7 @@ static void dir_entry_destroy(struct dir_entry * entry)
{
assert(entry);
- if (entry->hash != NULL)
- free(entry->hash);
-
+ free(entry->hash);
free(entry);
}
@@ -258,13 +254,15 @@ static struct reg_entry * find_reg_entry_by_hash(struct shim_data * data,
static struct dir_entry * find_dir_entry(struct shim_data * data,
const uint8_t * hash,
- uint64_t addr)
+ struct addr addr)
{
struct list_head * h;
list_for_each(h, &data->directory) {
struct dir_entry * e = list_entry(h, struct dir_entry, list);
- if (e->addr == addr &&
- !memcmp(e->hash, hash, ipcp_dir_hash_len()))
+ if (memcmp(&e->addr, &addr, sizeof(addr)) != 0)
+ continue;
+
+ if (memcmp(e->hash, hash, ipcp_dir_hash_len()) == 0)
return e;
}
@@ -364,7 +362,7 @@ bool shim_data_reg_has(struct shim_data * data,
int shim_data_dir_add_entry(struct shim_data * data,
const uint8_t * hash,
- uint64_t addr)
+ struct addr addr)
{
struct dir_entry * entry;
uint8_t * entry_hash;
@@ -400,7 +398,7 @@ int shim_data_dir_add_entry(struct shim_data * data,
int shim_data_dir_del_entry(struct shim_data * data,
const uint8_t * hash,
- uint64_t addr)
+ struct addr addr)
{
struct dir_entry * e;
if (data == NULL)
@@ -437,11 +435,11 @@ bool shim_data_dir_has(struct shim_data * data,
return ret;
}
-uint64_t shim_data_dir_get_addr(struct shim_data * data,
- const uint8_t * hash)
+struct addr shim_data_dir_get_addr(struct shim_data * data,
+ const uint8_t * hash)
{
struct dir_entry * entry;
- uint64_t addr;
+ struct addr addr = {0};
pthread_rwlock_rdlock(&data->dir_lock);
@@ -449,7 +447,7 @@ uint64_t shim_data_dir_get_addr(struct shim_data * data,
if (entry == NULL) {
pthread_rwlock_unlock(&data->dir_lock);
log_warn("No address for " HASH_FMT32 ".", HASH_VAL32(hash));
- return 0; /* undefined behaviour, 0 may be a valid address */
+ return addr; /* undefined behaviour, 0 may be a valid address */
}
addr = entry->addr;
diff --git a/src/ipcpd/shim-data.h b/src/ipcpd/shim-data.h
index 372b4ea7..ea4ce413 100644
--- a/src/ipcpd/shim-data.h
+++ b/src/ipcpd/shim-data.h
@@ -25,9 +25,12 @@
#include <ouroboros/list.h>
-#include <sys/types.h>
#include <pthread.h>
#include <stdint.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#define MAC_SIZE 6
enum dir_query_state {
QUERY_INIT = 0,
@@ -46,6 +49,14 @@ struct dir_query {
pthread_cond_t cond;
};
+struct addr {
+ union {
+ uint8_t mac[MAC_SIZE];
+ struct in_addr ip4;
+ struct in6_addr ip6;
+ };
+};
+
struct shim_data {
struct list_head registry;
pthread_rwlock_t reg_lock;
@@ -72,16 +83,16 @@ bool shim_data_reg_has(struct shim_data * data,
int shim_data_dir_add_entry(struct shim_data * data,
const uint8_t * hash,
- uint64_t addr);
+ struct addr addr);
int shim_data_dir_del_entry(struct shim_data * data,
const uint8_t * hash,
- uint64_t addr);
+ struct addr addr);
bool shim_data_dir_has(struct shim_data * data,
const uint8_t * hash);
-uint64_t shim_data_dir_get_addr(struct shim_data * data,
+struct addr shim_data_dir_get_addr(struct shim_data * data,
const uint8_t * hash);
struct dir_query * shim_data_dir_query_create(struct shim_data * data,
diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt
index 8ae5518e..159e9bf5 100644
--- a/src/ipcpd/udp/CMakeLists.txt
+++ b/src/ipcpd/udp/CMakeLists.txt
@@ -1,69 +1,17 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+# UDP IPCPs build configuration (UDP4 and UDP6)
+# DDNS detection is in cmake/dependencies/udp/ddns.cmake
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+set(IPCP_UDP4_TARGET ipcpd-udp4)
+set(IPCP_UDP6_TARGET ipcpd-udp6)
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
+add_executable(${IPCP_UDP4_TARGET} udp4.c ${IPCP_SOURCES})
+add_executable(${IPCP_UDP6_TARGET} udp6.c ${IPCP_SOURCES})
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
+foreach(target ${IPCP_UDP4_TARGET} ${IPCP_UDP6_TARGET})
+ target_include_directories(${target} PRIVATE ${IPCP_INCLUDE_DIRS})
+ target_link_libraries(${target} PRIVATE ouroboros-dev)
+ ouroboros_target_debug_definitions(${target})
+endforeach()
-set(IPCP_UDP_TARGET ipcpd-udp CACHE INTERNAL "")
-
-set(UDP_SOURCES
- # Add source files here
- ${CMAKE_CURRENT_SOURCE_DIR}/main.c
- )
-
-add_executable(ipcpd-udp ${UDP_SOURCES} ${IPCP_SOURCES})
-
-target_link_libraries(ipcpd-udp LINK_PUBLIC ouroboros-dev)
-
-# Find the nsupdate executable
-find_program(NSUPDATE_EXECUTABLE
- NAMES nsupdate
- DOC "The nsupdate tool that enables DDNS")
-
-# Find the nslookup executable
-find_program(NSLOOKUP_EXECUTABLE
- NAMES nslookup
- DOC "The nslookup tool that resolves DNS names")
-
-mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE)
-
-if (NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE)
- set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support")
- if (NOT DISABLE_DNS)
- message(STATUS "DDNS support enabled")
- set(HAVE_DDNS TRUE CACHE INTERNAL "")
- else ()
- message(STATUS "DDNS support disabled by user")
- unset(HAVE_DDNS CACHE)
- endif ()
-else ()
- if (NSLOOKUP_EXECUTABLE)
- message(STATUS "Install nsupdate to enable DDNS support")
- elseif (NSUPDATE_EXECUTABLE)
- message(STATUS "Install nslookup to enable DDNS support")
- else ()
- message(STATUS "Install nslookup and nsupdate to enable DDNS support")
- endif ()
-endif ()
-
-set(IPCP_UDP_RD_THR 3 CACHE STRING
- "Number of reader threads in UDP IPCP")
-set(IPCP_UDP_WR_THR 3 CACHE STRING
- "Number of writer threads in UDP IPCP")
-set(IPCP_UDP_MPL 60 CACHE STRING
- "Default maximum packet lifetime for the UDP IPCP, in seconds")
-
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-udp -DCONFIG_OUROBOROS_DEBUG)
-endif ()
-
-install(TARGETS ipcpd-udp RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+install(TARGETS ${IPCP_UDP4_TARGET} ${IPCP_UDP6_TARGET}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/udp.c
index 2e8d84ce..5803e674 100644
--- a/src/ipcpd/udp/main.c
+++ b/src/ipcpd/udp/udp.c
@@ -20,29 +20,21 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#if defined(__linux__) || defined(__CYGWIN__)
-#define _DEFAULT_SOURCE
-#else
-#define _POSIX_C_SOURCE 200112L
-#endif
-
#include "config.h"
-#define OUROBOROS_PREFIX "ipcpd/udp"
-
#include <ouroboros/bitmap.h>
#include <ouroboros/endian.h>
#include <ouroboros/hash.h>
#include <ouroboros/list.h>
#include <ouroboros/utils.h>
#include <ouroboros/dev.h>
-#include <ouroboros/ipcp-dev.h>
#include <ouroboros/fqueue.h>
#include <ouroboros/errno.h>
#include <ouroboros/logs.h>
#include <ouroboros/pthread.h>
#include "ipcp.h"
+#include "np1.h"
#include "shim-data.h"
#include <string.h>
@@ -59,12 +51,11 @@
#define FLOW_REQ 1
#define FLOW_REPLY 2
-#define THIS_TYPE IPCP_UDP
-#define IPCP_UDP_MAX_PACKET_SIZE 8980
#define OUR_HEADER_LEN sizeof(uint32_t) /* adds eid */
-#define IPCP_UDP_BUF_SIZE 8980
-#define IPCP_UDP_MSG_SIZE 8980
+#define IPCP_UDP_BUF_SIZE IPCP_UDP_MAX_PACKET_SIZE
+#define IPCP_UDP_MSG_SIZE IPCP_UDP_MAX_PACKET_SIZE
+
#define DNS_TTL 86400
#define SADDR ((struct sockaddr *) &udp_data.s_saddr)
@@ -81,47 +72,42 @@
#define SENDTO_FLAGS 0
#endif
-struct ipcp ipcpi;
-
/* Keep order for alignment. */
struct mgmt_msg {
uint32_t eid;
uint32_t s_eid;
uint32_t d_eid;
- uint8_t code;
- int8_t response;
- /* QoS parameters from spec */
- uint8_t availability;
- uint8_t in_order;
+ int32_t response;
uint64_t bandwidth;
uint32_t delay;
uint32_t loss;
uint32_t ber;
uint32_t max_gap;
uint32_t timeout;
- uint16_t cypher_s;
-
+ uint8_t code;
+ /* QoS parameters from spec */
+ uint8_t availability;
+ uint8_t in_order;
} __attribute__((packed));
struct mgmt_frame {
- struct list_head next;
- struct sockaddr_in r_saddr;
- uint8_t buf[MGMT_FRAME_BUF_SIZE];
- size_t len;
+ struct list_head next;
+ struct __SOCKADDR r_saddr;
+ uint8_t buf[MGMT_FRAME_BUF_SIZE];
+ size_t len;
};
/* UDP flow */
struct uf {
- int d_eid;
- struct sockaddr_in r_saddr;
+ int d_eid;
+ struct __SOCKADDR r_saddr;
};
struct {
struct shim_data * shim_data;
- uint32_t dns_addr;
-
- struct sockaddr_in s_saddr;
+ struct __ADDR dns_addr;
+ struct __SOCKADDR s_saddr;
int s_fd;
fset_t * np1_flows;
@@ -138,6 +124,12 @@ struct {
struct list_head mgmt_frames;
} udp_data;
+static const char * __inet_ntop(const struct __ADDR * addr,
+ char * buf)
+{
+ return inet_ntop(__AF, addr, buf, __ADDRSTRLEN);
+}
+
static int udp_data_init(void)
{
int i;
@@ -199,11 +191,11 @@ static void udp_data_fini(void)
pthread_mutex_destroy(&udp_data.mgmt_lock);
}
-static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr,
- uint32_t s_eid,
- const uint8_t * dst,
- qosspec_t qs,
- const buffer_t * data)
+static int udp_ipcp_port_alloc(const struct __SOCKADDR * r_saddr,
+ uint32_t s_eid,
+ const uint8_t * dst,
+ qosspec_t qs,
+ const buffer_t * data)
{
uint8_t * buf;
struct mgmt_msg * msg;
@@ -217,6 +209,8 @@ static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr,
if (buf == NULL)
return -1;
+ memset(buf, 0, len + data->len);
+
msg = (struct mgmt_msg *) buf;
msg->eid = hton32(MGMT_EID);
msg->code = FLOW_REQ;
@@ -228,7 +222,6 @@ static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
- msg->cypher_s = hton16(qs.cypher_s);
msg->timeout = hton32(qs.timeout);
memcpy(msg + 1, dst, ipcp_dir_hash_len());
@@ -238,6 +231,8 @@ static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr,
if (sendto(udp_data.s_fd, msg, len + data->len,
SENDTO_FLAGS,
(const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0) {
+ log_err("Failed to send flow allocation request: %s.",
+ strerror(errno));
free(buf);
return -1;
}
@@ -247,11 +242,11 @@ static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr,
return 0;
}
-static int udp_ipcp_port_alloc_resp(const struct sockaddr_in * r_saddr,
- uint32_t s_eid,
- uint32_t d_eid,
- int8_t response,
- const buffer_t * data)
+static int udp_ipcp_port_alloc_resp(const struct __SOCKADDR * r_saddr,
+ uint32_t s_eid,
+ uint32_t d_eid,
+ int32_t response,
+ const buffer_t * data)
{
struct mgmt_msg * msg;
@@ -259,11 +254,13 @@ static int udp_ipcp_port_alloc_resp(const struct sockaddr_in * r_saddr,
if (msg == NULL)
return -1;
+ memset(msg, 0, sizeof(*msg) + data->len);
+
msg->eid = hton32(MGMT_EID);
msg->code = FLOW_REPLY;
msg->s_eid = hton32(s_eid);
msg->d_eid = hton32(d_eid);
- msg->response = response;
+ msg->response = hton32(response);
if (data->len > 0)
memcpy(msg + 1, data->data, data->len);
@@ -280,11 +277,11 @@ static int udp_ipcp_port_alloc_resp(const struct sockaddr_in * r_saddr,
return 0;
}
-static int udp_ipcp_port_req(struct sockaddr_in * c_saddr,
- int d_eid,
- const uint8_t * dst,
- qosspec_t qs,
- const buffer_t * data)
+static int udp_ipcp_port_req(struct __SOCKADDR * c_saddr,
+ int d_eid,
+ const uint8_t * dst,
+ qosspec_t qs,
+ const buffer_t * data)
{
int fd;
@@ -307,20 +304,26 @@ static int udp_ipcp_port_req(struct sockaddr_in * c_saddr,
return 0;
}
-static int udp_ipcp_port_alloc_reply(const struct sockaddr_in * saddr,
- uint32_t s_eid,
- uint32_t d_eid,
- int8_t response,
- const buffer_t * data)
+static int udp_ipcp_port_alloc_reply(const struct __SOCKADDR * saddr,
+ uint32_t s_eid,
+ uint32_t d_eid,
+ int32_t response,
+ const buffer_t * data)
{
time_t mpl = IPCP_UDP_MPL;
pthread_rwlock_wrlock(&udp_data.flows_lock);
if (memcmp(&udp_data.fd_to_uf[s_eid].r_saddr, saddr, sizeof(*saddr))) {
+ char ipstr[__ADDRSTRLEN];
pthread_rwlock_unlock(&udp_data.flows_lock);
- log_err("Flow allocation reply for %u from wrong source.",
- s_eid);
+ #ifdef BUILD_IPCP_UDP4
+ __inet_ntop(&saddr->sin_addr, ipstr);
+ #else
+ __inet_ntop(&saddr->sin6_addr, ipstr);
+ #endif
+ log_err("Flow allocation reply for %u from wrong source %s.",
+ s_eid, ipstr);
return -1;
}
@@ -340,9 +343,9 @@ static int udp_ipcp_port_alloc_reply(const struct sockaddr_in * saddr,
return 0;
}
-static int udp_ipcp_mgmt_frame(const uint8_t * buf,
- size_t len,
- struct sockaddr_in c_saddr)
+static int udp_ipcp_mgmt_frame(struct __SOCKADDR c_saddr,
+ const uint8_t * buf,
+ size_t len)
{
struct mgmt_msg * msg;
size_t msg_len;
@@ -368,7 +371,6 @@ static int udp_ipcp_mgmt_frame(const uint8_t * buf,
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
- qs.cypher_s = ntoh16(msg->cypher_s);
qs.timeout = ntoh32(msg->timeout);
return udp_ipcp_port_req(&c_saddr, ntoh32(msg->s_eid),
@@ -383,7 +385,7 @@ static int udp_ipcp_mgmt_frame(const uint8_t * buf,
return udp_ipcp_port_alloc_reply(&c_saddr,
ntoh32(msg->s_eid),
ntoh32(msg->d_eid),
- msg->response,
+ ntoh32(msg->response),
&data);
default:
log_err("Unknown message received %d.", msg->code);
@@ -413,7 +415,7 @@ static void * udp_ipcp_mgmt_handler(void * o)
pthread_mutex_unlock(&udp_data.mgmt_lock);
- udp_ipcp_mgmt_frame(frame->buf, frame->len, frame->r_saddr);
+ udp_ipcp_mgmt_frame(frame->r_saddr, frame->buf, frame->len);
free(frame);
}
@@ -440,9 +442,9 @@ static void * udp_ipcp_packet_reader(void * o)
while (true) {
struct mgmt_frame * frame;
- struct sockaddr_in r_saddr;
+ struct __SOCKADDR r_saddr;
socklen_t len;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
uint8_t * head;
len = sizeof(r_saddr);
@@ -486,13 +488,13 @@ static void * udp_ipcp_packet_reader(void * o)
n-= sizeof(eid);
- if (ipcp_sdb_reserve(&sdb, n))
+ if (ipcp_spb_reserve(&spb, n))
continue;
- head = shm_du_buff_head(sdb);
+ head = ssm_pk_buff_head(spb);
memcpy(head, data, n);
- if (np1_flow_write(eid, sdb) < 0)
- ipcp_sdb_release(sdb);
+ if (np1_flow_write(eid, spb, NP1_GET_POOL(eid)) < 0)
+ ipcp_spb_release(spb);
}
return (void *) 0;
@@ -503,9 +505,9 @@ static void cleanup_fqueue(void * fq)
fqueue_destroy((fqueue_t *) fq);
}
-static void cleanup_sdb(void * sdb)
+static void cleanup_spb(void * spb)
{
- ipcp_sdb_release((struct shm_du_buff *) sdb);
+ ipcp_spb_release((struct ssm_pk_buff *) spb);
}
static void * udp_ipcp_packet_writer(void * o)
@@ -523,34 +525,34 @@ static void * udp_ipcp_packet_writer(void * o)
pthread_cleanup_push(cleanup_fqueue, fq);
while (true) {
- struct sockaddr_in saddr;
- int eid;
- int fd;
+ struct __SOCKADDR saddr;
+ int eid;
+ int fd;
fevent(udp_data.np1_flows, fq, NULL);
while ((fd = fqueue_next(fq)) >= 0) {
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
uint8_t * buf;
uint16_t len;
if (fqueue_type(fq) != FLOW_PKT)
continue;
- if (np1_flow_read(fd, &sdb)) {
+ if (np1_flow_read(fd, &spb, NP1_GET_POOL(fd))) {
log_dbg("Bad read from fd %d.", fd);
continue;
}
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
if (len > IPCP_UDP_MAX_PACKET_SIZE) {
log_dbg("Packet length exceeds MTU.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
continue;
}
- buf = shm_du_buff_head_alloc(sdb, OUR_HEADER_LEN);
+ buf = ssm_pk_buff_head_alloc(spb, OUR_HEADER_LEN);
if (buf == NULL) {
log_dbg("Failed to allocate header.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
continue;
}
@@ -563,7 +565,7 @@ static void * udp_ipcp_packet_writer(void * o)
memcpy(buf, &eid, sizeof(eid));
- pthread_cleanup_push(cleanup_sdb, sdb);
+ pthread_cleanup_push(cleanup_spb, spb);
if (sendto(udp_data.s_fd, buf, len + OUR_HEADER_LEN,
SENDTO_FLAGS,
@@ -580,31 +582,39 @@ static void * udp_ipcp_packet_writer(void * o)
return (void *) 1;
}
-static const char * inet4_ntop(const void * addr,
- char * buf)
+static bool is_addr_specified(const struct __ADDR * addr)
{
- return inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN);
+#ifdef BUILD_IPCP_UDP4
+ return addr->s_addr != 0;
+#else
+ return !IN6_IS_ADDR_UNSPECIFIED(addr);
+#endif
}
-static int udp_ipcp_bootstrap(const struct ipcp_config * conf)
+static int udp_ipcp_bootstrap(struct ipcp_config * conf)
{
- char ipstr[INET_ADDRSTRLEN];
- char dnsstr[INET_ADDRSTRLEN];
+ char ipstr[__ADDRSTRLEN];
+ char dnsstr[__ADDRSTRLEN];
int i = 1;
+#ifdef BUILD_IPCP_UDP4
+ struct udp4_config * udp;
+ udp = &conf->udp4;
+#else
+ struct udp6_config * udp;
+ udp = &conf->udp6;
+#endif
- assert(conf);
+ assert(conf != NULL);
assert(conf->type == THIS_TYPE);
+ assert(conf->layer_info.dir_hash_algo == (enum pol_dir_hash) HASH_MD5);
- ipcpi.dir_hash_algo = HASH_MD5;
- strcpy(ipcpi.layer_name, conf->layer_info.name);
-
- if (inet4_ntop(&conf->udp.ip_addr, ipstr) == NULL) {
+ if (__inet_ntop(&udp->ip_addr, ipstr) == NULL) {
log_err("Failed to convert IP address.");
return -1;
}
- if (conf->udp.dns_addr != 0) {
- if (inet4_ntop(&conf->udp.dns_addr, dnsstr) == NULL) {
+ if (is_addr_specified(&udp->dns_addr)) {
+ if (__inet_ntop(&udp->dns_addr, dnsstr) == NULL) {
log_err("Failed to convert DNS address.");
return -1;
}
@@ -616,24 +626,29 @@ static int udp_ipcp_bootstrap(const struct ipcp_config * conf)
}
/* UDP listen server */
- udp_data.s_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ udp_data.s_fd = socket(__AF, SOCK_DGRAM, IPPROTO_UDP);
if (udp_data.s_fd < 0) {
log_err("Can't create socket: %s", strerror(errno));
goto fail_socket;
}
memset((char *) &udp_data.s_saddr, 0, sizeof(udp_data.s_saddr));
- udp_data.s_saddr.sin_family = AF_INET;
- udp_data.s_saddr.sin_addr.s_addr = conf->udp.ip_addr;
- udp_data.s_saddr.sin_port = htons(conf->udp.port);
-
+#ifdef BUILD_IPCP_UDP4
+ udp_data.s_saddr.sin_family = AF_INET;
+ udp_data.s_saddr.sin_addr = udp->ip_addr;
+ udp_data.s_saddr.sin_port = htons(udp->port);
+#else
+ udp_data.s_saddr.sin6_family = AF_INET6;
+ udp_data.s_saddr.sin6_addr = udp->ip_addr;
+ udp_data.s_saddr.sin6_port = htons(udp->port);
+#endif
if (bind(udp_data.s_fd, SADDR, SADDR_SIZE) < 0) {
log_err("Couldn't bind to %s:%d. %s.",
- ipstr, conf->udp.port, strerror(errno));
+ ipstr, udp->port, strerror(errno));
goto fail_bind;
}
- udp_data.dns_addr = conf->udp.dns_addr;
+ udp_data.dns_addr = udp->dns_addr;
if (pthread_create(&udp_data.mgmt_handler, NULL,
udp_ipcp_mgmt_handler, NULL)) {
@@ -657,10 +672,10 @@ static int udp_ipcp_bootstrap(const struct ipcp_config * conf)
}
}
- log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid());
+ log_dbg("Bootstrapped " TYPE_STR " with pid %d.", getpid());
log_dbg("Bound to IP address %s.", ipstr);
- log_dbg("Using port %u.", conf->udp.port);
- if (conf->udp.dns_addr != 0)
+ log_dbg("Using port %u.", udp->port);
+ if (is_addr_specified(&udp_data.dns_addr))
log_dbg("DNS server address is %s.", dnsstr);
else
log_dbg("DNS server not in use.");
@@ -668,14 +683,14 @@ static int udp_ipcp_bootstrap(const struct ipcp_config * conf)
return 0;
fail_packet_writer:
- while (i > 0) {
- pthread_cancel(udp_data.packet_writer[--i]);
+ while (i-- > 0) {
+ pthread_cancel(udp_data.packet_writer[i]);
pthread_join(udp_data.packet_writer[i], NULL);
}
i = IPCP_UDP_RD_THR;
fail_packet_reader:
- while (i > 0) {
- pthread_cancel(udp_data.packet_reader[--i]);
+ while (i-- > 0) {
+ pthread_cancel(udp_data.packet_reader[i]);
pthread_join(udp_data.packet_reader[i], NULL);
}
pthread_cancel(udp_data.mgmt_handler);
@@ -738,26 +753,26 @@ static int ddns_send(char * cmd)
return 0;
}
-static uint32_t ddns_resolve(char * name,
- uint32_t dns_addr)
+static struct __ADDR ddns_resolve(char * name,
+ struct __ADDR dns_addr)
{
- pid_t pid = -1;
- int wstatus;
- int pipe_fd[2];
- char dnsstr[INET_ADDRSTRLEN];
- char buf[IPCP_UDP_BUF_SIZE];
- ssize_t count = 0;
- char * substr = NULL;
- char * substr2 = NULL;
- char * addr_str = "Address:";
- uint32_t ip_addr = 0;
-
- if (inet4_ntop(&dns_addr, dnsstr) == NULL)
- return 0;
+ pid_t pid = -1;
+ int wstatus;
+ int pipe_fd[2];
+ char dnsstr[__ADDRSTRLEN];
+ char buf[IPCP_UDP_BUF_SIZE];
+ ssize_t count = 0;
+ char * substr = NULL;
+ char * substr2 = NULL;
+ char * addr_str = "Address:";
+ struct __ADDR ip_addr = __ADDR_ANY_INIT;
+
+ if (__inet_ntop(&dns_addr, dnsstr) == NULL)
+ return ip_addr;
if (pipe(pipe_fd)) {
log_err("Failed to create pipe: %s.", strerror(errno));
- return 0;
+ return ip_addr;
}
pid = fork();
@@ -765,7 +780,7 @@ static uint32_t ddns_resolve(char * name,
log_err("Failed to fork: %s.", strerror(errno));
close(pipe_fd[0]);
close(pipe_fd[1]);
- return -1;
+ return ip_addr;
}
if (pid == 0) {
@@ -785,7 +800,7 @@ static uint32_t ddns_resolve(char * name,
if (count <= 0) {
log_err("Failed to communicate with nslookup.");
close(pipe_fd[0]);
- return 0;
+ return ip_addr;
}
close(pipe_fd[0]);
@@ -806,12 +821,13 @@ static uint32_t ddns_resolve(char * name,
if (substr2 == NULL || strstr(substr2, addr_str) == NULL) {
log_err("Failed to resolve DNS address.");
- return 0;
+ return ip_addr;
}
- if (inet_pton(AF_INET, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) {
+ if (inet_pton(__AF, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) {
log_err("Failed to resolve DNS address.");
- return 0;
+ assert(!is_addr_specified(&ip_addr));
+ return ip_addr;
}
return ip_addr;
@@ -821,11 +837,11 @@ static uint32_t ddns_resolve(char * name,
static int udp_ipcp_reg(const uint8_t * hash)
{
#ifdef HAVE_DDNS
- char ipstr[INET_ADDRSTRLEN];
- char dnsstr[INET_ADDRSTRLEN];
- char cmd[1000];
- uint32_t dns_addr;
- uint32_t ip_addr;
+ char ipstr[__ADDRSTRLEN];
+ char dnsstr[__ADDRSTRLEN];
+ char cmd[1000];
+ struct __ADDR dns_addr;
+ struct __ADDR ip_addr;
#endif
char * hashstr;
@@ -851,16 +867,19 @@ static int udp_ipcp_reg(const uint8_t * hash)
dns_addr = udp_data.dns_addr;
- if (dns_addr != 0) {
- ip_addr = udp_data.s_saddr.sin_addr.s_addr;
-
- if (inet4_ntop(&ip_addr, ipstr) == NULL) {
+ if (is_addr_specified(&dns_addr)) {
+#ifdef BUILD_IPCP_UDP4
+ ip_addr = udp_data.s_saddr.sin_addr;
+#else
+ ip_addr = udp_data.s_saddr.sin6_addr;
+#endif
+ if (__inet_ntop(&ip_addr, ipstr) == NULL) {
log_err("Failed to convert IP address to string.");
free(hashstr);
return -1;
}
- if (inet4_ntop(&dns_addr, dnsstr) == NULL) {
+ if (__inet_ntop(&dns_addr, dnsstr) == NULL) {
log_err("Failed to convert DNS address to string.");
free(hashstr);
return -1;
@@ -885,12 +904,12 @@ static int udp_ipcp_reg(const uint8_t * hash)
static int udp_ipcp_unreg(const uint8_t * hash)
{
#ifdef HAVE_DDNS
- char dnsstr[INET_ADDRSTRLEN];
+ char dnsstr[__ADDRSTRLEN];
/* max DNS name length + max IP length + max command length */
- char cmd[100];
- uint32_t dns_addr;
+ char cmd[100];
+ struct __ADDR dns_addr;
#endif
- char * hashstr;
+ char * hashstr;
assert(hash);
@@ -907,8 +926,8 @@ static int udp_ipcp_unreg(const uint8_t * hash)
dns_addr = udp_data.dns_addr;
- if (dns_addr != 0) {
- if (inet4_ntop(&dns_addr, dnsstr) == NULL) {
+ if (is_addr_specified(&dns_addr)) {
+ if (__inet_ntop(&dns_addr, dnsstr) == NULL) {
log_err("Failed to convert DNS address to string.");
free(hashstr);
return -1;
@@ -929,11 +948,13 @@ static int udp_ipcp_unreg(const uint8_t * hash)
static int udp_ipcp_query(const uint8_t * hash)
{
- uint32_t ip_addr = 0;
- char * hashstr;
- struct hostent * h;
+ struct addr addr = {};
+ char * hashstr;
+ struct addrinfo hints;
+ struct addrinfo * ai;
#ifdef HAVE_DDNS
- uint32_t dns_addr = 0;
+ struct __ADDR dns_addr = __ADDR_ANY_INIT;
+ struct __ADDR ip_addr = __ADDR_ANY_INIT;
#endif
assert(hash);
@@ -953,28 +974,42 @@ static int udp_ipcp_query(const uint8_t * hash)
#ifdef HAVE_DDNS
dns_addr = udp_data.dns_addr;
- if (dns_addr != 0) {
+ if (is_addr_specified(&dns_addr)) {
ip_addr = ddns_resolve(hashstr, dns_addr);
- if (ip_addr == 0) {
+ if (!is_addr_specified(&ip_addr)) {
log_err("Could not resolve %s.", hashstr);
free(hashstr);
return -1;
}
} else {
#endif
- h = gethostbyname(hashstr);
- if (h == NULL) {
- log_err("Could not resolve %s.", hashstr);
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = __AF;
+ if (getaddrinfo(hashstr, NULL, &hints, &ai) != 0) {
+ log_err("Could not resolve %s: %s.", hashstr,
+ gai_strerror(errno));
+ free(hashstr);
+ return -1;
+ }
+
+ if (ai->ai_family != __AF) {
+ log_err("Wrong addres family for %s.", hashstr);
+ freeaddrinfo(ai);
free(hashstr);
return -1;
}
- ip_addr = *((uint32_t *) (h->h_addr_list[0]));
+ #ifdef BUILD_IPCP_UDP4
+ addr.ip4 = ((struct sockaddr_in *) (ai->ai_addr))->sin_addr;
+ #else
+ addr.ip6 = ((struct sockaddr_in6 *) (ai->ai_addr))->sin6_addr;
+ #endif
+ freeaddrinfo(ai);
#ifdef HAVE_DDNS
}
#endif
-
- if (shim_data_dir_add_entry(udp_data.shim_data, hash, ip_addr)) {
+ if (shim_data_dir_add_entry(udp_data.shim_data, hash, addr)) {
log_err("Failed to add directory entry.");
free(hashstr);
return -1;
@@ -990,9 +1025,10 @@ static int udp_ipcp_flow_alloc(int fd,
qosspec_t qs,
const buffer_t * data)
{
- struct sockaddr_in r_saddr; /* Server address */
- uint32_t ip_addr = 0;
- char ipstr[INET_ADDRSTRLEN];
+ struct __SOCKADDR r_saddr; /* Server address */
+ struct __ADDR ip_addr;
+ struct addr addr;
+ char ipstr[__ADDRSTRLEN];
(void) qs;
@@ -1003,9 +1039,13 @@ static int udp_ipcp_flow_alloc(int fd,
return -1;
}
- ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst);
-
- if (inet4_ntop(&ip_addr, ipstr) == NULL) {
+ addr = shim_data_dir_get_addr(udp_data.shim_data, dst);
+#ifdef BUILD_IPCP_UDP4
+ ip_addr = addr.ip4;
+#else
+ ip_addr = addr.ip6;
+#endif
+ if (__inet_ntop(&ip_addr, ipstr) == NULL) {
log_err("Could not convert IP address.");
return -1;
}
@@ -1014,9 +1054,15 @@ static int udp_ipcp_flow_alloc(int fd,
HASH_VAL32(dst), ipstr);
memset((char *) &r_saddr, 0, sizeof(r_saddr));
- r_saddr.sin_family = AF_INET;
- r_saddr.sin_addr.s_addr = ip_addr;
- r_saddr.sin_port = udp_data.s_saddr.sin_port;
+#ifdef BUILD_IPCP_UDP4
+ r_saddr.sin_family = AF_INET;
+ r_saddr.sin_addr = addr.ip4;
+ r_saddr.sin_port = udp_data.s_saddr.sin_port;
+#else
+ r_saddr.sin6_family = AF_INET6;
+ r_saddr.sin6_addr = addr.ip6;
+ r_saddr.sin6_port = udp_data.s_saddr.sin6_port;
+#endif
if (udp_ipcp_port_alloc(&r_saddr, fd, dst, qs, data) < 0) {
log_err("Could not allocate port.");
@@ -1039,8 +1085,8 @@ static int udp_ipcp_flow_alloc_resp(int fd,
int resp,
const buffer_t * data)
{
- struct sockaddr_in saddr;
- int d_eid;
+ struct __SOCKADDR saddr;
+ int d_eid;
if (ipcp_wait_flow_resp(fd) < 0) {
log_err("Failed to wait for flow response.");
diff --git a/src/ipcpd/udp/udp4.c b/src/ipcpd/udp/udp4.c
new file mode 100644
index 00000000..07d5f818
--- /dev/null
+++ b/src/ipcpd/udp/udp4.c
@@ -0,0 +1,42 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * IPC process over UDP/IPv4
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/ipcp-dev.h>
+
+#define BUILD_IPCP_UDP4
+#define THIS_TYPE IPCP_UDP4
+#define TYPE_STR "IPCP over UDP/IPv4"
+#define OUROBOROS_PREFIX "ipcpd/udp4"
+#define IPCP_UDP_MAX_PACKET_SIZE 8980
+#define __AF AF_INET
+#define __ADDRSTRLEN INET_ADDRSTRLEN
+#define __SOCKADDR sockaddr_in
+#define __ADDR in_addr
+#define __ADDR_ANY_INIT { .s_addr = INADDR_ANY }
+
+#include "udp.c"
diff --git a/src/ipcpd/udp/udp6.c b/src/ipcpd/udp/udp6.c
new file mode 100644
index 00000000..b7924a3f
--- /dev/null
+++ b/src/ipcpd/udp/udp6.c
@@ -0,0 +1,42 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * IPC process over UDP/IPv6
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/ipcp-dev.h>
+
+#define BUILD_IPCP_UDP6
+#define THIS_TYPE IPCP_UDP6
+#define TYPE_STR "IPCP over UDP/IPv6"
+#define OUROBOROS_PREFIX "ipcpd/udp6"
+#define IPCP_UDP_MAX_PACKET_SIZE 8952
+#define __AF AF_INET6
+#define __ADDRSTRLEN INET6_ADDRSTRLEN
+#define __SOCKADDR sockaddr_in6
+#define __ADDR in6_addr
+#define __ADDR_ANY_INIT IN6ADDR_ANY_INIT
+
+#include "udp.c"
diff --git a/src/ipcpd/unicast/CMakeLists.txt b/src/ipcpd/unicast/CMakeLists.txt
index ca742871..1e095f8b 100644
--- a/src/ipcpd/unicast/CMakeLists.txt
+++ b/src/ipcpd/unicast/CMakeLists.txt
@@ -1,38 +1,11 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+# Unicast IPCP build configuration
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+set(IPCP_UNICAST_TARGET ipcpd-unicast)
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
+protobuf_generate_c(DHT_PROTO_SRCS DHT_PROTO_HDRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/dir/dht.proto")
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(IPCP_UNICAST_TARGET ipcpd-unicast CACHE INTERNAL "")
-set(IPCP_UNICAST_MPL 60 CACHE STRING
- "Default maximum packet lifetime for the unicast IPCP, in seconds")
-
-protobuf_generate_c(DHT_PROTO_SRCS DHT_PROTO_HDRS dir/dht.proto)
-
-math(EXPR PFT_EXPR "1 << 12")
-set(PFT_SIZE ${PFT_EXPR} CACHE STRING
- "Size of the PDU forwarding table")
-if (HAVE_FUSE)
- set(IPCP_FLOW_STATS TRUE CACHE BOOL
- "Enable flow statistics tracking in IPCP")
- if (IPCP_FLOW_STATS)
- message(STATUS "IPCP flow statistics enabled")
- else ()
- message(STATUS "IPCP flow statistics disabled")
- endif ()
-endif ()
-
-set(SOURCE_FILES
- # Add source files here
+set(UNICAST_SOURCES
addr-auth.c
ca.c
connmgr.c
@@ -43,7 +16,6 @@ set(SOURCE_FILES
pff.c
routing.c
psched.c
- # Add policies last
addr-auth/flat.c
ca/mb-ecn.c
ca/nop.c
@@ -54,22 +26,26 @@ set(SOURCE_FILES
pff/pft.c
routing/link-state.c
routing/graph.c
- )
+)
-add_executable(ipcpd-unicast ${SOURCE_FILES} ${IPCP_SOURCES} ${COMMON_SOURCES}
- ${DHT_PROTO_SRCS} ${LAYER_CONFIG_PROTO_SRCS})
-target_link_libraries(ipcpd-unicast LINK_PUBLIC ouroboros-dev)
+add_executable(${IPCP_UNICAST_TARGET}
+ ${UNICAST_SOURCES}
+ ${IPCP_SOURCES}
+ ${COMMON_SOURCES}
+ ${DHT_PROTO_SRCS}
+)
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-unicast -DCONFIG_OUROBOROS_DEBUG)
-endif ()
+target_include_directories(${IPCP_UNICAST_TARGET} PRIVATE ${IPCP_INCLUDE_DIRS})
+target_include_directories(${IPCP_UNICAST_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(${IPCP_UNICAST_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(${IPCP_UNICAST_TARGET} PRIVATE ouroboros-dev)
-install(TARGETS ipcpd-unicast RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+ouroboros_target_debug_definitions(${IPCP_UNICAST_TARGET})
-add_subdirectory(pff/tests)
-add_subdirectory(routing/tests)
+install(TARGETS ${IPCP_UNICAST_TARGET} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
-if (NOT GNU)
+if(BUILD_TESTS)
add_subdirectory(dir/tests)
-endif ()
+ add_subdirectory(pff/tests)
+ add_subdirectory(routing/tests)
+endif()
diff --git a/src/ipcpd/unicast/addr-auth.h b/src/ipcpd/unicast/addr-auth.h
index e119dff3..0d2cd4c0 100644
--- a/src/ipcpd/unicast/addr-auth.h
+++ b/src/ipcpd/unicast/addr-auth.h
@@ -27,6 +27,14 @@
#include <stdint.h>
+#define ADDR_FMT32 "%02x.%02x.%02x.%02x"
+#define ADDR_VAL32(a) \
+ ((uint8_t *) a)[0], ((uint8_t *) a)[1], \
+ ((uint8_t *) a)[2], ((uint8_t *) a)[3]
+
+#define ADDR_FMT64 ADDR_FMT32 "." ADDR_FMT32
+#define ADDR_VAL64(a) ADDR_VAL32(a), ADDR_VAL32(a + 4)
+
int addr_auth_init(enum pol_addr_auth type,
const void * info);
diff --git a/src/ipcpd/unicast/addr-auth/flat.c b/src/ipcpd/unicast/addr-auth/flat.c
index c4562935..34ca1cef 100644
--- a/src/ipcpd/unicast/addr-auth/flat.c
+++ b/src/ipcpd/unicast/addr-auth/flat.c
@@ -31,17 +31,18 @@
#include <ouroboros/logs.h>
#include <ouroboros/random.h>
+#include "addr-auth.h"
#include "ipcp.h"
#include "flat.h"
-#define NAME_LEN 8
+#define NAME_LEN 8
+#define INVALID_ADDRESS 0
struct {
- uint8_t addr_size;
+ uint8_t addr_size;
+ uint32_t addr;
} flat;
-#define INVALID_ADDRESS 0
-
struct addr_auth_ops flat_ops = {
.init = flat_init,
.fini = flat_fini,
@@ -57,6 +58,15 @@ int flat_init(const void * info)
return -1;
}
+#if defined (CONFIG_OUROBOROS_DEBUG) && defined (IPCP_DEBUG_LOCAL)
+ flat.addr = getpid();
+#else
+ while (flat.addr == INVALID_ADDRESS)
+ random_buffer(&flat.addr,sizeof(flat.addr));
+#endif
+ log_dbg("Flat address initialized to " ADDR_FMT32 ".",
+ ADDR_VAL32((uint8_t *) &flat.addr));
+
return 0;
}
@@ -67,13 +77,5 @@ int flat_fini(void)
uint64_t flat_address(void)
{
- uint32_t addr = INVALID_ADDRESS;
-
-#if defined (CONFIG_OUROBOROS_DEBUG) && defined (IPCP_DEBUG_LOCAL)
- addr = getpid();
-#else
- while (addr == INVALID_ADDRESS)
- random_buffer(&addr,sizeof(addr));
-#endif
- return addr;
+ return (uint64_t) flat.addr;
}
diff --git a/src/ipcpd/unicast/ca.c b/src/ipcpd/unicast/ca.c
index 287eaf41..1fcc9bb2 100644
--- a/src/ipcpd/unicast/ca.c
+++ b/src/ipcpd/unicast/ca.c
@@ -49,7 +49,6 @@ int ca_init(enum pol_cong_avoid pol)
return 0;
}
-
void ca_fini(void)
{
ca.ops = NULL;
diff --git a/src/ipcpd/unicast/connmgr.c b/src/ipcpd/unicast/connmgr.c
index 11c5d5b6..07568fb5 100644
--- a/src/ipcpd/unicast/connmgr.c
+++ b/src/ipcpd/unicast/connmgr.c
@@ -32,8 +32,4 @@
#define BUILD_IPCP_UNICAST
-#ifdef IPCP_CONN_WAIT_DIR
- #include "dir.h"
-#endif
-
#include "common/connmgr.c"
diff --git a/src/ipcpd/unicast/dir.c b/src/ipcpd/unicast/dir.c
index e0cb09fc..2b305626 100644
--- a/src/ipcpd/unicast/dir.c
+++ b/src/ipcpd/unicast/dir.c
@@ -44,50 +44,57 @@
struct {
struct dir_ops * ops;
- void * dir;
-} dirmgr;
+} dir;
-int dir_init(void)
+int dir_init(struct dir_config * conf)
{
- dirmgr.ops = &dht_dir_ops;
+ void * cfg;
- dirmgr.dir = dirmgr.ops->create();
- if (dirmgr.dir == NULL) {
- dirmgr.ops = NULL;
- return -ENOMEM;
+ assert(conf != NULL);
+
+ switch (conf->pol) {
+ case DIR_DHT:
+ log_info("Using DHT policy.");
+ dir.ops = &dht_dir_ops;
+ cfg = &conf->dht;
+ break;
+ default: /* DIR_INVALID */
+ log_err("Invalid directory policy %d.", conf->pol);
+ return -EINVAL;
}
- return 0;
+ assert(dir.ops->init != NULL);
+
+ return dir.ops->init(cfg);
}
void dir_fini(void)
{
- dirmgr.ops->destroy(dirmgr.dir);
- dirmgr.ops = NULL;
- dirmgr.dir = NULL;
+ dir.ops->fini();
+ dir.ops = NULL;
}
-int dir_bootstrap(void)
+int dir_start(void)
{
- return dirmgr.ops->bootstrap(dirmgr.dir);
+ return dir.ops->start();
}
-int dir_reg(const uint8_t * hash)
+void dir_stop(void)
{
- return dirmgr.ops->reg(dirmgr.dir, hash);
+ dir.ops->stop();
}
-int dir_unreg(const uint8_t * hash)
+int dir_reg(const uint8_t * hash)
{
- return dirmgr.ops->unreg(dirmgr.dir, hash);
+ return dir.ops->reg(hash);
}
-uint64_t dir_query(const uint8_t * hash)
+int dir_unreg(const uint8_t * hash)
{
- return dirmgr.ops->query(dirmgr.dir, hash);
+ return dir.ops->unreg(hash);
}
-int dir_wait_running(void)
+uint64_t dir_query(const uint8_t * hash)
{
- return dirmgr.ops->wait_running(dirmgr.dir);
+ return dir.ops->query(hash);
}
diff --git a/src/ipcpd/unicast/dir.h b/src/ipcpd/unicast/dir.h
index b261ea2c..dbfde19f 100644
--- a/src/ipcpd/unicast/dir.h
+++ b/src/ipcpd/unicast/dir.h
@@ -25,11 +25,14 @@
#include <inttypes.h>
-int dir_init(void);
+/* may update the config! */
+int dir_init(struct dir_config * conf);
void dir_fini(void);
-int dir_bootstrap(void);
+int dir_start(void);
+
+void dir_stop(void);
int dir_reg(const uint8_t * hash);
@@ -37,6 +40,4 @@ int dir_unreg(const uint8_t * hash);
uint64_t dir_query(const uint8_t * hash);
-int dir_wait_running(void);
-
#endif /* OUROBOROS_IPCPD_UNICAST_DIR_H */
diff --git a/src/ipcpd/unicast/dir/dht.c b/src/ipcpd/unicast/dir/dht.c
index 08a5a5a9..69309091 100644
--- a/src/ipcpd/unicast/dir/dht.c
+++ b/src/ipcpd/unicast/dir/dht.c
@@ -4,7 +4,6 @@
* Distributed Hash Table based on Kademlia
*
* Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -20,10 +19,12 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#if defined(__linux__) || defined(__CYGWIN__)
-#define _DEFAULT_SOURCE
-#else
-#define _POSIX_C_SOURCE 200112L
+#if !defined (__DHT_TEST__)
+ #if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+ #else
+ #define _POSIX_C_SOURCE 200112L
+ #endif
#endif
#include "config.h"
@@ -38,13 +39,14 @@
#include <ouroboros/errno.h>
#include <ouroboros/logs.h>
#include <ouroboros/list.h>
-#include <ouroboros/notifier.h>
#include <ouroboros/random.h>
+#include <ouroboros/rib.h>
#include <ouroboros/time.h>
#include <ouroboros/tpm.h>
#include <ouroboros/utils.h>
#include <ouroboros/pthread.h>
+#include "addr-auth.h"
#include "common/connmgr.h"
#include "dht.h"
#include "dt.h"
@@ -58,143 +60,153 @@
#include <limits.h>
#include "dht.pb-c.h"
-typedef DhtMsg dht_msg_t;
-typedef DhtContactMsg dht_contact_msg_t;
+typedef DhtMsg dht_msg_t;
+typedef DhtContactMsg dht_contact_msg_t;
+typedef DhtStoreMsg dht_store_msg_t;
+typedef DhtFindReqMsg dht_find_req_msg_t;
+typedef DhtFindNodeRspMsg dht_find_node_rsp_msg_t;
+typedef DhtFindValueRspMsg dht_find_value_rsp_msg_t;
+typedef ProtobufCBinaryData binary_data_t;
#ifndef CLOCK_REALTIME_COARSE
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
#endif
-#define DHT_MAX_REQS 2048 /* KAD recommends rnd(), bmp can be changed. */
-#define KAD_ALPHA 3 /* Parallel factor, proven optimal value. */
-#define KAD_K 8 /* Replication factor, MDHT value. */
-#define KAD_T_REPL 900 /* Replication time, tied to k. MDHT value. */
-#define KAD_T_REFR 900 /* Refresh time stale bucket, MDHT value. */
-#define KAD_T_JOIN 8 /* Response time to wait for a join. */
-#define KAD_T_RESP 5 /* Response time to wait for a response. */
-#define KAD_R_PING 2 /* Ping retries before declaring peer dead. */
-#define KAD_QUEER 15 /* Time to declare peer questionable. */
-#define KAD_BETA 8 /* Bucket split factor, must be 1, 2, 4 or 8. */
-#define KAD_RESP_RETR 6 /* Number of retries on sending a response. */
-#define KAD_JOIN_RETR 8 /* Number of retries sending a join. */
-#define KAD_JOIN_INTV 1 /* Time (seconds) between join retries. */
+#define DHT_MAX_REQS 128 /* KAD recommends rnd(), bmp can be changed. */
+#define DHT_WARN_REQS 100 /* Warn if number of requests exceeds this. */
+#define DHT_MAX_VALS 8 /* Max number of values to return for a key. */
+#define DHT_T_CACHE 60 /* Max cache time for values (s) */
+#define DHT_T_RESP 2 /* Response time to wait for a response (s). */
+#define DHT_N_REPUB 5 /* Republish if expiry within n replications. */
+#define DHT_R_PING 2 /* Ping retries before declaring peer dead. */
+#define DHT_QUEER 15 /* Time to declare peer questionable. */
+#define DHT_BETA 8 /* Bucket split factor, must be 1, 2, 4 or 8. */
+#define DHT_RESP_RETR 6 /* Number of retries on sending a response. */
#define HANDLE_TIMEO 1000 /* Timeout for dht_handle_packet tpm check (ms) */
-#define DHT_RETR_ADDR 1 /* Number of addresses to return on retrieve */
+#define DHT_INVALID 0 /* Invalid cookie value. */
-enum dht_state {
- DHT_INIT = 0,
- DHT_SHUTDOWN,
- DHT_JOINING,
- DHT_RUNNING,
-};
+#define KEY_FMT "K<" HASH_FMT64 ">"
+#define KEY_VAL(key) HASH_VAL64(key)
-enum kad_code {
- KAD_JOIN = 0,
- KAD_FIND_NODE,
- KAD_FIND_VALUE,
- /* Messages without a response below. */
- KAD_STORE,
- KAD_RESPONSE
-};
+#define VAL_FMT "V<" HASH_FMT64 ">"
+#define VAL_VAL(val) HASH_VAL64((val).data)
-enum kad_req_state {
- REQ_NULL = 0,
- REQ_INIT,
- REQ_PENDING,
- REQ_RESPONSE,
- REQ_DONE,
- REQ_DESTROY
-};
+#define KV_FMT "<" HASH_FMT64 ", " HASH_FMT64 ">"
+#define KV_VAL(key, val) HASH_VAL64(key), HASH_VAL64((val).data)
-enum lookup_state {
- LU_NULL = 0,
- LU_INIT,
- LU_PENDING,
- LU_UPDATE,
- LU_COMPLETE,
- LU_DESTROY
-};
+#define PEER_FMT "[" HASH_FMT64 "|" ADDR_FMT32 "]"
+#define PEER_VAL(id, addr) HASH_VAL64(id), ADDR_VAL32(&(addr))
-struct kad_req {
- struct list_head next;
+#define DHT_CODE(msg) dht_code_str[(msg)->code]
- uint32_t cookie;
- enum kad_code code;
- uint8_t * key;
- uint64_t addr;
+#define TX_HDR_FMT "%s --> " PEER_FMT
+#define TX_HDR_VAL(msg, id, addr) DHT_CODE(msg), PEER_VAL(id, addr)
- enum kad_req_state state;
- pthread_cond_t cond;
- pthread_mutex_t lock;
+#define RX_HDR_FMT "%s <-- " PEER_FMT
+#define RX_HDR_VAL(msg) DHT_CODE(msg), \
+ PEER_VAL(msg->src->id.data, msg->src->addr)
- time_t t_exp;
+#define CK_FMT "|" HASH_FMT64 "|"
+#define CK_VAL(cookie) HASH_VAL64(&(cookie))
+
+#define IS_REQUEST(code) \
+ (code == DHT_FIND_NODE_REQ || code == DHT_FIND_VALUE_REQ)
+
+enum dht_code {
+ DHT_STORE,
+ DHT_FIND_NODE_REQ,
+ DHT_FIND_NODE_RSP,
+ DHT_FIND_VALUE_REQ,
+ DHT_FIND_VALUE_RSP
};
-struct cookie_el {
- struct list_head next;
+const char * dht_code_str[] = {
+ "DHT_STORE",
+ "DHT_FIND_NODE_REQ",
+ "DHT_FIND_NODE_RSP",
+ "DHT_FIND_VALUE_REQ",
+ "DHT_FIND_VALUE_RSP"
+};
- uint32_t cookie;
+enum dht_state {
+ DHT_NULL = 0,
+ DHT_INIT,
+ DHT_RUNNING
};
-struct lookup {
- struct list_head next;
+struct val_entry {
+ struct list_head next;
- struct list_head cookies;
+ buffer_t val;
- uint8_t * key;
+ time_t t_exp; /* Expiry time */
+ time_t t_repl; /* Last replication time */
+};
+
+struct dht_entry {
+ struct list_head next;
- struct list_head contacts;
- size_t n_contacts;
+ uint8_t * key;
- uint64_t * addrs;
- size_t n_addrs;
+ struct {
+ struct list_head list;
+ size_t len;
+ } vals; /* We don't own these, only replicate */
- enum lookup_state state;
- pthread_cond_t cond;
- pthread_mutex_t lock;
+ struct {
+ struct list_head list;
+ size_t len;
+ } lvals; /* We own these, must be republished */
};
-struct val {
+struct contact {
struct list_head next;
+ uint8_t * id;
uint64_t addr;
- time_t t_exp;
- time_t t_rep;
+ size_t fails;
+ time_t t_seen;
};
-struct ref_entry {
+struct peer_entry {
struct list_head next;
- uint8_t * key;
+ uint64_t cookie;
+ uint8_t * id;
+ uint64_t addr;
+ enum dht_code code;
- time_t t_rep;
+ time_t t_sent;
};
-struct dht_entry {
+struct dht_req {
struct list_head next;
uint8_t * key;
- size_t n_vals;
- struct list_head vals;
-};
-
-struct contact {
- struct list_head next;
+ time_t t_exp;
- uint8_t * id;
- uint64_t addr;
+ struct {
+ struct list_head list;
+ size_t len;
+ } peers;
- size_t fails;
- time_t t_seen;
+ struct {
+ struct list_head list;
+ size_t len;
+ } cache;
};
struct bucket {
- struct list_head contacts;
- size_t n_contacts;
+ struct {
+ struct list_head list;
+ size_t len;
+ } contacts;
- struct list_head alts;
- size_t n_alts;
+ struct {
+ struct list_head list;
+ size_t len;
+ } alts;
time_t t_refr;
@@ -202,1088 +214,1464 @@ struct bucket {
uint8_t mask;
struct bucket * parent;
- struct bucket * children[1L << KAD_BETA];
+ struct bucket * children[1L << DHT_BETA];
};
struct cmd {
- struct list_head next;
-
- struct shm_du_buff * sdb;
+ struct list_head next;
+ buffer_t cbuf;
};
struct dir_ops dht_dir_ops = {
- .create = dht_create,
- .destroy = dht_destroy,
- .bootstrap = dht_bootstrap,
- .reg = dht_reg,
- .unreg = dht_unreg,
- .query = dht_query,
- .wait_running = dht_wait_running
+ .init = (int (*)(void *)) dht_init,
+ .fini = dht_fini,
+ .start = dht_start,
+ .stop = dht_stop,
+ .reg = dht_reg,
+ .unreg = dht_unreg,
+ .query = dht_query
};
-struct dht {
- size_t alpha;
- size_t b;
- size_t k;
+struct {
+ struct { /* Kademlia parameters */
+ uint32_t alpha; /* Number of concurrent requests */
+ size_t k; /* Number of replicas to store */
+ time_t t_expire; /* Expiry time for values (s) */
+ time_t t_refresh; /* Refresh time for contacts (s) */
+ time_t t_repl; /* Replication time for values (s) */
+ };
- time_t t_expire;
- time_t t_refresh;
- time_t t_replic;
- time_t t_repub;
+ buffer_t id;
- uint8_t * id;
- uint64_t addr;
+ time_t t0; /* Creation time */
+ uint64_t addr; /* Our own address */
+ uint64_t peer; /* Enrollment peer address */
+ uint64_t magic; /* Magic cookie for retransmit */
- struct bucket * buckets;
+ uint64_t eid; /* Entity ID */
- struct list_head entries;
+ struct tpm * tpm;
+ pthread_t worker;
- struct list_head refs;
+ enum dht_state state;
- struct list_head lookups;
+ struct {
+ struct {
+ struct bucket * root;
+ } contacts;
+
+ struct {
+ struct list_head list;
+ size_t len;
+ size_t vals;
+ size_t lvals;
+ } kv;
+
+ pthread_rwlock_t lock;
+ } db;
+
+ struct {
+ struct list_head list;
+ size_t len;
+ pthread_cond_t cond;
+ pthread_mutex_t mtx;
+ } reqs;
+
+ struct {
+ struct list_head list;
+ pthread_cond_t cond;
+ pthread_mutex_t mtx;
+ } cmds;
+} dht;
+
+
+/* DHT RIB */
+
+static const char * dht_dir[] = {
+ "database",
+ "stats",
+ NULL
+};
- struct list_head requests;
- struct bmp * cookies;
+const char * dht_stats = \
+ "DHT: " HASH_FMT64 "\n"
+ " Created: %s\n"
+ " Address: " ADDR_FMT32 "\n"
+ " Kademlia parameters:\n"
+ " Number of concurrent requests (alpha): %10zu\n"
+ " Number of replicas (k): %10zu\n"
+ " Expiry time for values (s): %10ld\n"
+ " Refresh time for contacts (s): %10ld\n"
+ " Replication time for values (s): %10ld\n"
+ " Number of keys: %10zu\n"
+ " Number of local values: %10zu\n"
+ " Number of non-local values: %10zu\n";
- enum dht_state state;
- struct list_head cmds;
- pthread_cond_t cond;
- pthread_mutex_t mtx;
+static int dht_rib_statfile(char * buf,
+ size_t len)
+{
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ size_t keys;
+ size_t vals;
+ size_t lvals;
- pthread_rwlock_t lock;
+ assert(buf != NULL);
+ assert(len > 0);
- uint64_t eid;
+ pthread_rwlock_rdlock(&dht.db.lock);
- struct tpm * tpm;
+ keys = dht.db.kv.len;
+ lvals = dht.db.kv.lvals;
+ vals = dht.db.kv.vals;
- pthread_t worker;
-};
+ pthread_rwlock_unlock(&dht.db.lock);
-struct join_info {
- struct dht * dht;
- uint64_t addr;
-};
+ tm = gmtime(&dht.t0);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
-struct packet_info {
- struct dht * dht;
- struct shm_du_buff * sdb;
-};
+ snprintf(buf, len, dht_stats,
+ HASH_VAL64(dht.id.data),
+ tmstr,
+ ADDR_VAL32(&dht.addr),
+ dht.alpha, dht.k,
+ dht.t_expire, dht.t_refresh, dht.t_repl,
+ keys, vals, lvals);
-static uint8_t * dht_dup_key(const uint8_t * key,
- size_t len)
+ return strlen(buf);
+}
+
+static size_t dht_db_file_len(void)
{
- uint8_t * dup;
+ size_t sz;
+ size_t vals;
- dup = malloc(sizeof(*dup) * len);
- if (dup == NULL)
- return NULL;
+ sz = 18; /* DHT database + 2 * \n */
- memcpy(dup, key, len);
+ pthread_rwlock_rdlock(&dht.db.lock);
- return dup;
-}
+ if (dht.db.kv.len == 0) {
+ pthread_rwlock_unlock(&dht.db.lock);
+ sz += 14; /* No entries */
+ return sz;
+ }
-static enum dht_state dht_get_state(struct dht * dht)
-{
- enum dht_state state;
+ sz += 39 * 3 + 1; /* tally + extra newline */
+ sz += dht.db.kv.len * (25 + 19 + 23 + 1);
- pthread_mutex_lock(&dht->mtx);
+ vals = dht.db.kv.vals + dht.db.kv.lvals;
- state = dht->state;
+ sz += vals * (48 + 2 * RIB_TM_STRLEN);
- pthread_mutex_unlock(&dht->mtx);
+ pthread_rwlock_unlock(&dht.db.lock);
- return state;
+ return sz;
}
-static int dht_set_state(struct dht * dht,
- enum dht_state state)
+static int dht_rib_dbfile(char * buf,
+ size_t len)
{
- pthread_mutex_lock(&dht->mtx);
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ char exstr[RIB_TM_STRLEN];
+ size_t i = 0;
+ struct list_head * p;
+
+ assert(buf != NULL);
+ assert(len > 0);
+
+ pthread_rwlock_rdlock(&dht.db.lock);
- if (state == DHT_JOINING && dht->state != DHT_INIT) {
- pthread_mutex_unlock(&dht->mtx);
- return -1;
+ if (dht.db.kv.len == 0) {
+ i += snprintf(buf, len, " No entries.\n");
+ pthread_rwlock_unlock(&dht.db.lock);
+ return i;
}
- dht->state = state;
+ i += snprintf(buf + i, len - i, "DHT database:\n\n");
+ i += snprintf(buf + i, len - i,
+ "Number of keys: %10zu\n"
+ "Number of local values: %10zu\n"
+ "Number of non-local values: %10zu\n\n",
+ dht.db.kv.len, dht.db.kv.vals, dht.db.kv.lvals);
+
+ list_for_each(p, &dht.db.kv.list) {
+ struct dht_entry * e = list_entry(p, struct dht_entry, next);
+ struct list_head * h;
+
+ i += snprintf(buf + i, len - i, "Key: " KEY_FMT "\n",
+ KEY_VAL(e->key));
+ i += snprintf(buf + i, len - i, " Local entries:\n");
+
+ list_for_each(h, &e->vals.list) {
+ struct val_entry * v;
+
+ v = list_entry(h, struct val_entry, next);
+
+ tm = gmtime(&v->t_repl);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+
+ tm = gmtime(&v->t_exp);
+ strftime(exstr, sizeof(exstr), RIB_TM_FORMAT, tm);
+
+ i += snprintf(buf + i, len - i,
+ " " VAL_FMT
+ ", t_replicated=%.*s, t_expire=%.*s\n",
+ VAL_VAL(v->val),
+ RIB_TM_STRLEN, tmstr,
+ RIB_TM_STRLEN, exstr);
+ }
+
+ i += snprintf(buf + i, len - i, "\n");
+
+ i += snprintf(buf + i, len - i, " Non-local entries:\n");
- pthread_cond_broadcast(&dht->cond);
+ list_for_each(h, &e->lvals.list) {
+ struct val_entry * v;
- pthread_mutex_unlock(&dht->mtx);
+ v= list_entry(h, struct val_entry, next);
+
+ tm = gmtime(&v->t_repl);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+
+ tm = gmtime(&v->t_exp);
+ strftime(exstr, sizeof(exstr), RIB_TM_FORMAT, tm);
+
+ i += snprintf(buf + i, len - i,
+ " " VAL_FMT
+ ", t_replicated=%.*s, t_expire=%.*s\n",
+ VAL_VAL(v->val),
+ RIB_TM_STRLEN, tmstr,
+ RIB_TM_STRLEN, exstr);
+
+ }
+ }
+
+ pthread_rwlock_unlock(&dht.db.lock);
+
+ printf("DHT RIB DB file generated (%zu bytes).\n", i);
+
+ return i;
+}
+
+static int dht_rib_read(const char * path,
+ char * buf,
+ size_t len)
+{
+ char * entry;
+
+ entry = strstr(path, RIB_SEPARATOR) + 1;
+
+ if (strcmp(entry, "database") == 0) {
+ return dht_rib_dbfile(buf, len);
+ } else if (strcmp(entry, "stats") == 0) {
+ return dht_rib_statfile(buf, len);
+ }
return 0;
}
-int dht_wait_running(void * dir)
+static int dht_rib_readdir(char *** buf)
{
- struct dht * dht;
- int ret = 0;
+ int i = 0;
- dht = (struct dht *) dir;
+ while (dht_dir[i++] != NULL);
- pthread_mutex_lock(&dht->mtx);
+ *buf = malloc(sizeof(**buf) * i);
+ if (*buf == NULL)
+ goto fail_buf;
- pthread_cleanup_push(__cleanup_mutex_unlock, &dht->mtx);
+ i = 0;
- while (dht->state == DHT_JOINING)
- pthread_cond_wait(&dht->cond, &dht->mtx);
+ while (dht_dir[i] != NULL) {
+ (*buf)[i] = strdup(dht_dir[i]);
+ if ((*buf)[i] == NULL)
+ goto fail_dup;
+ i++;
+ }
- if (dht->state != DHT_RUNNING)
- ret = -1;
+ return i;
+ fail_dup:
+ freepp(char, *buf, i);
+ fail_buf:
+ return -ENOMEM;
+}
- pthread_cleanup_pop(true);
+static int dht_rib_getattr(const char * path,
+ struct rib_attr * attr)
+{
+ struct timespec now;
+ char * entry;
- return ret;
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ attr->mtime = now.tv_sec;
+
+ entry = strstr(path, RIB_SEPARATOR) + 1;
+
+ if (strcmp(entry, "database") == 0) {
+ attr->size = dht_db_file_len();
+ } else if (strcmp(entry, "stats") == 0) {
+ attr->size = 545;
+ }
+
+ return 0;
}
-static uint8_t * create_id(size_t len)
+static struct rib_ops r_ops = {
+ .read = dht_rib_read,
+ .readdir = dht_rib_readdir,
+ .getattr = dht_rib_getattr
+};
+
+/* Helper functions */
+
+static uint8_t * generate_id(void)
{
uint8_t * id;
- id = malloc(len);
- if (id == NULL)
+ if(dht.id.len < sizeof(uint64_t)) {
+ log_err("DHT ID length is too short (%zu < %zu).",
+ dht.id.len, sizeof(uint64_t));
return NULL;
+ }
- if (random_buffer(id, len) < 0) {
- free(id);
- return NULL;
+ id = malloc(dht.id.len);
+ if (id == NULL) {
+ log_err("Failed to malloc ID.");
+ goto fail_id;
+ }
+
+ if (random_buffer(id, dht.id.len) < 0) {
+ log_err("Failed to generate random ID.");
+ goto fail_rnd;
}
return id;
+ fail_rnd:
+ free(id);
+ fail_id:
+ return NULL;
}
-static void kad_req_create(struct dht * dht,
- dht_msg_t * msg,
- uint64_t addr)
+static uint64_t generate_cookie(void)
{
- struct kad_req * req;
- pthread_condattr_t cattr;
- struct timespec t;
- size_t b;
+ uint64_t cookie = DHT_INVALID;
- clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ while (cookie == DHT_INVALID)
+ random_buffer((uint8_t *) &cookie, sizeof(cookie));
- req = malloc(sizeof(*req));
- if (req == NULL)
- goto fail_malloc;
+ return cookie;
+}
- list_head_init(&req->next);
+/*
+ * If someone builds a network where the n (n > k) closest nodes all
+ * have IDs starting with the same 64 bits: by all means, change this.
+ */
+static uint64_t dist(const uint8_t * src,
+ const uint8_t * dst)
+{
+ assert(dht.id.len >= sizeof(uint64_t));
- req->t_exp = t.tv_sec + KAD_T_RESP;
- req->addr = addr;
- req->state = REQ_INIT;
- req->cookie = msg->cookie;
- req->code = msg->code;
- req->key = NULL;
+ return betoh64(*((uint64_t *) src) ^ *((uint64_t *) dst));
+}
- pthread_rwlock_rdlock(&dht->lock);
- b = dht->b;
- pthread_rwlock_unlock(&dht->lock);
+#define IS_CLOSER(x, y) (dist((x), dht.id.data) < dist((y), dht.id.data))
- if (msg->has_key) {
- req->key = dht_dup_key(msg->key.data, b);
- if (req->key == NULL)
- goto fail_dup_key;
- }
+static int addr_to_buf(const uint64_t addr,
+ buffer_t * buf)
+{
+ size_t len;
+ uint64_t _addr;
- if (pthread_mutex_init(&req->lock, NULL))
- goto fail_mutex;
+ len = sizeof(addr);
+ _addr = hton64(addr);
+ assert(buf != NULL);
- if (pthread_condattr_init(&cattr))
- goto fail_condattr;
-#ifndef __APPLE__
- pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
-#endif
+ buf->data = malloc(len);
+ if (buf->data == NULL)
+ goto fail_malloc;
- if (pthread_cond_init(&req->cond, &cattr))
- goto fail_cond_init;
+ buf->len = sizeof(_addr);
+ memcpy(buf->data, &_addr, sizeof(_addr));
- pthread_condattr_destroy(&cattr);
+ return 0;
+ fail_malloc:
+ return -ENOMEM;
+}
- pthread_rwlock_wrlock(&dht->lock);
+static int buf_to_addr(const buffer_t buf,
+ uint64_t * addr)
+{
+ assert(addr != NULL);
+ assert(buf.data != NULL);
- list_add(&req->next, &dht->requests);
+ if (buf.len != sizeof(*addr))
+ return - EINVAL;
- pthread_rwlock_unlock(&dht->lock);
+ *addr = ntoh64(*((uint64_t *) buf.data));
- return;
+ if (*addr == dht.addr)
+ *addr = INVALID_ADDR;
- fail_cond_init:
- pthread_condattr_destroy(&cattr);
- fail_condattr:
- pthread_mutex_destroy(&req->lock);
- fail_mutex:
- free(req->key);
- fail_dup_key:
- free(req);
- fail_malloc:
- return;
+ return 0;
}
-static void cancel_req_destroy(void * o)
+static uint8_t * dht_dup_key(const uint8_t * key)
{
- struct kad_req * req = (struct kad_req *) o;
+ uint8_t * dup;
- pthread_mutex_unlock(&req->lock);
+ assert(key != NULL);
+ assert(dht.id.len != 0);
- pthread_cond_destroy(&req->cond);
- pthread_mutex_destroy(&req->lock);
+ dup = malloc(dht.id.len);
+ if (dup == NULL)
+ return NULL;
- if (req->key != NULL)
- free(req->key);
+ memcpy(dup, key, dht.id.len);
- free(req);
+ return dup;
}
-static void kad_req_destroy(struct kad_req * req)
+/* DHT */
+
+static struct val_entry * val_entry_create(const buffer_t val,
+ time_t exp)
{
- assert(req);
+ struct val_entry * e;
+ struct timespec now;
- pthread_mutex_lock(&req->lock);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- switch (req->state) {
- case REQ_DESTROY:
- pthread_mutex_unlock(&req->lock);
- return;
- case REQ_PENDING:
- req->state = REQ_DESTROY;
- pthread_cond_broadcast(&req->cond);
- break;
- case REQ_INIT:
- case REQ_DONE:
- req->state = REQ_NULL;
- break;
- case REQ_RESPONSE:
- case REQ_NULL:
- default:
- break;
- }
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- pthread_cleanup_push(cancel_req_destroy, req);
+#ifndef __DHT_TEST_ALLOW_EXPIRED__
+ if (exp < now.tv_sec)
+ return NULL; /* Refuse to add expired values */
+#endif
+ e = malloc(sizeof(*e));
+ if (e == NULL)
+ goto fail_entry;
- while (req->state != REQ_NULL && req->state != REQ_DONE)
- pthread_cond_wait(&req->cond, &req->lock);
+ list_head_init(&e->next);
- pthread_cleanup_pop(true);
+ e->val.len = val.len;
+ e->val.data = malloc(val.len);
+ if (e->val.data == NULL)
+ goto fail_val;
+
+ memcpy(e->val.data, val.data, val.len);
+
+ e->t_repl = 0;
+ e->t_exp = exp;
+
+ return e;
+
+ fail_val:
+ free(e);
+ fail_entry:
+ return NULL;
}
-static int kad_req_wait(struct kad_req * req,
- time_t t)
+static void val_entry_destroy(struct val_entry * v)
{
- struct timespec timeo = TIMESPEC_INIT_S(0);
- struct timespec abs;
- int ret = 0;
+ assert(v->val.data != NULL);
- assert(req);
+ freebuf(v->val);
+ free(v);
+}
- timeo.tv_sec = t;
+static struct dht_entry * dht_entry_create(const uint8_t * key)
+{
+ struct dht_entry * e;
- clock_gettime(PTHREAD_COND_CLOCK, &abs);
+ assert(key != NULL);
- ts_add(&abs, &timeo, &abs);
+ e = malloc(sizeof(*e));
+ if (e == NULL)
+ goto fail_entry;
- pthread_mutex_lock(&req->lock);
+ list_head_init(&e->next);
+ list_head_init(&e->vals.list);
+ list_head_init(&e->lvals.list);
+
+ e->vals.len = 0;
+ e->lvals.len = 0;
+
+ e->key = dht_dup_key(key);
+ if (e->key == NULL)
+ goto fail_key;
+
+ return e;
+ fail_key:
+ free(e);
+ fail_entry:
+ return NULL;
+}
- req->state = REQ_PENDING;
+static void dht_entry_destroy(struct dht_entry * e)
+{
+ struct list_head * p;
+ struct list_head * h;
- pthread_cleanup_push(__cleanup_mutex_unlock, &req->lock);
+ assert(e != NULL);
- while (req->state == REQ_PENDING && ret != -ETIMEDOUT)
- ret = -pthread_cond_timedwait(&req->cond, &req->lock, &abs);
+ list_for_each_safe(p, h, &e->vals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ list_del(&v->next);
+ val_entry_destroy(v);
+ --e->vals.len;
+ --dht.db.kv.vals;
+ }
- switch(req->state) {
- case REQ_DESTROY:
- ret = -1;
- req->state = REQ_NULL;
- pthread_cond_signal(&req->cond);
- break;
- case REQ_PENDING: /* ETIMEDOUT */
- case REQ_RESPONSE:
- req->state = REQ_DONE;
- pthread_cond_broadcast(&req->cond);
- break;
- default:
- break;
+ list_for_each_safe(p, h, &e->lvals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ list_del(&v->next);
+ val_entry_destroy(v);
+ --e->lvals.len;
+ --dht.db.kv.lvals;
}
- pthread_cleanup_pop(true);
+ free(e->key);
+
+ assert(e->vals.len == 0 && e->lvals.len == 0);
- return ret;
+ free(e);
}
-static void kad_req_respond(struct kad_req * req)
+static struct val_entry * dht_entry_get_lval(const struct dht_entry * e,
+ const buffer_t val)
{
- pthread_mutex_lock(&req->lock);
+ struct list_head * p;
+
+ assert(e != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- req->state = REQ_RESPONSE;
- pthread_cond_signal(&req->cond);
+ list_for_each(p, &e->lvals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ if (bufcmp(&v->val, &val) == 0)
+ return v;
+ }
- pthread_mutex_unlock(&req->lock);
+ return NULL;
}
-static struct contact * contact_create(const uint8_t * id,
- size_t len,
- uint64_t addr)
+static struct val_entry * dht_entry_get_val(const struct dht_entry * e,
+ const buffer_t val)
{
- struct contact * c;
- struct timespec t;
-
- c = malloc(sizeof(*c));
- if (c == NULL)
- return NULL;
+ struct list_head * p;
- list_head_init(&c->next);
+ assert(e != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ list_for_each(p, &e->vals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ if (bufcmp(&v->val, &val) == 0)
+ return v;
- c->addr = addr;
- c->fails = 0;
- c->t_seen = t.tv_sec;
- c->id = dht_dup_key(id, len);
- if (c->id == NULL) {
- free(c);
- return NULL;
}
- return c;
+ return NULL;
}
-static void contact_destroy(struct contact * c)
+static int dht_entry_update_val(struct dht_entry * e,
+ buffer_t val,
+ time_t exp)
{
- if (c != NULL)
- free(c->id);
+ struct val_entry * v;
+ struct timespec now;
- free(c);
+ assert(e != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (exp < now.tv_sec)
+ return -EINVAL; /* Refuse to add expired values */
+
+ if (dht_entry_get_lval(e, val) != NULL) {
+ log_dbg(KV_FMT " Val already in lvals.", KV_VAL(e->key, val));
+ return 0; /* Refuse to add local values */
+ }
+
+ v = dht_entry_get_val(e, val);
+ if (v == NULL) {
+ v = val_entry_create(val, exp);
+ if (v == NULL)
+ return -ENOMEM;
+
+ list_add_tail(&v->next, &e->vals.list);
+ ++e->vals.len;
+ ++dht.db.kv.vals;
+
+ return 0;
+ }
+
+ if (v->t_exp < exp)
+ v->t_exp = exp;
+
+ return 0;
}
-static struct bucket * iter_bucket(struct bucket * b,
- const uint8_t * id)
+static int dht_entry_update_lval(struct dht_entry * e,
+ buffer_t val)
{
- uint8_t byte;
- uint8_t mask;
+ struct val_entry * v;
+ struct timespec now;
- assert(b);
+ assert(e != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- if (b->children[0] == NULL)
- return b;
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- byte = id[(b->depth * KAD_BETA) / CHAR_BIT];
+ v = dht_entry_get_lval(e, val);
+ if (v == NULL) {
+ log_dbg(KV_FMT " Adding lval.", KV_VAL(e->key, val));
+ v = val_entry_create(val, now.tv_sec + dht.t_expire);
+ if (v == NULL)
+ return -ENOMEM;
- mask = ((1L << KAD_BETA) - 1) & 0xFF;
+ list_add_tail(&v->next, &e->lvals.list);
+ ++e->lvals.len;
+ ++dht.db.kv.lvals;
- byte >>= (CHAR_BIT - KAD_BETA) -
- (((b->depth) * KAD_BETA) & (CHAR_BIT - 1));
+ return 0;
+ }
- return iter_bucket(b->children[(byte & mask)], id);
+ return 0;
}
-static struct bucket * dht_get_bucket(struct dht * dht,
- const uint8_t * id)
+static int dht_entry_remove_lval(struct dht_entry * e,
+ buffer_t val)
{
- assert(dht->buckets);
+ struct val_entry * v;
- return iter_bucket(dht->buckets, id);
+ assert(e != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
+
+ v = dht_entry_get_lval(e, val);
+ if (v == NULL)
+ return -ENOENT;
+
+ log_dbg(KV_FMT " Removing lval.", KV_VAL(e->key, val));
+
+ list_del(&v->next);
+ val_entry_destroy(v);
+ --e->lvals.len;
+ --dht.db.kv.lvals;
+
+ return 0;
}
-/*
- * If someone builds a network where the n (n > k) closest nodes all
- * have IDs starting with the same 64 bits: by all means, change this.
- */
-static uint64_t dist(const uint8_t * src,
- const uint8_t * dst)
+#define IS_EXPIRED(v, now) ((now)->tv_sec > (v)->t_exp)
+static void dht_entry_remove_expired_vals(struct dht_entry * e)
{
- return betoh64(*((uint64_t *) src) ^ *((uint64_t *) dst));
+ struct list_head * p;
+ struct list_head * h;
+ struct timespec now;
+
+ assert(e != NULL);
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ list_for_each_safe(p, h, &e->vals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ if (!IS_EXPIRED(v, &now))
+ continue;
+
+ log_dbg(KV_FMT " Value expired." , KV_VAL(e->key, v->val));
+ list_del(&v->next);
+ val_entry_destroy(v);
+ --e->vals.len;
+ --dht.db.kv.vals;
+ }
}
-static size_t list_add_sorted(struct list_head * l,
- struct contact * c,
- const uint8_t * key)
+static struct dht_entry * __dht_kv_find_entry(const uint8_t * key)
{
struct list_head * p;
- assert(l);
- assert(c);
- assert(key);
- assert(c->id);
+ assert(key != NULL);
- list_for_each(p, l) {
- struct contact * e = list_entry(p, struct contact, next);
- if (dist(c->id, key) > dist(e->id, key))
- break;
+ list_for_each(p, &dht.db.kv.list) {
+ struct dht_entry * e = list_entry(p, struct dht_entry, next);
+ if (!memcmp(key, e->key, dht.id.len))
+ return e;
}
- list_add_tail(&c->next, p);
-
- return 1;
+ return NULL;
}
-static size_t dht_contact_list(struct dht * dht,
- struct list_head * l,
- const uint8_t * key)
+static void dht_kv_remove_expired_entries(void)
{
struct list_head * p;
- struct bucket * b;
- size_t len = 0;
- size_t i;
- struct timespec t;
+ struct list_head * h;
+ struct timespec now;
- assert(l);
- assert(dht);
- assert(key);
- assert(list_is_empty(l));
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ pthread_rwlock_wrlock(&dht.db.lock);
- b = dht_get_bucket(dht, key);
- if (b == NULL)
- return 0;
-
- b->t_refr = t.tv_sec + KAD_T_REFR;
+ list_for_each_safe(p, h, &dht.db.kv.list) {
+ struct dht_entry * e = list_entry(p, struct dht_entry, next);
+ dht_entry_remove_expired_vals(e);
+ if (e->lvals.len > 0 || e->vals.len > 0)
+ continue;
- if (b->n_contacts == dht->k || b->parent == NULL) {
- list_for_each(p, &b->contacts) {
- struct contact * c;
- c = list_entry(p, struct contact, next);
- c = contact_create(c->id, dht->b, c->addr);
- if (list_add_sorted(l, c, key) == 1)
- if (++len == dht->k)
- break;
- }
- } else {
- struct bucket * d = b->parent;
- for (i = 0; i < (1L << KAD_BETA) && len < dht->k; ++i) {
- list_for_each(p, &d->children[i]->contacts) {
- struct contact * c;
- c = list_entry(p, struct contact, next);
- c = contact_create(c->id, dht->b, c->addr);
- if (c == NULL)
- continue;
- if (list_add_sorted(l, c, key) == 1)
- if (++len == dht->k)
- break;
- }
- }
+ log_dbg(KEY_FMT " Entry removed. ", KEY_VAL(e->key));
+ list_del(&e->next);
+ dht_entry_destroy(e);
+ --dht.db.kv.len;
}
- assert(len == dht->k || b->parent == NULL);
-
- return len;
+ pthread_rwlock_unlock(&dht.db.lock);
}
-static struct lookup * lookup_create(struct dht * dht,
- const uint8_t * id)
+
+static struct contact * contact_create(const uint8_t * id,
+ uint64_t addr)
{
- struct lookup * lu;
- pthread_condattr_t cattr;
+ struct contact * c;
+ struct timespec t;
+
+ c = malloc(sizeof(*c));
+ if (c == NULL)
+ return NULL;
- assert(dht);
- assert(id);
+ list_head_init(&c->next);
- lu = malloc(sizeof(*lu));
- if (lu == NULL)
- goto fail_malloc;
+ clock_gettime(CLOCK_REALTIME_COARSE, &t);
- list_head_init(&lu->contacts);
- list_head_init(&lu->cookies);
+ c->addr = addr;
+ c->fails = 0;
+ c->t_seen = t.tv_sec;
+ c->id = dht_dup_key(id);
+ if (c->id == NULL) {
+ free(c);
+ return NULL;
+ }
- lu->state = LU_INIT;
- lu->addrs = NULL;
- lu->n_addrs = 0;
- lu->key = dht_dup_key(id, dht->b);
- if (lu->key == NULL)
- goto fail_id;
+ return c;
+}
- if (pthread_mutex_init(&lu->lock, NULL))
- goto fail_mutex;
+static void contact_destroy(struct contact * c)
+{
+ assert(c != NULL);
+ assert(list_is_empty(&c->next));
- pthread_condattr_init(&cattr);
-#ifndef __APPLE__
- pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
-#endif
+ free(c->id);
+ free(c);
+}
- if (pthread_cond_init(&lu->cond, &cattr))
- goto fail_cond;
+static struct dht_req * dht_req_create(const uint8_t * key)
+{
+ struct dht_req * req;
+ struct timespec now;
- pthread_condattr_destroy(&cattr);
+ assert(key != NULL);
- pthread_rwlock_wrlock(&dht->lock);
+ clock_gettime(PTHREAD_COND_CLOCK, &now);
- list_add(&lu->next, &dht->lookups);
+ req = malloc(sizeof(*req));
+ if (req == NULL)
+ goto fail_malloc;
- lu->n_contacts = dht_contact_list(dht, &lu->contacts, id);
+ list_head_init(&req->next);
- pthread_rwlock_unlock(&dht->lock);
+ req->t_exp = now.tv_sec + DHT_T_RESP;
- return lu;
+ list_head_init(&req->peers.list);
+ req->peers.len = 0;
- fail_cond:
- pthread_condattr_destroy(&cattr);
- pthread_mutex_destroy(&lu->lock);
- fail_mutex:
- free(lu->key);
- fail_id:
- free(lu);
+ req->key = dht_dup_key(key);
+ if (req->key == NULL)
+ goto fail_dup_key;
+
+ list_head_init(&req->cache.list);
+ req->cache.len = 0;
+
+ return req;
+
+ fail_dup_key:
+ free(req);
fail_malloc:
return NULL;
}
-static void cancel_lookup_destroy(void * o)
+static void dht_req_destroy(struct dht_req * req)
{
- struct lookup * lu;
struct list_head * p;
struct list_head * h;
- lu = (struct lookup *) o;
-
- if (lu->key != NULL)
- free(lu->key);
- if (lu->addrs != NULL)
- free(lu->addrs);
+ assert(req);
+ assert(req->key);
- list_for_each_safe(p, h, &lu->contacts) {
- struct contact * c = list_entry(p, struct contact, next);
- list_del(&c->next);
- contact_destroy(c);
+ list_for_each_safe(p, h, &req->peers.list) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ list_del(&e->next);
+ free(e->id);
+ free(e);
+ --req->peers.len;
}
- list_for_each_safe(p, h, &lu->cookies) {
- struct cookie_el * c = list_entry(p, struct cookie_el, next);
- list_del(&c->next);
- free(c);
+ list_for_each_safe(p, h, &req->cache.list) {
+ struct val_entry * e = list_entry(p, struct val_entry, next);
+ list_del(&e->next);
+ val_entry_destroy(e);
+ --req->cache.len;
}
- pthread_mutex_unlock(&lu->lock);
+ free(req->key);
- pthread_mutex_destroy(&lu->lock);
+ assert(req->peers.len == 0);
- free(lu);
+ free(req);
}
-static void lookup_destroy(struct lookup * lu)
+static struct peer_entry * dht_req_get_peer(struct dht_req * req,
+ struct peer_entry * e)
{
- assert(lu);
-
- pthread_mutex_lock(&lu->lock);
+ struct list_head * p;
- switch (lu->state) {
- case LU_DESTROY:
- pthread_mutex_unlock(&lu->lock);
- return;
- case LU_PENDING:
- lu->state = LU_DESTROY;
- pthread_cond_broadcast(&lu->cond);
- break;
- case LU_INIT:
- case LU_UPDATE:
- case LU_COMPLETE:
- lu->state = LU_NULL;
- break;
- case LU_NULL:
- default:
- break;
+ list_for_each(p, &req->peers.list) {
+ struct peer_entry * x = list_entry(p, struct peer_entry, next);
+ if (x->addr == e->addr)
+ return x;
}
- pthread_cleanup_push(cancel_lookup_destroy, lu);
-
- while (lu->state != LU_NULL)
- pthread_cond_wait(&lu->cond, &lu->lock);
-
- pthread_cleanup_pop(true);
+ return NULL;
}
-static void lookup_update(struct dht * dht,
- struct lookup * lu,
- dht_msg_t * msg)
+#define IS_MAGIC(peer) ((peer)->cookie == dht.magic)
+void dht_req_add_peer(struct dht_req * req,
+ struct peer_entry * e)
{
- struct list_head * p = NULL;
- struct list_head * h;
- struct contact * c = NULL;
- size_t n;
- size_t pos = 0;
- bool mod = false;
-
- assert(lu);
- assert(msg);
+ struct peer_entry * x; /* existing */
+ struct list_head * p; /* iterator */
+ size_t pos = 0;
- if (dht_get_state(dht) != DHT_RUNNING)
- return;
+ assert(req != NULL);
+ assert(e != NULL);
+ assert(e->id != NULL);
- pthread_mutex_lock(&lu->lock);
+ /*
+ * Dedupe messages to the same peer, unless
+ * 1) The previous request was FIND_NODE and now it's FIND_VALUE
+ * 2) We urgently need contacts from emergency peer (magic cookie)
+ */
+ x = dht_req_get_peer(req, e);
+ if (x != NULL && x->code >= e->code && !IS_MAGIC(e))
+ goto skip;
- list_for_each_safe(p, h, &lu->cookies) {
- struct cookie_el * e = list_entry(p, struct cookie_el, next);
- if (e->cookie == msg->cookie) {
- list_del(&e->next);
- free(e);
- break;
+ /* Find how this contact ranks in distance to the key */
+ list_for_each(p, &req->peers.list) {
+ struct peer_entry * y = list_entry(p, struct peer_entry, next);
+ if (IS_CLOSER(y->id, e->id)) {
+ pos++;
+ continue;
}
+ break;
}
- if (lu->state == LU_COMPLETE) {
- pthread_mutex_unlock(&lu->lock);
- return;
- }
+ /* Add a new peer to this request if we need to */
+ if (pos < dht.alpha || !IS_MAGIC(e)) {
+ x = malloc(sizeof(*x));
+ if (x == NULL) {
+ log_err("Failed to malloc peer entry.");
+ goto skip;
+ }
- if (msg->n_addrs > 0) {
- if (lu->addrs == NULL) {
- lu->addrs = malloc(sizeof(*lu->addrs) * msg->n_addrs);
- for (n = 0; n < msg->n_addrs; ++n)
- lu->addrs[n] = msg->addrs[n];
- lu->n_addrs = msg->n_addrs;
+ x->cookie = e->cookie;
+ x->addr = e->addr;
+ x->code = e->code;
+ x->t_sent = e->t_sent;
+ x->id = dht_dup_key(e->id);
+ if (x->id == NULL) {
+ log_err("Failed to dup peer ID.");
+ free(x);
+ goto skip;
}
- lu->state = LU_COMPLETE;
- pthread_cond_broadcast(&lu->cond);
- pthread_mutex_unlock(&lu->lock);
+ if (IS_MAGIC(e))
+ list_add(&x->next, p);
+ else
+ list_add_tail(&x->next, p);
+ ++req->peers.len;
return;
}
+ skip:
+ list_del(&e->next);
+ free(e->id);
+ free(e);
+}
+
+static size_t dht_req_add_peers(struct dht_req * req,
+ struct list_head * pl)
+{
+ struct list_head * p;
+ struct list_head * h;
+ size_t n = 0;
- pthread_cleanup_push(__cleanup_mutex_unlock, &lu->lock);
+ assert(req != NULL);
+ assert(pl != NULL);
- while (lu->state == LU_INIT) {
- pthread_rwlock_unlock(&dht->lock);
- pthread_cond_wait(&lu->cond, &lu->lock);
- pthread_rwlock_rdlock(&dht->lock);
+ list_for_each_safe(p, h, pl) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ dht_req_add_peer(req, e);
}
- pthread_cleanup_pop(false);
+ return n;
+}
- for (n = 0; n < msg->n_contacts; ++n) {
- c = contact_create(msg->contacts[n]->id.data,
- dht->b, msg->contacts[n]->addr);
- if (c == NULL)
- continue;
+static bool dht_req_has_peer(struct dht_req * req,
+ uint64_t cookie)
+{
+ struct list_head * p;
- pos = 0;
+ assert(req != NULL);
- list_for_each(p, &lu->contacts) {
- struct contact * e;
- e = list_entry(p, struct contact, next);
- if (!memcmp(e->id, c->id, dht->b)) {
- contact_destroy(c);
- c = NULL;
- break;
- }
+ list_for_each(p, &req->peers.list) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ if (e->cookie == cookie)
+ return true;
+ }
- if (dist(c->id, lu->key) > dist(e->id, lu->key))
- break;
+ return false;
+}
- pos++;
- }
+static void peer_list_destroy(struct list_head * pl)
+{
+ struct list_head * p;
+ struct list_head * h;
- if (c == NULL)
- continue;
+ assert(pl != NULL);
- if (lu->n_contacts < dht->k) {
- list_add_tail(&c->next, p);
- ++lu->n_contacts;
- mod = true;
- } else if (pos == dht->k) {
- contact_destroy(c);
- } else {
- struct contact * d;
- list_add_tail(&c->next, p);
- d = list_last_entry(&lu->contacts,
- struct contact, next);
- list_del(&d->next);
- assert(lu->contacts.prv != &d->next);
- contact_destroy(d);
- mod = true;
- }
+ list_for_each_safe(p, h, pl) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ list_del(&e->next);
+ free(e->id);
+ free(e);
}
-
- if (list_is_empty(&lu->cookies) && !mod)
- lu->state = LU_COMPLETE;
- else
- lu->state = LU_UPDATE;
-
- pthread_cond_broadcast(&lu->cond);
- pthread_mutex_unlock(&lu->lock);
- return;
}
-static ssize_t lookup_get_addrs(struct lookup * lu,
- uint64_t * addrs)
+static int dht_kv_create_peer_list(struct list_head * cl,
+ struct list_head * pl,
+ enum dht_code code)
{
- ssize_t n;
+ struct list_head * p;
+ struct list_head * h;
+ struct timespec now;
+ size_t len;
- assert(lu);
+ assert(cl != NULL);
+ assert(pl != NULL);
+ assert(list_is_empty(pl));
- pthread_mutex_lock(&lu->lock);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- for (n = 0; (size_t) n < lu->n_addrs; ++n)
- addrs[n] = lu->addrs[n];
+ len = 0;
- assert((size_t) n == lu->n_addrs);
+ list_for_each_safe(p, h, cl) {
+ struct contact * c = list_entry(p, struct contact, next);
+ struct peer_entry * e;
+ if (len++ == dht.alpha)
+ break;
- pthread_mutex_unlock(&lu->lock);
+ e = malloc(sizeof(*e));
+ if (e == NULL)
+ return -ENOMEM;
- return n;
-}
+ e->cookie = generate_cookie();
+ e->code = code;
+ e->addr = c->addr;
+ e->t_sent = now.tv_sec;
-static ssize_t lookup_contact_addrs(struct lookup * lu,
- uint64_t * addrs)
-{
- struct list_head * p;
- ssize_t n = 0;
-
- assert(lu);
- assert(addrs);
+ e->id = c->id;
- pthread_mutex_lock(&lu->lock);
+ list_add_tail(&e->next, pl);
- list_for_each(p, &lu->contacts) {
- struct contact * c = list_entry(p, struct contact, next);
- addrs[n] = c->addr;
- n++;
+ list_del(&c->next);
+ c->id = NULL; /* we stole the id */
+ contact_destroy(c);
}
- pthread_mutex_unlock(&lu->lock);
-
- return n;
+ return 0;
}
-static void lookup_new_addrs(struct lookup * lu,
- uint64_t * addrs)
+static struct dht_req * __dht_kv_req_get_req(const uint8_t * key)
{
struct list_head * p;
- size_t n = 0;
- assert(lu);
- assert(addrs);
+ list_for_each(p, &dht.reqs.list) {
+ struct dht_req * r = list_entry(p, struct dht_req, next);
+ if (memcmp(r->key, key, dht.id.len) == 0)
+ return r;
+ }
- pthread_mutex_lock(&lu->lock);
+ return NULL;
+}
- /* Uses fails to check if the contact has been contacted. */
- list_for_each(p, &lu->contacts) {
- struct contact * c = list_entry(p, struct contact, next);
- if (c->fails == 0) {
- c->fails = 1;
- addrs[n] = c->addr;
- n++;
- }
+static struct dht_req * __dht_kv_get_req_cache(const uint8_t * key)
+{
+ struct dht_req * req;
- if (n == KAD_ALPHA)
- break;
- }
+ assert(key != NULL);
- assert(n <= KAD_ALPHA);
+ req = __dht_kv_req_get_req(key);
+ if (req == NULL)
+ return NULL;
- addrs[n] = 0;
+ if (req->cache.len == 0)
+ return NULL;
- pthread_mutex_unlock(&lu->lock);
+ return req;
}
-static void lookup_set_state(struct lookup * lu,
- enum lookup_state state)
+static void __dht_kv_req_remove(const uint8_t * key)
{
- pthread_mutex_lock(&lu->lock);
+ struct dht_req * req;
- lu->state = state;
- pthread_cond_broadcast(&lu->cond);
+ assert(key != NULL);
- pthread_mutex_unlock(&lu->lock);
-}
+ req = __dht_kv_req_get_req(key);
+ if (req == NULL)
+ return;
-static void cancel_lookup_wait(void * o)
-{
- struct lookup * lu = (struct lookup *) o;
- lu->state = LU_NULL;
- pthread_mutex_unlock(&lu->lock);
- lookup_destroy(lu);
+ list_del(&req->next);
+ --dht.reqs.len;
+
+ dht_req_destroy(req);
}
-static enum lookup_state lookup_wait(struct lookup * lu)
+static struct dht_req * __dht_kv_get_req_peer(const uint8_t * key,
+ uint64_t cookie)
{
- struct timespec timeo = TIMESPEC_INIT_S(KAD_T_RESP);
- struct timespec abs;
- enum lookup_state state;
- int ret = 0;
-
- clock_gettime(PTHREAD_COND_CLOCK, &abs);
+ struct dht_req * req;
- ts_add(&abs, &timeo, &abs);
+ assert(key != NULL);
- pthread_mutex_lock(&lu->lock);
-
- if (lu->state == LU_INIT || lu->state == LU_UPDATE)
- lu->state = LU_PENDING;
+ req = __dht_kv_req_get_req(key);
+ if (req == NULL)
+ return NULL;
- pthread_cleanup_push(cancel_lookup_wait, lu);
+ if (!dht_req_has_peer(req, cookie))
+ return NULL;
- while (lu->state == LU_PENDING && ret != -ETIMEDOUT)
- ret = -pthread_cond_timedwait(&lu->cond, &lu->lock, &abs);
+ return req;
+}
- pthread_cleanup_pop(false);
+static bool dht_kv_has_req(const uint8_t * key,
+ uint64_t cookie)
+{
+ bool found;
- if (ret == -ETIMEDOUT)
- lu->state = LU_COMPLETE;
+ pthread_mutex_lock(&dht.reqs.mtx);
- state = lu->state;
+ found = __dht_kv_get_req_peer(key, cookie) != NULL;
- pthread_mutex_unlock(&lu->lock);
+ pthread_mutex_unlock(&dht.reqs.mtx);
- return state;
+ return found;
}
-static struct kad_req * dht_find_request(struct dht * dht,
- dht_msg_t * msg)
+/*
+ * This will filter the peer list for addresses that still need to be
+ * contacted.
+ */
+static int dht_kv_update_req(const uint8_t * key,
+ struct list_head * pl)
{
- struct list_head * p;
+ struct dht_req * req;
+ struct timespec now;
- assert(dht);
- assert(msg);
+ assert(key != NULL);
+ assert(pl != NULL);
+ assert(!list_is_empty(pl));
- list_for_each(p, &dht->requests) {
- struct kad_req * r = list_entry(p, struct kad_req, next);
- if (r->cookie == msg->cookie)
- return r;
- }
+ clock_gettime(PTHREAD_COND_CLOCK, &now);
- return NULL;
-}
+ pthread_mutex_lock(&dht.reqs.mtx);
-static struct lookup * dht_find_lookup(struct dht * dht,
- uint32_t cookie)
-{
- struct list_head * p;
- struct list_head * p2;
- struct list_head * h2;
-
- assert(dht);
- assert(cookie > 0);
-
- list_for_each(p, &dht->lookups) {
- struct lookup * l = list_entry(p, struct lookup, next);
- pthread_mutex_lock(&l->lock);
- list_for_each_safe(p2, h2, &l->cookies) {
- struct cookie_el * e;
- e = list_entry(p2, struct cookie_el, next);
- if (e->cookie == cookie) {
- list_del(&e->next);
- free(e);
- pthread_mutex_unlock(&l->lock);
- return l;
- }
+ req = __dht_kv_req_get_req(key);
+ if (req == NULL) {
+ if (dht.reqs.len == DHT_MAX_REQS) {
+ log_err(KEY_FMT " Max reqs reached (%zu).",
+ KEY_VAL(key), dht.reqs.len);
+ peer_list_destroy(pl);
+ goto fail_req;
}
- pthread_mutex_unlock(&l->lock);
+ req = dht_req_create(key);
+ if (req == NULL) {
+ log_err(KEY_FMT "Failed to create req.", KEY_VAL(key));
+ goto fail_req;
+ }
+ list_add_tail(&req->next, &dht.reqs.list);
+ ++dht.reqs.len;
}
- return NULL;
+ if (req->cache.len > 0) /* Already have values */
+ peer_list_destroy(pl);
+
+ dht_req_add_peers(req, pl);
+ req->t_exp = now.tv_sec + DHT_T_RESP;
+
+ if (dht.reqs.len > DHT_WARN_REQS) {
+ log_warn("Number of outstanding requests (%zu) exceeds %u.",
+ dht.reqs.len, DHT_WARN_REQS);
+ }
+
+ pthread_mutex_unlock(&dht.reqs.mtx);
+
+ return 0;
+ fail_req:
+ pthread_mutex_unlock(&dht.reqs.mtx);
+ return -1;
}
-static struct val * val_create(uint64_t addr,
- time_t exp)
+static int dht_kv_respond_req(uint8_t * key,
+ binary_data_t * vals,
+ size_t len)
{
- struct val * v;
- struct timespec t;
+ struct dht_req * req;
+ struct timespec now;
+ size_t i;
- v = malloc(sizeof(*v));
- if (v == NULL)
- return NULL;
+ assert(key != NULL);
+ assert(vals != NULL);
+ assert(len > 0);
- list_head_init(&v->next);
- v->addr = addr;
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ pthread_mutex_lock(&dht.reqs.mtx);
+
+ req = __dht_kv_req_get_req(key);
+ if (req == NULL) {
+ log_dbg(KEY_FMT " Failed to find req.", KEY_VAL(key));
+ goto fail_req;
+ }
+
+ for (i = 0; i < len; ++i) {
+ struct val_entry * e;
+ buffer_t val;
+ val.data = vals[i].data;
+ val.len = vals[i].len;
+ e = val_entry_create(val, now.tv_sec + DHT_T_CACHE);
+ if (e == NULL) {
+ log_err(" Failed to create val_entry.");
+ continue;
+ }
- v->t_exp = t.tv_sec + exp;
- v->t_rep = t.tv_sec + KAD_T_REPL;
+ list_add_tail(&e->next, &req->cache.list);
+ ++req->cache.len;
+ }
- return v;
-}
+ pthread_cond_broadcast(&dht.reqs.cond);
-static void val_destroy(struct val * v)
-{
- assert(v);
+ pthread_mutex_unlock(&dht.reqs.mtx);
- free(v);
+ return 0;
+ fail_req:
+ pthread_mutex_unlock(&dht.reqs.mtx);
+ return -1;
}
-static struct ref_entry * ref_entry_create(struct dht * dht,
- const uint8_t * key)
+static ssize_t dht_kv_wait_req(const uint8_t * key,
+ buffer_t ** vals)
{
- struct ref_entry * e;
+ struct list_head * p;
+ struct dht_req * req;
struct timespec t;
+#ifdef __DHT_TEST__
+ struct timespec intv = TIMESPEC_INIT_MS(10);
+#else
+ struct timespec intv = TIMESPEC_INIT_S(DHT_T_RESP);
+#endif
+ size_t max;
+ size_t i = 0;
+ int ret = 0;
- assert(dht);
- assert(key);
+ assert(key != NULL);
+ assert(vals != NULL);
- e = malloc(sizeof(*e));
- if (e == NULL)
- return NULL;
+ clock_gettime(PTHREAD_COND_CLOCK, &t);
- e->key = dht_dup_key(key, dht->b);
- if (e->key == NULL) {
- free(e);
- return NULL;
+ ts_add(&t, &intv, &t);
+
+ pthread_mutex_lock(&dht.reqs.mtx);
+
+ pthread_cleanup_push(__cleanup_mutex_unlock, &dht.reqs.mtx);
+
+ while ((req = __dht_kv_get_req_cache(key)) == NULL) {
+ ret = pthread_cond_timedwait(&dht.reqs.cond, &dht.reqs.mtx, &t);
+ if (ret == ETIMEDOUT)
+ break;
}
- clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ pthread_cleanup_pop(false);
- e->t_rep = t.tv_sec + dht->t_repub;
+ if (ret == ETIMEDOUT) {
+ log_warn(KEY_FMT " Req timed out.", KEY_VAL(key));
+ __dht_kv_req_remove(key);
+ goto timedout;
+ }
- return e;
-}
+ max = MIN(req->cache.len, DHT_MAX_VALS);
+ if (max == 0)
+ goto no_vals;
-static void ref_entry_destroy(struct ref_entry * e)
-{
- free(e->key);
- free(e);
+ *vals = malloc(max * sizeof(**vals));
+ if (*vals == NULL) {
+ log_err(KEY_FMT "Failed to malloc val buffer.", KEY_VAL(key));
+ goto fail_vals;
+ }
+
+ memset(*vals, 0, max * sizeof(**vals));
+
+ list_for_each(p, &req->cache.list) {
+ struct val_entry * v;
+ if (i == max)
+ break; /* We have enough values */
+ v = list_entry(p, struct val_entry, next);
+ (*vals)[i].data = malloc(v->val.len);
+ if ((*vals)[i].data == NULL)
+ goto fail_val_data;
+
+ (*vals)[i].len = v->val.len;
+ memcpy((*vals)[i++].data, v->val.data, v->val.len);
+ }
+
+ pthread_mutex_unlock(&dht.reqs.mtx);
+
+ return i;
+ no_vals:
+ pthread_mutex_unlock(&dht.reqs.mtx);
+ *vals = NULL;
+ return 0;
+ fail_val_data:
+ freebufs(*vals, i);
+ fail_vals:
+ pthread_mutex_unlock(&dht.reqs.mtx);
+ return -ENOMEM;
+ timedout:
+ pthread_mutex_unlock(&dht.reqs.mtx);
+ return -ETIMEDOUT;
}
-static struct dht_entry * dht_entry_create(struct dht * dht,
- const uint8_t * key)
+static struct bucket * iter_bucket(struct bucket * b,
+ const uint8_t * id)
{
- struct dht_entry * e;
+ uint8_t byte;
+ uint8_t mask;
- assert(dht);
- assert(key);
+ assert(b != NULL);
- e = malloc(sizeof(*e));
- if (e == NULL)
- return NULL;
+ if (b->children[0] == NULL)
+ return b;
- list_head_init(&e->next);
- list_head_init(&e->vals);
+ byte = id[(b->depth * DHT_BETA) / CHAR_BIT];
- e->n_vals = 0;
+ mask = ((1L << DHT_BETA) - 1) & 0xFF;
- e->key = dht_dup_key(key, dht->b);
- if (e->key == NULL) {
- free(e);
- return NULL;
- }
+ byte >>= (CHAR_BIT - DHT_BETA) -
+ (((b->depth) * DHT_BETA) & (CHAR_BIT - 1));
- return e;
+ return iter_bucket(b->children[(byte & mask)], id);
}
-static void dht_entry_destroy(struct dht_entry * e)
+static struct bucket * __dht_kv_get_bucket(const uint8_t * id)
+{
+ assert(dht.db.contacts.root != NULL);
+
+ return iter_bucket(dht.db.contacts.root, id);
+}
+
+static void contact_list_add(struct list_head * l,
+ struct contact * c)
{
struct list_head * p;
- struct list_head * h;
- assert(e);
+ assert(l != NULL);
+ assert(c != NULL);
- list_for_each_safe(p, h, &e->vals) {
- struct val * v = list_entry(p, struct val, next);
- list_del(&v->next);
- val_destroy(v);
+ list_for_each(p, l) {
+ struct contact * e = list_entry(p, struct contact, next);
+ if (IS_CLOSER(e->id, c->id))
+ continue;
}
- free(e->key);
-
- free(e);
+ list_add_tail(&c->next, p);
}
-static int dht_entry_add_addr(struct dht_entry * e,
- uint64_t addr,
- time_t exp)
+static ssize_t dht_kv_contact_list(const uint8_t * key,
+ struct list_head * l,
+ size_t max)
{
struct list_head * p;
- struct val * val;
- struct timespec t;
+ struct bucket * b;
+ struct timespec t;
+ size_t i;
+ size_t len = 0;
+
+ assert(l != NULL);
+ assert(key != NULL);
+ assert(list_is_empty(l));
clock_gettime(CLOCK_REALTIME_COARSE, &t);
- list_for_each(p, &e->vals) {
- struct val * v = list_entry(p, struct val, next);
- if (v->addr == addr) {
- if (v->t_exp < t.tv_sec + exp) {
- v->t_exp = t.tv_sec + exp;
- v->t_rep = t.tv_sec + KAD_T_REPL;
- }
+ max = MIN(max, dht.k);
- return 0;
- }
+ pthread_rwlock_rdlock(&dht.db.lock);
+
+ b = __dht_kv_get_bucket(key);
+ if (b == NULL) {
+ log_err(KEY_FMT " Failed to get bucket.", KEY_VAL(key));
+ goto fail_bucket;
}
- val = val_create(addr, exp);
- if (val == NULL)
- return -ENOMEM;
+ b->t_refr = t.tv_sec + dht.t_refresh;
- list_add(&val->next, &e->vals);
- ++e->n_vals;
+ if (b->contacts.len == dht.k || b->parent == NULL) {
+ list_for_each(p, &b->contacts.list) {
+ struct contact * c;
+ struct contact * d;
+ c = list_entry(p, struct contact, next);
+ if (c->addr == dht.addr)
+ continue;
+ d = contact_create(c->id, c->addr);
+ if (d == NULL)
+ continue;
+ contact_list_add(l, d);
+ if (++len == max)
+ break;
+ }
+ } else {
+ struct bucket * d = b->parent;
+ for (i = 0; i < (1L << DHT_BETA) && len < dht.k; ++i) {
+ list_for_each(p, &d->children[i]->contacts.list) {
+ struct contact * c;
+ struct contact * d;
+ c = list_entry(p, struct contact, next);
+ if (c->addr == dht.addr)
+ continue;
+ d = contact_create(c->id, c->addr);
+ if (d == NULL)
+ continue;
+ contact_list_add(l, d);
+ if (++len == max)
+ break;
+ }
+ }
+ }
- return 0;
-}
+ pthread_rwlock_unlock(&dht.db.lock);
+ return len;
+ fail_bucket:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -1;
+}
-static void dht_entry_del_addr(struct dht_entry * e,
- uint64_t addr)
+static void contact_list_destroy(struct list_head * l)
{
struct list_head * p;
struct list_head * h;
- assert(e);
-
- list_for_each_safe(p, h, &e->vals) {
- struct val * v = list_entry(p, struct val, next);
- if (v->addr == addr) {
- list_del(&v->next);
- val_destroy(v);
- --e->n_vals;
- }
- }
+ assert(l != NULL);
- if (e->n_vals == 0) {
- list_del(&e->next);
- dht_entry_destroy(e);
+ list_for_each_safe(p, h, l) {
+ struct contact * c = list_entry(p, struct contact, next);
+ list_del(&c->next);
+ contact_destroy(c);
}
}
-static uint64_t dht_entry_get_addr(struct dht * dht,
- struct dht_entry * e)
+static ssize_t dht_kv_get_contacts(const uint8_t * key,
+ dht_contact_msg_t *** msgs)
{
+ struct list_head cl;
struct list_head * p;
+ struct list_head * h;
+ size_t len;
+ size_t i = 0;
- assert(e);
- assert(!list_is_empty(&e->vals));
+ assert(key != NULL);
+ assert(msgs != NULL);
- list_for_each(p, &e->vals) {
- struct val * v = list_entry(p, struct val, next);
- if (v->addr != dht->addr)
- return v->addr;
+ list_head_init(&cl);
+
+ len = dht_kv_contact_list(key, &cl, dht.k);
+ if (len == 0) {
+ *msgs = NULL;
+ return 0;
}
- return 0;
-}
+ *msgs = malloc(len * sizeof(**msgs));
+ if (*msgs == NULL)
+ goto fail_msgs;
-/* Forward declaration. */
-static struct lookup * kad_lookup(struct dht * dht,
- const uint8_t * key,
- enum kad_code code);
+ list_for_each_safe(p, h, &cl) {
+ struct contact * c;
+ (*msgs)[i] = malloc(sizeof(***msgs));
+ if ((*msgs)[i] == NULL)
+ goto fail_contact;
+ dht_contact_msg__init((*msgs)[i]);
+ c = list_entry(p, struct contact, next);
+ list_del(&c->next);
+ (*msgs)[i]->id.data = c->id;
+ (*msgs)[i]->id.len = dht.id.len;
+ (*msgs)[i++]->addr = c->addr;
+ free(c);
+ }
+
+ return i;
+ fail_contact:
+ while (i-- > 0)
+ dht_contact_msg__free_unpacked((*msgs)[i], NULL);
+ free(*msgs);
+ *msgs = NULL;
+ fail_msgs:
+ contact_list_destroy(&cl);
+ return -ENOMEM;
+}
/* Build a refresh list. */
-static void bucket_refresh(struct dht * dht,
- struct bucket * b,
- time_t t,
- struct list_head * r)
+static void __dht_kv_bucket_refresh_list(struct bucket * b,
+ time_t t,
+ struct list_head * r)
{
- size_t i;
+ struct contact * c;
+ struct contact * d;
- if (*b->children != NULL)
- for (i = 0; i < (1L << KAD_BETA); ++i)
- bucket_refresh(dht, b->children[i], t, r);
+ assert(b != NULL);
- if (b->n_contacts == 0)
+ if (t < b->t_refr)
return;
- if (t > b->t_refr) {
- struct contact * c;
- struct contact * d;
- c = list_first_entry(&b->contacts, struct contact, next);
- d = contact_create(c->id, dht->b, c->addr);
+ if (*b->children != NULL) {
+ size_t i;
+ for (i = 0; i < (1L << DHT_BETA); ++i)
+ __dht_kv_bucket_refresh_list(b->children[i], t, r);
+ }
+
+ if (b->contacts.len == 0)
+ return;
+
+ c = list_first_entry(&b->contacts.list, struct contact, next);
+ if (t > c->t_seen + dht.t_refresh) {
+ d = contact_create(c->id, c->addr);
if (d != NULL)
list_add(&d->next, r);
- return;
}
}
-
static struct bucket * bucket_create(void)
{
struct bucket * b;
@@ -1294,20 +1682,21 @@ static struct bucket * bucket_create(void)
if (b == NULL)
return NULL;
- list_head_init(&b->contacts);
- b->n_contacts = 0;
+ list_head_init(&b->contacts.list);
+ b->contacts.len = 0;
- list_head_init(&b->alts);
- b->n_alts = 0;
+ list_head_init(&b->alts.list);
+ b->alts.len = 0;
clock_gettime(CLOCK_REALTIME_COARSE, &t);
- b->t_refr = t.tv_sec + KAD_T_REFR;
+ b->t_refr = t.tv_sec + dht.t_refresh;
- for (i = 0; i < (1L << KAD_BETA); ++i)
+ for (i = 0; i < (1L << DHT_BETA); ++i)
b->children[i] = NULL;
b->parent = NULL;
b->depth = 0;
+ b->mask = 0;
return b;
}
@@ -1318,24 +1707,24 @@ static void bucket_destroy(struct bucket * b)
struct list_head * h;
size_t i;
- assert(b);
+ assert(b != NULL);
- for (i = 0; i < (1L << KAD_BETA); ++i)
+ for (i = 0; i < (1L << DHT_BETA); ++i)
if (b->children[i] != NULL)
bucket_destroy(b->children[i]);
- list_for_each_safe(p, h, &b->contacts) {
+ list_for_each_safe(p, h, &b->contacts.list) {
struct contact * c = list_entry(p, struct contact, next);
list_del(&c->next);
contact_destroy(c);
- --b->n_contacts;
+ --b->contacts.len;
}
- list_for_each_safe(p, h, &b->alts) {
+ list_for_each_safe(p, h, &b->alts.list) {
struct contact * c = list_entry(p, struct contact, next);
list_del(&c->next);
contact_destroy(c);
- --b->n_contacts;
+ --b->alts.len;
}
free(b);
@@ -1350,1534 +1739,2316 @@ static bool bucket_has_id(struct bucket * b,
if (b->depth == 0)
return true;
- byte = id[(b->depth * KAD_BETA) / CHAR_BIT];
+ byte = id[(b->depth * DHT_BETA) / CHAR_BIT];
- mask = ((1L << KAD_BETA) - 1) & 0xFF;
+ mask = ((1L << DHT_BETA) - 1) & 0xFF;
- byte >>= (CHAR_BIT - KAD_BETA) -
- (((b->depth - 1) * KAD_BETA) & (CHAR_BIT - 1));
+ byte >>= (CHAR_BIT - DHT_BETA) -
+ (((b->depth - 1) * DHT_BETA) & (CHAR_BIT - 1));
return ((byte & mask) == b->mask);
}
-static int split_bucket(struct bucket * b)
+static int move_contacts(struct bucket * b,
+ struct bucket * c)
{
struct list_head * p;
struct list_head * h;
+ struct contact * d;
+
+ assert(b != NULL);
+ assert(c != NULL);
+
+ list_for_each_safe(p, h, &b->contacts.list) {
+ d = list_entry(p, struct contact, next);
+ if (bucket_has_id(c, d->id)) {
+ list_del(&d->next);
+ --b->contacts.len;
+ list_add_tail(&d->next, &c->contacts.list);
+ ++c->contacts.len;
+ }
+ }
+
+ return 0;
+}
+
+static int split_bucket(struct bucket * b)
+{
uint8_t mask = 0;
size_t i;
- size_t c;
+ size_t b_len;
assert(b);
- assert(b->n_alts == 0);
- assert(b->n_contacts);
+ assert(b->alts.len == 0);
+ assert(b->contacts.len != 0);
assert(b->children[0] == NULL);
- c = b->n_contacts;
+ b_len = b->contacts.len;
- for (i = 0; i < (1L << KAD_BETA); ++i) {
+ for (i = 0; i < (1L << DHT_BETA); ++i) {
b->children[i] = bucket_create();
- if (b->children[i] == NULL) {
- size_t j;
- for (j = 0; j < i; ++j)
- bucket_destroy(b->children[j]);
- return -1;
- }
+ if (b->children[i] == NULL)
+ goto fail_child;
b->children[i]->depth = b->depth + 1;
b->children[i]->mask = mask;
b->children[i]->parent = b;
- list_for_each_safe(p, h, &b->contacts) {
- struct contact * c;
- c = list_entry(p, struct contact, next);
- if (bucket_has_id(b->children[i], c->id)) {
- list_del(&c->next);
- --b->n_contacts;
- list_add(&c->next, &b->children[i]->contacts);
- ++b->children[i]->n_contacts;
- }
- }
+ move_contacts(b, b->children[i]);
mask++;
}
- for (i = 0; i < (1L << KAD_BETA); ++i)
- if (b->children[i]->n_contacts == c)
+ for (i = 0; i < (1L << DHT_BETA); ++i)
+ if (b->children[i]->contacts.len == b_len)
split_bucket(b->children[i]);
return 0;
+ fail_child:
+ while (i-- > 0)
+ bucket_destroy(b->children[i]);
+ return -1;
}
-/* Locked externally to mandate update as (final) part of join transaction. */
-static int dht_update_bucket(struct dht * dht,
- const uint8_t * id,
- uint64_t addr)
+static int dht_kv_update_contacts(const uint8_t * id,
+ uint64_t addr)
{
struct list_head * p;
struct list_head * h;
struct bucket * b;
struct contact * c;
- assert(dht);
+ assert(id != NULL);
+ assert(addr != INVALID_ADDR);
- b = dht_get_bucket(dht, id);
- if (b == NULL)
- return -1;
+ pthread_rwlock_wrlock(&dht.db.lock);
- c = contact_create(id, dht->b, addr);
- if (c == NULL)
- return -1;
+ b = __dht_kv_get_bucket(id);
+ if (b == NULL) {
+ log_err(PEER_FMT " Failed to get bucket.", PEER_VAL(id, addr));
+ goto fail_update;
+ }
- list_for_each_safe(p, h, &b->contacts) {
+ c = contact_create(id, addr);
+ if (c == NULL) {
+ log_err(PEER_FMT " Failed to create contact.",
+ PEER_VAL(id, addr));
+ goto fail_update;
+ }
+
+ list_for_each_safe(p, h, &b->contacts.list) {
struct contact * d = list_entry(p, struct contact, next);
if (d->addr == addr) {
list_del(&d->next);
contact_destroy(d);
- --b->n_contacts;
+ --b->contacts.len;
}
}
- if (b->n_contacts == dht->k) {
- if (bucket_has_id(b, dht->id)) {
- list_add_tail(&c->next, &b->contacts);
- ++b->n_contacts;
+ if (b->contacts.len == dht.k) {
+ if (bucket_has_id(b, dht.id.data)) {
+ list_add_tail(&c->next, &b->contacts.list);
+ ++b->contacts.len;
if (split_bucket(b)) {
list_del(&c->next);
contact_destroy(c);
- --b->n_contacts;
+ --b->contacts.len;
}
- } else if (b->n_alts == dht->k) {
+ } else if (b->alts.len == dht.k) {
struct contact * d;
- d = list_first_entry(&b->alts, struct contact, next);
+ d = list_first_entry(&b->alts.list,
+ struct contact, next);
list_del(&d->next);
contact_destroy(d);
- list_add_tail(&c->next, &b->alts);
+ list_add_tail(&c->next, &b->alts.list);
+ ++b->alts.len;
} else {
- list_add_tail(&c->next, &b->alts);
- ++b->n_alts;
+ list_add_tail(&c->next, &b->alts.list);
+ ++b->alts.len;
}
} else {
- list_add_tail(&c->next, &b->contacts);
- ++b->n_contacts;
+ list_add_tail(&c->next, &b->contacts.list);
+ ++b->contacts.len;
}
+ pthread_rwlock_unlock(&dht.db.lock);
+
return 0;
+ fail_update:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -1;
}
-static int send_msg(struct dht * dht,
- dht_msg_t * msg,
- uint64_t addr)
+static time_t gcd(time_t a,
+ time_t b)
{
-#ifndef __DHT_TEST__
- struct shm_du_buff * sdb;
- size_t len;
-#endif
- int retr = 0;
+ if (a == 0)
+ return b;
- if (msg->code == KAD_RESPONSE)
- retr = KAD_RESP_RETR;
+ return gcd(b % a, a);
+}
- pthread_rwlock_wrlock(&dht->lock);
+static dht_contact_msg_t * dht_kv_src_contact_msg(void)
+{
+ dht_contact_msg_t * src;
- if (dht->id != NULL) {
- msg->has_s_id = true;
- msg->s_id.data = dht->id;
- msg->s_id.len = dht->b;
- }
+ src = malloc(sizeof(*src));
+ if (src == NULL)
+ goto fail_malloc;
- msg->s_addr = dht->addr;
+ dht_contact_msg__init(src);
- if (msg->code < KAD_STORE) {
- msg->cookie = bmp_allocate(dht->cookies);
- if (!bmp_is_id_valid(dht->cookies, msg->cookie)) {
- pthread_rwlock_unlock(&dht->lock);
- goto fail_bmp_alloc;
- }
- }
+ src->id.data = dht_dup_key(dht.id.data);
+ if (src->id.data == NULL)
+ goto fail_id;
- pthread_rwlock_unlock(&dht->lock);
+ src->id.len = dht.id.len;
+ src->addr = dht.addr;
-#ifndef __DHT_TEST__
- len = dht_msg__get_packed_size(msg);
- if (len == 0)
- goto fail_msg;
+ return src;
+ fail_id:
+ dht_contact_msg__free_unpacked(src, NULL);
+ fail_malloc:
+ return NULL;
+}
- while (true) {
- if (ipcp_sdb_reserve(&sdb, len))
- goto fail_msg;
+static dht_msg_t * dht_kv_find_req_msg(const uint8_t * key,
+ enum dht_code code)
+{
+ dht_msg_t * msg;
- dht_msg__pack(msg, shm_du_buff_head(sdb));
+ assert(key != NULL);
- if (dt_write_packet(addr, QOS_CUBE_BE, dht->eid, sdb) == 0)
- break;
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
- ipcp_sdb_release(sdb);
+ dht_msg__init(msg);
+ msg->code = code;
- sleep(1);
+ msg->src = dht_kv_src_contact_msg();
+ if (msg->src == NULL)
+ goto fail_msg;
- if (--retr < 0)
- goto fail_msg;
- }
+ msg->find = malloc(sizeof(*msg->find));
+ if (msg->find == NULL)
+ goto fail_msg;
-#else
- (void) addr;
- (void) retr;
-#endif /* __DHT_TEST__ */
+ dht_find_req_msg__init(msg->find);
- if (msg->code < KAD_STORE && dht_get_state(dht) != DHT_SHUTDOWN)
- kad_req_create(dht, msg, addr);
+ msg->find->key.data = dht_dup_key(key);
+ if (msg->find->key.data == NULL)
+ goto fail_msg;
+
+ msg->find->key.len = dht.id.len;
+ msg->find->cookie = DHT_INVALID;
+
+ return msg;
- return msg->cookie;
-#ifndef __DHT_TEST__
fail_msg:
- pthread_rwlock_wrlock(&dht->lock);
- bmp_release(dht->cookies, msg->cookie);
- pthread_rwlock_unlock(&dht->lock);
-#endif /* !__DHT_TEST__ */
- fail_bmp_alloc:
- return -1;
+ dht_msg__free_unpacked(msg, NULL);
+ fail_malloc:
+ return NULL;
}
-static struct dht_entry * dht_find_entry(struct dht * dht,
- const uint8_t * key)
+static dht_msg_t * dht_kv_find_node_req_msg(const uint8_t * key)
{
- struct list_head * p;
+ return dht_kv_find_req_msg(key, DHT_FIND_NODE_REQ);
+}
- list_for_each(p, &dht->entries) {
- struct dht_entry * e = list_entry(p, struct dht_entry, next);
- if (!memcmp(key, e->key, dht->b))
- return e;
+static dht_msg_t * dht_kv_find_value_req_msg(const uint8_t * key)
+{
+ return dht_kv_find_req_msg(key, DHT_FIND_VALUE_REQ);
+}
+
+static dht_msg_t * dht_kv_find_node_rsp_msg(uint8_t * key,
+ uint64_t cookie,
+ dht_contact_msg_t *** contacts,
+ size_t len)
+{
+ dht_msg_t * msg;
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
+
+ dht_msg__init(msg);
+ msg->code = DHT_FIND_NODE_RSP;
+
+ msg->src = dht_kv_src_contact_msg();
+ if (msg->src == NULL)
+ goto fail_msg;
+
+ msg->node = malloc(sizeof(*msg->node));
+ if (msg->node == NULL)
+ goto fail_msg;
+
+ dht_find_node_rsp_msg__init(msg->node);
+
+ msg->node->key.data = dht_dup_key(key);
+ if (msg->node->key.data == NULL)
+ goto fail_msg;
+
+ msg->node->cookie = cookie;
+ msg->node->key.len = dht.id.len;
+ msg->node->n_contacts = len;
+ if (len != 0) { /* Steal the ptr */
+ msg->node->contacts = *contacts;
+ *contacts = NULL;
}
+ return msg;
+
+ fail_msg:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_malloc:
return NULL;
}
-static int kad_add(struct dht * dht,
- const dht_contact_msg_t * contacts,
- ssize_t n,
- time_t exp)
+static dht_msg_t * dht_kv_find_value_rsp_msg(uint8_t * key,
+ uint64_t cookie,
+ dht_contact_msg_t *** contacts,
+ size_t n_contacts,
+ buffer_t ** vals,
+ size_t n_vals)
{
- struct dht_entry * e;
+ dht_msg_t * msg;
- pthread_rwlock_wrlock(&dht->lock);
+ msg = dht_kv_find_node_rsp_msg(key, cookie, contacts, n_contacts);
+ if (msg == NULL)
+ goto fail_node_rsp;
- while (n-- > 0) {
- if (contacts[n].id.len != dht->b)
- log_warn("Bad key length in contact data.");
+ msg->code = DHT_FIND_VALUE_RSP;
- e = dht_find_entry(dht, contacts[n].id.data);
- if (e != NULL) {
- if (dht_entry_add_addr(e, contacts[n].addr, exp))
- goto fail;
- } else {
- e = dht_entry_create(dht, contacts[n].id.data);
- if (e == NULL)
- goto fail;
+ msg->val = malloc(sizeof(*msg->val));
+ if (msg->val == NULL)
+ goto fail_msg;
- if (dht_entry_add_addr(e, contacts[n].addr, exp)) {
- dht_entry_destroy(e);
- goto fail;
- }
+ dht_find_value_rsp_msg__init(msg->val);
- list_add(&e->next, &dht->entries);
- }
- }
+ msg->val->n_values = n_vals;
+ if (n_vals != 0) /* Steal the ptr */
+ msg->val->values = (binary_data_t *) *vals;
- pthread_rwlock_unlock(&dht->lock);
- return 0;
+ return msg;
- fail:
- pthread_rwlock_unlock(&dht->lock);
- return -ENOMEM;
+ fail_msg:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_node_rsp:
+ return NULL;
}
-static int wait_resp(struct dht * dht,
- dht_msg_t * msg,
- time_t timeo)
+static dht_msg_t * dht_kv_store_msg(const uint8_t * key,
+ const buffer_t val,
+ time_t exp)
{
- struct kad_req * req;
+ dht_msg_t * msg;
- assert(dht);
- assert(msg);
+ assert(key != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- pthread_rwlock_rdlock(&dht->lock);
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
- req = dht_find_request(dht, msg);
- if (req == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- return -EPERM;
- }
+ dht_msg__init(msg);
- pthread_rwlock_unlock(&dht->lock);
+ msg->code = DHT_STORE;
- return kad_req_wait(req, timeo);
+ msg->src = dht_kv_src_contact_msg();
+ if (msg->src == NULL)
+ goto fail_msg;
+
+ msg->store = malloc(sizeof(*msg->store));
+ if (msg->store == NULL)
+ goto fail_msg;
+
+ dht_store_msg__init(msg->store);
+
+ msg->store->key.data = dht_dup_key(key);
+ if (msg->store->key.data == NULL)
+ goto fail_msg;
+
+ msg->store->key.len = dht.id.len;
+ msg->store->val.data = malloc(val.len);
+ if (msg->store->val.data == NULL)
+ goto fail_msg;
+
+ memcpy(msg->store->val.data, val.data, val.len);
+
+ msg->store->val.len = val.len;
+ msg->store->exp = exp;
+
+ return msg;
+
+ fail_msg:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_malloc:
+ return NULL;
}
-static int kad_store(struct dht * dht,
- const uint8_t * key,
- uint64_t addr,
- uint64_t r_addr,
- time_t ttl)
+static ssize_t dht_kv_retrieve(const uint8_t * key,
+ buffer_t ** vals)
{
- dht_msg_t msg = DHT_MSG__INIT;
- dht_contact_msg_t cmsg = DHT_CONTACT_MSG__INIT;
- dht_contact_msg_t * cmsgp[1];
+ struct dht_entry * e;
+ struct list_head * p;
+ size_t n;
+ size_t i;
+
+ assert(key != NULL);
- cmsg.id.data = (uint8_t *) key;
- cmsg.addr = addr;
+ pthread_rwlock_rdlock(&dht.db.lock);
- pthread_rwlock_rdlock(&dht->lock);
+ e = __dht_kv_find_entry(key);
+ if (e == NULL)
+ goto no_vals;
- cmsg.id.len = dht->b;
+ n = MIN(DHT_MAX_VALS, e->vals.len + e->lvals.len);
+ if (n == 0)
+ goto no_vals;
- pthread_rwlock_unlock(&dht->lock);
+ *vals = malloc(n * sizeof(**vals));
+ if (*vals == NULL)
+ goto fail_vals;
- cmsgp[0] = &cmsg;
+ memset(*vals, 0, n * sizeof(**vals));
- msg.code = KAD_STORE;
- msg.has_t_expire = true;
- msg.t_expire = ttl;
- msg.n_contacts = 1;
- msg.contacts = cmsgp;
+ i = 0;
- if (send_msg(dht, &msg, r_addr) < 0)
- return -1;
+ list_for_each(p, &e->vals.list) {
+ struct val_entry * v;
+ if (i == n)
+ break; /* We have enough values */
+ v = list_entry(p, struct val_entry, next);
+ (*vals)[i].data = malloc(v->val.len);
+ if ((*vals)[i].data == NULL)
+ goto fail_val_data;
+
+ (*vals)[i].len = v->val.len;
+ memcpy((*vals)[i++].data, v->val.data, v->val.len);
+ }
+
+ list_for_each(p, &e->lvals.list) {
+ struct val_entry * v;
+ if (i == n)
+ break; /* We have enough values */
+ v = list_entry(p, struct val_entry, next);
+ (*vals)[i].data = malloc(v->val.len);
+ if ((*vals)[i].data == NULL)
+ goto fail_val_data;
+
+ (*vals)[i].len = v->val.len;
+ memcpy((*vals)[i++].data, v->val.data, v->val.len);
+ }
+
+ pthread_rwlock_unlock(&dht.db.lock);
+ return (ssize_t) i;
+
+ fail_val_data:
+ pthread_rwlock_unlock(&dht.db.lock);
+ freebufs(*vals, i);
+ *vals = NULL;
+ return -ENOMEM;
+ fail_vals:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -ENOMEM;
+ no_vals:
+ pthread_rwlock_unlock(&dht.db.lock);
+ *vals = NULL;
return 0;
}
-static ssize_t kad_find(struct dht * dht,
- struct lookup * lu,
- const uint64_t * addrs,
- enum kad_code code)
+static void __cleanup_dht_msg(void * msg)
+{
+ dht_msg__free_unpacked((dht_msg_t *) msg, NULL);
+}
+
+#ifdef DEBUG_PROTO_DHT
+static void dht_kv_debug_msg(dht_msg_t * msg)
+{
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ time_t stamp;
+ size_t i;
+
+ if (msg == NULL)
+ return;
+
+ pthread_cleanup_push(__cleanup_dht_msg, msg);
+
+ switch (msg->code) {
+ case DHT_STORE:
+ log_proto(" key: " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->store->key.data),
+ msg->store->key.len);
+ log_proto(" val: " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->store->val.data),
+ msg->store->val.len);
+ stamp = msg->store->exp;
+ tm = gmtime(&stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+ log_proto(" exp: %s.", tmstr);
+ break;
+ case DHT_FIND_NODE_REQ:
+ /* FALLTHRU */
+ case DHT_FIND_VALUE_REQ:
+ log_proto(" cookie: " HASH_FMT64,
+ HASH_VAL64(&msg->find->cookie));
+ log_proto(" key: " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->find->key.data),
+ msg->find->key.len);
+ break;
+ case DHT_FIND_VALUE_RSP:
+ log_proto(" cookie: " HASH_FMT64,
+ HASH_VAL64(&msg->node->cookie));
+ log_proto(" key: " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->node->key.data),
+ msg->node->key.len);
+ log_proto(" values: [%zd]", msg->val->n_values);
+ for (i = 0; i < msg->val->n_values; i++)
+ log_proto(" " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->val->values[i].data),
+ msg->val->values[i].len);
+ log_proto(" contacts: [%zd]", msg->node->n_contacts);
+ for (i = 0; i < msg->node->n_contacts; i++) {
+ dht_contact_msg_t * c = msg->node->contacts[i];
+ log_proto(" " PEER_FMT,
+ PEER_VAL(c->id.data, c->addr));
+ }
+ break;
+ case DHT_FIND_NODE_RSP:
+ log_proto(" cookie: " HASH_FMT64,
+ HASH_VAL64(&msg->node->cookie));
+ log_proto(" key: " HASH_FMT64 " [%zu bytes]",
+ HASH_VAL64(msg->node->key.data), msg->node->key.len);
+ log_proto(" contacts: [%zd]", msg->node->n_contacts);
+ for (i = 0; i < msg->node->n_contacts; i++) {
+ dht_contact_msg_t * c = msg->node->contacts[i];
+ log_proto(" " PEER_FMT,
+ PEER_VAL(c->id.data, c->addr));
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ pthread_cleanup_pop(false);
+}
+
+static void dht_kv_debug_msg_snd(dht_msg_t * msg,
+ uint8_t * id,
+ uint64_t addr)
{
- dht_msg_t msg = DHT_MSG__INIT;
- ssize_t sent = 0;
+ if (msg == NULL)
+ return;
- assert(dht);
- assert(lu->key);
+ log_proto(TX_HDR_FMT ".", TX_HDR_VAL(msg, id, addr));
- msg.code = code;
+ dht_kv_debug_msg(msg);
+}
- msg.has_key = true;
- msg.key.data = (uint8_t *) lu->key;
- msg.key.len = dht->b;
+static void dht_kv_debug_msg_rcv(dht_msg_t * msg)
+{
+ if (msg == NULL)
+ return;
- while (*addrs != 0) {
- struct cookie_el * c;
- int ret;
+ log_proto(RX_HDR_FMT ".", RX_HDR_VAL(msg));
- if (*addrs == dht->addr) {
- ++addrs;
- continue;
- }
+ dht_kv_debug_msg(msg);
+}
+#endif
- ret = send_msg(dht, &msg, *addrs);
- if (ret < 0)
- break;
+#ifndef __DHT_TEST__
+static int dht_send_msg(dht_msg_t * msg,
+ uint64_t addr)
+{
+ size_t len;
+ struct ssm_pk_buff * spb;
- c = malloc(sizeof(*c));
- if (c == NULL)
- break;
+ if (msg == NULL)
+ return 0;
+
+ assert(addr != INVALID_ADDR && addr != dht.addr);
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ log_warn("%s failed to pack.", DHT_CODE(msg));
+ goto fail_msg;
+ }
+
+ if (ipcp_spb_reserve(&spb, len)) {
+ log_warn("%s failed to get spb.", DHT_CODE(msg));
+ goto fail_msg;
+ }
+
+ dht_msg__pack(msg, ssm_pk_buff_head(spb));
+
+ if (dt_write_packet(addr, QOS_CUBE_BE, dht.eid, spb) < 0) {
+ log_warn("%s write failed", DHT_CODE(msg));
+ goto fail_send;
+ }
+
+ return 0;
+ fail_send:
+ ipcp_spb_release(spb);
+ fail_msg:
+ return -1;
+}
+#else /* funtion for testing */
+static int dht_send_msg(dht_msg_t * msg,
+ uint64_t addr)
+{
+ buffer_t buf;
- c->cookie = (uint32_t) ret;
+ assert(msg != NULL);
+ assert(addr != INVALID_ADDR && addr != dht.addr);
- pthread_mutex_lock(&lu->lock);
+ buf.len = dht_msg__get_packed_size(msg);
+ if (buf.len == 0) {
+ log_warn("%s failed to pack.", DHT_CODE(msg));
+ goto fail_msg;
+ }
- list_add_tail(&c->next, &lu->cookies);
+ buf.data = malloc(buf.len);
+ if (buf.data == NULL) {
+ log_warn("%s failed to malloc buf.", DHT_CODE(msg));
+ goto fail_msg;
+ }
- pthread_mutex_unlock(&lu->lock);
+ dht_msg__pack(msg, buf.data);
- ++sent;
- ++addrs;
+ if (sink_send_msg(&buf, addr) < 0) {
+ log_warn("%s write failed", DHT_CODE(msg));
+ goto fail_send;
}
- return sent;
+ return 0;
+ fail_send:
+ freebuf(buf);
+ fail_msg:
+ return -1;
}
+#endif /* __DHT_TEST__ */
-static void lookup_detach(struct dht * dht,
- struct lookup * lu)
+static void __cleanup_peer_list(void * pl)
{
- pthread_rwlock_wrlock(&dht->lock);
+ struct list_head * p;
+ struct list_head * h;
- list_del(&lu->next);
+ assert(pl != NULL);
- pthread_rwlock_unlock(&dht->lock);
+ list_for_each_safe(p, h, (struct list_head *) pl) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ list_del(&e->next);
+ free(e->id);
+ free(e);
+ }
}
-static struct lookup * kad_lookup(struct dht * dht,
- const uint8_t * id,
- enum kad_code code)
+
+static int dht_kv_send_msgs(dht_msg_t * msg,
+ struct list_head * pl)
{
- uint64_t addrs[KAD_ALPHA + 1];
- enum lookup_state state;
- struct lookup * lu;
+ struct list_head * p;
+ struct list_head * h;
- lu = lookup_create(dht, id);
- if (lu == NULL)
- return NULL;
+ pthread_cleanup_push(__cleanup_dht_msg, msg);
+ pthread_cleanup_push(__cleanup_peer_list, pl);
- lookup_new_addrs(lu, addrs);
+ list_for_each_safe(p, h, pl) {
+ struct peer_entry * e = list_entry(p, struct peer_entry, next);
+ if (IS_REQUEST(msg->code)) {
+ msg->find->cookie = e->cookie;
+ assert(msg->find->cookie != DHT_INVALID);
+ }
+ if (dht_send_msg(msg, e->addr) < 0)
+ continue;
- if (addrs[0] == 0) {
- lookup_detach(dht, lu);
- lookup_destroy(lu);
- return NULL;
+#ifdef DEBUG_PROTO_DHT
+ dht_kv_debug_msg_snd(msg, e->id, e->addr);
+#endif
+ list_del(&e->next);
+ free(e->id);
+ free(e);
}
- if (kad_find(dht, lu, addrs, code) == 0) {
- lookup_detach(dht, lu);
- return lu;
+ pthread_cleanup_pop(false);
+ pthread_cleanup_pop(false);
+
+ return list_is_empty(pl) ? 0 : -1;
+}
+
+static int dht_kv_get_peer_list_for_msg(dht_msg_t * msg,
+ struct list_head * pl)
+{
+ struct list_head cl; /* contact list */
+ uint8_t * key; /* key in the request */
+ size_t max;
+
+ assert(msg != NULL);
+
+ assert(list_is_empty(pl));
+
+ max = msg->code == DHT_STORE ? dht.k : dht.alpha;
+
+ switch (msg->code) {
+ case DHT_FIND_NODE_REQ:
+ /* FALLTHRU */
+ case DHT_FIND_VALUE_REQ:
+ key = msg->find->key.data;
+ break;
+ case DHT_STORE:
+ key = msg->store->key.data;
+ break;
+ default:
+ log_err("Invalid DHT msg code (%d).", msg->code);
+ return -1;
}
- while ((state = lookup_wait(lu)) != LU_COMPLETE) {
- switch (state) {
- case LU_UPDATE:
- lookup_new_addrs(lu, addrs);
- if (addrs[0] == 0)
- break;
+ list_head_init(&cl);
- kad_find(dht, lu, addrs, code);
- break;
- case LU_DESTROY:
- lookup_detach(dht, lu);
- lookup_set_state(lu, LU_NULL);
- return NULL;
- default:
- break;
- }
+ if (dht_kv_contact_list(key, &cl, max) < 0) {
+ log_err(KEY_FMT " Failed to get contact list.", KEY_VAL(key));
+ goto fail_contacts;
}
- assert(state == LU_COMPLETE);
+ if (list_is_empty(&cl)) {
+ log_warn(KEY_FMT " No available contacts.", KEY_VAL(key));
+ goto fail_contacts;
+ }
- lookup_detach(dht, lu);
+ if (dht_kv_create_peer_list(&cl, pl, msg->code) < 0) {
+ log_warn(KEY_FMT " Failed to get peer list.", KEY_VAL(key));
+ goto fail_peers;
+ }
- return lu;
+ contact_list_destroy(&cl);
+ return 0;
+ fail_peers:
+ contact_list_destroy(&cl);
+ fail_contacts:
+ return -1;
}
-static void kad_publish(struct dht * dht,
- const uint8_t * key,
- uint64_t addr,
- time_t exp)
+static int dht_kv_store_remote(const uint8_t * key,
+ const buffer_t val,
+ time_t exp)
{
- struct lookup * lu;
- uint64_t * addrs;
- ssize_t n;
- size_t k;
- time_t t_expire;
+ dht_msg_t * msg;
+ struct timespec now;
+ struct list_head pl;
+ assert(key != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- assert(dht);
- assert(key);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- pthread_rwlock_rdlock(&dht->lock);
+ msg = dht_kv_store_msg(key, val, exp);
+ if (msg == NULL) {
+ log_err(KV_FMT " Failed to create %s.",
+ KV_VAL(key, val), dht_code_str[DHT_STORE]);
+ goto fail_msg;
+ }
- k = dht->k;
- t_expire = dht->t_expire;
+ list_head_init(&pl);
- pthread_rwlock_unlock(&dht->lock);
+ if (dht_kv_get_peer_list_for_msg(msg, &pl) < 0) {
+ log_dbg(KV_FMT " Failed to get peer list.", KV_VAL(key, val));
+ goto fail_peer_list;
+ }
- addrs = malloc(k * sizeof(*addrs));
- if (addrs == NULL)
- return;
+ if (dht_kv_send_msgs(msg, &pl) < 0) {
+ log_warn(KV_FMT " Failed to send any %s msg.",
+ KV_VAL(key, val), DHT_CODE(msg));
+ goto fail_msgs;
+ }
- lu = kad_lookup(dht, key, KAD_FIND_NODE);
- if (lu == NULL) {
- free(addrs);
- return;
+ dht_msg__free_unpacked(msg, NULL);
+
+ return 0;
+ fail_msgs:
+ peer_list_destroy(&pl);
+ fail_peer_list:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ return -1;
+}
+
+/* recursive lookup, start with pl NULL */
+static int dht_kv_query_contacts(const uint8_t * key,
+ struct list_head * pl)
+{
+ struct list_head p;
+
+ dht_msg_t * msg;
+
+ assert(key != NULL);
+
+ msg = dht_kv_find_node_req_msg(key);
+ if (msg == NULL) {
+ log_err(KEY_FMT " Failed to create %s msg.",
+ KEY_VAL(key), dht_code_str[DHT_FIND_NODE_REQ]);
+ goto fail_msg;
}
- n = lookup_contact_addrs(lu, addrs);
+ if (pl == NULL) {
+ list_head_init(&p);
+ pl = &p;
+ }
- while (n-- > 0) {
- if (addrs[n] == dht->addr) {
- dht_contact_msg_t msg = DHT_CONTACT_MSG__INIT;
- msg.id.data = (uint8_t *) key;
- msg.id.len = dht->b;
- msg.addr = addr;
- kad_add(dht, &msg, 1, exp);
- } else {
- if (kad_store(dht, key, addr, addrs[n], t_expire))
- log_warn("Failed to send store message.");
- }
+ if (list_is_empty(pl) && dht_kv_get_peer_list_for_msg(msg, pl) < 0) {
+ log_warn(KEY_FMT " Failed to get peer list.", KEY_VAL(key));
+ goto fail_peer_list;
}
- lookup_destroy(lu);
+ if (dht_kv_update_req(key, pl) < 0) {
+ log_warn(KEY_FMT " Failed to update req.", KEY_VAL(key));
+ goto fail_update;
+ }
- free(addrs);
+ if (dht_kv_send_msgs(msg, pl)) {
+ log_warn(KEY_FMT " Failed to send any %s msg.",
+ KEY_VAL(key), DHT_CODE(msg));
+ goto fail_update;
+ }
+
+ dht_msg__free_unpacked(msg, NULL);
+
+ return 0;
+ fail_update:
+ peer_list_destroy(pl);
+ fail_peer_list:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ return -1;
}
-static int kad_join(struct dht * dht,
- uint64_t addr)
+/* recursive lookup, start with pl NULL */
+static ssize_t dht_kv_query_remote(const uint8_t * key,
+ buffer_t ** vals,
+ struct list_head * pl)
{
- dht_msg_t msg = DHT_MSG__INIT;
+ struct list_head p;
+ dht_msg_t * msg;
- msg.code = KAD_JOIN;
+ assert(key != NULL);
- msg.has_alpha = true;
- msg.has_b = true;
- msg.has_k = true;
- msg.has_t_refresh = true;
- msg.has_t_replicate = true;
- msg.alpha = KAD_ALPHA;
- msg.k = KAD_K;
- msg.t_refresh = KAD_T_REFR;
- msg.t_replicate = KAD_T_REPL;
+ msg = dht_kv_find_value_req_msg(key);
+ if (msg == NULL) {
+ log_err(KEY_FMT " Failed to create value req.", KEY_VAL(key));
+ goto fail_msg;
+ }
- pthread_rwlock_rdlock(&dht->lock);
+ if (pl == NULL) {
+ list_head_init(&p);
+ pl = &p;
+ }
- msg.b = dht->b;
+ if (list_is_empty(pl) && dht_kv_get_peer_list_for_msg(msg, pl) < 0) {
+ log_warn(KEY_FMT " Failed to get peer list.", KEY_VAL(key));
+ goto fail_peer_list;
+ }
- pthread_rwlock_unlock(&dht->lock);
+ if (dht_kv_update_req(key, pl) < 0) {
+ log_err(KEY_FMT " Failed to update request.", KEY_VAL(key));
+ goto fail_update;
+ }
- if (send_msg(dht, &msg, addr) < 0)
- return -1;
+ if (dht_kv_send_msgs(msg, pl)) {
+ log_warn(KEY_FMT " Failed to send %s msg.",
+ KEY_VAL(key), DHT_CODE(msg));
+ goto fail_update;
+ }
- if (wait_resp(dht, &msg, KAD_T_JOIN) < 0)
- return -1;
+ dht_msg__free_unpacked(msg, NULL);
- dht->id = create_id(dht->b);
- if (dht->id == NULL)
- return -1;
+ if (vals == NULL) /* recursive lookup, already waiting */
+ return 0;
- pthread_rwlock_wrlock(&dht->lock);
+ return dht_kv_wait_req(key, vals);
+ fail_update:
+ peer_list_destroy(pl);
+ fail_peer_list:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ return -1;
+}
- dht_update_bucket(dht, dht->id, dht->addr);
+static void __add_dht_kv_entry(struct dht_entry * e)
+{
+ struct list_head * p;
- pthread_rwlock_unlock(&dht->lock);
+ assert(e != NULL);
- return 0;
+ list_for_each(p, &dht.db.kv.list) {
+ struct dht_entry * d = list_entry(p, struct dht_entry, next);
+ if (IS_CLOSER(d->key, e->key))
+ continue;
+ break;
+ }
+
+ list_add_tail(&e->next, p);
+ ++dht.db.kv.len;
}
-static void dht_dead_peer(struct dht * dht,
- uint8_t * key,
- uint64_t addr)
+/* incoming store message */
+static int dht_kv_store(const uint8_t * key,
+ const buffer_t val,
+ time_t exp)
{
- struct list_head * p;
- struct list_head * h;
- struct bucket * b;
+ struct dht_entry * e;
+ bool new = false;
- b = dht_get_bucket(dht, key);
+ assert(key != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- list_for_each_safe(p, h, &b->contacts) {
- struct contact * c = list_entry(p, struct contact, next);
- if (b->n_contacts + b->n_alts <= dht->k) {
- ++c->fails;
- return;
- }
+ pthread_rwlock_wrlock(&dht.db.lock);
- if (c->addr == addr) {
- list_del(&c->next);
- contact_destroy(c);
- --b->n_contacts;
- break;
- }
+ e = __dht_kv_find_entry(key);
+ if (e == NULL) {
+ log_dbg(KV_FMT " Adding entry (store).", KV_VAL(key, val));
+ e = dht_entry_create(key);
+ if (e == NULL)
+ goto fail;
+
+ new = true;
+
+ __add_dht_kv_entry(e);
}
- while (b->n_contacts < dht->k && b->n_alts > 0) {
- struct contact * c;
- c = list_first_entry(&b->alts, struct contact, next);
- list_del(&c->next);
- --b->n_alts;
- list_add(&c->next, &b->contacts);
- ++b->n_contacts;
+ if (dht_entry_update_val(e, val, exp) < 0)
+ goto fail_add;
+
+ pthread_rwlock_unlock(&dht.db.lock);
+
+ return 0;
+ fail_add:
+ if (new) {
+ list_del(&e->next);
+ dht_entry_destroy(e);
+ --dht.db.kv.len;
}
+ fail:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -1;
}
-static int dht_del(struct dht * dht,
- const uint8_t * key,
- uint64_t addr)
+static int dht_kv_publish(const uint8_t * key,
+ const buffer_t val)
{
struct dht_entry * e;
+ struct timespec now;
+ bool new = false;
+
+ assert(key != NULL);
+ assert(val.data != NULL);
+ assert(val.len > 0);
- e = dht_find_entry(dht, key);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ pthread_rwlock_wrlock(&dht.db.lock);
+
+ e = __dht_kv_find_entry(key);
if (e == NULL) {
- return -EPERM;
+ log_dbg(KV_FMT " Adding entry (publish).", KV_VAL(key, val));
+ e = dht_entry_create(key);
+ if (e == NULL)
+ goto fail;
+
+ __add_dht_kv_entry(e);
+ new = true;
}
- dht_entry_del_addr(e, addr);
+ if (dht_entry_update_lval(e, val) < 0)
+ goto fail_add;
+
+ pthread_rwlock_unlock(&dht.db.lock);
+
+ dht_kv_store_remote(key, val, now.tv_sec + dht.t_expire);
return 0;
+ fail_add:
+ if (new) {
+ list_del(&e->next);
+ dht_entry_destroy(e);
+ --dht.db.kv.len;
+ }
+ fail:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -1;
}
-static buffer_t dht_retrieve(struct dht * dht,
- const uint8_t * key)
+static int dht_kv_unpublish(const uint8_t * key,
+ const buffer_t val)
{
struct dht_entry * e;
- struct list_head * p;
- buffer_t buf;
- uint64_t * pos;
- size_t addrs = 0;
+ int rc;
- pthread_rwlock_rdlock(&dht->lock);
+ assert(key != NULL);
- e = dht_find_entry(dht, key);
+ pthread_rwlock_wrlock(&dht.db.lock);
+
+ e = __dht_kv_find_entry(key);
if (e == NULL)
- goto fail;
+ goto no_entry;
- buf.len = MIN(DHT_RETR_ADDR, e->n_vals);
- if (buf.len == 0)
- goto fail;
+ rc = dht_entry_remove_lval(e, val);
- pos = malloc(sizeof(dht->addr) * buf.len);
- if (pos == NULL)
- goto fail;
+ pthread_rwlock_unlock(&dht.db.lock);
- buf.data = (uint8_t *) pos;
+ return rc;
+ no_entry:
+ pthread_rwlock_unlock(&dht.db.lock);
+ return -ENOENT;
- list_for_each(p, &e->vals) {
- struct val * v = list_entry(p, struct val, next);
- *pos++ = v->addr;
- if (++addrs >= buf.len)
- break;
+}
+
+/* message validation */
+static int dht_kv_validate_store_msg(const dht_store_msg_t * store)
+{
+ if (store == NULL) {
+ log_warn("Store in msg is NULL.");
+ return -EINVAL;
}
- pthread_rwlock_unlock(&dht->lock);
+ if (store->key.data == NULL || store->key.len == 0) {
+ log_warn("Invalid key in DHT store msg.");
+ return -EINVAL;
+ }
- return buf;
+ if (store->key.len != dht.id.len) {
+ log_warn("Invalid key length in DHT store msg.");
+ return -EINVAL;
+ }
- fail:
- pthread_rwlock_unlock(&dht->lock);
- buf.len = 0;
- buf.data = NULL;
- return buf;
+ if (store->val.data == NULL || store->val.len == 0) {
+ log_warn("Invalid value in DHT store msg.");
+ return -EINVAL;
+ }
+
+ return 0;
}
-static ssize_t dht_get_contacts(struct dht * dht,
- const uint8_t * key,
- dht_contact_msg_t *** msgs)
+static int validate_find_req_msg(const dht_find_req_msg_t * req)
{
- struct list_head l;
- struct list_head * p;
- struct list_head * h;
- size_t len;
- size_t i = 0;
+ if (req == NULL) {
+ log_warn("Request in msg is NULL.");
+ return -EINVAL;
+ }
- list_head_init(&l);
+ if (req->key.data == NULL || req->key.len == 0) {
+ log_warn("Find request without key.");
+ return -EINVAL;
+ }
- pthread_rwlock_wrlock(&dht->lock);
+ if (req->key.len != dht.id.len) {
+ log_warn("Invalid key length in request msg.");
+ return -EINVAL;
+ }
- len = dht_contact_list(dht, &l, key);
- if (len == 0) {
- pthread_rwlock_unlock(&dht->lock);
- *msgs = NULL;
+ return 0;
+}
+
+static int validate_node_rsp_msg(const dht_find_node_rsp_msg_t * rsp)
+{
+ if (rsp == NULL) {
+ log_warn("Node rsp in msg is NULL.");
+ return -EINVAL;
+ }
+
+ if (rsp->key.data == NULL) {
+ log_warn("Invalid key in DHT response msg.");
+ return -EINVAL;
+ }
+
+ if (rsp->key.len != dht.id.len) {
+ log_warn("Invalid key length in DHT response msg.");
+ return -EINVAL;
+ }
+
+ if (!dht_kv_has_req(rsp->key.data, rsp->cookie)) {
+ log_warn(KEY_FMT " No request " CK_FMT ".",
+ KEY_VAL(rsp->key.data), CK_VAL(rsp->cookie));
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_value_rsp_msg(const dht_find_value_rsp_msg_t * rsp)
+{
+ if (rsp == NULL) {
+ log_warn("Invalid DHT find value response msg.");
+ return -EINVAL;
+ }
+
+ if (rsp->values == NULL && rsp->n_values > 0) {
+ log_dbg("No values in DHT response msg.");
return 0;
}
- *msgs = malloc(len * sizeof(**msgs));
- if (*msgs == NULL) {
- pthread_rwlock_unlock(&dht->lock);
+ if (rsp->n_values == 0 && rsp->values != NULL) {
+ log_dbg("DHT response did not set values NULL.");
return 0;
}
- list_for_each_safe(p, h, &l) {
- struct contact * c = list_entry(p, struct contact, next);
- (*msgs)[i] = malloc(sizeof(***msgs));
- if ((*msgs)[i] == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- while (i > 0)
- free(*msgs[--i]);
- free(*msgs);
- *msgs = NULL;
- return 0;
- }
+ return 0;
+}
- dht_contact_msg__init((*msgs)[i]);
+static int dht_kv_validate_msg(dht_msg_t * msg)
+{
- (*msgs)[i]->id.data = c->id;
- (*msgs)[i]->id.len = dht->b;
- (*msgs)[i++]->addr = c->addr;
- list_del(&c->next);
- free(c);
+ assert(msg != NULL);
+
+ if (msg->src->id.len != dht.id.len) {
+ log_warn("%s Invalid source contact ID.", DHT_CODE(msg));
+ return -EINVAL;
}
- pthread_rwlock_unlock(&dht->lock);
+ if (msg->src->addr == INVALID_ADDR) {
+ log_warn("%s Invalid source address.", DHT_CODE(msg));
+ return -EINVAL;
+ }
- return i;
+ switch (msg->code) {
+ case DHT_FIND_VALUE_REQ:
+ /* FALLTHRU */
+ case DHT_FIND_NODE_REQ:
+ if (validate_find_req_msg(msg->find) < 0)
+ return -EINVAL;
+ break;
+ case DHT_FIND_VALUE_RSP:
+ if (validate_value_rsp_msg(msg->val) < 0)
+ return -EINVAL;
+ /* FALLTHRU */
+ case DHT_FIND_NODE_RSP:
+ if (validate_node_rsp_msg(msg->node) < 0)
+ return -EINVAL;
+ break;
+ case DHT_STORE:
+ if (dht_kv_validate_store_msg(msg->store) < 0)
+ return -EINVAL;
+ break;
+ default:
+ log_warn("Invalid DHT msg code (%d).", msg->code);
+ return -ENOENT;
+ }
+
+ return 0;
}
-static time_t gcd(time_t a,
- time_t b)
+static void do_dht_kv_store(const dht_store_msg_t * store)
{
- if (a == 0)
- return b;
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ buffer_t val;
+ uint8_t * key;
+ time_t exp;
- return gcd(b % a, a);
+ (void) key; /* Only in logs, not used with DISABLE_TEST_LOGGING */
+
+ assert(store != NULL);
+
+ val.data = store->val.data;
+ val.len = store->val.len;
+ key = store->key.data;
+ exp = store->exp;
+
+ if (dht_kv_store(store->key.data, val, store->exp) < 0) {
+ log_err(KV_FMT " Failed to store.", KV_VAL(key, val));
+ return;
+ }
+
+ tm = gmtime(&exp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+ log_dbg(KV_FMT " Stored value until %s.", KV_VAL(key, val), tmstr);
}
-static void * work(void * o)
+static dht_msg_t * do_dht_kv_find_node_req(const dht_find_req_msg_t * req)
{
- struct dht * dht;
- struct timespec now;
- struct list_head * p;
- struct list_head * h;
- struct list_head reflist;
- time_t intv;
- struct lookup * lu;
+ dht_contact_msg_t ** contacts;
+ dht_msg_t * rsp;
+ uint8_t * key;
+ uint64_t cookie;
+ ssize_t len;
- dht = (struct dht *) o;
+ assert(req != NULL);
- pthread_rwlock_rdlock(&dht->lock);
+ key = req->key.data;
+ cookie = req->cookie;
- intv = gcd(dht->t_expire, dht->t_repub);
- intv = gcd(intv, gcd(KAD_T_REPL, KAD_T_REFR)) / 2;
+ len = dht_kv_get_contacts(key, &contacts);
+ if (len < 0) {
+ log_warn(KEY_FMT " Failed to get contacts.", KEY_VAL(key));
+ goto fail_contacts;
+ }
- pthread_rwlock_unlock(&dht->lock);
+ rsp = dht_kv_find_node_rsp_msg(key, cookie, &contacts, len);
+ if (rsp == NULL) {
+ log_err(KEY_FMT " Failed to create %s.", KEY_VAL(key),
+ dht_code_str[DHT_FIND_NODE_RSP]);
+ goto fail_msg;
+ }
- list_head_init(&reflist);
+ assert(rsp->code == DHT_FIND_NODE_RSP);
- while (true) {
- clock_gettime(CLOCK_REALTIME_COARSE, &now);
-
- pthread_rwlock_wrlock(&dht->lock);
-
- /* Republish registered hashes. */
- list_for_each(p, &dht->refs) {
- struct ref_entry * e;
- uint8_t * key;
- uint64_t addr;
- time_t t_expire;
- e = list_entry(p, struct ref_entry, next);
- if (now.tv_sec > e->t_rep) {
- key = dht_dup_key(e->key, dht->b);
- if (key == NULL)
- continue;
- addr = dht->addr;
- t_expire = dht->t_expire;
- e->t_rep = now.tv_sec + dht->t_repub;
-
- pthread_rwlock_unlock(&dht->lock);
- kad_publish(dht, key, addr, t_expire);
- pthread_rwlock_wrlock(&dht->lock);
- free(key);
- }
- }
+ log_info(KEY_FMT " Responding with %zd contacts", KEY_VAL(key), len);
- /* Remove stale entries and republish if necessary. */
- list_for_each_safe(p, h, &dht->entries) {
- struct list_head * p1;
- struct list_head * h1;
- struct dht_entry * e;
- uint8_t * key;
- time_t t_expire;
- e = list_entry (p, struct dht_entry, next);
- list_for_each_safe(p1, h1, &e->vals) {
- struct val * v;
- uint64_t addr;
- v = list_entry(p1, struct val, next);
- if (now.tv_sec > v->t_exp) {
- list_del(&v->next);
- val_destroy(v);
- continue;
- }
-
- if (now.tv_sec > v->t_rep) {
- key = dht_dup_key(e->key, dht->b);
- addr = v->addr;
- t_expire = dht->t_expire = now.tv_sec;
- v->t_rep = now.tv_sec + dht->t_replic;
- pthread_rwlock_unlock(&dht->lock);
- kad_publish(dht, key, addr, t_expire);
- pthread_rwlock_wrlock(&dht->lock);
- free(key);
- }
- }
- }
+ return rsp;
+ fail_msg:
+ while (len-- > 0)
+ dht_contact_msg__free_unpacked(contacts[len], NULL);
+ free(contacts);
+ fail_contacts:
+ return NULL;
+}
- /* Check the requests list for unresponsive nodes. */
- list_for_each_safe(p, h, &dht->requests) {
- struct kad_req * r;
- r = list_entry(p, struct kad_req, next);
- if (now.tv_sec > r->t_exp) {
- list_del(&r->next);
- bmp_release(dht->cookies, r->cookie);
- dht_dead_peer(dht, r->key, r->addr);
- kad_req_destroy(r);
- }
- }
+static void dht_kv_process_node_rsp(dht_contact_msg_t ** contacts,
+ size_t len,
+ struct list_head * pl,
+ enum dht_code code)
+{
+ struct timespec now;
+ size_t i;
- /* Refresh unaccessed buckets. */
- bucket_refresh(dht, dht->buckets, now.tv_sec, &reflist);
+ assert(contacts != NULL);
+ assert(len > 0);
+ assert(pl != NULL);
+ assert(list_is_empty(pl));
- pthread_rwlock_unlock(&dht->lock);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- list_for_each_safe(p, h, &reflist) {
- struct contact * c;
- c = list_entry(p, struct contact, next);
- lu = kad_lookup(dht, c->id, KAD_FIND_NODE);
- if (lu != NULL)
- lookup_destroy(lu);
- list_del(&c->next);
- contact_destroy(c);
+ for (i = 0; i < len; i++) {
+ dht_contact_msg_t * c = contacts[i];
+ struct peer_entry * e;
+ if (c->addr == dht.addr)
+ continue;
+
+ if (dht_kv_update_contacts(c->id.data, c->addr) < 0)
+ log_warn(PEER_FMT " Failed to update contacts.",
+ PEER_VAL(c->id.data, c->addr));
+
+ e = malloc(sizeof(*e));
+ if (e == NULL) {
+ log_err(PEER_FMT " Failed to malloc entry.",
+ PEER_VAL(c->id.data, c->addr));
+ continue;
}
- sleep(intv);
- }
+ e->id = dht_dup_key(c->id.data);
+ if (e->id == NULL) {
+ log_warn(PEER_FMT " Failed to duplicate id.",
+ PEER_VAL(c->id.data, c->addr));
+ free(e);
+ continue;
+ }
- return (void *) 0;
+ e->cookie = generate_cookie();
+ e->code = code;
+ e->addr = c->addr;
+ e->t_sent = now.tv_sec;
+
+ list_add_tail(&e->next, pl);
+ }
}
-static int kad_handle_join_resp(struct dht * dht,
- struct kad_req * req,
- dht_msg_t * msg)
+static dht_msg_t * do_dht_kv_find_value_req(const dht_find_req_msg_t * req)
{
- assert(dht);
- assert(req);
- assert(msg);
+ dht_contact_msg_t ** contacts;
+ ssize_t n_contacts;
+ buffer_t * vals;
+ ssize_t n_vals;
+ dht_msg_t * rsp;
+ uint8_t * key;
+ uint64_t cookie;
- /* We might send version numbers later to warn of updates if needed. */
- if (!(msg->has_alpha && msg->has_b && msg->has_k && msg->has_t_expire &&
- msg->has_t_refresh && msg->has_t_replicate)) {
- log_warn("Join refused by remote.");
- return -1;
+ assert(req != NULL);
+
+ key = req->key.data;
+ cookie = req->cookie;
+
+ n_contacts = dht_kv_get_contacts(key, &contacts);
+ if (n_contacts < 0) {
+ log_warn(KEY_FMT " Failed to get contacts.", KEY_VAL(key));
+ goto fail_contacts;
}
- if (msg->b < sizeof(uint64_t)) {
- log_err("Hash sizes less than 8 bytes unsupported.");
- return -1;
+ assert(n_contacts > 0 || contacts == NULL);
+
+ n_vals = dht_kv_retrieve(key, &vals);
+ if (n_vals < 0) {
+ log_dbg(KEY_FMT " Failed to get values.", KEY_VAL(key));
+ goto fail_vals;
}
- pthread_rwlock_wrlock(&dht->lock);
+ if (n_vals == 0)
+ log_dbg(KEY_FMT " No values found.", KEY_VAL(key));
- dht->buckets = bucket_create();
- if (dht->buckets == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- return -1;
+ rsp = dht_kv_find_value_rsp_msg(key, cookie, &contacts, n_contacts,
+ &vals, n_vals);
+ if (rsp == NULL) {
+ log_err(KEY_FMT " Failed to create %s msg.",
+ KEY_VAL(key), dht_code_str[DHT_FIND_VALUE_RSP]);
+ goto fail_msg;
}
- /* Likely corrupt packet. The member will refuse, we might here too. */
- if (msg->alpha != KAD_ALPHA || msg->k != KAD_K)
- log_warn("Different kademlia parameters detected.");
+ log_info(KEY_FMT " Responding with %zd contacts, %zd values.",
+ KEY_VAL(req->key.data), n_contacts, n_vals);
- if (msg->t_replicate != KAD_T_REPL)
- log_warn("Different kademlia replication time detected.");
+ return rsp;
- if (msg->t_refresh != KAD_T_REFR)
- log_warn("Different kademlia refresh time detected.");
+ fail_msg:
+ freebufs(vals, n_vals);
+ fail_vals:
+ while (n_contacts-- > 0)
+ dht_contact_msg__free_unpacked(contacts[n_contacts], NULL);
+ free(contacts);
+ fail_contacts:
+ return NULL;
+}
- dht->k = msg->k;
- dht->b = msg->b;
- dht->t_expire = msg->t_expire;
- dht->t_repub = MAX(1, dht->t_expire - 10);
+static void do_dht_kv_find_node_rsp(const dht_find_node_rsp_msg_t * rsp)
+{
+ struct list_head pl;
- if (pthread_create(&dht->worker, NULL, work, dht)) {
- bucket_destroy(dht->buckets);
- pthread_rwlock_unlock(&dht->lock);
- return -1;
- }
+ assert(rsp != NULL);
- kad_req_respond(req);
+ list_head_init(&pl);
- dht_update_bucket(dht, msg->s_id.data, msg->s_addr);
+ dht_kv_process_node_rsp(rsp->contacts, rsp->n_contacts, &pl,
+ DHT_FIND_NODE_REQ);
- pthread_rwlock_unlock(&dht->lock);
+ if (list_is_empty(&pl))
+ goto no_contacts;
- log_dbg("Enrollment of DHT completed.");
+ if (dht_kv_update_req(rsp->key.data, &pl) < 0) {
+ log_err(KEY_FMT " Failed to update request.",
+ KEY_VAL(rsp->key.data));
+ goto fail_update;
+ }
- return 0;
+ dht_kv_query_contacts(rsp->key.data, &pl);
+
+ return;
+
+ fail_update:
+ peer_list_destroy(&pl);
+ no_contacts:
+ return;
}
-static int kad_handle_find_resp(struct dht * dht,
- struct kad_req * req,
- dht_msg_t * msg)
+static void do_dht_kv_find_value_rsp(const dht_find_node_rsp_msg_t * node,
+ const dht_find_value_rsp_msg_t * val)
{
- struct lookup * lu;
+ struct list_head pl;
+ uint8_t * key;
- assert(dht);
- assert(req);
- assert(msg);
+ assert(node != NULL);
+ assert(val != NULL);
- pthread_rwlock_rdlock(&dht->lock);
+ list_head_init(&pl);
- lu = dht_find_lookup(dht, req->cookie);
- if (lu == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- return -1;
+ key = node->key.data;
+
+ dht_kv_process_node_rsp(node->contacts, node->n_contacts, &pl,
+ DHT_FIND_VALUE_REQ);
+
+ if (val->n_values > 0) {
+ log_dbg(KEY_FMT " %zd new values received.",
+ KEY_VAL(key), val->n_values);
+ if (dht_kv_respond_req(key, val->values, val->n_values) < 0)
+ log_warn(KEY_FMT " Failed to respond to request.",
+ KEY_VAL(key));
+ peer_list_destroy(&pl);
+ return; /* done! */
}
- lookup_update(dht, lu, msg);
+ if (list_is_empty(&pl))
+ goto no_contacts;
- pthread_rwlock_unlock(&dht->lock);
+ if (dht_kv_update_req(key, &pl) < 0) {
+ log_err(KEY_FMT " Failed to update request.", KEY_VAL(key));
+ goto fail_update;
+ }
- return 0;
+ dht_kv_query_remote(key, NULL, &pl);
+
+ return;
+ fail_update:
+ peer_list_destroy(&pl);
+ no_contacts:
+ return;
}
-static void kad_handle_response(struct dht * dht,
- dht_msg_t * msg)
+static dht_msg_t * dht_wait_for_dht_msg(void)
{
- struct kad_req * req;
+ dht_msg_t * msg;
+ struct cmd * cmd;
- assert(dht);
- assert(msg);
+ pthread_mutex_lock(&dht.cmds.mtx);
- pthread_rwlock_wrlock(&dht->lock);
+ pthread_cleanup_push(__cleanup_mutex_unlock, &dht.cmds.mtx);
- req = dht_find_request(dht, msg);
- if (req == NULL) {
- pthread_rwlock_unlock(&dht->lock);
+ while (list_is_empty(&dht.cmds.list))
+ pthread_cond_wait(&dht.cmds.cond, &dht.cmds.mtx);
+
+ cmd = list_last_entry(&dht.cmds.list, struct cmd, next);
+ list_del(&cmd->next);
+
+ pthread_cleanup_pop(true);
+
+ msg = dht_msg__unpack(NULL, cmd->cbuf.len, cmd->cbuf.data);
+ if (msg == NULL)
+ log_warn("Failed to unpack DHT msg.");
+
+ freebuf(cmd->cbuf);
+ free(cmd);
+
+ return msg;
+}
+
+static void do_dht_msg(dht_msg_t * msg)
+{
+ dht_msg_t * rsp = NULL;
+ uint8_t * id;
+ uint64_t addr;
+
+#ifdef DEBUG_PROTO_DHT
+ dht_kv_debug_msg_rcv(msg);
+#endif
+ if (dht_kv_validate_msg(msg) == -EINVAL) {
+ log_warn("%s Validation failed.", DHT_CODE(msg));
+ dht_msg__free_unpacked(msg, NULL);
return;
}
- bmp_release(dht->cookies, req->cookie);
- list_del(&req->next);
+ id = msg->src->id.data;
+ addr = msg->src->addr;
- pthread_rwlock_unlock(&dht->lock);
+ if (dht_kv_update_contacts(id, addr) < 0)
+ log_warn(PEER_FMT " Failed to update contact from msg src.",
+ PEER_VAL(id, addr));
- switch(req->code) {
- case KAD_JOIN:
- if (kad_handle_join_resp(dht, req, msg))
- log_err("Enrollment of DHT failed.");
+ pthread_cleanup_push(__cleanup_dht_msg, msg);
+
+ switch(msg->code) {
+ case DHT_FIND_VALUE_REQ:
+ rsp = do_dht_kv_find_value_req(msg->find);
break;
- case KAD_FIND_VALUE:
- case KAD_FIND_NODE:
- if (dht_get_state(dht) != DHT_RUNNING)
- break;
- kad_handle_find_resp(dht, req, msg);
+ case DHT_FIND_NODE_REQ:
+ rsp = do_dht_kv_find_node_req(msg->find);
break;
- default:
+ case DHT_STORE:
+ do_dht_kv_store(msg->store);
break;
+ case DHT_FIND_NODE_RSP:
+ do_dht_kv_find_node_rsp(msg->node);
+ break;
+ case DHT_FIND_VALUE_RSP:
+ do_dht_kv_find_value_rsp(msg->node, msg->val);
+ break;
+ default:
+ assert(false); /* already validated */
}
- kad_req_destroy(req);
+ pthread_cleanup_pop(true);
+
+ if (rsp == NULL)
+ return;
+
+ pthread_cleanup_push(__cleanup_dht_msg, rsp);
+
+ dht_send_msg(rsp, addr);
+
+ pthread_cleanup_pop(true); /* free rsp */
}
-int dht_bootstrap(void * dir)
+static void * dht_handle_packet(void * o)
{
- struct dht * dht;
+ (void) o;
- dht = (struct dht *) dir;
+ while (true) {
+ dht_msg_t * msg;
- assert(dht);
+ msg = dht_wait_for_dht_msg();
+ if (msg == NULL)
+ continue;
+
+ tpm_begin_work(dht.tpm);
+
+ do_dht_msg(msg);
- pthread_rwlock_wrlock(&dht->lock);
+ tpm_end_work(dht.tpm);
+ }
+ return (void *) 0;
+}
#ifndef __DHT_TEST__
- dht->b = hash_len(ipcpi.dir_hash_algo);
-#else
- dht->b = DHT_TEST_KEY_LEN;
-#endif
+static void dht_post_packet(void * comp,
+ struct ssm_pk_buff * spb)
+{
+ struct cmd * cmd;
- dht->id = create_id(dht->b);
- if (dht->id == NULL)
- goto fail_id;
+ (void) comp;
- dht->buckets = bucket_create();
- if (dht->buckets == NULL)
- goto fail_buckets;
+ cmd = malloc(sizeof(*cmd));
+ if (cmd == NULL) {
+ log_err("Command malloc failed.");
+ goto fail_cmd;
+ }
- dht->buckets->depth = 0;
- dht->buckets->mask = 0;
+ cmd->cbuf.data = malloc(ssm_pk_buff_len(spb));
+ if (cmd->cbuf.data == NULL) {
+ log_err("Command buffer malloc failed.");
+ goto fail_buf;
+ }
- dht->t_expire = 86400; /* 1 day */
- dht->t_repub = dht->t_expire - 10;
- dht->k = KAD_K;
+ cmd->cbuf.len = ssm_pk_buff_len(spb);
- if (pthread_create(&dht->worker, NULL, work, dht))
- goto fail_pthread_create;
+ memcpy(cmd->cbuf.data, ssm_pk_buff_head(spb), cmd->cbuf.len);
- dht->state = DHT_RUNNING;
+ ipcp_spb_release(spb);
- dht_update_bucket(dht, dht->id, dht->addr);
+ pthread_mutex_lock(&dht.cmds.mtx);
- pthread_rwlock_unlock(&dht->lock);
+ list_add(&cmd->next, &dht.cmds.list);
- return 0;
+ pthread_cond_signal(&dht.cmds.cond);
- fail_pthread_create:
- bucket_destroy(dht->buckets);
- dht->buckets = NULL;
- fail_buckets:
- free(dht->id);
- dht->id = NULL;
- fail_id:
- pthread_rwlock_unlock(&dht->lock);
- return -1;
+ pthread_mutex_unlock(&dht.cmds.mtx);
+
+ return;
+
+ fail_buf:
+ free(cmd);
+ fail_cmd:
+ ipcp_spb_release(spb);
+ return;
}
+#endif
-static struct ref_entry * ref_entry_get(struct dht * dht,
- const uint8_t * key)
+int dht_reg(const uint8_t * key)
{
- struct list_head * p;
+ buffer_t val;
- list_for_each(p, &dht->refs) {
- struct ref_entry * r = list_entry(p, struct ref_entry, next);
- if (!memcmp(key, r->key, dht-> b) )
- return r;
+ if (addr_to_buf(dht.addr, &val) < 0) {
+ log_err("Failed to convert address to buffer.");
+ goto fail_a2b;
}
- return NULL;
+ if (dht_kv_publish(key, val)) {
+ log_err(KV_FMT " Failed to publish.", KV_VAL(key, val));
+ goto fail_publish;
+ }
+
+ freebuf(val);
+
+ return 0;
+ fail_publish:
+ freebuf(val);
+ fail_a2b:
+ return -1;
}
-int dht_reg(void * dir,
- const uint8_t * key)
+int dht_unreg(const uint8_t * key)
{
- struct dht * dht;
- struct ref_entry * e;
- uint64_t addr;
- time_t t_expire;
+ buffer_t val;
- dht = (struct dht *) dir;
+ if (addr_to_buf(dht.addr, &val) < 0) {
+ log_err("Failed to convert address to buffer.");
+ goto fail_a2b;
+ }
- assert(dht);
- assert(key);
- assert(dht->addr != 0);
+ if (dht_kv_unpublish(key, val)) {
+ log_err(KV_FMT " Failed to unpublish.", KV_VAL(key, val));
+ goto fail_unpublish;
+ }
- if (dht_wait_running(dht))
- return -1;
+ freebuf(val);
- pthread_rwlock_wrlock(&dht->lock);
+ return 0;
+ fail_unpublish:
+ freebuf(val);
+ fail_a2b:
+ return -ENOMEM;
+}
- if (ref_entry_get(dht, key) != NULL) {
- log_dbg("Name already registered.");
- pthread_rwlock_unlock(&dht->lock);
- return 0;
- }
+uint64_t dht_query(const uint8_t * key)
+{
+ buffer_t * vals;
+ ssize_t n;
+ uint64_t addr;
- e = ref_entry_create(dht, key);
- if (e == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- return -ENOMEM;
+ n = dht_kv_retrieve(key, &vals);
+ if (n < 0) {
+ log_err(KEY_FMT " Failed to query db.", KEY_VAL(key));
+ goto fail_vals;
}
- list_add(&e->next, &dht->refs);
+ if (n == 0) {
+ assert(vals == NULL);
- t_expire = dht->t_expire;
- addr = dht->addr;
+ log_dbg(KEY_FMT " No local values.", KEY_VAL(key));
+ n = dht_kv_query_remote(key, &vals, NULL);
+ if (n < 0) {
+ log_warn(KEY_FMT " Failed to query DHT.", KEY_VAL(key));
+ goto fail_vals;
+ }
+ if (n == 0) {
+ log_dbg(KEY_FMT " No values.", KEY_VAL(key));
+ goto no_vals;
+ }
+ }
- pthread_rwlock_unlock(&dht->lock);
+ if (buf_to_addr(vals[0], &addr) < 0) {
+ log_err(VAL_FMT " Failed addr conversion.", VAL_VAL(vals[0]));
+ goto fail_b2a;
+ }
- kad_publish(dht, key, addr, t_expire);
+ if (n > 1 && addr == INVALID_ADDR && buf_to_addr(vals[1], &addr) < 0) {
+ log_err(VAL_FMT " Failed addr conversion.", VAL_VAL(vals[1]));
+ goto fail_b2a;
+ }
- return 0;
+ freebufs(vals, n);
+
+ return addr;
+ fail_b2a:
+ freebufs(vals, n);
+ return INVALID_ADDR;
+ no_vals:
+ free(vals);
+ fail_vals:
+ return INVALID_ADDR;
}
-int dht_unreg(void * dir,
- const uint8_t * key)
+static int emergency_peer(struct list_head * pl)
{
- struct dht * dht;
- struct list_head * p;
- struct list_head * h;
-
- dht = (struct dht *) dir;
+ struct peer_entry * e;
+ struct timespec now;
- assert(dht);
- assert(key);
+ assert(pl != NULL);
+ assert(list_is_empty(pl));
- if (dht_get_state(dht) != DHT_RUNNING)
+ if (dht.peer == INVALID_ADDR)
return -1;
- pthread_rwlock_wrlock(&dht->lock);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- list_for_each_safe(p, h, &dht->refs) {
- struct ref_entry * r = list_entry(p, struct ref_entry, next);
- if (!memcmp(key, r->key, dht-> b) ) {
- list_del(&r->next);
- ref_entry_destroy(r);
- }
+ e = malloc(sizeof(*e));
+ if (e == NULL) {
+ log_err("Failed to malloc emergency peer entry.");
+ goto fail_malloc;
}
- dht_del(dht, key, dht->addr);
+ e->id = dht_dup_key(dht.id.data);
+ if (e->id == NULL) {
+ log_err("Failed to duplicate DHT ID for emergency peer.");
+ goto fail_id;
+ }
- pthread_rwlock_unlock(&dht->lock);
+ e->addr = dht.peer;
+ e->cookie = dht.magic;
+ e->code = DHT_FIND_NODE_REQ;
+ e->t_sent = now.tv_sec;
+
+ list_add_tail(&e->next, pl);
return 0;
+ fail_id:
+ free(e);
+ fail_malloc:
+ return -ENOMEM;
}
-uint64_t dht_query(void * dir,
- const uint8_t * key)
+static int dht_kv_seed_bootstrap_peer(void)
{
- struct dht * dht;
- struct dht_entry * e;
- struct lookup * lu;
- uint64_t addrs[KAD_K];
- size_t n;
+ struct list_head pl;
- dht = (struct dht *) dir;
+ list_head_init(&pl);
- assert(dht);
+ if (dht.peer == INVALID_ADDR) {
+ log_dbg("No-one to contact.");
+ return 0;
+ }
- addrs[0] = 0;
+ if (emergency_peer(&pl) < 0) {
+ log_err("Could not create emergency peer.");
+ goto fail_peer;
+ }
- if (dht_wait_running(dht))
- return 0;
+ log_dbg("Pinging emergency peer " ADDR_FMT32 ".",
+ ADDR_VAL32(&dht.peer));
- pthread_rwlock_rdlock(&dht->lock);
+ if (dht_kv_query_contacts(dht.id.data, &pl) < 0) {
+ log_warn("Failed to bootstrap peer.");
+ goto fail_query;
+ }
- e = dht_find_entry(dht, key);
- if (e != NULL)
- addrs[0] = dht_entry_get_addr(dht, e);
+ peer_list_destroy(&pl);
- pthread_rwlock_unlock(&dht->lock);
+ return 0;
+ fail_query:
+ peer_list_destroy(&pl);
+ fail_peer:
+ return -EAGAIN;
+}
- if (addrs[0] != 0)
- return addrs[0];
+static void dht_kv_check_contacts(void)
+{
+ struct list_head cl;
+ struct list_head pl;
- lu = kad_lookup(dht, key, KAD_FIND_VALUE);
- if (lu == NULL)
- return 0;
+ list_head_init(&cl);
- n = lookup_get_addrs(lu, addrs);
- if (n == 0) {
- lookup_destroy(lu);
- return 0;
+ dht_kv_contact_list(dht.id.data, &cl, dht.k);
+
+ if (!list_is_empty(&cl))
+ goto success;
+
+ contact_list_destroy(&cl);
+
+ list_head_init(&pl);
+
+ if (dht.peer == INVALID_ADDR) {
+ log_dbg("No-one to contact.");
+ return;
}
- lookup_destroy(lu);
+ if (emergency_peer(&pl) < 0) {
+ log_err("Could not create emergency peer.");
+ goto fail_peer;
+ }
- /* Current behaviour is anycast and return the first peer address. */
- if (addrs[0] != dht->addr)
- return addrs[0];
+ log_dbg("No contacts found, using emergency peer " ADDR_FMT32 ".",
+ ADDR_VAL32(&dht.peer));
- if (n > 1)
- return addrs[1];
+ dht_kv_query_contacts(dht.id.data, &pl);
- return 0;
+ peer_list_destroy(&pl);
+
+ return;
+ success:
+ contact_list_destroy(&cl);
+ return;
+ fail_peer:
+ return;
}
-static void * dht_handle_packet(void * o)
+static void dht_kv_remove_expired_reqs(void)
{
- struct dht * dht = (struct dht *) o;
+ struct list_head * p;
+ struct list_head * h;
+ struct timespec now;
- assert(dht);
+ clock_gettime(PTHREAD_COND_CLOCK, &now);
- while (true) {
- dht_msg_t * msg;
- dht_contact_msg_t ** cmsgs;
- dht_msg_t resp_msg = DHT_MSG__INIT;
- uint64_t addr;
- buffer_t buf;
- size_t i;
- size_t b;
- size_t t_expire;
- struct cmd * cmd;
+ pthread_mutex_lock(&dht.reqs.mtx);
- pthread_mutex_lock(&dht->mtx);
+ list_for_each_safe(p, h, &dht.reqs.list) {
+ struct dht_req * e;
+ e = list_entry(p, struct dht_req, next);
+ if (IS_EXPIRED(e, &now)) {
+ log_dbg(KEY_FMT " Removing expired request.",
+ KEY_VAL(e->key));
+ list_del(&e->next);
+ dht_req_destroy(e);
+ --dht.reqs.len;
+ }
+ }
- pthread_cleanup_push(__cleanup_mutex_unlock, &dht->mtx);
+ pthread_mutex_unlock(&dht.reqs.mtx);
+}
- while (list_is_empty(&dht->cmds))
- pthread_cond_wait(&dht->cond, &dht->mtx);
+static void value_list_destroy(struct list_head * vl)
+{
+ struct list_head * p;
+ struct list_head * h;
- cmd = list_last_entry(&dht->cmds, struct cmd, next);
- list_del(&cmd->next);
+ assert(vl != NULL);
- pthread_cleanup_pop(true);
+ list_for_each_safe(p, h, vl) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ list_del(&v->next);
+ val_entry_destroy(v);
+ }
+}
- i = shm_du_buff_len(cmd->sdb);
+#define MUST_REPLICATE(v, now) ((now)->tv_sec > (v)->t_repl + dht.t_repl)
+#define MUST_REPUBLISH(v, now) /* Close to expiry deadline */ \
+ (((v)->t_exp - (now)->tv_sec) < (DHT_N_REPUB * dht.t_repl))
+static void dht_entry_get_repl_lists(const struct dht_entry * e,
+ struct list_head * repl,
+ struct list_head * rebl,
+ struct timespec * now)
+{
+ struct list_head * p;
+ struct val_entry * n;
- msg = dht_msg__unpack(NULL, i, shm_du_buff_head(cmd->sdb));
-#ifndef __DHT_TEST__
- ipcp_sdb_release(cmd->sdb);
-#endif
- free(cmd);
+ list_for_each(p, &e->vals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ if (MUST_REPLICATE(v, now) && !IS_EXPIRED(v, now)) {
+ n = val_entry_create(v->val, v->t_exp);
+ if (n == NULL)
+ continue;
- if (msg == NULL) {
- log_err("Failed to unpack message.");
- continue;
+ list_add_tail(&n->next, repl);
}
+ }
- if (msg->code != KAD_RESPONSE && dht_wait_running(dht)) {
- dht_msg__free_unpacked(msg, NULL);
- log_dbg("Got a request message when not running.");
- continue;
+ list_for_each(p, &e->lvals.list) {
+ struct val_entry * v = list_entry(p, struct val_entry, next);
+ if (MUST_REPLICATE(v, now) && MUST_REPUBLISH(v, now)) {
+ /* Add expire time here, to allow creating val_entry */
+ n = val_entry_create(v->val, now->tv_sec + dht.t_expire);
+ if (n == NULL)
+ continue;
+
+ list_add_tail(&n->next, rebl);
}
+ }
+}
- pthread_rwlock_rdlock(&dht->lock);
+static int dht_kv_next_values(uint8_t * key,
+ struct list_head * repl,
+ struct list_head * rebl)
+{
+ struct timespec now;
+ struct list_head * p;
+ struct list_head * h;
+ struct dht_entry * e = NULL;
- b = dht->b;
- t_expire = dht->t_expire;
+ assert(key != NULL);
+ assert(repl != NULL);
+ assert(rebl != NULL);
- pthread_rwlock_unlock(&dht->lock);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- if (msg->has_key && msg->key.len != b) {
- dht_msg__free_unpacked(msg, NULL);
- log_warn("Bad key in message.");
- continue;
- }
+ assert(list_is_empty(repl));
+ assert(list_is_empty(rebl));
- if (msg->has_s_id && !msg->has_b && msg->s_id.len != b) {
- dht_msg__free_unpacked(msg, NULL);
- log_warn("Bad source ID in message of type %d.",
- msg->code);
- continue;
- }
+ pthread_rwlock_rdlock(&dht.db.lock);
- tpm_dec(dht->tpm);
+ if (dht.db.kv.len == 0)
+ goto no_entries;
- addr = msg->s_addr;
+ list_for_each_safe(p, h, &dht.db.kv.list) {
+ e = list_entry(p, struct dht_entry, next);
+ if (IS_CLOSER(e->key, key))
+ continue; /* Already processed */
+ }
- resp_msg.code = KAD_RESPONSE;
- resp_msg.cookie = msg->cookie;
+ if (e != NULL) {
+ memcpy(key, e->key, dht.id.len);
+ dht_entry_get_repl_lists(e, repl, rebl, &now);
+ }
+ no_entries:
+ pthread_rwlock_unlock(&dht.db.lock);
- switch(msg->code) {
- case KAD_JOIN:
- /* Refuse enrollee on check fails. */
- if (msg->alpha != KAD_ALPHA || msg->k != KAD_K) {
- log_warn("Parameter mismatch. "
- "DHT enrolment refused.");
- break;
- }
+ return list_is_empty(repl) && list_is_empty(rebl) ? -ENOENT : 0;
+}
- if (msg->t_replicate != KAD_T_REPL) {
- log_warn("Replication time mismatch. "
- "DHT enrolment refused.");
+static void dht_kv_replicate_value(const uint8_t * key,
+ struct val_entry * v,
+ const struct timespec * now)
+{
+ assert(MUST_REPLICATE(v, now));
- break;
- }
+ (void) now;
- if (msg->t_refresh != KAD_T_REFR) {
- log_warn("Refresh time mismatch. "
- "DHT enrolment refused.");
- break;
- }
+ if (dht_kv_store_remote(key, v->val, v->t_exp) == 0) {
+ log_dbg(KV_FMT " Replicated.", KV_VAL(key, v->val));
+ return;
+ }
- resp_msg.has_alpha = true;
- resp_msg.has_b = true;
- resp_msg.has_k = true;
- resp_msg.has_t_expire = true;
- resp_msg.has_t_refresh = true;
- resp_msg.has_t_replicate = true;
- resp_msg.alpha = KAD_ALPHA;
- resp_msg.b = b;
- resp_msg.k = KAD_K;
- resp_msg.t_expire = t_expire;
- resp_msg.t_refresh = KAD_T_REFR;
- resp_msg.t_replicate = KAD_T_REPL;
- break;
- case KAD_FIND_VALUE:
- buf = dht_retrieve(dht, msg->key.data);
- if (buf.len != 0) {
- resp_msg.n_addrs = buf.len;
- resp_msg.addrs = (uint64_t *) buf.data;
- break;
- }
- /* FALLTHRU */
- case KAD_FIND_NODE:
- /* Return k closest contacts. */
- resp_msg.n_contacts =
- dht_get_contacts(dht, msg->key.data, &cmsgs);
- resp_msg.contacts = cmsgs;
- break;
- case KAD_STORE:
- if (msg->n_contacts < 1) {
- log_warn("No contacts in store message.");
- break;
- }
+ log_dbg(KV_FMT " Replication failed.", KV_VAL(key, v->val));
- if (!msg->has_t_expire) {
- log_warn("No expiry time in store message.");
- break;
- }
+ list_del(&v->next);
+ val_entry_destroy(v);
+}
- kad_add(dht, *msg->contacts, msg->n_contacts,
- msg->t_expire);
- break;
- case KAD_RESPONSE:
- kad_handle_response(dht, msg);
- break;
- default:
- assert(false);
- break;
- }
+static void dht_kv_republish_value(const uint8_t * key,
+ struct val_entry * v,
+ const struct timespec * now)
+{
+ assert(MUST_REPLICATE(v, now));
- if (msg->code != KAD_JOIN) {
- pthread_rwlock_wrlock(&dht->lock);
- if (dht_get_state(dht) == DHT_JOINING &&
- dht->buckets == NULL) {
- pthread_rwlock_unlock(&dht->lock);
- goto finish;
- }
+ if (MUST_REPUBLISH(v, now))
+ assert(v->t_exp >= now->tv_sec + dht.t_expire);
- if (dht_update_bucket(dht, msg->s_id.data, addr))
- log_warn("Failed to update bucket.");
- pthread_rwlock_unlock(&dht->lock);
- }
+ if (dht_kv_store_remote(key, v->val, v->t_exp) == 0) {
+ log_dbg(KV_FMT " Republished.", KV_VAL(key, v->val));
+ return;
+ }
- if (msg->code < KAD_STORE && send_msg(dht, &resp_msg, addr) < 0)
- log_warn("Failed to send response.");
+ if (MUST_REPUBLISH(v, now))
+ log_warn(KV_FMT " Republish failed.", KV_VAL(key, v->val));
+ else
+ log_dbg(KV_FMT " Replication failed.", KV_VAL(key, v->val));
- finish:
- dht_msg__free_unpacked(msg, NULL);
+ list_del(&v->next);
+ val_entry_destroy(v);
+}
- if (resp_msg.n_addrs > 0)
- free(resp_msg.addrs);
+static void dht_kv_update_replication_times(const uint8_t * key,
+ struct list_head * repl,
+ struct list_head * rebl,
+ const struct timespec * now)
+{
+ struct dht_entry * e;
+ struct list_head * p;
+ struct list_head * h;
+ struct val_entry * v;
+
+ assert(key != NULL);
+ assert(repl != NULL);
+ assert(rebl != NULL);
+ assert(now != NULL);
+
+ pthread_rwlock_wrlock(&dht.db.lock);
- if (resp_msg.n_contacts == 0) {
- tpm_inc(dht->tpm);
+ e = __dht_kv_find_entry(key);
+ if (e == NULL) {
+ pthread_rwlock_unlock(&dht.db.lock);
+ return;
+ }
+
+ list_for_each_safe(p, h, repl) {
+ struct val_entry * x;
+ v = list_entry(p, struct val_entry, next);
+ x = dht_entry_get_val(e, v->val);
+ if (x == NULL) {
+ log_err(KV_FMT " Not in vals.", KV_VAL(key, v->val));
continue;
}
- for (i = 0; i < resp_msg.n_contacts; ++i)
- dht_contact_msg__free_unpacked(resp_msg.contacts[i],
- NULL);
- free(resp_msg.contacts);
+ x->t_repl = now->tv_sec;
- tpm_inc(dht->tpm);
+ list_del(&v->next);
+ val_entry_destroy(v);
}
- return (void *) 0;
+ list_for_each_safe(p, h, rebl) {
+ struct val_entry * x;
+ v = list_entry(p, struct val_entry, next);
+ x = dht_entry_get_lval(e, v->val);
+ if (x == NULL) {
+ log_err(KV_FMT " Not in lvals.", KV_VAL(key, v->val));
+ continue;
+ }
+
+ x->t_repl = now->tv_sec;
+ if (v->t_exp > x->t_exp) {
+ x->t_exp = v->t_exp; /* update expiration time */
+ }
+
+ list_del(&v->next);
+ val_entry_destroy(v);
+ }
+
+ pthread_rwlock_unlock(&dht.db.lock);
}
-static void dht_post_packet(void * comp,
- struct shm_du_buff * sdb)
+static void __cleanup_value_list(void * o)
{
- struct cmd * cmd;
- struct dht * dht = (struct dht *) comp;
+ return value_list_destroy((struct list_head *) o);
+}
- if (dht_get_state(dht) == DHT_SHUTDOWN) {
-#ifndef __DHT_TEST__
- ipcp_sdb_release(sdb);
-#endif
- return;
+static void dht_kv_replicate_values(const uint8_t * key,
+ struct list_head * repl,
+ struct list_head * rebl)
+{
+ struct timespec now;
+ struct list_head * p;
+ struct list_head * h;
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ pthread_cleanup_push(__cleanup_value_list, repl);
+ pthread_cleanup_push(__cleanup_value_list, rebl);
+
+ list_for_each_safe(p, h, repl) {
+ struct val_entry * v;
+ v = list_entry(p, struct val_entry, next);
+ dht_kv_replicate_value(key, v, &now);
}
- cmd = malloc(sizeof(*cmd));
- if (cmd == NULL) {
- log_err("Command failed. Out of memory.");
+ list_for_each_safe(p, h, rebl) {
+ struct val_entry * v;
+ v = list_entry(p, struct val_entry, next);
+ dht_kv_republish_value(key, v, &now);
+ }
+
+ pthread_cleanup_pop(false);
+ pthread_cleanup_pop(false);
+
+ /* removes non-replicated items from the list */
+ dht_kv_update_replication_times(key, repl, rebl, &now);
+
+ if (list_is_empty(repl) && list_is_empty(rebl))
+ return;
+
+ log_warn(KEY_FMT " Failed to update replication times.", KEY_VAL(key));
+}
+
+static void dht_kv_replicate(void)
+{
+ struct list_head repl; /* list of values to replicate */
+ struct list_head rebl; /* list of local values to republish */
+ uint8_t * key;
+
+ key = dht_dup_key(dht.id.data); /* dist == 0 */
+ if (key == NULL) {
+ log_err("Replicate: Failed to duplicate DHT ID.");
return;
}
- cmd->sdb = sdb;
+ list_head_init(&repl);
+ list_head_init(&rebl);
- pthread_mutex_lock(&dht->mtx);
+ pthread_cleanup_push(free, key);
- list_add(&cmd->next, &dht->cmds);
+ while (dht_kv_next_values(key, &repl, &rebl) == 0) {
+ dht_kv_replicate_values(key, &repl, &rebl);
+ if (!list_is_empty(&repl)) {
+ log_warn(KEY_FMT " Replication items left.",
+ KEY_VAL(key));
+ value_list_destroy(&repl);
+ }
- pthread_cond_signal(&dht->cond);
+ if (!list_is_empty(&rebl)) {
+ log_warn(KEY_FMT " Republish items left.",
+ KEY_VAL(key));
+ value_list_destroy(&rebl);
+ }
+ }
- pthread_mutex_unlock(&dht->mtx);
+ pthread_cleanup_pop(true);
}
-void dht_destroy(void * dir)
+static void dht_kv_refresh_contacts(void)
{
- struct dht * dht;
struct list_head * p;
struct list_head * h;
+ struct list_head rl; /* refresh list */
+ struct timespec now;
- dht = (struct dht *) dir;
- if (dht == NULL)
- return;
+ list_head_init(&rl);
-#ifndef __DHT_TEST__
- tpm_stop(dht->tpm);
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- tpm_destroy(dht->tpm);
-#endif
- if (dht_get_state(dht) == DHT_RUNNING) {
- dht_set_state(dht, DHT_SHUTDOWN);
- pthread_cancel(dht->worker);
- pthread_join(dht->worker, NULL);
- }
+ pthread_rwlock_rdlock(&dht.db.lock);
- pthread_rwlock_wrlock(&dht->lock);
+ __dht_kv_bucket_refresh_list(dht.db.contacts.root, now.tv_sec, &rl);
- list_for_each_safe(p, h, &dht->cmds) {
- struct cmd * c = list_entry(p, struct cmd, next);
+ pthread_rwlock_unlock(&dht.db.lock);
+
+ list_for_each_safe(p, h, &rl) {
+ struct contact * c;
+ c = list_entry(p, struct contact, next);
+ log_dbg(PEER_FMT " Refreshing contact.",
+ PEER_VAL(c->id, c->addr));
+ dht_kv_query_contacts(c->id, NULL);
list_del(&c->next);
-#ifndef __DHT_TEST__
- ipcp_sdb_release(c->sdb);
-#endif
- free(c);
+ contact_destroy(c);
}
- list_for_each_safe(p, h, &dht->entries) {
- struct dht_entry * e = list_entry(p, struct dht_entry, next);
- list_del(&e->next);
- dht_entry_destroy(e);
- }
+ assert(list_is_empty(&rl));
+}
- list_for_each_safe(p, h, &dht->requests) {
- struct kad_req * r = list_entry(p, struct kad_req, next);
- list_del(&r->next);
- kad_req_destroy(r);
- }
+static void (*tasks[])(void) = {
+ dht_kv_check_contacts,
+ dht_kv_remove_expired_entries,
+ dht_kv_remove_expired_reqs,
+ dht_kv_replicate,
+ dht_kv_refresh_contacts,
+ NULL
+};
- list_for_each_safe(p, h, &dht->refs) {
- struct ref_entry * e = list_entry(p, struct ref_entry, next);
- list_del(&e->next);
- ref_entry_destroy(e);
+static void * work(void * o)
+{
+ struct timespec now = TIMESPEC_INIT_MS(1);
+ time_t intv;
+ size_t n; /* number of tasks */
+
+ n = sizeof(tasks) / sizeof(tasks[0]) - 1; /* last is NULL */
+
+ (void) o;
+
+ while (dht_kv_seed_bootstrap_peer() == -EAGAIN) {
+ ts_add(&now, &now, &now); /* exponential backoff */
+ if (now.tv_sec > 1) /* cap at 1 second */
+ now.tv_sec = 1;
+ nanosleep(&now, NULL);
}
- list_for_each_safe(p, h, &dht->lookups) {
- struct lookup * l = list_entry(p, struct lookup, next);
- list_del(&l->next);
- lookup_destroy(l);
+ intv = gcd(dht.t_expire, (dht.t_expire - DHT_N_REPUB * dht.t_repl));
+ intv = gcd(intv, gcd(dht.t_repl, dht.t_refresh)) / 2;
+ intv = MAX(1, intv / n);
+
+ log_dbg("DHT worker starting %ld seconds interval.", intv * n);
+
+ while (true) {
+ int i = 0;
+ while (tasks[i] != NULL) {
+ tasks[i++]();
+ sleep(intv);
+ }
}
- pthread_rwlock_unlock(&dht->lock);
+ return (void *) 0;
+}
- if (dht->buckets != NULL)
- bucket_destroy(dht->buckets);
+int dht_start(void)
+{
+ dht.state = DHT_RUNNING;
- bmp_destroy(dht->cookies);
+ if (tpm_start(dht.tpm))
+ goto fail_tpm_start;
- pthread_mutex_destroy(&dht->mtx);
+#ifndef __DHT_TEST__
+ if (pthread_create(&dht.worker, NULL, work, NULL)) {
+ log_err("Failed to create DHT worker thread.");
+ goto fail_worker;
+ }
- pthread_rwlock_destroy(&dht->lock);
+ dht.eid = dt_reg_comp(&dht, &dht_post_packet, DHT);
+ if ((int) dht.eid < 0) {
+ log_err("Failed to register DHT component.");
+ goto fail_reg;
+ }
+#else
+ (void) work;
+#endif
+ return 0;
+#ifndef __DHT_TEST__
+ fail_reg:
+ pthread_cancel(dht.worker);
+ pthread_join(dht.worker, NULL);
+ fail_worker:
+ tpm_stop(dht.tpm);
+#endif
+ fail_tpm_start:
+ dht.state = DHT_INIT;
+ return -1;
+}
+
+void dht_stop(void)
+{
+ assert(dht.state == DHT_RUNNING);
- free(dht->id);
+#ifndef __DHT_TEST__
+ dt_unreg_comp(dht.eid);
+
+ pthread_cancel(dht.worker);
+ pthread_join(dht.worker, NULL);
+#endif
+ tpm_stop(dht.tpm);
- free(dht);
+ dht.state = DHT_INIT;
}
-static void * join_thr(void * o)
+int dht_init(struct dir_dht_config * conf)
{
- struct join_info * info = (struct join_info *) o;
- struct lookup * lu;
- size_t retr = 0;
+ struct timespec now;
+ pthread_condattr_t cattr;
- assert(info);
+ assert(conf != NULL);
- while (kad_join(info->dht, info->addr)) {
- if (dht_get_state(info->dht) == DHT_SHUTDOWN) {
- log_dbg("DHT enrollment aborted.");
- goto finish;
- }
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
- if (retr++ == KAD_JOIN_RETR) {
- dht_set_state(info->dht, DHT_INIT);
- log_warn("DHT enrollment attempt failed.");
- goto finish;
- }
+#ifndef __DHT_TEST__
+ dht.id.len = ipcp_dir_hash_len();
+ dht.addr = addr_auth_address();
+#else
+ dht.id.len = DHT_TEST_KEY_LEN;
+ dht.addr = DHT_TEST_ADDR;
+#endif
+ dht.t0 = now.tv_sec;
+ dht.alpha = conf->params.alpha;
+ dht.k = conf->params.k;
+ dht.t_expire = conf->params.t_expire;
+ dht.t_refresh = conf->params.t_refresh;
+ dht.t_repl = conf->params.t_replicate;
+ dht.peer = conf->peer;
+
+ dht.magic = generate_cookie();
+
+ /* Send my address on enrollment */
+ conf->peer = dht.addr;
+
+ dht.id.data = generate_id();
+ if (dht.id.data == NULL) {
+ log_err("Failed to create DHT ID.");
+ goto fail_id;
+ }
+
+ list_head_init(&dht.cmds.list);
- sleep(KAD_JOIN_INTV);
+ if (pthread_mutex_init(&dht.cmds.mtx, NULL)) {
+ log_err("Failed to initialize command mutex.");
+ goto fail_cmds_mutex;
}
- dht_set_state(info->dht, DHT_RUNNING);
+ if (pthread_cond_init(&dht.cmds.cond, NULL)) {
+ log_err("Failed to initialize command condvar.");
+ goto fail_cmds_cond;
+ }
- lu = kad_lookup(info->dht, info->dht->id, KAD_FIND_NODE);
- if (lu != NULL)
- lookup_destroy(lu);
+ list_head_init(&dht.reqs.list);
+ dht.reqs.len = 0;
- finish:
- free(info);
+ if (pthread_mutex_init(&dht.reqs.mtx, NULL)) {
+ log_err("Failed to initialize request mutex.");
+ goto fail_reqs_mutex;
+ }
- return (void *) 0;
-}
+ if (pthread_condattr_init(&cattr)) {
+ log_err("Failed to initialize request condvar attributes.");
+ goto fail_cattr;
+ }
+#ifndef __APPLE__
+ if (pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK)) {
+ log_err("Failed to set request condvar clock.");
+ goto fail_cattr;
+ }
+#endif
+ if (pthread_cond_init(&dht.reqs.cond, &cattr)) {
+ log_err("Failed to initialize request condvar.");
+ goto fail_reqs_cond;
+ }
-static void handle_event(void * self,
- int event,
- const void * o)
-{
- struct dht * dht = (struct dht *) self;
+ list_head_init(&dht.db.kv.list);
+ dht.db.kv.len = 0;
+ dht.db.kv.vals = 0;
+ dht.db.kv.lvals = 0;
- if (event == NOTIFY_DT_CONN_ADD) {
- pthread_t thr;
- struct join_info * inf;
- struct conn * c = (struct conn *) o;
- struct timespec slack = TIMESPEC_INIT_MS(DHT_ENROLL_SLACK);
+ if (pthread_rwlock_init(&dht.db.lock, NULL)) {
+ log_err("Failed to initialize store rwlock.");
+ goto fail_rwlock;
+ }
- /* Give the pff some time to update for the new link. */
- nanosleep(&slack, NULL);
+ dht.db.contacts.root = bucket_create();
+ if (dht.db.contacts.root == NULL) {
+ log_err("Failed to create DHT buckets.");
+ goto fail_buckets;
+ }
- switch(dht_get_state(dht)) {
- case DHT_INIT:
- inf = malloc(sizeof(*inf));
- if (inf == NULL)
- break;
+ if (rib_reg(DHT, &r_ops) < 0) {
+ log_err("Failed to register DHT RIB operations.");
+ goto fail_rib_reg;
+ }
- inf->dht = dht;
- inf->addr = c->conn_info.addr;
-
- if (dht_set_state(dht, DHT_JOINING) == 0 ||
- dht_wait_running(dht)) {
- if (pthread_create(&thr, NULL, join_thr, inf)) {
- dht_set_state(dht, DHT_INIT);
- free(inf);
- return;
- }
- pthread_detach(thr);
- } else {
- free(inf);
- }
- break;
- case DHT_RUNNING:
- /*
- * FIXME: this lookup for effiency reasons
- * causes a SEGV when stressed with rapid
- * enrollments.
- * lu = kad_lookup(dht, dht->id, KAD_FIND_NODE);
- * if (lu != NULL)
- * lookup_destroy(lu);
- */
- break;
- default:
- break;
- }
+ dht.tpm = tpm_create(2, 1, dht_handle_packet, NULL);
+ if (dht.tpm == NULL) {
+ log_err("Failed to create TPM for DHT.");
+ goto fail_tpm_create;
}
+
+ if (dht_kv_update_contacts(dht.id.data, dht.addr) < 0)
+ log_warn("Failed to update contacts with DHT ID.");
+
+ pthread_condattr_destroy(&cattr);
+#ifndef __DHT_TEST__
+ log_info("DHT initialized.");
+ log_dbg(" ID: " HASH_FMT64 " [%zu bytes].",
+ HASH_VAL64(dht.id.data), dht.id.len);
+ log_dbg(" address: " ADDR_FMT32 ".", ADDR_VAL32(&dht.addr));
+ log_dbg(" peer: " ADDR_FMT32 ".", ADDR_VAL32(&dht.peer));
+ log_dbg(" magic cookie: " HASH_FMT64 ".", HASH_VAL64(&dht.magic));
+ log_info(" parameters: alpha=%u, k=%zu, t_expire=%ld, "
+ "t_refresh=%ld, t_replicate=%ld.",
+ dht.alpha, dht.k, dht.t_expire, dht.t_refresh, dht.t_repl);
+#endif
+ dht.state = DHT_INIT;
+
+ return 0;
+
+ fail_tpm_create:
+ rib_unreg(DHT);
+ fail_rib_reg:
+ bucket_destroy(dht.db.contacts.root);
+ fail_buckets:
+ pthread_rwlock_destroy(&dht.db.lock);
+ fail_rwlock:
+ pthread_cond_destroy(&dht.reqs.cond);
+ fail_reqs_cond:
+ pthread_condattr_destroy(&cattr);
+ fail_cattr:
+ pthread_mutex_destroy(&dht.reqs.mtx);
+ fail_reqs_mutex:
+ pthread_cond_destroy(&dht.cmds.cond);
+ fail_cmds_cond:
+ pthread_mutex_destroy(&dht.cmds.mtx);
+ fail_cmds_mutex:
+ freebuf(dht.id);
+ fail_id:
+ return -1;
}
-void * dht_create(void)
+void dht_fini(void)
{
- struct dht * dht;
+ struct list_head * p;
+ struct list_head * h;
- dht = malloc(sizeof(*dht));
- if (dht == NULL)
- goto fail_malloc;
+ rib_unreg(DHT);
- dht->buckets = NULL;
+ tpm_destroy(dht.tpm);
- list_head_init(&dht->entries);
- list_head_init(&dht->requests);
- list_head_init(&dht->refs);
- list_head_init(&dht->lookups);
- list_head_init(&dht->cmds);
+ pthread_mutex_lock(&dht.cmds.mtx);
- if (pthread_rwlock_init(&dht->lock, NULL))
- goto fail_rwlock;
+ list_for_each_safe(p, h, &dht.cmds.list) {
+ struct cmd * c = list_entry(p, struct cmd, next);
+ list_del(&c->next);
+ freebuf(c->cbuf);
+ free(c);
+ }
- if (pthread_mutex_init(&dht->mtx, NULL))
- goto fail_mutex;
+ pthread_mutex_unlock(&dht.cmds.mtx);
- if (pthread_cond_init(&dht->cond, NULL))
- goto fail_cond;
+ pthread_cond_destroy(&dht.cmds.cond);
+ pthread_mutex_destroy(&dht.cmds.mtx);
- dht->cookies = bmp_create(DHT_MAX_REQS, 1);
- if (dht->cookies == NULL)
- goto fail_bmp;
+ pthread_mutex_lock(&dht.reqs.mtx);
- dht->b = 0;
- dht->id = NULL;
-#ifndef __DHT_TEST__
- dht->addr = ipcpi.dt_addr;
- dht->tpm = tpm_create(2, 1, dht_handle_packet, dht);
- if (dht->tpm == NULL)
- goto fail_tpm_create;
+ list_for_each_safe(p, h, &dht.reqs.list) {
+ struct dht_req * r = list_entry(p, struct dht_req, next);
+ list_del(&r->next);
+ dht_req_destroy(r);
+ dht.reqs.len--;
+ }
- if (tpm_start(dht->tpm))
- goto fail_tpm_start;
+ pthread_mutex_unlock(&dht.reqs.mtx);
- dht->eid = dt_reg_comp(dht, &dht_post_packet, DHT);
- if ((int) dht->eid < 0)
- goto fail_tpm_start;
+ pthread_cond_destroy(&dht.reqs.cond);
+ pthread_mutex_destroy(&dht.reqs.mtx);
- if (notifier_reg(handle_event, dht))
- goto fail_notifier_reg;
-#else
- (void) handle_event;
- (void) dht_handle_packet;
- (void) dht_post_packet;
-#endif
- dht->state = DHT_INIT;
+ pthread_rwlock_wrlock(&dht.db.lock);
- return (void *) dht;
-#ifndef __DHT_TEST__
- fail_notifier_reg:
- tpm_stop(dht->tpm);
- fail_tpm_start:
- tpm_destroy(dht->tpm);
- fail_tpm_create:
- bmp_destroy(dht->cookies);
-#endif
- fail_bmp:
- pthread_cond_destroy(&dht->cond);
- fail_cond:
- pthread_mutex_destroy(&dht->mtx);
- fail_mutex:
- pthread_rwlock_destroy(&dht->lock);
- fail_rwlock:
- free(dht);
- fail_malloc:
- return NULL;
+ list_for_each_safe(p, h, &dht.db.kv.list) {
+ struct dht_entry * e = list_entry(p, struct dht_entry, next);
+ list_del(&e->next);
+ dht_entry_destroy(e);
+ dht.db.kv.len--;
+ }
+
+ if (dht.db.contacts.root != NULL)
+ bucket_destroy(dht.db.contacts.root);
+
+ pthread_rwlock_unlock(&dht.db.lock);
+
+ pthread_rwlock_destroy(&dht.db.lock);
+
+ assert(dht.db.kv.len == 0);
+ assert(dht.db.kv.vals == 0);
+ assert(dht.db.kv.lvals == 0);
+ assert(dht.reqs.len == 0);
+
+ freebuf(dht.id);
}
diff --git a/src/ipcpd/unicast/dir/dht.h b/src/ipcpd/unicast/dir/dht.h
index 311c6b23..852a5130 100644
--- a/src/ipcpd/unicast/dir/dht.h
+++ b/src/ipcpd/unicast/dir/dht.h
@@ -30,22 +30,19 @@
#include <stdint.h>
#include <sys/types.h>
-void * dht_create(void);
+int dht_init(struct dir_dht_config * conf);
-void dht_destroy(void * dir);
+void dht_fini(void);
-int dht_bootstrap(void * dir);
+int dht_start(void);
-int dht_reg(void * dir,
- const uint8_t * key);
+void dht_stop(void);
-int dht_unreg(void * dir,
- const uint8_t * key);
+int dht_reg(const uint8_t * key);
-uint64_t dht_query(void * dir,
- const uint8_t * key);
+int dht_unreg(const uint8_t * key);
-int dht_wait_running(void * dir);
+uint64_t dht_query(const uint8_t * key);
extern struct dir_ops dht_dir_ops;
diff --git a/src/ipcpd/unicast/dir/dht.proto b/src/ipcpd/unicast/dir/dht.proto
index 4c5b06db..ea74805f 100644
--- a/src/ipcpd/unicast/dir/dht.proto
+++ b/src/ipcpd/unicast/dir/dht.proto
@@ -27,19 +27,32 @@ message dht_contact_msg {
required uint64 addr = 2;
}
+message dht_find_req_msg {
+ required uint64 cookie = 1;
+ required bytes key = 2;
+}
+
+message dht_find_node_rsp_msg {
+ required uint64 cookie = 1;
+ required bytes key = 2;
+ repeated dht_contact_msg contacts = 3;
+}
+
+message dht_find_value_rsp_msg {
+ repeated bytes values = 1;
+}
+
+message dht_store_msg {
+ required bytes key = 1;
+ required bytes val = 2;
+ required uint32 exp = 3;
+}
+
message dht_msg {
- required uint32 code = 1;
- required uint32 cookie = 2;
- required uint64 s_addr = 3;
- optional bytes s_id = 4;
- optional bytes key = 5;
- repeated uint64 addrs = 6;
- repeated dht_contact_msg contacts = 7;
- // enrolment parameters
- optional uint32 alpha = 8;
- optional uint32 b = 9;
- optional uint32 k = 10;
- optional uint32 t_expire = 11;
- optional uint32 t_refresh = 12;
- optional uint32 t_replicate = 13;
+ required uint32 code = 1;
+ required dht_contact_msg src = 2;
+ optional dht_store_msg store = 3;
+ optional dht_find_req_msg find = 4;
+ optional dht_find_node_rsp_msg node = 5;
+ optional dht_find_value_rsp_msg val = 6;
}
diff --git a/src/ipcpd/unicast/dir/ops.h b/src/ipcpd/unicast/dir/ops.h
index 6ff61ce6..8c6e5eb5 100644
--- a/src/ipcpd/unicast/dir/ops.h
+++ b/src/ipcpd/unicast/dir/ops.h
@@ -23,24 +23,20 @@
#ifndef OUROBOROS_IPCPD_UNICAST_DIR_OPS_H
#define OUROBOROS_IPCPD_UNICAST_DIR_OPS_H
-
struct dir_ops {
- void * (* create)(void);
+ int (* init)(void * config);
- void (* destroy)(void * dir);
+ void (* fini)(void);
- int (* bootstrap)(void * dir);
+ int (* start)(void);
- int (* reg)(void * dir,
- const uint8_t * hash);
+ void (* stop)(void);
- int (* unreg)(void * dir,
- const uint8_t * hash);
+ int (* reg)(const uint8_t * hash);
- uint64_t (* query)(void * dir,
- const uint8_t * hash);
+ int (* unreg)(const uint8_t * hash);
- int (* wait_running)(void * dir);
+ uint64_t (* query)(const uint8_t * hash);
};
#endif /* OUROBOROS_IPCPD_UNICAST_DIR_OPS_H */
diff --git a/src/ipcpd/unicast/dir/tests/CMakeLists.txt b/src/ipcpd/unicast/dir/tests/CMakeLists.txt
index c850e41d..eded823f 100644
--- a/src/ipcpd/unicast/dir/tests/CMakeLists.txt
+++ b/src/ipcpd/unicast/dir/tests/CMakeLists.txt
@@ -3,34 +3,36 @@ get_filename_component(CURRENT_SOURCE_PARENT_DIR
get_filename_component(CURRENT_BINARY_PARENT_DIR
${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+compute_test_prefix()
+
create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
# Add new tests here
dht_test.c
- )
+)
-protobuf_generate_c(DHT_PROTO_SRCS KAD_PROTO_HDRS ../dht.proto)
-add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests}
+protobuf_generate_c(DHT_PROTO_SRCS KAD_PROTO_HDRS ${CURRENT_SOURCE_PARENT_DIR}/dht.proto)
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests}
${DHT_PROTO_SRCS})
-target_link_libraries(${PARENT_DIR}_test ouroboros-common)
-add_dependencies(check ${PARENT_DIR}_test)
+target_include_directories(${PARENT_DIR}_test PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CURRENT_SOURCE_PARENT_DIR}
+ ${CURRENT_BINARY_PARENT_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src/ipcpd
+ ${CMAKE_BINARY_DIR}/src/ipcpd
+ ${CMAKE_SOURCE_DIR}/src/ipcpd/unicast
+ ${CMAKE_BINARY_DIR}/src/ipcpd/unicast
+)
+
+disable_test_logging_for_target(${PARENT_DIR}_test)
-set(tests_to_run ${${PARENT_DIR}_tests})
-remove(tests_to_run test_suite.c)
+target_link_libraries(${PARENT_DIR}_test ouroboros-common)
+add_dependencies(build_tests ${PARENT_DIR}_test)
-foreach (test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(${test_name} ${C_TEST_PATH}/${PARENT_DIR}_test ${test_name})
-endforeach (test)
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
diff --git a/src/ipcpd/unicast/dir/tests/dht_test.c b/src/ipcpd/unicast/dir/tests/dht_test.c
index bea2c3e7..4f0ee00a 100644
--- a/src/ipcpd/unicast/dir/tests/dht_test.c
+++ b/src/ipcpd/unicast/dir/tests/dht_test.c
@@ -4,7 +4,6 @@
* Unit tests of the DHT
*
* Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -21,76 +20,1906 @@
*/
#define __DHT_TEST__
-#define DHT_TEST_KEY_LEN 32
-#include "dht.c"
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <test/test.h>
+#include <ouroboros/list.h>
+#include <ouroboros/utils.h>
-#include <pthread.h>
+#include "dht.pb-c.h"
+
+#include <assert.h>
+#include <inttypes.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
-#define CONTACTS 1000
+#define DHT_MAX_RAND_SIZE 64
+#define DHT_TEST_KEY_LEN 32
+#define DHT_TEST_ADDR 0x1234567890abcdefULL
-int dht_test(int argc,
- char ** argv)
+/* forward declare for use in the dht code */
+/* Packet sink for DHT tests */
+struct {
+ bool enabled;
+
+ struct list_head list;
+ size_t len;
+} sink;
+
+struct message {
+ struct list_head next;
+ void * msg;
+ uint64_t dst;
+};
+
+static int sink_send_msg(buffer_t * pkt,
+ uint64_t addr)
{
- struct dht * dht;
- uint8_t key[DHT_TEST_KEY_LEN];
- size_t i;
+ struct message * m;
- (void) argc;
- (void) argv;
+ assert(pkt != NULL);
+ assert(addr != 0);
+
+ assert(!list_is_empty(&sink.list) || sink.len == 0);
+
+ if (!sink.enabled)
+ goto finish;
+
+ m = malloc(sizeof(*m));
+ if (m == NULL) {
+ printf("Failed to malloc message.");
+ goto fail_malloc;
+ }
+
+ m->msg = dht_msg__unpack(NULL, pkt->len, pkt->data);
+ if (m->msg == NULL)
+ goto fail_unpack;
+
+ m->dst = addr;
+
+ list_add_tail(&m->next, &sink.list);
+
+ ++sink.len;
+ finish:
+ freebuf(*pkt);
+
+ return 0;
+ fail_unpack:
+ free(m);
+ fail_malloc:
+ freebuf(*pkt);
+ return -1;
+}
+
+#include "dht.c"
+
+/* Test helpers */
+
+static void sink_init(void)
+{
+ list_head_init(&sink.list);
+ sink.len = 0;
+ sink.enabled = true;
+}
+
+static void sink_clear(void)
+{
+ struct list_head * p;
+ struct list_head * h;
+
+ list_for_each_safe(p, h, &sink.list) {
+ struct message * m = list_entry(p, struct message, next);
+ list_del(&m->next);
+ dht_msg__free_unpacked((dht_msg_t *) m->msg, NULL);
+ free(m);
+ --sink.len;
+ }
+
+ assert(list_is_empty(&sink.list));
+}
+
+static void sink_fini(void)
+{
+ sink_clear();
+
+ assert(list_is_empty(&sink.list) || sink.len != 0);
+}
+
+static dht_msg_t * sink_read(void)
+{
+ struct message * m;
+ dht_msg_t * msg;
+
+ assert(!list_is_empty(&sink.list) || sink.len == 0);
+
+ if (list_is_empty(&sink.list))
+ return NULL;
+
+ m = list_first_entry(&sink.list, struct message, next);
+
+ --sink.len;
+
+ list_del(&m->next);
+
+ msg = m->msg;
+
+ free(m);
+
+ return (dht_msg_t *) msg;
+}
+
+static const buffer_t test_val = {
+ .data = (uint8_t *) "test_value",
+ .len = 10
+};
+
+static const buffer_t test_val2 = {
+ .data = (uint8_t *) "test_value_2",
+ .len = 12
+};
+
+static int random_value_len(buffer_t * b)
+{
+ assert(b != NULL);
+ assert(b->len > 0 && b->len <= DHT_MAX_RAND_SIZE);
+
+ b->data = malloc(b->len);
+ if (b->data == NULL)
+ goto fail_malloc;
+
+ random_buffer(b->data, b->len);
+
+ return 0;
+
+ fail_malloc:
+ return -ENOMEM;
+}
+
+static int random_value(buffer_t * b)
+{
+ assert(b != NULL);
+
+ b->len = rand() % DHT_MAX_RAND_SIZE + 1;
+
+ return random_value_len(b);
+}
+
+static int fill_dht_with_contacts(size_t n)
+{
+ size_t i;
+ uint8_t * id;
+
+ for (i = 0; i < n; i++) {
+ uint64_t addr = generate_cookie();
+ id = generate_id();
+ if (id == NULL)
+ goto fail_id;
+
+ if (dht_kv_update_contacts(id, addr) < 0)
+ goto fail_update;
+ free(id);
+ }
+
+ return 0;
+
+ fail_update:
+ free(id);
+ fail_id:
+ return -1;
+}
+
+static int fill_store_with_random_values(const uint8_t * key,
+ size_t len,
+ size_t n_values)
+{
+ buffer_t val;
+ struct timespec now;
+ size_t i;
+ uint8_t * _key;
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ for (i = 0; i < n_values; ++i) {
+ if (key != NULL)
+ _key = (uint8_t *) key;
+ else {
+ _key = generate_id();
+ if (_key == NULL)
+ goto fail_key;
+ }
+
+ if (len == 0)
+ val.len = rand() % DHT_MAX_RAND_SIZE + 1;
+ else
+ val.len = len;
+
+ if (random_value_len(&val) < 0)
+ goto fail_value;
+
+ if (dht_kv_store(_key, val, now.tv_sec + 10) < 0)
+ goto fail_store;
+
+ freebuf(val);
+ if (key == NULL)
+ free(_key);
+ }
+
+ return 0;
- dht = dht_create();
- if (dht == NULL) {
+ fail_store:
+ freebuf(val);
+ fail_value:
+ free(_key);
+ fail_key:
+ return -1;
+}
+
+static int random_contact_list(dht_contact_msg_t *** contacts,
+ size_t max)
+{
+ size_t i;
+
+ assert(contacts != NULL);
+ assert(max > 0);
+
+ *contacts = malloc(max * sizeof(**contacts));
+ if (*contacts == NULL)
+ goto fail_malloc;
+
+ for (i = 0; i < max; i++) {
+ (*contacts)[i] = malloc(sizeof(*(*contacts)[i]));
+ if ((*contacts)[i] == NULL)
+ goto fail_contacts;
+
+ dht_contact_msg__init((*contacts)[i]);
+
+ (*contacts)[i]->id.data = generate_id();
+ if ((*contacts)[i]->id.data == NULL)
+ goto fail_contact;
+
+ (*contacts)[i]->id.len = dht.id.len;
+ (*contacts)[i]->addr = generate_cookie();
+ }
+
+ return 0;
+
+ fail_contact:
+ dht_contact_msg__free_unpacked((*contacts)[i], NULL);
+ fail_contacts:
+ while (i-- > 0)
+ free((*contacts)[i]);
+ free(*contacts);
+ fail_malloc:
+ return -ENOMEM;
+}
+
+static void clear_contacts(dht_contact_msg_t ** contacts,
+ size_t len)
+{
+ size_t i;
+
+ assert(contacts != NULL);
+ if (*contacts == NULL)
+ return;
+
+ for (i = 0; i < len; ++i)
+ dht_contact_msg__free_unpacked((contacts)[i], NULL);
+
+ free(*contacts);
+ *contacts = NULL;
+}
+
+/* Start of actual tests */
+static struct dir_dht_config test_dht_config = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+};
+
+static int test_dht_init_fini(void)
+{
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_start_stop(void)
+{
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (dht_start() < 0) {
+ printf("Failed to start dht.\n");
+ goto fail_start;
+ }
+
+ dht_stop();
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_start:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_val_entry_create_destroy(void)
+{
+ struct val_entry * e;
+ struct timespec now;
+
+ TEST_START();
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (dht_init(&test_dht_config) < 0) {
printf("Failed to create dht.\n");
- return -1;
+ goto fail_init;
}
- dht_destroy(dht);
+ e = val_entry_create(test_val, now.tv_sec + 10);
+ if (e == NULL) {
+ printf("Failed to create val entry.\n");
+ goto fail_entry;
+ }
+
+ val_entry_destroy(e);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
- dht = dht_create();
- if (dht == NULL) {
- printf("Failed to re-create dht.\n");
- return -1;
+ fail_entry:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_entry_create_destroy(void)
+{
+ struct dht_entry * e;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
}
- if (dht_bootstrap(dht)) {
- printf("Failed to bootstrap dht.\n");
- dht_destroy(dht);
- return -1;
+ e = dht_entry_create(dht.id.data);
+ if (e == NULL) {
+ printf("Failed to create dht entry.\n");
+ goto fail_entry;
}
- dht_destroy(dht);
+ dht_entry_destroy(e);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_entry:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_entry_update_get_val(void)
+{
+ struct dht_entry * e;
+ struct val_entry * v;
+ struct timespec now;
+
+ TEST_START();
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ e = dht_entry_create(dht.id.data);
+ if (e == NULL) {
+ printf("Failed to create dht entry.\n");
+ goto fail_entry;
+ }
+
+ if (dht_entry_get_val(e, test_val) != NULL) {
+ printf("Found value in empty dht entry.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_update_val(e, test_val, now.tv_sec + 10) < 0) {
+ printf("Failed to update dht entry value.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_get_val(e, test_val2) != NULL) {
+ printf("Found value in dht entry with different key.\n");
+ goto fail_get;
+ }
+
+ v = dht_entry_get_val(e, test_val);
+ if (v == NULL) {
+ printf("Failed to get value from dht entry.\n");
+ goto fail_get;
+ }
+
+ if (v->val.len != test_val.len) {
+ printf("Length in dht entry does not match expected.\n");
+ goto fail_get;
+ }
+
+ if(memcmp(v->val.data, test_val.data, test_val.len) != 0) {
+ printf("Data in dht entry does not match expected.\n");
+ goto fail_get;
+ }
- dht = dht_create();
- if (dht == NULL) {
- printf("Failed to re-create dht.\n");
- return -1;
+ if (dht_entry_update_val(e, test_val, now.tv_sec + 15) < 0) {
+ printf("Failed to update exsting dht entry value.\n");
+ goto fail_get;
}
- if (dht_bootstrap(dht)) {
- printf("Failed to bootstrap dht.\n");
- dht_destroy(dht);
- return -1;
+ if (v->t_exp != now.tv_sec + 15) {
+ printf("Expiration time in dht entry value not updated.\n");
+ goto fail_get;
}
- for (i = 0; i < CONTACTS; ++i) {
- uint64_t addr;
- random_buffer(&addr, sizeof(addr));
- random_buffer(key, DHT_TEST_KEY_LEN);
- pthread_rwlock_wrlock(&dht->lock);
- if (dht_update_bucket(dht, key, addr)) {
- pthread_rwlock_unlock(&dht->lock);
- printf("Failed to update bucket.\n");
- dht_destroy(dht);
- return -1;
+ if (dht_entry_update_val(e, test_val, now.tv_sec + 5) < 0) {
+ printf("Failed to update existing dht entry value (5).\n");
+ goto fail_get;
+ }
+
+ if (v->t_exp != now.tv_sec + 15) {
+ printf("Expiration time in dht entry shortened.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_get_val(e, test_val) != v) {
+ printf("Wrong value in dht entry found after update.\n");
+ goto fail_get;
+ }
+
+ dht_entry_destroy(e);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_get:
+ dht_entry_destroy(e);
+ fail_entry:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_entry_update_get_lval(void)
+{
+ struct dht_entry * e;
+ struct val_entry * v;
+ struct timespec now;
+
+ TEST_START();
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ e = dht_entry_create(dht.id.data);
+ if (e == NULL) {
+ printf("Failed to create dht entry.\n");
+ goto fail_entry;
+ }
+
+ if (dht_entry_get_lval(e, test_val) != NULL) {
+ printf("Found value in empty dht entry.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_update_lval(e, test_val) < 0) {
+ printf("Failed to update dht entry value.\n");
+ goto fail_get;
+ }
+
+ v = dht_entry_get_lval(e, test_val);
+ if (v== NULL) {
+ printf("Failed to get value from dht entry.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_get_lval(e, test_val2) != NULL) {
+ printf("Found value in dht entry in vals.\n");
+ goto fail_get;
+ }
+
+ if (v->val.len != test_val.len) {
+ printf("Length in dht entry does not match expected.\n");
+ goto fail_get;
+ }
+
+ if(memcmp(v->val.data, test_val.data, test_val.len) != 0) {
+ printf("Data in dht entry does not match expected.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_update_lval(e, test_val) < 0) {
+ printf("Failed to update existing dht entry value.\n");
+ goto fail_get;
+ }
+
+ if (dht_entry_get_lval(e, test_val) != v) {
+ printf("Wrong value in dht entry found after update.\n");
+ goto fail_get;
+ }
+
+ dht_entry_destroy(e);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_get:
+ dht_entry_destroy(e);
+ fail_entry:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_contact_create_destroy(void)
+{
+ struct contact * c;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ c = contact_create(dht.id.data, dht.addr);
+ if (c == NULL) {
+ printf("Failed to create contact.\n");
+ goto fail_contact;
+ }
+
+ contact_destroy(c);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_contact:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_update_bucket(void)
+{
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(1000) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_update;
+ }
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_update:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_contact_list(void)
+{
+ struct list_head cl;
+ ssize_t len;
+ ssize_t items;
+
+ TEST_START();
+
+ list_head_init(&cl);
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ items = 5;
+
+ if (fill_dht_with_contacts(items) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_fill;
+ }
+
+ len = dht_kv_contact_list(dht.id.data, &cl, dht.k);
+ if (len < 0) {
+ printf("Failed to get contact list.\n");
+ goto fail_fill;
+ }
+
+ if (len != items) {
+ printf("Failed to get contacts (%zu != %zu).\n", len, items);
+ goto fail_contact_list;
+ }
+
+ contact_list_destroy(&cl);
+
+ items = 100;
+
+ if (fill_dht_with_contacts(items) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_fill;
+ }
+
+ len = dht_kv_contact_list(dht.id.data, &cl, items);
+ if (len < 0) {
+ printf("Failed to get contact list.\n");
+ goto fail_fill;
+ }
+
+ if ((size_t) len < dht.k) {
+ printf("Failed to get contacts (%zu < %zu).\n", len, dht.k);
+ goto fail_contact_list;
+ }
+
+ contact_list_destroy(&cl);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_contact_list:
+ contact_list_destroy(&cl);
+ fail_fill:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_get_values(void)
+{
+ buffer_t * vals;
+ ssize_t len;
+ size_t n = sizeof(uint64_t);
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_store_with_random_values(dht.id.data, n, 3) < 0) {
+ printf("Failed to fill store with random values.\n");
+ goto fail_fill;
+ }
+
+ len = dht_kv_retrieve(dht.id.data, &vals);
+ if (len < 0) {
+ printf("Failed to get values from store.\n");
+ goto fail_fill;
+ }
+
+ if (len != 3) {
+ printf("Failed to get %ld values (%zu).\n", 3L, len);
+ goto fail_get_values;
+ }
+
+ freebufs(vals, len);
+
+ if (fill_store_with_random_values(dht.id.data, n, 20) < 0) {
+ printf("Failed to fill store with random values.\n");
+ goto fail_fill;
+ }
+
+ len = dht_kv_retrieve(dht.id.data, &vals);
+ if (len < 0) {
+ printf("Failed to get values from store.\n");
+ goto fail_fill;
+ }
+
+ if (len != DHT_MAX_VALS) {
+ printf("Failed to get %d values.\n", DHT_MAX_VALS);
+ goto fail_get_values;
+ }
+
+ freebufs(vals, len);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_get_values:
+ freebufs(vals, len);
+ fail_fill:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_node_req_msg(void)
+{
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ msg = dht_kv_find_node_req_msg(dht.id.data);
+ if (msg == NULL) {
+ printf("Failed to get find node request message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_FIND_NODE_REQ) {
+ printf("Wrong code in find_node_req message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_FIND_NODE_REQ]);
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_node_req.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_node_req buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_node_req message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_value_req message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_node_rsp_msg(void)
+{
+ dht_contact_msg_t ** contacts;
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ msg = dht_kv_find_node_rsp_msg(dht.id.data, 0, &contacts, 0);
+ if (msg == NULL) {
+ printf("Failed to get find node response message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_FIND_NODE_RSP) {
+ printf("Wrong code in find_node_rsp message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_FIND_NODE_RSP]);
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_node_rsp.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_node_rsp buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_node_rsp message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_node_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_node_rsp_msg_contacts(void)
+{
+ dht_contact_msg_t ** contacts;
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ uint8_t * buf;
+ size_t len;
+ ssize_t n;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(100) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_fill;
+ }
+
+ n = dht_kv_get_contacts(dht.id.data, &contacts);
+ if (n < 0) {
+ printf("Failed to get contacts.\n");
+ goto fail_fill;
+ }
+
+ if ((size_t) n < dht.k) {
+ printf("Failed to get enough contacts (%zu < %zu).\n", n, dht.k);
+ goto fail_fill;
+ }
+
+ msg = dht_kv_find_node_rsp_msg(dht.id.data, 0, &contacts, n);
+ if (msg == NULL) {
+ printf("Failed to build find node response message.\n");
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_node_rsp.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_node_rsp buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_node_rsp message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_node_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ clear_contacts(contacts, n);
+ fail_fill:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_value_req_msg(void)
+{
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ msg = dht_kv_find_value_req_msg(dht.id.data);
+ if (msg == NULL) {
+ printf("Failed to build find value request message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_FIND_VALUE_REQ) {
+ printf("Wrong code in find_value_req message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_FIND_VALUE_REQ]);
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_value_req.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_node_req buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_value_req message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_value_req message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_value_rsp_msg(void)
+{
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ msg = dht_kv_find_value_rsp_msg(dht.id.data, 0, NULL, 0, NULL, 0);
+ if (msg == NULL) {
+ printf("Failed to build find value response message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_FIND_VALUE_RSP) {
+ printf("Wrong code in find_value_rsp message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_FIND_VALUE_RSP]);
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_value_rsp.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_value_rsp buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_value_rsp message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_value_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_value_rsp_msg_contacts(void)
+{
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+ dht_contact_msg_t ** contacts;
+ ssize_t n;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(100) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_fill;
+ }
+
+ n = dht_kv_get_contacts(dht.id.data, &contacts);
+ if (n < 0) {
+ printf("Failed to get contacts.\n");
+ goto fail_fill;
+ }
+
+ if ((size_t) n < dht.k) {
+ printf("Failed to get enough contacts (%zu < %zu).\n", n, dht.k);
+ goto fail_fill;
+ }
+
+ msg = dht_kv_find_value_rsp_msg(dht.id.data, 0, &contacts, n, NULL, 0);
+ if (msg == NULL) {
+ printf("Failed to build find value response message.\n");
+ goto fail_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_value_rsp.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_value_rsp buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_value_rsp message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_value_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ clear_contacts(contacts, n);
+ fail_fill:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_find_value_rsp_msg_values(void)
+{
+ dht_msg_t * msg;
+ dht_msg_t * upk;
+ size_t len;
+ uint8_t * buf;
+ buffer_t * values;
+ size_t i;
+ uint64_t ck;
+
+ TEST_START();
+
+ ck = generate_cookie();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ values = malloc(sizeof(*values) * 8);
+ if (values == NULL) {
+ printf("Failed to malloc values.\n");
+ goto fail_values;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (random_value(&values[i]) < 0) {
+ printf("Failed to create random value.\n");
+ goto fail_fill;
}
- pthread_rwlock_unlock(&dht->lock);
}
- dht_destroy(dht);
+ msg = dht_kv_find_value_rsp_msg(dht.id.data, ck, NULL, 0, &values, 8);
+ if (msg == NULL) {
+ printf("Failed to build find value response message.\n");
+ goto fail_msg;
+ }
- return 0;
+ values = NULL; /* msg owns the values now */
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed length of find_value_rsp.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc find_value_rsp buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack find_value_rsp message.\n");
+ goto fail_pack;
+ }
+
+ upk = dht_msg__unpack(NULL, len, buf);
+ if (upk == NULL) {
+ printf("Failed to unpack find_value_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ if (upk->code != DHT_FIND_VALUE_RSP) {
+ printf("Wrong code in find_value_rsp message (%s != %s).\n",
+ dht_code_str[upk->code],
+ dht_code_str[DHT_FIND_VALUE_RSP]);
+ goto fail_unpack;
+ }
+
+ if (upk->val == NULL) {
+ printf("No values in find_value_rsp message.\n");
+ goto fail_unpack;
+ }
+
+ if (upk->val->n_values != 8) {
+ printf("Not enough values in find_value_rsp (%zu != %lu).\n",
+ upk->val->n_values, 8UL);
+ goto fail_unpack;
+ }
+
+ free(buf);
+ dht_msg__free_unpacked(msg, NULL);
+ dht_msg__free_unpacked(upk, NULL);
+
+ free(values);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_unpack:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_pack:
+ free(buf);
+ fail_msg:
+ fail_fill:
+ while((i--) > 0)
+ freebuf(values[i]);
+ free(values);
+ fail_values:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_store_msg(void)
+{
+ dht_msg_t * msg;
+ size_t len;
+ uint8_t * buf;
+ struct timespec now;
+
+ TEST_START();
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ msg = dht_kv_store_msg(dht.id.data, test_val, now.tv_sec + 10);
+ if (msg == NULL) {
+ printf("Failed to get store message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_STORE) {
+ printf("Wrong code in store message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_STORE]);
+ goto fail_store_msg;
+ }
+
+ if (dht_kv_validate_msg(msg) < 0) {
+ printf("Failed to validate store message.\n");
+ goto fail_store_msg;
+ }
+
+ len = dht_msg__get_packed_size(msg);
+ if (len == 0) {
+ printf("Failed to get packed msg length.\n");
+ goto fail_msg;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("Failed to malloc store msg buf.\n");
+ goto fail_msg;
+ }
+
+ if (dht_msg__pack(msg, buf) != len) {
+ printf("Failed to pack store message.\n");
+ goto fail_pack;
+ }
+
+ free(buf);
+
+ dht_msg__free_unpacked(msg, NULL);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_pack:
+ free(buf);
+ fail_store_msg:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_kv_query_contacts_req_rsp(void)
+{
+ dht_msg_t * req;
+ dht_msg_t * rsp;
+ dht_contact_msg_t ** contacts;
+ size_t len = 2;
+
+ uint8_t * key;
+
+ TEST_START();
+
+ sink_init();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(1) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_prep;
+ }
+
+ key = generate_id();
+ if (key == NULL) {
+ printf("Failed to generate key.\n");
+ goto fail_prep;
+ }
+
+ if (dht_kv_query_contacts(key, NULL) < 0) {
+ printf("Failed to query contacts.\n");
+ goto fail_query;
+ }
+
+ req = sink_read();
+ if (req == NULL) {
+ printf("Failed to read request from sink.\n");
+ goto fail_query;
+ }
+
+ if (dht_kv_validate_msg(req) < 0) {
+ printf("Failed to validate find node req.\n");
+ goto fail_val_req;
+ }
+
+ if (random_contact_list(&contacts, len) < 0) {
+ printf("Failed to create random contact.\n");
+ goto fail_val_req;
+ }
+
+ rsp = dht_kv_find_node_rsp_msg(key, req->find->cookie, &contacts, len);
+ if (rsp == NULL) {
+ printf("Failed to create find node response message.\n");
+ goto fail_rsp;
+ }
+
+ memcpy(rsp->src->id.data, dht.id.data, dht.id.len);
+ rsp->src->addr = generate_cookie();
+
+ if (dht_kv_validate_msg(rsp) < 0) {
+ printf("Failed to validate find node response message.\n");
+ goto fail_val_rsp;
+ }
+
+ do_dht_kv_find_node_rsp(rsp->node);
+
+ /* dht_contact_msg__free_unpacked(contacts[0], NULL); set to NULL */
+
+ free(contacts);
+
+ dht_msg__free_unpacked(rsp, NULL);
+
+ free(key);
+
+ dht_msg__free_unpacked(req, NULL);
+
+ sink_fini();
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_val_rsp:
+ dht_msg__free_unpacked(rsp, NULL);
+ fail_rsp:
+ while (len-- > 0)
+ dht_contact_msg__free_unpacked(contacts[len], NULL);
+ free(contacts);
+ fail_val_req:
+ dht_msg__free_unpacked(req, NULL);
+ fail_query:
+ free(key);
+ fail_prep:
+ dht_fini();
+ fail_init:
+ sink_fini();
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_req_create_destroy(void)
+{
+ struct dht_req * req;
+
+ TEST_START();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ req = dht_req_create(dht.id.data);
+ if (req == NULL) {
+ printf("Failed to create kad request.\n");
+ goto fail_req;
+ }
+
+ dht_req_destroy(req);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_req:
+ dht_fini();
+ fail_init:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_reg_unreg(void)
+{
+ TEST_START();
+
+ sink_init();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (dht_reg(dht.id.data) < 0) {
+ printf("Failed to register own id.\n");
+ goto fail_reg;
+ }
+
+ if (sink.len != 0) {
+ printf("Packet sent without contacts!");
+ goto fail_msg;
+ }
+
+ if (dht_unreg(dht.id.data) < 0) {
+ printf("Failed to unregister own id.\n");
+ goto fail_msg;
+ }
+
+ dht_fini();
+
+ sink_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_msg:
+ dht_unreg(dht.id.data);
+ fail_reg:
+ dht_fini();
+ fail_init:
+ sink_fini();
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_reg_unreg_contacts(void)
+{
+ dht_msg_t * msg;
+
+ TEST_START();
+
+ sink_init();
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(4) < 0) {
+ printf("Failed to fill bucket with contacts.\n");
+ goto fail_reg;
+ }
+
+ if (dht_reg(dht.id.data) < 0) {
+ printf("Failed to register own id.\n");
+ goto fail_reg;
+ }
+
+ if (sink.len != dht.alpha) {
+ printf("Packet sent to too few contacts!\n");
+ goto fail_msg;
+ }
+
+ msg = sink_read();
+ if (msg == NULL) {
+ printf("Failed to read message from sink.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_STORE) {
+ printf("Wrong code in dht reg message (%s != %s).\n",
+ dht_code_str[msg->code],
+ dht_code_str[DHT_STORE]);
+ goto fail_validation;
+ }
+
+ if (dht_kv_validate_msg(msg) < 0) {
+ printf("Failed to validate dht message.\n");
+ goto fail_validation;
+ }
+
+ if (dht_unreg(dht.id.data) < 0) {
+ printf("Failed to unregister own id.\n");
+ goto fail_validation;
+ }
+
+ dht_msg__free_unpacked(msg, NULL);
+
+ dht_fini();
+
+ sink_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_validation:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ sink_clear();
+ dht_unreg(dht.id.data);
+ fail_reg:
+ dht_fini();
+ fail_init:
+ sink_fini();
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_reg_query_local(void)
+{
+ struct timespec now;
+ buffer_t test_addr;
+
+ TEST_START();
+
+ clock_gettime(CLOCK_REALTIME_COARSE, &now);
+
+ if (addr_to_buf(1234321, &test_addr) < 0) {
+ printf("Failed to convert test address to buffer.\n");
+ goto fail_buf;
+ }
+
+ if (dht_init(&test_dht_config) < 0) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (dht_reg(dht.id.data) < 0) {
+ printf("Failed to register own id.\n");
+ goto fail_reg;
+ }
+
+ if (dht_query(dht.id.data) == dht.addr) {
+ printf("Succeeded to query own id.\n");
+ goto fail_get;
+ }
+
+ if (dht_kv_store(dht.id.data, test_addr, now.tv_sec + 5) < 0) {
+ printf("Failed to publish value.\n");
+ goto fail_get;
+ }
+
+ if (dht_query(dht.id.data) != 1234321) {
+ printf("Failed to return remote addr.\n");
+ goto fail_get;
+ }
+
+ if (dht_unreg(dht.id.data) < 0) {
+ printf("Failed to unregister own id.\n");
+ goto fail_get;
+ }
+
+ freebuf(test_addr);
+
+ dht_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_get:
+ dht_unreg(dht.id.data);
+ fail_reg:
+ dht_fini();
+ fail_init:
+ freebuf(test_addr);
+ fail_buf:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_query(void)
+{
+ uint8_t * key;
+ struct dir_dht_config cfg;
+
+ TEST_START();
+
+ sink_init();
+
+ cfg = test_dht_config;
+ cfg.peer = generate_cookie();
+
+ if (dht_init(&cfg)) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ key = generate_id();
+ if (key == NULL) {
+ printf("Failed to generate key.\n");
+ goto fail_key;
+ }
+
+ if (dht_query(key) != INVALID_ADDR) {
+ printf("Succeeded to get address without contacts.\n");
+ goto fail_get;
+ }
+
+ if (sink.len != 0) {
+ printf("Packet sent without contacts!");
+ goto fail_test;
+ }
+
+ free(key);
+
+ dht_fini();
+
+ sink_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_test:
+ sink_clear();
+ fail_get:
+ free(key);
+ fail_key:
+ dht_fini();
+ fail_init:
+ sink_fini();
+ return TEST_RC_FAIL;
+}
+
+static int test_dht_query_contacts(void)
+{
+ dht_msg_t * msg;
+ uint8_t * key;
+ struct dir_dht_config cfg;
+
+
+ TEST_START();
+
+ sink_init();
+
+ cfg = test_dht_config;
+ cfg.peer = generate_cookie();
+
+ if (dht_init(&cfg)) {
+ printf("Failed to create dht.\n");
+ goto fail_init;
+ }
+
+ if (fill_dht_with_contacts(10) < 0) {
+ printf("Failed to fill with contacts!");
+ goto fail_contacts;
+ }
+
+ key = generate_id();
+ if (key == NULL) {
+ printf("Failed to generate key.");
+ goto fail_contacts;
+ }
+
+ if (dht_query(key) != INVALID_ADDR) {
+ printf("Succeeded to get address for random id.\n");
+ goto fail_query;
+ }
+
+ msg = sink_read();
+ if (msg == NULL) {
+ printf("Failed to read message.!\n");
+ goto fail_read;
+ }
+
+ if (dht_kv_validate_msg(msg) < 0) {
+ printf("Failed to validate dht message.\n");
+ goto fail_msg;
+ }
+
+ if (msg->code != DHT_FIND_VALUE_REQ) {
+ printf("Failed to validate dht message.\n");
+ goto fail_msg;
+ }
+
+ dht_msg__free_unpacked(msg, NULL);
+
+ free(key);
+
+ sink_clear();
+
+ dht_fini();
+
+ sink_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_msg:
+ dht_msg__free_unpacked(msg, NULL);
+ fail_read:
+ sink_clear();
+ fail_query:
+ free(key);
+ fail_contacts:
+ dht_fini();
+ fail_init:
+ sink_fini();
+ return TEST_RC_FAIL;
+}
+
+int dht_test(int argc,
+ char ** argv)
+{
+ int rc = 0;
+
+ (void) argc;
+ (void) argv;
+
+ rc |= test_dht_init_fini();
+ rc |= test_dht_start_stop();
+ rc |= test_val_entry_create_destroy();
+ rc |= test_dht_entry_create_destroy();
+ rc |= test_dht_entry_update_get_val();
+ rc |= test_dht_entry_update_get_lval();
+ rc |= test_dht_kv_contact_create_destroy();
+ rc |= test_dht_kv_contact_list();
+ rc |= test_dht_kv_update_bucket();
+ rc |= test_dht_kv_get_values();
+ rc |= test_dht_kv_find_node_req_msg();
+ rc |= test_dht_kv_find_node_rsp_msg();
+ rc |= test_dht_kv_find_node_rsp_msg_contacts();
+ rc |= test_dht_kv_query_contacts_req_rsp();
+ rc |= test_dht_kv_find_value_req_msg();
+ rc |= test_dht_kv_find_value_rsp_msg();
+ rc |= test_dht_kv_find_value_rsp_msg_contacts();
+ rc |= test_dht_kv_find_value_rsp_msg_values();
+ rc |= test_dht_kv_store_msg();
+ rc |= test_dht_req_create_destroy();
+ rc |= test_dht_reg_unreg();
+ rc |= test_dht_reg_unreg_contacts();
+ rc |= test_dht_reg_query_local();
+ rc |= test_dht_query();
+ rc |= test_dht_query_contacts();
+
+ return rc;
}
diff --git a/src/ipcpd/unicast/dt.c b/src/ipcpd/unicast/dt.c
index 2bb5ed2f..9435350f 100644
--- a/src/ipcpd/unicast/dt.c
+++ b/src/ipcpd/unicast/dt.c
@@ -41,6 +41,7 @@
#include <ouroboros/fccntl.h>
#endif
+#include "addr-auth.h"
#include "common/comp.h"
#include "common/connmgr.h"
#include "ca.h"
@@ -59,7 +60,7 @@
#include <assert.h>
#define QOS_BLOCK_LEN 672
-#define RIB_FILE_STRLEN (189 + QOS_BLOCK_LEN * QOS_CUBE_MAX)
+#define RIB_FILE_STRLEN (169 + RIB_TM_STRLEN + QOS_BLOCK_LEN * QOS_CUBE_MAX)
#define RIB_NAME_STRLEN 256
#ifndef CLOCK_REALTIME_COARSE
@@ -67,7 +68,7 @@
#endif
struct comp_info {
- void (* post_packet)(void * comp, struct shm_du_buff * sdb);
+ void (* post_packet)(void * comp, struct ssm_pk_buff * spb);
void * comp;
char * name;
};
@@ -134,16 +135,18 @@ static void dt_pci_des(uint8_t * head,
memcpy(&dt_pci->eid, head + dt_pci_info.eid_o, dt_pci_info.eid_size);
}
-static void dt_pci_shrink(struct shm_du_buff * sdb)
+static void dt_pci_shrink(struct ssm_pk_buff * spb)
{
- assert(sdb);
+ assert(spb);
- shm_du_buff_head_release(sdb, dt_pci_info.head_size);
+ ssm_pk_buff_head_release(spb, dt_pci_info.head_size);
}
struct {
struct psched * psched;
+ uint64_t addr;
+
struct pff * pff[QOS_CUBE_MAX];
struct routing_i * routing[QOS_CUBE_MAX];
#ifdef IPCP_FLOW_STATS
@@ -186,7 +189,7 @@ static int dt_rib_read(const char * path,
char str[QOS_BLOCK_LEN + 1];
char addrstr[20];
char * entry;
- char tmstr[20];
+ char tmstr[RIB_TM_STRLEN];
size_t rxqlen = 0;
size_t txqlen = 0;
struct tm * tm;
@@ -209,13 +212,13 @@ static int dt_rib_read(const char * path,
return 0;
}
- if (dt.stat[fd].addr == ipcpi.dt_addr)
+ if (dt.stat[fd].addr == dt.addr)
sprintf(addrstr, "%s", dt.comps[fd].name);
else
- sprintf(addrstr, "%" PRIu64, dt.stat[fd].addr);
+ sprintf(addrstr, ADDR_FMT32, ADDR_VAL32(&dt.stat[fd].addr));
- tm = localtime(&dt.stat[fd].stamp);
- strftime(tmstr, sizeof(tmstr), "%F %T", tm);
+ tm = gmtime(&dt.stat[fd].stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
if (fd >= PROG_RES_FDS) {
fccntl(fd, FLOWGRXQLEN, &rxqlen);
@@ -223,11 +226,11 @@ static int dt_rib_read(const char * path,
}
sprintf(buf,
- "Flow established at: %20s\n"
+ "Flow established at: %.*s\n"
"Endpoint address: %20s\n"
"Queued packets (rx): %20zu\n"
"Queued packets (tx): %20zu\n\n",
- tmstr, addrstr, rxqlen, txqlen);
+ RIB_TM_STRLEN - 1, tmstr, addrstr, rxqlen, txqlen);
for (i = 0; i < QOS_CUBE_MAX; ++i) {
sprintf(str,
"Qos cube %3d:\n"
@@ -285,48 +288,45 @@ static int dt_rib_readdir(char *** buf)
pthread_rwlock_rdlock(&dt.lock);
if (dt.n_flows < 1) {
- pthread_rwlock_unlock(&dt.lock);
- return 0;
+ *buf = NULL;
+ goto no_flows;
}
*buf = malloc(sizeof(**buf) * dt.n_flows);
- if (*buf == NULL) {
- pthread_rwlock_unlock(&dt.lock);
- return -ENOMEM;
- }
+ if (*buf == NULL)
+ goto fail_entries;
for (i = 0; i < PROG_MAX_FLOWS; ++i) {
pthread_mutex_lock(&dt.stat[i].lock);
if (dt.stat[i].stamp == 0) {
pthread_mutex_unlock(&dt.stat[i].lock);
- /* Optimization: skip unused res_fds. */
- if (i < PROG_RES_FDS)
- i = PROG_RES_FDS;
- continue;
+ break;
}
+ pthread_mutex_unlock(&dt.stat[i].lock);
+
sprintf(entry, "%zu", i);
(*buf)[idx] = malloc(strlen(entry) + 1);
- if ((*buf)[idx] == NULL) {
- while (idx-- > 0)
- free((*buf)[idx]);
- free(*buf);
- pthread_mutex_unlock(&dt.stat[i].lock);
- pthread_rwlock_unlock(&dt.lock);
- return -ENOMEM;
- }
+ if ((*buf)[idx] == NULL)
+ goto fail_entry;
strcpy((*buf)[idx++], entry);
- pthread_mutex_unlock(&dt.stat[i].lock);
}
- assert((size_t) idx == dt.n_flows);
-
+ no_flows:
pthread_rwlock_unlock(&dt.lock);
return idx;
+
+ fail_entry:
+ while (idx-- > 0)
+ free((*buf)[idx]);
+ free(*buf);
+ fail_entries:
+ pthread_rwlock_unlock(&dt.lock);
+ return -ENOMEM;
#else
(void) buf;
return 0;
@@ -429,7 +429,7 @@ static void handle_event(void * self,
static void packet_handler(int fd,
qoscube_t qc,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct dt_pci dt_pci;
int ret;
@@ -437,7 +437,7 @@ static void packet_handler(int fd,
uint8_t * head;
size_t len;
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
#ifndef IPCP_FLOW_STATS
(void) fd;
@@ -451,13 +451,13 @@ static void packet_handler(int fd,
#endif
memset(&dt_pci, 0, sizeof(dt_pci));
- head = shm_du_buff_head(sdb);
+ head = ssm_pk_buff_head(spb);
dt_pci_des(head, &dt_pci);
- if (dt_pci.dst_addr != ipcpi.dt_addr) {
+ if (dt_pci.dst_addr != dt.addr) {
if (dt_pci.ttl == 0) {
log_dbg("TTL was zero.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
#ifdef IPCP_FLOW_STATS
pthread_mutex_lock(&dt.stat[fd].lock);
@@ -474,7 +474,7 @@ static void packet_handler(int fd,
if (ofd < 0) {
log_dbg("No next hop for %" PRIu64 ".",
dt_pci.dst_addr);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
#ifdef IPCP_FLOW_STATS
pthread_mutex_lock(&dt.stat[fd].lock);
@@ -488,12 +488,12 @@ static void packet_handler(int fd,
(void) ca_calc_ecn(ofd, head + dt_pci_info.ecn_o, qc, len);
- ret = ipcp_flow_write(ofd, sdb);
+ ret = ipcp_flow_write(ofd, spb);
if (ret < 0) {
log_dbg("Failed to write packet to fd %d.", ofd);
if (ret == -EFLOWDOWN)
notifier_event(NOTIFY_DT_FLOW_DOWN, &ofd);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
#ifdef IPCP_FLOW_STATS
pthread_mutex_lock(&dt.stat[ofd].lock);
@@ -513,17 +513,17 @@ static void packet_handler(int fd,
pthread_mutex_unlock(&dt.stat[ofd].lock);
#endif
} else {
- dt_pci_shrink(sdb);
+ dt_pci_shrink(spb);
if (dt_pci.eid >= PROG_RES_FDS) {
uint8_t ecn = *(head + dt_pci_info.ecn_o);
- fa_np1_rcv(dt_pci.eid, ecn, sdb);
+ fa_np1_rcv(dt_pci.eid, ecn, spb);
return;
}
if (dt.comps[dt_pci.eid].post_packet == NULL) {
log_err("No registered component on eid %" PRIu64 ".",
dt_pci.eid);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return;
}
#ifdef IPCP_FLOW_STATS
@@ -541,7 +541,7 @@ static void packet_handler(int fd,
pthread_mutex_unlock(&dt.stat[dt_pci.eid].lock);
#endif
dt.comps[dt_pci.eid].post_packet(dt.comps[dt_pci.eid].comp,
- sdb);
+ spb);
}
}
@@ -570,16 +570,22 @@ int dt_init(struct dt_config cfg)
int i;
int j;
char dtstr[RIB_NAME_STRLEN + 1];
- int pp;
+ enum pol_pff pp;
struct conn_info info;
memset(&info, 0, sizeof(info));
+ dt.addr = addr_auth_address();
+ if (dt.addr == INVALID_ADDR) {
+ log_err("Failed to get address");
+ return -1;
+ }
+
strcpy(info.comp_name, DT_COMP);
strcpy(info.protocol, DT_PROTO);
info.pref_version = 1;
info.pref_syntax = PROTO_FIXED;
- info.addr = ipcpi.dt_addr;
+ info.addr = dt.addr;
if (cfg.eid_size != 8) { /* only support 64 bits from now */
log_warn("Invalid EID size. Only 64 bit is supported.");
@@ -601,8 +607,7 @@ int dt_init(struct dt_config cfg)
goto fail_connmgr_comp_init;
}
- pp = routing_init(cfg.routing_type);
- if (pp < 0) {
+ if (routing_init(&cfg.routing, &pp) < 0) {
log_err("Failed to init routing.");
goto fail_routing;
}
@@ -647,7 +652,7 @@ int dt_init(struct dt_config cfg)
dt.n_flows = 0;
#endif
- sprintf(dtstr, "%s.%" PRIu64, DT, ipcpi.dt_addr);
+ sprintf(dtstr, "%s." ADDR_FMT32, DT, ADDR_VAL32(&dt.addr));
if (rib_reg(dtstr, &r_ops)) {
log_err("Failed to register RIB.");
goto fail_rib_reg;
@@ -683,7 +688,7 @@ void dt_fini(void)
char dtstr[RIB_NAME_STRLEN + 1];
int i;
- sprintf(dtstr, "%s.%" PRIu64, DT, ipcpi.dt_addr);
+ sprintf(dtstr, "%s.%" PRIu64, DT, dt.addr);
rib_unreg(dtstr);
#ifdef IPCP_FLOW_STATS
for (i = 0; i < PROG_MAX_FLOWS; ++i)
@@ -719,21 +724,31 @@ int dt_start(void)
if (pthread_create(&dt.listener, NULL, dt_conn_handle, NULL)) {
log_err("Failed to create listener thread.");
- psched_destroy(dt.psched);
- return -1;
+ goto fail_listener;
+ }
+
+ if (routing_start() < 0) {
+ log_err("Failed to start routing.");
+ goto fail_routing;
}
return 0;
+ fail_routing:
+ pthread_cancel(dt.listener);
+ pthread_join(dt.listener, NULL);
+ fail_listener:
+ notifier_unreg(&handle_event);
fail_notifier_reg:
psched_destroy(dt.psched);
fail_psched:
return -1;
-
}
void dt_stop(void)
{
+ routing_stop();
+
pthread_cancel(dt.listener);
pthread_join(dt.listener, NULL);
@@ -743,12 +758,12 @@ void dt_stop(void)
}
int dt_reg_comp(void * comp,
- void (* func)(void * func, struct shm_du_buff *),
+ void (* func)(void * func, struct ssm_pk_buff *),
char * name)
{
int eid;
- assert(func);
+ assert(func != NULL);
pthread_rwlock_wrlock(&dt.lock);
@@ -769,15 +784,32 @@ int dt_reg_comp(void * comp,
pthread_rwlock_unlock(&dt.lock);
#ifdef IPCP_FLOW_STATS
- stat_used(eid, ipcpi.dt_addr);
+ stat_used(eid, dt.addr);
#endif
return eid;
}
+void dt_unreg_comp(int eid)
+{
+ assert(eid >= 0 && eid < PROG_RES_FDS);
+
+ pthread_rwlock_wrlock(&dt.lock);
+
+ assert(dt.comps[eid].post_packet != NULL);
+
+ dt.comps[eid].post_packet = NULL;
+ dt.comps[eid].comp = NULL;
+ dt.comps[eid].name = NULL;
+
+ pthread_rwlock_unlock(&dt.lock);
+
+ return;
+}
+
int dt_write_packet(uint64_t dst_addr,
qoscube_t qc,
uint64_t eid,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct dt_pci dt_pci;
int fd;
@@ -785,10 +817,10 @@ int dt_write_packet(uint64_t dst_addr,
uint8_t * head;
size_t len;
- assert(sdb);
- assert(dst_addr != ipcpi.dt_addr);
+ assert(spb);
+ assert(dst_addr != dt.addr);
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
#ifdef IPCP_FLOW_STATS
if (eid < PROG_RES_FDS) {
@@ -802,7 +834,8 @@ int dt_write_packet(uint64_t dst_addr,
#endif
fd = pff_nhop(dt.pff[qc], dst_addr);
if (fd < 0) {
- log_dbg("Could not get nhop for addr %" PRIu64 ".", dst_addr);
+ log_dbg("Could not get nhop for " ADDR_FMT32 ".",
+ ADDR_VAL32(&dst_addr));
#ifdef IPCP_FLOW_STATS
if (eid < PROG_RES_FDS) {
pthread_mutex_lock(&dt.stat[eid].lock);
@@ -816,13 +849,13 @@ int dt_write_packet(uint64_t dst_addr,
return -EPERM;
}
- head = shm_du_buff_head_alloc(sdb, dt_pci_info.head_size);
+ head = ssm_pk_buff_head_alloc(spb, dt_pci_info.head_size);
if (head == NULL) {
log_dbg("Failed to allocate DT header.");
goto fail_write;
}
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
dt_pci.dst_addr = dst_addr;
dt_pci.qc = qc;
@@ -833,7 +866,7 @@ int dt_write_packet(uint64_t dst_addr,
dt_pci_ser(head, &dt_pci);
- ret = ipcp_flow_write(fd, sdb);
+ ret = ipcp_flow_write(fd, spb);
if (ret < 0) {
log_dbg("Failed to write packet to fd %d.", fd);
if (ret == -EFLOWDOWN)
diff --git a/src/ipcpd/unicast/dt.h b/src/ipcpd/unicast/dt.h
index 7198a013..2e713700 100644
--- a/src/ipcpd/unicast/dt.h
+++ b/src/ipcpd/unicast/dt.h
@@ -25,7 +25,7 @@
#include <ouroboros/ipcp.h>
#include <ouroboros/qoscube.h>
-#include <ouroboros/shm_rdrbuff.h>
+#include <ouroboros/ssm_pool.h>
#define DT_COMP "Data Transfer"
#define DT_PROTO "dtp"
@@ -39,13 +39,15 @@ int dt_start(void);
void dt_stop(void);
-int dt_reg_comp(void * comp,
- void (* func)(void * comp, struct shm_du_buff * sdb),
- char * name);
+int dt_reg_comp(void * comp,
+ void (* func)(void * comp, struct ssm_pk_buff * spb),
+ char * name);
+
+void dt_unreg_comp(int eid);
int dt_write_packet(uint64_t dst_addr,
qoscube_t qc,
uint64_t eid,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb);
#endif /* OUROBOROS_IPCPD_UNICAST_DT_H */
diff --git a/src/ipcpd/unicast/fa.c b/src/ipcpd/unicast/fa.c
index 3631fd7b..07f5b46c 100644
--- a/src/ipcpd/unicast/fa.c
+++ b/src/ipcpd/unicast/fa.c
@@ -41,12 +41,14 @@
#include <ouroboros/random.h>
#include <ouroboros/pthread.h>
+#include "addr-auth.h"
#include "dir.h"
#include "fa.h"
#include "psched.h"
#include "ipcp.h"
#include "dt.h"
#include "ca.h"
+#include "np1.h"
#include <inttypes.h>
#include <stdlib.h>
@@ -69,24 +71,22 @@ struct fa_msg {
uint64_t s_addr;
uint64_t r_eid;
uint64_t s_eid;
- uint8_t code;
- int8_t response;
- uint16_t ece;
- /* QoS parameters from spec, aligned */
- uint32_t delay;
uint64_t bandwidth;
+ int32_t response;
+ uint32_t delay;
uint32_t loss;
uint32_t ber;
uint32_t max_gap;
uint32_t timeout;
- uint16_t cypher_s;
+ uint16_t ece;
+ uint8_t code;
uint8_t availability;
uint8_t in_order;
} __attribute__((packed));
struct cmd {
struct list_head next;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
};
struct fa_flow {
@@ -135,7 +135,7 @@ static int fa_rib_read(const char * path,
char r_addrstr[21];
char s_eidstr[21];
char r_eidstr[21];
- char tmstr[20];
+ char tmstr[RIB_TM_STRLEN];
char castr[1024];
char * entry;
struct tm * tm;
@@ -166,8 +166,8 @@ static int fa_rib_read(const char * path,
sprintf(s_eidstr, "%" PRIu64, flow->s_eid);
sprintf(r_eidstr, "%" PRIu64, flow->r_eid);
- tm = localtime(&flow->stamp);
- strftime(tmstr, sizeof(tmstr), "%F %T", tm);
+ tm = gmtime(&flow->stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
ca_print_stats(flow->ctx, castr, 1024);
@@ -217,15 +217,13 @@ static int fa_rib_readdir(char *** buf)
pthread_rwlock_rdlock(&fa.flows_lock);
if (fa.n_flows < 1) {
- pthread_rwlock_unlock(&fa.flows_lock);
- return 0;
+ *buf = NULL;
+ goto no_flows;
}
*buf = malloc(sizeof(**buf) * fa.n_flows);
- if (*buf == NULL) {
- pthread_rwlock_unlock(&fa.flows_lock);
- return -ENOMEM;
- }
+ if (*buf == NULL)
+ goto fail_entries;
for (i = 0; i < PROG_MAX_FLOWS; ++i) {
struct fa_flow * flow;
@@ -237,22 +235,25 @@ static int fa_rib_readdir(char *** buf)
sprintf(entry, "%zu", i);
(*buf)[idx] = malloc(strlen(entry) + 1);
- if ((*buf)[idx] == NULL) {
- while (idx-- > 0)
- free((*buf)[idx]);
- free(*buf);
- pthread_rwlock_unlock(&fa.flows_lock);
- return -ENOMEM;
- }
+ if ((*buf)[idx] == NULL)
+ goto fail_entry;
strcpy((*buf)[idx++], entry);
}
assert((size_t) idx == fa.n_flows);
-
+ no_flows:
pthread_rwlock_unlock(&fa.flows_lock);
return idx;
+
+ fail_entry:
+ while (idx-- > 0)
+ free((*buf)[idx]);
+ free(*buf);
+ fail_entries:
+ pthread_rwlock_unlock(&fa.flows_lock);
+ return -ENOMEM;
#else
(void) buf;
return 0;
@@ -330,7 +331,7 @@ static uint64_t gen_eid(int fd)
static void packet_handler(int fd,
qoscube_t qc,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct fa_flow * flow;
uint64_t r_addr;
@@ -342,7 +343,7 @@ static void packet_handler(int fd,
pthread_rwlock_wrlock(&fa.flows_lock);
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
#ifdef IPCP_FLOW_STATS
++flow->p_snd;
@@ -357,8 +358,8 @@ static void packet_handler(int fd,
ca_wnd_wait(wnd);
- if (dt_write_packet(r_addr, qc, r_eid, sdb)) {
- ipcp_sdb_release(sdb);
+ if (dt_write_packet(r_addr, qc, r_eid, spb)) {
+ ipcp_spb_release(spb);
log_dbg("Failed to forward packet.");
#ifdef IPCP_FLOW_STATS
pthread_rwlock_wrlock(&fa.flows_lock);
@@ -411,7 +412,7 @@ static void fa_flow_fini(struct fa_flow * flow)
}
static void fa_post_packet(void * comp,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct cmd * cmd;
@@ -422,11 +423,11 @@ static void fa_post_packet(void * comp,
cmd = malloc(sizeof(*cmd));
if (cmd == NULL) {
log_err("Command failed. Out of memory.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return;
}
- cmd->sdb = sdb;
+ cmd->spb = spb;
pthread_mutex_lock(&fa.mtx);
@@ -454,16 +455,16 @@ static size_t fa_wait_for_fa_msg(struct fa_msg * msg)
pthread_cleanup_pop(true);
- len = shm_du_buff_len(cmd->sdb);
+ len = ssm_pk_buff_len(cmd->spb);
if (len > MSGBUFSZ || len < sizeof(*msg)) {
log_warn("Invalid flow allocation message (len: %zd).", len);
free(cmd);
return 0; /* No valid message */
}
- memcpy(msg, shm_du_buff_head(cmd->sdb), len);
+ memcpy(msg, ssm_pk_buff_head(cmd->spb), len);
- ipcp_sdb_release(cmd->sdb);
+ ipcp_spb_release(cmd->spb);
free(cmd);
@@ -497,7 +498,6 @@ static int fa_handle_flow_req(struct fa_msg * msg,
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
- qs.cypher_s = ntoh16(msg->cypher_s);
qs.timeout = ntoh32(msg->timeout);
fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_UNICAST_MPL, &data);
@@ -526,6 +526,7 @@ static int fa_handle_flow_reply(struct fa_msg * msg,
struct fa_flow * flow;
buffer_t data; /* Piggbacked data on flow alloc request. */
time_t mpl = IPCP_UNICAST_MPL;
+ int response;
assert(len >= sizeof(*msg));
@@ -545,15 +546,19 @@ static int fa_handle_flow_reply(struct fa_msg * msg,
flow = &fa.flows[fd];
flow->r_eid = ntoh64(msg->s_eid);
+ response = ntoh32(msg->response);
+
+ log_dbg("IPCP received msg response %d for flow on fd %d.",
+ response, fd);
- if (msg->response < 0)
+ if (response < 0)
fa_flow_fini(flow);
else
psched_add(fa.psched, fd);
pthread_rwlock_unlock(&fa.flows_lock);
- if (ipcp_flow_alloc_reply(fd, msg->response, mpl, &data) < 0) {
+ if (ipcp_flow_alloc_reply(fd, response, mpl, &data) < 0) {
log_err("Failed to reply for flow allocation on fd %d.", fd);
return -EIRMD;
}
@@ -647,19 +652,21 @@ int fa_init(void)
if (pthread_cond_init(&fa.cond, &cattr))
goto fail_cond;
- pthread_condattr_destroy(&cattr);
-
- list_head_init(&fa.cmds);
-
if (rib_reg(FA, &r_ops))
goto fail_rib_reg;
fa.eid = dt_reg_comp(&fa, &fa_post_packet, FA);
if ((int) fa.eid < 0)
- goto fail_rib_reg;
+ goto fail_dt_reg;
+
+ list_head_init(&fa.cmds);
+
+ pthread_condattr_destroy(&cattr);
return 0;
+ fail_dt_reg:
+ rib_unreg(FA);
fail_rib_reg:
pthread_cond_destroy(&fa.cond);
fail_cond:
@@ -669,7 +676,6 @@ int fa_init(void)
fail_mtx:
pthread_rwlock_destroy(&fa.flows_lock);
fail_rwlock:
-
return -1;
}
@@ -682,13 +688,21 @@ void fa_fini(void)
pthread_rwlock_destroy(&fa.flows_lock);
}
+static int np1_flow_read_fa(int fd,
+ struct ssm_pk_buff ** spb)
+{
+ return np1_flow_read(fd, spb, NP1_GET_POOL(fd));
+}
+
int fa_start(void)
{
+#ifndef BUILD_CONTAINER
struct sched_param par;
int pol;
int max;
+#endif
- fa.psched = psched_create(packet_handler, np1_flow_read);
+ fa.psched = psched_create(packet_handler, np1_flow_read_fa);
if (fa.psched == NULL) {
log_err("Failed to start packet scheduler.");
goto fail_psched;
@@ -699,6 +713,7 @@ int fa_start(void)
goto fail_thread;
}
+#ifndef BUILD_CONTAINER
if (pthread_getschedparam(fa.worker, &pol, &par)) {
log_err("Failed to get worker thread scheduling parameters.");
goto fail_sched;
@@ -716,12 +731,15 @@ int fa_start(void)
log_err("Failed to set scheduler priority to maximum.");
goto fail_sched;
}
+#endif
return 0;
+#ifndef BUILD_CONTAINER
fail_sched:
pthread_cancel(fa.worker);
pthread_join(fa.worker, NULL);
+#endif
fail_thread:
psched_destroy(fa.psched);
fail_psched:
@@ -742,7 +760,7 @@ int fa_alloc(int fd,
const buffer_t * data)
{
struct fa_msg * msg;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
struct fa_flow * flow;
uint64_t addr;
qoscube_t qc = QOS_CUBE_BE;
@@ -755,17 +773,17 @@ int fa_alloc(int fd,
len = sizeof(*msg) + ipcp_dir_hash_len();
- if (ipcp_sdb_reserve(&sdb, len + data->len))
+ if (ipcp_spb_reserve(&spb, len + data->len))
return -1;
- msg = (struct fa_msg *) shm_du_buff_head(sdb);
+ msg = (struct fa_msg *) ssm_pk_buff_head(spb);
memset(msg, 0, sizeof(*msg));
eid = gen_eid(fd);
msg->code = FLOW_REQ;
msg->s_eid = hton64(eid);
- msg->s_addr = hton64(ipcpi.dt_addr);
+ msg->s_addr = hton64(addr_auth_address());
msg->delay = hton32(qs.delay);
msg->bandwidth = hton64(qs.bandwidth);
msg->availability = qs.availability;
@@ -773,16 +791,15 @@ int fa_alloc(int fd,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
- msg->cypher_s = hton16(qs.cypher_s);
msg->timeout = hton32(qs.timeout);
memcpy(msg + 1, dst, ipcp_dir_hash_len());
if (data->len > 0)
- memcpy(shm_du_buff_head(sdb) + len, data->data, data->len);
+ memcpy(ssm_pk_buff_head(spb) + len, data->data, data->len);
- if (dt_write_packet(addr, qc, fa.eid, sdb)) {
+ if (dt_write_packet(addr, qc, fa.eid, spb)) {
log_err("Failed to send flow allocation request packet.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return -1;
}
@@ -804,7 +821,7 @@ int fa_alloc_resp(int fd,
const buffer_t * data)
{
struct fa_msg * msg;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
struct fa_flow * flow;
qoscube_t qc = QOS_CUBE_BE;
@@ -815,17 +832,17 @@ int fa_alloc_resp(int fd,
goto fail_alloc_resp;
}
- if (ipcp_sdb_reserve(&sdb, sizeof(*msg) + data->len)) {
- log_err("Failed to reserve sdb (%zu bytes).",
+ if (ipcp_spb_reserve(&spb, sizeof(*msg) + data->len)) {
+ log_err("Failed to reserve spb (%zu bytes).",
sizeof(*msg) + data->len);
goto fail_reserve;
}
- msg = (struct fa_msg *) shm_du_buff_head(sdb);
+ msg = (struct fa_msg *) ssm_pk_buff_head(spb);
memset(msg, 0, sizeof(*msg));
msg->code = FLOW_REPLY;
- msg->response = response;
+ msg->response = hton32(response);
if (data->len > 0)
memcpy(msg + 1, data->data, data->len);
@@ -836,13 +853,13 @@ int fa_alloc_resp(int fd,
pthread_rwlock_unlock(&fa.flows_lock);
- if (dt_write_packet(flow->r_addr, qc, fa.eid, sdb)) {
+ if (dt_write_packet(flow->r_addr, qc, fa.eid, spb)) {
log_err("Failed to send flow allocation response packet.");
goto fail_packet;
}
if (response < 0) {
- pthread_rwlock_rdlock(&fa.flows_lock);
+ pthread_rwlock_wrlock(&fa.flows_lock);
fa_flow_fini(flow);
pthread_rwlock_unlock(&fa.flows_lock);
} else {
@@ -852,7 +869,7 @@ int fa_alloc_resp(int fd,
return 0;
fail_packet:
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
fail_reserve:
pthread_rwlock_wrlock(&fa.flows_lock);
fa_flow_fini(flow);
@@ -883,17 +900,17 @@ static int fa_update_remote(int fd,
uint16_t ece)
{
struct fa_msg * msg;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
qoscube_t qc = QOS_CUBE_BE;
struct fa_flow * flow;
uint64_t r_addr;
- if (ipcp_sdb_reserve(&sdb, sizeof(*msg))) {
- log_err("Failed to reserve sdb (%zu bytes).", sizeof(*msg));
+ if (ipcp_spb_reserve(&spb, sizeof(*msg))) {
+ log_err("Failed to reserve spb (%zu bytes).", sizeof(*msg));
return -1;
}
- msg = (struct fa_msg *) shm_du_buff_head(sdb);
+ msg = (struct fa_msg *) ssm_pk_buff_head(spb);
memset(msg, 0, sizeof(*msg));
@@ -912,9 +929,9 @@ static int fa_update_remote(int fd,
pthread_rwlock_unlock(&fa.flows_lock);
- if (dt_write_packet(r_addr, qc, fa.eid, sdb)) {
+ if (dt_write_packet(r_addr, qc, fa.eid, spb)) {
log_err("Failed to send flow update packet.");
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return -1;
}
@@ -923,7 +940,7 @@ static int fa_update_remote(int fd,
void fa_np1_rcv(uint64_t eid,
uint8_t ecn,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct fa_flow * flow;
bool update;
@@ -931,7 +948,7 @@ void fa_np1_rcv(uint64_t eid,
int fd;
size_t len;
- len = shm_du_buff_len(sdb);
+ len = ssm_pk_buff_len(spb);
pthread_rwlock_wrlock(&fa.flows_lock);
@@ -939,7 +956,7 @@ void fa_np1_rcv(uint64_t eid,
if (fd < 0) {
pthread_rwlock_unlock(&fa.flows_lock);
log_dbg("Received packet for unknown EID %" PRIu64 ".", eid);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return;
}
@@ -953,9 +970,9 @@ void fa_np1_rcv(uint64_t eid,
pthread_rwlock_unlock(&fa.flows_lock);
- if (ipcp_flow_write(fd, sdb) < 0) {
+ if (np1_flow_write(fd, spb, NP1_GET_POOL(fd)) < 0) {
log_dbg("Failed to write to flow %d.", fd);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
#ifdef IPCP_FLOW_STATS
pthread_rwlock_wrlock(&fa.flows_lock);
++flow->p_rcv_f;
diff --git a/src/ipcpd/unicast/fa.h b/src/ipcpd/unicast/fa.h
index 1e716966..6cd30995 100644
--- a/src/ipcpd/unicast/fa.h
+++ b/src/ipcpd/unicast/fa.h
@@ -47,6 +47,6 @@ int fa_dealloc(int fd);
void fa_np1_rcv(uint64_t eid,
uint8_t ecn,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb);
#endif /* OUROBOROS_IPCPD_UNICAST_FA_H */
diff --git a/src/ipcpd/unicast/main.c b/src/ipcpd/unicast/main.c
index e6cb2994..7989d3e1 100644
--- a/src/ipcpd/unicast/main.c
+++ b/src/ipcpd/unicast/main.c
@@ -55,13 +55,8 @@
#include <assert.h>
#include <inttypes.h>
-struct ipcp ipcpi;
-
-static int initialize_components(const struct ipcp_config * conf)
+static int initialize_components(struct ipcp_config * conf)
{
- strcpy(ipcpi.layer_name, conf->layer_info.name);
- ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo;
-
assert(ipcp_dir_hash_len() != 0);
if (addr_auth_init(conf->unicast.addr_auth_type,
@@ -70,13 +65,7 @@ static int initialize_components(const struct ipcp_config * conf)
goto fail_addr_auth;
}
- ipcpi.dt_addr = addr_auth_address();
- if (ipcpi.dt_addr == 0) {
- log_err("Failed to get a valid address.");
- goto fail_addr_auth;
- }
-
- log_info("IPCP got address %" PRIu64 ".", ipcpi.dt_addr);
+ log_info("IPCP got address %" PRIu64 ".", addr_auth_address());
if (ca_init(conf->unicast.cong_avoid)) {
log_err("Failed to initialize congestion avoidance.");
@@ -88,23 +77,25 @@ static int initialize_components(const struct ipcp_config * conf)
goto fail_dt;
}
- if (fa_init()) {
- log_err("Failed to initialize flow allocator component.");
- goto fail_fa;
- }
+ ipcp_set_dir_hash_algo((enum hash_algo) conf->layer_info.dir_hash_algo);
- if (dir_init()) {
+ if (dir_init(&conf->unicast.dir)) {
log_err("Failed to initialize directory.");
goto fail_dir;
}
+ if (fa_init()) {
+ log_err("Failed to initialize flow allocator component.");
+ goto fail_fa;
+ }
+
ipcp_set_state(IPCP_INIT);
return 0;
- fail_dir:
- fa_fini();
fail_fa:
+ dir_fini();
+ fail_dir:
dt_fini();
fail_dt:
ca_fini();
@@ -116,10 +107,10 @@ static int initialize_components(const struct ipcp_config * conf)
static void finalize_components(void)
{
- dir_fini();
-
fa_fini();
+ dir_fini();
+
dt_fini();
ca_fini();
@@ -129,6 +120,11 @@ static void finalize_components(void)
static int start_components(void)
{
+ if (connmgr_start() < 0) {
+ log_err("Failed to start AP connection manager.");
+ goto fail_connmgr_start;
+ }
+
if (dt_start() < 0) {
log_err("Failed to start data transfer.");
goto fail_dt_start;
@@ -144,27 +140,29 @@ static int start_components(void)
goto fail_enroll_start;
}
- if (connmgr_start() < 0) {
- log_err("Failed to start AP connection manager.");
- goto fail_connmgr_start;
+ if (dir_start() < 0) {
+ log_err("Failed to start directory.");
+ goto fail_dir_start;
}
return 0;
- fail_connmgr_start:
+ fail_dir_start:
enroll_stop();
fail_enroll_start:
fa_stop();
fail_fa_start:
dt_stop();
fail_dt_start:
+ connmgr_stop();
+ fail_connmgr_start:
ipcp_set_state(IPCP_INIT);
return -1;
}
static void stop_components(void)
{
- connmgr_stop();
+ dir_stop();
enroll_stop();
@@ -172,24 +170,17 @@ static void stop_components(void)
dt_stop();
- ipcp_set_state(IPCP_INIT);
-}
-
-static int bootstrap_components(void)
-{
- if (dir_bootstrap()) {
- log_err("Failed to bootstrap directory.");
- return -1;
- }
+ connmgr_stop();
- return 0;
+ ipcp_set_state(IPCP_BOOT);
}
static int unicast_ipcp_enroll(const char * dst,
struct layer_info * info)
{
- struct conn conn;
- uint8_t id[ENROLL_ID_LEN];
+ struct ipcp_config * conf;
+ struct conn conn;
+ uint8_t id[ENROLL_ID_LEN];
if (random_buffer(id, ENROLL_ID_LEN) < 0) {
log_err("Failed to generate enrollment ID.");
@@ -209,7 +200,11 @@ static int unicast_ipcp_enroll(const char * dst,
goto fail_enroll_boot;
}
- if (initialize_components(enroll_get_conf()) < 0) {
+ conf = enroll_get_conf();
+
+ *info = conf->layer_info;
+
+ if (initialize_components(conf) < 0) {
log_err_id(id, "Failed to initialize components.");
goto fail_enroll_boot;
}
@@ -227,9 +222,6 @@ static int unicast_ipcp_enroll(const char * dst,
log_info_id(id, "Enrolled with %s.", dst);
- info->dir_hash_algo = (enum pol_dir_hash) ipcpi.dir_hash_algo;
- strcpy(info->name, ipcpi.layer_name);
-
return 0;
fail_start_comp:
@@ -240,32 +232,25 @@ static int unicast_ipcp_enroll(const char * dst,
return -1;
}
-static int unicast_ipcp_bootstrap(const struct ipcp_config * conf)
+static int unicast_ipcp_bootstrap(struct ipcp_config * conf)
{
assert(conf);
assert(conf->type == THIS_TYPE);
- enroll_bootstrap(conf);
-
if (initialize_components(conf) < 0) {
log_err("Failed to init IPCP components.");
goto fail_init;
}
+ enroll_bootstrap(conf);
+
if (start_components() < 0) {
log_err("Failed to init IPCP components.");
goto fail_start;
}
- if (bootstrap_components() < 0) {
- log_err("Failed to bootstrap IPCP components.");
- goto fail_bootstrap;
- }
-
return 0;
- fail_bootstrap:
- stop_components();
fail_start:
finalize_components();
fail_init:
@@ -323,11 +308,12 @@ int main(int argc,
if (ipcp_get_state() == IPCP_SHUTDOWN) {
stop_components();
+ ipcp_stop();
finalize_components();
+ } else {
+ ipcp_stop();
}
- ipcp_stop();
-
enroll_fini();
connmgr_fini();
diff --git a/src/ipcpd/unicast/pff/tests/CMakeLists.txt b/src/ipcpd/unicast/pff/tests/CMakeLists.txt
index e7082372..8c0e3d51 100644
--- a/src/ipcpd/unicast/pff/tests/CMakeLists.txt
+++ b/src/ipcpd/unicast/pff/tests/CMakeLists.txt
@@ -3,32 +3,32 @@ get_filename_component(CURRENT_SOURCE_PARENT_DIR
get_filename_component(CURRENT_BINARY_PARENT_DIR
${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+compute_test_prefix()
+
create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
# Add new tests here
pft_test.c
)
-add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests})
-target_link_libraries(${PARENT_DIR}_test ouroboros-common)
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests})
+
+target_include_directories(${PARENT_DIR}_test PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CURRENT_SOURCE_PARENT_DIR}
+ ${CURRENT_BINARY_PARENT_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src/ipcpd
+ ${CMAKE_BINARY_DIR}/src/ipcpd
+)
-add_dependencies(check ${PARENT_DIR}_test)
+disable_test_logging_for_target(${PARENT_DIR}_test)
+target_link_libraries(${PARENT_DIR}_test PRIVATE ouroboros-common)
-set(tests_to_run ${${PARENT_DIR}_tests})
-remove(tests_to_run test_suite.c)
+add_dependencies(build_tests ${PARENT_DIR}_test)
-foreach (test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(${test_name} ${C_TEST_PATH}/${PARENT_DIR}_test ${test_name})
-endforeach (test)
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
diff --git a/src/ipcpd/unicast/psched.c b/src/ipcpd/unicast/psched.c
index 7e12148b..8c8caf19 100644
--- a/src/ipcpd/unicast/psched.c
+++ b/src/ipcpd/unicast/psched.c
@@ -41,11 +41,13 @@
#include <stdlib.h>
#include <string.h>
+#ifndef BUILD_CONTAINER
static int qos_prio [] = {
QOS_PRIO_BE,
QOS_PRIO_VIDEO,
QOS_PRIO_VOICE,
};
+#endif
struct psched {
fset_t * set[QOS_CUBE_MAX];
@@ -67,7 +69,7 @@ static void cleanup_reader(void * o)
static void * packet_reader(void * o)
{
struct psched * sched;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
int fd;
fqueue_t * fq;
qoscube_t qc;
@@ -102,10 +104,10 @@ static void * packet_reader(void * o)
notifier_event(NOTIFY_DT_FLOW_UP, &fd);
break;
case FLOW_PKT:
- if (sched->read(fd, &sdb) < 0)
+ if (sched->read(fd, &spb) < 0)
continue;
- sched->callback(fd, qc, sdb);
+ sched->callback(fd, qc, spb);
break;
default:
break;
@@ -168,6 +170,7 @@ struct psched * psched_create(next_packet_fn_t callback,
}
}
+#ifndef BUILD_CONTAINER
for (i = 0; i < QOS_CUBE_MAX * IPCP_SCHED_THR_MUL; ++i) {
struct sched_param par;
int pol = SCHED_RR;
@@ -185,14 +188,17 @@ struct psched * psched_create(next_packet_fn_t callback,
if (pthread_setschedparam(psched->readers[i], pol, &par))
goto fail_sched;
}
+#endif
return psched;
+#ifndef BUILD_CONTAINER
fail_sched:
for (j = 0; j < QOS_CUBE_MAX * IPCP_SCHED_THR_MUL; ++j)
pthread_cancel(psched->readers[j]);
for (j = 0; j < QOS_CUBE_MAX * IPCP_SCHED_THR_MUL; ++j)
pthread_join(psched->readers[j], NULL);
+#endif
fail_infos:
for (j = 0; j < QOS_CUBE_MAX; ++j)
fset_destroy(psched->set[j]);
diff --git a/src/ipcpd/unicast/psched.h b/src/ipcpd/unicast/psched.h
index 831f8084..be9c09e3 100644
--- a/src/ipcpd/unicast/psched.h
+++ b/src/ipcpd/unicast/psched.h
@@ -28,10 +28,10 @@
typedef void (* next_packet_fn_t)(int fd,
qoscube_t qc,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb);
typedef int (* read_fn_t)(int fd,
- struct shm_du_buff ** sdb);
+ struct ssm_pk_buff ** spb);
struct psched * psched_create(next_packet_fn_t callback,
read_fn_t read);
diff --git a/src/ipcpd/unicast/routing.c b/src/ipcpd/unicast/routing.c
index f5417c24..2ad7b234 100644
--- a/src/ipcpd/unicast/routing.c
+++ b/src/ipcpd/unicast/routing.c
@@ -30,31 +30,26 @@
struct routing_ops * r_ops;
-int routing_init(enum pol_routing pr)
+int routing_init(struct routing_config * conf,
+ enum pol_pff * pff_type)
{
- enum pol_pff pff_type;
+ void * cfg;
- switch (pr) {
+ switch (conf->pol) {
case ROUTING_LINK_STATE:
- pff_type = PFF_SIMPLE;
- r_ops = &link_state_ops;
- break;
- case ROUTING_LINK_STATE_LFA:
- pff_type = PFF_ALTERNATE;
- r_ops = &link_state_ops;
- break;
- case ROUTING_LINK_STATE_ECMP:
- pff_type=PFF_MULTIPATH;
r_ops = &link_state_ops;
+ cfg = &conf->ls;
break;
default:
return -ENOTSUP;
}
- if (r_ops->init(pr))
- return -1;
+ return r_ops->init(cfg, pff_type);
+}
- return pff_type;
+int routing_start(void)
+{
+ return r_ops->start();
}
struct routing_i * routing_i_create(struct pff * pff)
@@ -67,6 +62,11 @@ void routing_i_destroy(struct routing_i * instance)
return r_ops->routing_i_destroy(instance);
}
+void routing_stop(void)
+{
+ r_ops->stop();
+}
+
void routing_fini(void)
{
r_ops->fini();
diff --git a/src/ipcpd/unicast/routing.h b/src/ipcpd/unicast/routing.h
index d5d833ae..e14960b5 100644
--- a/src/ipcpd/unicast/routing.h
+++ b/src/ipcpd/unicast/routing.h
@@ -30,10 +30,15 @@
#include <stdint.h>
-int routing_init(enum pol_routing pr);
+int routing_init(struct routing_config * conf,
+ enum pol_pff * pff_type);
void routing_fini(void);
+int routing_start(void);
+
+void routing_stop(void);
+
struct routing_i * routing_i_create(struct pff * pff);
void routing_i_destroy(struct routing_i * instance);
diff --git a/src/ipcpd/unicast/routing/graph.c b/src/ipcpd/unicast/routing/graph.c
index 32f3e6fb..32442dad 100644
--- a/src/ipcpd/unicast/routing/graph.c
+++ b/src/ipcpd/unicast/routing/graph.c
@@ -57,8 +57,11 @@ struct edge {
};
struct graph {
- size_t nr_vertices;
- struct list_head vertices;
+ struct {
+ struct list_head list;
+ size_t len;
+ } vertices;
+
pthread_mutex_t lock;
};
@@ -67,7 +70,7 @@ static struct edge * find_edge_by_addr(struct vertex * vertex,
{
struct list_head * p;
- assert(vertex);
+ assert(vertex != NULL);
list_for_each(p, &vertex->edges) {
struct edge * e = list_entry(p, struct edge, next);
@@ -85,7 +88,7 @@ static struct vertex * find_vertex_by_addr(struct graph * graph,
assert(graph);
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
struct vertex * e = list_entry(p, struct vertex, next);
if (e->addr == addr)
return e;
@@ -99,8 +102,8 @@ static struct edge * add_edge(struct vertex * vertex,
{
struct edge * edge;
- assert(vertex);
- assert(nb);
+ assert(vertex != NULL);
+ assert(nb != NULL);
edge = malloc(sizeof(*edge));
if (edge == NULL)
@@ -139,7 +142,7 @@ static struct vertex * add_vertex(struct graph * graph,
vertex->addr = addr;
/* Keep them ordered on address. */
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
struct vertex * v = list_entry(p, struct vertex, next);
if (v->addr > addr)
break;
@@ -151,13 +154,13 @@ static struct vertex * add_vertex(struct graph * graph,
list_add_tail(&vertex->next, p);
/* Increase the index of the vertices to the right. */
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &vertex->next) {
struct vertex * v = list_entry(p, struct vertex, next);
if (v->addr > addr)
v->index++;
}
- graph->nr_vertices++;
+ ++graph->vertices.len;
return vertex;
}
@@ -168,13 +171,13 @@ static void del_vertex(struct graph * graph,
struct list_head * p;
struct list_head * h;
- assert(graph);
- assert(vertex);
+ assert(graph != NULL);
+ assert(vertex != NULL);
list_del(&vertex->next);
/* Decrease the index of the vertices to the right. */
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
struct vertex * v = list_entry(p, struct vertex, next);
if (v->addr > vertex->addr)
v->index--;
@@ -187,7 +190,7 @@ static void del_vertex(struct graph * graph,
free(vertex);
- graph->nr_vertices--;
+ --graph->vertices.len;
}
struct graph * graph_create(void)
@@ -203,8 +206,8 @@ struct graph * graph_create(void)
return NULL;
}
- graph->nr_vertices = 0;
- list_head_init(&graph->vertices);
+ graph->vertices.len = 0;
+ list_head_init(&graph->vertices.list);
return graph;
}
@@ -218,7 +221,7 @@ void graph_destroy(struct graph * graph)
pthread_mutex_lock(&graph->lock);
- list_for_each_safe(p, n, &graph->vertices) {
+ list_for_each_safe(p, n, &graph->vertices.list) {
struct vertex * e = list_entry(p, struct vertex, next);
del_vertex(graph, e);
}
@@ -227,6 +230,8 @@ void graph_destroy(struct graph * graph)
pthread_mutex_destroy(&graph->lock);
+ assert(graph->vertices.len == 0);
+
free(graph);
}
@@ -240,63 +245,35 @@ int graph_update_edge(struct graph * graph,
struct vertex * nb;
struct edge * nb_e;
- assert(graph);
+ assert(graph != NULL);
pthread_mutex_lock(&graph->lock);
v = find_vertex_by_addr(graph, s_addr);
- if (v == NULL) {
- v = add_vertex(graph, s_addr);
- if (v == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add vertex.");
- return -ENOMEM;
- }
+ if (v == NULL && ((v = add_vertex(graph, s_addr)) == NULL)) {;
+ log_err("Failed to add src vertex.");
+ goto fail_add_s;
}
nb = find_vertex_by_addr(graph, d_addr);
- if (nb == NULL) {
- nb = add_vertex(graph, d_addr);
- if (nb == NULL) {
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add vertex.");
- return -ENOMEM;
- }
+ if (nb == NULL && ((nb = add_vertex(graph, d_addr)) == NULL)) {
+ log_err("Failed to add dst vertex.");
+ goto fail_add_d;
}
e = find_edge_by_addr(v, d_addr);
- if (e == NULL) {
- e = add_edge(v, nb);
- if (e == NULL) {
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- if (list_is_empty(&nb->edges))
- del_vertex(graph, nb);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add edge.");
- return -ENOMEM;
- }
+ if (e == NULL && ((e = add_edge(v, nb)) == NULL)) {
+ log_err("Failed to add edge to dst.");
+ goto fail_add_edge_d;
}
e->announced++;
e->qs = qs;
nb_e = find_edge_by_addr(nb, s_addr);
- if (nb_e == NULL) {
- nb_e = add_edge(nb, v);
- if (nb_e == NULL) {
- if (--e->announced == 0)
- del_edge(e);
- if (list_is_empty(&v->edges))
- del_vertex(graph, v);
- if (list_is_empty(&nb->edges))
- del_vertex(graph, nb);
- pthread_mutex_unlock(&graph->lock);
- log_err("Failed to add edge.");
- return -ENOMEM;
- }
+ if (nb_e == NULL && ((nb_e = add_edge(nb, v)) == NULL)) {;
+ log_err("Failed to add edge to src.");
+ goto fail_add_edge_s;
}
nb_e->announced++;
@@ -305,6 +282,19 @@ int graph_update_edge(struct graph * graph,
pthread_mutex_unlock(&graph->lock);
return 0;
+ fail_add_edge_s:
+ if (--e->announced == 0)
+ del_edge(e);
+ fail_add_edge_d:
+ if (list_is_empty(&nb->edges))
+ del_vertex(graph, nb);
+ fail_add_d:
+ if (list_is_empty(&v->edges))
+ del_vertex(graph, v);
+ fail_add_s:
+ pthread_mutex_unlock(&graph->lock);
+ return -ENOMEM;
+
}
int graph_del_edge(struct graph * graph,
@@ -322,30 +312,26 @@ int graph_del_edge(struct graph * graph,
v = find_vertex_by_addr(graph, s_addr);
if (v == NULL) {
- pthread_mutex_unlock(&graph->lock);
- log_err("No such source vertex.");
- return -1;
+ log_err("Failed to find src vertex.");
+ goto fail;
}
nb = find_vertex_by_addr(graph, d_addr);
if (nb == NULL) {
- pthread_mutex_unlock(&graph->lock);
log_err("No such destination vertex.");
- return -1;
+ goto fail;
}
e = find_edge_by_addr(v, d_addr);
if (e == NULL) {
- pthread_mutex_unlock(&graph->lock);
log_err("No such source edge.");
- return -1;
+ goto fail;
}
nb_e = find_edge_by_addr(nb, s_addr);
if (nb_e == NULL) {
- pthread_mutex_unlock(&graph->lock);
log_err("No such destination edge.");
- return -1;
+ goto fail;
}
if (--e->announced == 0)
@@ -362,6 +348,10 @@ int graph_del_edge(struct graph * graph,
pthread_mutex_unlock(&graph->lock);
return 0;
+
+ fail:
+ pthread_mutex_unlock(&graph->lock);
+ return -1;
}
static int get_min_vertex(struct graph * graph,
@@ -381,7 +371,7 @@ static int get_min_vertex(struct graph * graph,
*v = NULL;
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
if (!used[i] && dist[i] < min) {
min = dist[i];
index = i;
@@ -413,24 +403,24 @@ static int dijkstra(struct graph * graph,
assert(nhops);
assert(dist);
- *nhops = malloc(sizeof(**nhops) * graph->nr_vertices);
+ *nhops = malloc(sizeof(**nhops) * graph->vertices.len);
if (*nhops == NULL)
goto fail_pnhops;
- *dist = malloc(sizeof(**dist) * graph->nr_vertices);
+ *dist = malloc(sizeof(**dist) * graph->vertices.len);
if (*dist == NULL)
goto fail_pdist;
- used = malloc(sizeof(*used) * graph->nr_vertices);
+ used = malloc(sizeof(*used) * graph->vertices.len);
if (used == NULL)
goto fail_used;
/* Init the data structures */
- memset(used, 0, sizeof(*used) * graph->nr_vertices);
- memset(*nhops, 0, sizeof(**nhops) * graph->nr_vertices);
- memset(*dist, 0, sizeof(**dist) * graph->nr_vertices);
+ memset(used, 0, sizeof(*used) * graph->vertices.len);
+ memset(*nhops, 0, sizeof(**nhops) * graph->vertices.len);
+ memset(*dist, 0, sizeof(**dist) * graph->vertices.len);
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
v = list_entry(p, struct vertex, next);
(*dist)[i++] = (v->addr == src) ? 0 : INT_MAX;
}
@@ -527,7 +517,7 @@ static int graph_routing_table_simple(struct graph * graph,
assert(dist);
/* We need at least 2 vertices for a table */
- if (graph->nr_vertices < 2)
+ if (graph->vertices.len < 2)
goto fail_vertices;
if (dijkstra(graph, s_addr, &nhops, dist))
@@ -536,7 +526,7 @@ static int graph_routing_table_simple(struct graph * graph,
list_head_init(table);
/* Now construct the routing table from the nhops. */
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
v = list_entry(p, struct vertex, next);
/* This is the src */
@@ -634,7 +624,7 @@ static int graph_routing_table_lfa(struct graph * graph,
addrs[j] = -1;
}
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
v = list_entry(p, struct vertex, next);
if (v->addr != s_addr)
@@ -660,7 +650,7 @@ static int graph_routing_table_lfa(struct graph * graph,
}
/* Loop though all nodes to see if we have a LFA for them. */
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
v = list_entry(p, struct vertex, next);
if (v->addr == s_addr)
@@ -717,14 +707,14 @@ static int graph_routing_table_ecmp(struct graph * graph,
assert(graph);
assert(dist);
- if (graph-> nr_vertices < 2)
+ if (graph->vertices.len < 2)
goto fail_vertices;
- forwarding = malloc(sizeof(*forwarding) * graph->nr_vertices);
+ forwarding = malloc(sizeof(*forwarding) * graph->vertices.len);
if (forwarding == NULL)
goto fail_vertices;
- for (i = 0; i < graph->nr_vertices; ++i)
+ for (i = 0; i < graph->vertices.len; ++i)
list_head_init(&forwarding[i]);
if (dijkstra(graph, s_addr, &nhops, dist))
@@ -745,7 +735,7 @@ static int graph_routing_table_ecmp(struct graph * graph,
free(nhops);
- list_for_each(h, &graph->vertices) {
+ list_for_each(h, &graph->vertices.list) {
v = list_entry(h, struct vertex, next);
if (tmp_dist[v->index] + 1 == (*dist)[v->index]) {
n = malloc(sizeof(*n));
@@ -763,7 +753,7 @@ static int graph_routing_table_ecmp(struct graph * graph,
list_head_init(table);
i = 0;
- list_for_each(p, &graph->vertices) {
+ list_for_each(p, &graph->vertices.list) {
v = list_entry(p, struct vertex, next);
if (v->addr == s_addr) {
++i;
diff --git a/src/ipcpd/unicast/routing/link-state.c b/src/ipcpd/unicast/routing/link-state.c
index 57c0c7cb..95a104bb 100644
--- a/src/ipcpd/unicast/routing/link-state.c
+++ b/src/ipcpd/unicast/routing/link-state.c
@@ -42,6 +42,7 @@
#include <ouroboros/rib.h>
#include <ouroboros/utils.h>
+#include "addr-auth.h"
#include "common/comp.h"
#include "common/connmgr.h"
#include "graph.h"
@@ -54,16 +55,19 @@
#include <inttypes.h>
#include <string.h>
-#define RECALC_TIME 4
-#define LS_UPDATE_TIME 15
-#define LS_TIMEO 60
#define LS_ENTRY_SIZE 104
-#define LSDB "lsdb"
+#define Lspb "lspb"
#ifndef CLOCK_REALTIME_COARSE
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
#endif
+#define LINK_FMT ADDR_FMT32 "--" ADDR_FMT32
+#define LINK_VAL(src, dst) ADDR_VAL32(&src), ADDR_VAL32(&dst)
+
+#define LSU_FMT "LSU ["ADDR_FMT32 " -- " ADDR_FMT32 " seq: %09" PRIu64 "]"
+#define LSU_VAL(src, dst, seqno) ADDR_VAL32(&src), ADDR_VAL32(&dst), seqno
+
struct lsa {
uint64_t d_addr;
uint64_t s_addr;
@@ -106,30 +110,45 @@ struct nb {
};
struct {
- struct list_head nbs;
- size_t nbs_len;
+ uint64_t addr;
+
+ enum routing_algo routing_algo;
+
+ struct ls_config conf;
+
fset_t * mgmt_set;
- struct list_head db;
- size_t db_len;
+ struct graph * graph;
+
+ struct {
+ struct {
+ struct list_head list;
+ size_t len;
+ } nbs;
+
+ struct {
+ struct list_head list;
+ size_t len;
+ } db;
- pthread_rwlock_t db_lock;
+ pthread_rwlock_t lock;
+ };
- struct graph * graph;
+ struct {
+ struct list_head list;
+ pthread_mutex_t mtx;
+ } instances;
pthread_t lsupdate;
pthread_t lsreader;
pthread_t listener;
-
- struct list_head routing_instances;
- pthread_mutex_t routing_i_lock;
-
- enum routing_algo routing_algo;
} ls;
struct routing_ops link_state_ops = {
- .init = link_state_init,
+ .init = (int (*)(void *, enum pol_pff *)) link_state_init,
.fini = link_state_fini,
+ .start = link_state_start,
+ .stop = link_state_stop,
.routing_i_create = link_state_routing_i_create,
.routing_i_destroy = link_state_routing_i_destroy
};
@@ -138,7 +157,7 @@ static int str_adj(struct adjacency * adj,
char * buf,
size_t len)
{
- char tmbuf[64];
+ char tmstr[RIB_TM_STRLEN];
char srcbuf[64];
char dstbuf[64];
char seqnobuf[64];
@@ -149,15 +168,16 @@ static int str_adj(struct adjacency * adj,
if (len < LS_ENTRY_SIZE)
return -1;
- tm = localtime(&adj->stamp);
- strftime(tmbuf, sizeof(tmbuf), "%F %T", tm); /* 19 chars */
+ tm = gmtime(&adj->stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
- sprintf(srcbuf, "%" PRIu64, adj->src);
- sprintf(dstbuf, "%" PRIu64, adj->dst);
+ sprintf(srcbuf, ADDR_FMT32, ADDR_VAL32(&adj->src));
+ sprintf(dstbuf, ADDR_FMT32, ADDR_VAL32(&adj->dst));
sprintf(seqnobuf, "%" PRIu64, adj->seqno);
- sprintf(buf, "src: %20s\ndst: %20s\nseqno: %18s\nupd: %20s\n",
- srcbuf, dstbuf, seqnobuf, tmbuf);
+ sprintf(buf, "src: %20s\ndst: %20s\nseqno: %18s\n"
+ "upd: %s\n",
+ srcbuf, dstbuf, seqnobuf, tmstr);
return LS_ENTRY_SIZE;
}
@@ -169,9 +189,9 @@ static struct adjacency * get_adj(const char * path)
assert(path);
- list_for_each(p, &ls.db) {
+ list_for_each(p, &ls.db.list) {
struct adjacency * a = list_entry(p, struct adjacency, next);
- sprintf(entry, "%" PRIu64 ".%" PRIu64, a->src, a->dst);
+ sprintf(entry, LINK_FMT, LINK_VAL(a->src, a->dst));
if (strcmp(entry, path) == 0)
return a;
}
@@ -179,7 +199,7 @@ static struct adjacency * get_adj(const char * path)
return NULL;
}
-static int lsdb_rib_getattr(const char * path,
+static int lspb_rib_getattr(const char * path,
struct rib_attr * attr)
{
struct adjacency * adj;
@@ -194,7 +214,7 @@ static int lsdb_rib_getattr(const char * path,
clock_gettime(CLOCK_REALTIME_COARSE, &now);
- pthread_rwlock_rdlock(&ls.db_lock);
+ pthread_rwlock_rdlock(&ls.lock);
adj = get_adj(entry);
if (adj != NULL) {
@@ -205,12 +225,12 @@ static int lsdb_rib_getattr(const char * path,
attr->size = 0;
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return 0;
}
-static int lsdb_rib_read(const char * path,
+static int lspb_rib_read(const char * path,
char * buf,
size_t len)
{
@@ -223,9 +243,9 @@ static int lsdb_rib_read(const char * path,
entry = strstr(path, RIB_SEPARATOR) + 1;
assert(entry);
- pthread_rwlock_rdlock(&ls.db_lock);
+ pthread_rwlock_rdlock(&ls.lock);
- if (ls.db_len + ls.nbs_len == 0)
+ if (ls.db.len + ls.nbs.len == 0)
goto fail;
a = get_adj(entry);
@@ -236,111 +256,103 @@ static int lsdb_rib_read(const char * path,
if (size < 0)
goto fail;
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return size;
fail:
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -1;
}
-static int lsdb_rib_readdir(char *** buf)
+static int lspb_rib_readdir(char *** buf)
{
struct list_head * p;
char entry[RIB_PATH_LEN + 1];
ssize_t idx = 0;
- assert(buf);
+ assert(buf != NULL);
- pthread_rwlock_rdlock(&ls.db_lock);
+ pthread_rwlock_rdlock(&ls.lock);
- if (ls.db_len + ls.nbs_len == 0) {
- pthread_rwlock_unlock(&ls.db_lock);
- return 0;
+ if (ls.db.len + ls.nbs.len == 0) {
+ *buf = NULL;
+ goto no_entries;
}
- *buf = malloc(sizeof(**buf) * (ls.db_len + ls.nbs_len));
- if (*buf == NULL) {
- pthread_rwlock_unlock(&ls.db_lock);
- return -ENOMEM;
- }
- list_for_each(p, &ls.nbs) {
+ *buf = malloc(sizeof(**buf) * (ls.db.len + ls.nbs.len));
+ if (*buf == NULL)
+ goto fail_entries;
+
+ list_for_each(p, &ls.nbs.list) {
struct nb * nb = list_entry(p, struct nb, next);
- char * str = (nb->type == NB_DT ? "dt." : "mgmt.");
- sprintf(entry, "%s%" PRIu64, str, nb->addr);
+ char * str = (nb->type == NB_DT ? ".dt " : ".mgmt ");
+ sprintf(entry, "%s" ADDR_FMT32 , str, ADDR_VAL32(&nb->addr));
(*buf)[idx] = malloc(strlen(entry) + 1);
- if ((*buf)[idx] == NULL) {
- while (idx-- > 0)
- free((*buf)[idx]);
- free(*buf);
- pthread_rwlock_unlock(&ls.db_lock);
- return -ENOMEM;
- }
-
- strcpy((*buf)[idx], entry);
+ if ((*buf)[idx] == NULL)
+ goto fail_entry;
- idx++;
+ strcpy((*buf)[idx++], entry);
}
- list_for_each(p, &ls.db) {
+ list_for_each(p, &ls.db.list) {
struct adjacency * a = list_entry(p, struct adjacency, next);
- sprintf(entry, "%" PRIu64 ".%" PRIu64, a->src, a->dst);
+ sprintf(entry, LINK_FMT, LINK_VAL(a->src, a->dst));
(*buf)[idx] = malloc(strlen(entry) + 1);
- if ((*buf)[idx] == NULL) {
- ssize_t j;
- for (j = 0; j < idx; ++j)
- free(*buf[j]);
- free(buf);
- pthread_rwlock_unlock(&ls.db_lock);
- return -ENOMEM;
- }
-
- strcpy((*buf)[idx], entry);
+ if ((*buf)[idx] == NULL)
+ goto fail_entry;
- idx++;
+ strcpy((*buf)[idx++], entry);
}
-
- pthread_rwlock_unlock(&ls.db_lock);
+ no_entries:
+ pthread_rwlock_unlock(&ls.lock);
return idx;
+
+ fail_entry:
+ while (idx-- > 0)
+ free((*buf)[idx]);
+ free(*buf);
+ fail_entries:
+ pthread_rwlock_unlock(&ls.lock);
+ return -ENOMEM;
}
static struct rib_ops r_ops = {
- .read = lsdb_rib_read,
- .readdir = lsdb_rib_readdir,
- .getattr = lsdb_rib_getattr
+ .read = lspb_rib_read,
+ .readdir = lspb_rib_readdir,
+ .getattr = lspb_rib_getattr
};
-static int lsdb_add_nb(uint64_t addr,
+static int lspb_add_nb(uint64_t addr,
int fd,
enum nb_type type)
{
struct list_head * p;
struct nb * nb;
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- list_for_each(p, &ls.nbs) {
+ list_for_each(p, &ls.nbs.list) {
struct nb * el = list_entry(p, struct nb, next);
- if (el->addr == addr && el->type == type) {
- log_dbg("Already know %s neighbor %" PRIu64 ".",
- type == NB_DT ? "dt" : "mgmt", addr);
- if (el->fd != fd) {
- log_warn("Existing neighbor assigned new fd.");
- el->fd = fd;
- }
- pthread_rwlock_unlock(&ls.db_lock);
- return -EPERM;
- }
-
if (addr > el->addr)
break;
+ if (el->addr != addr || el->type != type)
+ continue;
+
+ log_dbg("Already know %s neighbor " ADDR_FMT32 ".",
+ type == NB_DT ? "dt" : "mgmt", ADDR_VAL32(&addr));
+ if (el->fd != fd) {
+ log_warn("Existing neighbor assigned new fd.");
+ el->fd = fd;
+ }
+ pthread_rwlock_unlock(&ls.lock);
+ return -EPERM;
}
nb = malloc(sizeof(*nb));
if (nb == NULL) {
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -ENOMEM;
}
@@ -350,38 +362,39 @@ static int lsdb_add_nb(uint64_t addr,
list_add_tail(&nb->next, p);
- ++ls.nbs_len;
+ ++ls.nbs.len;
- log_dbg("Type %s neighbor %" PRIu64 " added.",
- nb->type == NB_DT ? "dt" : "mgmt", addr);
+ log_dbg("Type %s neighbor " ADDR_FMT32 " added.",
+ nb->type == NB_DT ? "dt" : "mgmt", ADDR_VAL32(&addr));
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return 0;
}
-static int lsdb_del_nb(uint64_t addr,
+static int lspb_del_nb(uint64_t addr,
int fd)
{
struct list_head * p;
struct list_head * h;
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- list_for_each_safe(p, h, &ls.nbs) {
+ list_for_each_safe(p, h, &ls.nbs.list) {
struct nb * nb = list_entry(p, struct nb, next);
- if (nb->addr == addr && nb->fd == fd) {
- list_del(&nb->next);
- --ls.nbs_len;
- pthread_rwlock_unlock(&ls.db_lock);
- log_dbg("Type %s neighbor %" PRIu64 " deleted.",
- nb->type == NB_DT ? "dt" : "mgmt", addr);
- free(nb);
- return 0;
- }
+ if (nb->addr != addr || nb->fd != fd)
+ continue;
+
+ list_del(&nb->next);
+ --ls.nbs.len;
+ pthread_rwlock_unlock(&ls.lock);
+ log_dbg("Type %s neighbor " ADDR_FMT32 " deleted.",
+ nb->type == NB_DT ? "dt" : "mgmt", ADDR_VAL32(&addr));
+ free(nb);
+ return 0;
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -EPERM;
}
@@ -391,18 +404,18 @@ static int nbr_to_fd(uint64_t addr)
struct list_head * p;
int fd;
- pthread_rwlock_rdlock(&ls.db_lock);
+ pthread_rwlock_rdlock(&ls.lock);
- list_for_each(p, &ls.nbs) {
+ list_for_each(p, &ls.nbs.list) {
struct nb * nb = list_entry(p, struct nb, next);
if (nb->addr == addr && nb->type == NB_DT) {
fd = nb->fd;
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return fd;
}
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -1;
}
@@ -417,8 +430,7 @@ static void calculate_pff(struct routing_i * instance)
assert(instance);
- if (graph_routing_table(ls.graph, ls.routing_algo,
- ipcpi.dt_addr, &table))
+ if (graph_routing_table(ls.graph, ls.routing_algo, ls.addr, &table))
return;
pff_lock(instance->pff);
@@ -453,8 +465,8 @@ static void set_pff_modified(bool calc)
{
struct list_head * p;
- pthread_mutex_lock(&ls.routing_i_lock);
- list_for_each(p, &ls.routing_instances) {
+ pthread_mutex_lock(&ls.instances.mtx);
+ list_for_each(p, &ls.instances.list) {
struct routing_i * inst =
list_entry(p, struct routing_i, next);
pthread_mutex_lock(&inst->lock);
@@ -463,10 +475,10 @@ static void set_pff_modified(bool calc)
if (calc)
calculate_pff(inst);
}
- pthread_mutex_unlock(&ls.routing_i_lock);
+ pthread_mutex_unlock(&ls.instances.mtx);
}
-static int lsdb_add_link(uint64_t src,
+static int lspb_add_link(uint64_t src,
uint64_t dst,
uint64_t seqno,
qosspec_t * qs)
@@ -480,9 +492,9 @@ static int lsdb_add_link(uint64_t src,
clock_gettime(CLOCK_REALTIME_COARSE, &now);
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- list_for_each(p, &ls.db) {
+ list_for_each(p, &ls.db.list) {
struct adjacency * a = list_entry(p, struct adjacency, next);
if (a->dst == dst && a->src == src) {
if (a->seqno < seqno) {
@@ -490,7 +502,7 @@ static int lsdb_add_link(uint64_t src,
a->seqno = seqno;
ret = 0;
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return ret;
}
@@ -500,7 +512,7 @@ static int lsdb_add_link(uint64_t src,
adj = malloc(sizeof(*adj));
if (adj == NULL) {
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -ENOMEM;
}
@@ -511,43 +523,43 @@ static int lsdb_add_link(uint64_t src,
list_add_tail(&adj->next, p);
- ls.db_len++;
+ ls.db.len++;
if (graph_update_edge(ls.graph, src, dst, *qs))
log_warn("Failed to add edge to graph.");
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
set_pff_modified(true);
return 0;
}
-static int lsdb_del_link(uint64_t src,
+static int lspb_del_link(uint64_t src,
uint64_t dst)
{
struct list_head * p;
struct list_head * h;
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- list_for_each_safe(p, h, &ls.db) {
+ list_for_each_safe(p, h, &ls.db.list) {
struct adjacency * a = list_entry(p, struct adjacency, next);
if (a->dst == dst && a->src == src) {
list_del(&a->next);
if (graph_del_edge(ls.graph, src, dst))
log_warn("Failed to delete edge from graph.");
- ls.db_len--;
+ ls.db.len--;
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
set_pff_modified(false);
free(a);
return 0;
}
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
return -EPERM;
}
@@ -570,7 +582,7 @@ static void * periodic_recalc_pff(void * o)
if (modified)
calculate_pff(inst);
- sleep(RECALC_TIME);
+ sleep(ls.conf.t_recalc);
}
return (void *) 0;
@@ -587,15 +599,25 @@ static void send_lsm(uint64_t src,
lsm.s_addr = hton64(src);
lsm.seqno = hton64(seqno);
- list_for_each(p, &ls.nbs) {
+ list_for_each(p, &ls.nbs.list) {
struct nb * nb = list_entry(p, struct nb, next);
- if (nb->type == NB_MGMT)
- flow_write(nb->fd, &lsm, sizeof(lsm));
+ if (nb->type != NB_MGMT)
+ continue;
+
+ if (flow_write(nb->fd, &lsm, sizeof(lsm)) < 0)
+ log_err("Failed to send LSM to " ADDR_FMT32,
+ ADDR_VAL32(&nb->addr));
+#ifdef DEBUG_PROTO_LS
+ else
+ log_proto(LSU_FMT " --> " ADDR_FMT32,
+ LSU_VAL(src, dst, seqno),
+ ADDR_VAL32(&nb->addr));
+#endif
}
}
-/* replicate the lsdb to a mgmt neighbor */
-static void lsdb_replicate(int fd)
+/* replicate the lspb to a mgmt neighbor */
+static void lspb_replicate(int fd)
{
struct list_head * p;
struct list_head * h;
@@ -603,16 +625,16 @@ static void lsdb_replicate(int fd)
list_head_init(&copy);
- /* Lock the lsdb, copy the lsms and send outside of lock. */
- pthread_rwlock_rdlock(&ls.db_lock);
+ /* Lock the lspb, copy the lsms and send outside of lock. */
+ pthread_rwlock_rdlock(&ls.lock);
- list_for_each(p, &ls.db) {
+ list_for_each(p, &ls.db.list) {
struct adjacency * adj;
struct adjacency * cpy;
adj = list_entry(p, struct adjacency, next);
cpy = malloc(sizeof(*cpy));
if (cpy == NULL) {
- log_warn("Failed to replicate full lsdb.");
+ log_warn("Failed to replicate full lspb.");
break;
}
@@ -623,7 +645,7 @@ static void lsdb_replicate(int fd)
list_add_tail(&cpy->next, &copy);
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
list_for_each_safe(p, h, &copy) {
struct lsa lsm;
@@ -649,17 +671,17 @@ static void * lsupdate(void * o)
while (true) {
clock_gettime(CLOCK_REALTIME_COARSE, &now);
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.db_lock);
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.lock);
- list_for_each_safe(p, h, &ls.db) {
+ list_for_each_safe(p, h, &ls.db.list) {
struct adjacency * adj;
adj = list_entry(p, struct adjacency, next);
- if (now.tv_sec - adj->stamp > LS_TIMEO) {
+ if (now.tv_sec > adj->stamp + ls.conf.t_timeo) {
list_del(&adj->next);
- log_dbg("%" PRIu64 " - %" PRIu64" timed out.",
- adj->src, adj->dst);
+ log_dbg(LINK_FMT " timed out.",
+ LINK_VAL(adj->src, adj->dst));
if (graph_del_edge(ls.graph, adj->src,
adj->dst))
log_err("Failed to del edge.");
@@ -667,7 +689,7 @@ static void * lsupdate(void * o)
continue;
}
- if (adj->src == ipcpi.dt_addr) {
+ if (adj->src == ls.addr) {
adj->seqno++;
send_lsm(adj->src, adj->dst, adj->seqno);
adj->stamp = now.tv_sec;
@@ -676,7 +698,7 @@ static void * lsupdate(void * o)
pthread_cleanup_pop(true);
- sleep(LS_UPDATE_TIME);
+ sleep(ls.conf.t_update);
}
return (void *) 0;
@@ -708,15 +730,36 @@ static void forward_lsm(uint8_t * buf,
int in_fd)
{
struct list_head * p;
+#ifdef DEBUG_PROTO_LS
+ struct lsa lsm;
- pthread_rwlock_rdlock(&ls.db_lock);
+ assert(buf);
+ assert(len >= sizeof(struct lsa));
+
+ memcpy(&lsm, buf, sizeof(lsm));
+
+ lsm.s_addr = ntoh64(lsm.s_addr);
+ lsm.d_addr = ntoh64(lsm.d_addr);
+ lsm.seqno = ntoh64(lsm.seqno);
+#endif
+ pthread_rwlock_rdlock(&ls.lock);
- pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.db_lock);
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.lock);
- list_for_each(p, &ls.nbs) {
+ list_for_each(p, &ls.nbs.list) {
struct nb * nb = list_entry(p, struct nb, next);
- if (nb->type == NB_MGMT && nb->fd != in_fd)
- flow_write(nb->fd, buf, len);
+ if (nb->type != NB_MGMT || nb->fd == in_fd)
+ continue;
+
+ if (flow_write(nb->fd, buf, len) < 0)
+ log_err("Failed to forward LSM to " ADDR_FMT32,
+ ADDR_VAL32(&nb->addr));
+#ifdef DEBUG_PROTO_LS
+ else
+ log_proto(LSU_FMT " --> " ADDR_FMT32 " [forwarded]",
+ LSU_VAL(lsm.s_addr, lsm.d_addr, lsm.seqno),
+ ADDR_VAL32(&nb->addr));
+#endif
}
pthread_cleanup_pop(true);
@@ -729,13 +772,13 @@ static void cleanup_fqueue(void * fq)
static void * lsreader(void * o)
{
- fqueue_t * fq;
- int ret;
- uint8_t buf[sizeof(struct lsa)];
- int fd;
- qosspec_t qs;
- struct lsa * msg;
- size_t len;
+ fqueue_t * fq;
+ int ret;
+ uint8_t buf[sizeof(struct lsa)];
+ int fd;
+ qosspec_t qs;
+ struct lsa msg;
+ size_t len;
(void) o;
@@ -758,15 +801,22 @@ static void * lsreader(void * o)
if (fqueue_type(fq) != FLOW_PKT)
continue;
- len = flow_read(fd, buf, sizeof(*msg));
- if (len <= 0 || len != sizeof(*msg))
+ len = flow_read(fd, buf, sizeof(msg));
+ if (len <= 0 || len != sizeof(msg))
continue;
- msg = (struct lsa *) buf;
-
- if (lsdb_add_link(ntoh64(msg->s_addr),
- ntoh64(msg->d_addr),
- ntoh64(msg->seqno),
+ memcpy(&msg, buf, sizeof(msg));
+ msg.s_addr = ntoh64(msg.s_addr);
+ msg.d_addr = ntoh64(msg.d_addr);
+ msg.seqno = ntoh64(msg.seqno);
+#ifdef DEBUG_PROTO_LS
+ log_proto(LSU_FMT " <-- " ADDR_FMT32,
+ LSU_VAL(msg.s_addr, msg.d_addr, msg.seqno),
+ ADDR_VAL32(&ls.addr));
+#endif
+ if (lspb_add_link(msg.s_addr,
+ msg.d_addr,
+ msg.seqno,
&qs))
continue;
@@ -787,14 +837,14 @@ static void flow_event(int fd,
log_dbg("Notifying routing instances of flow event.");
- pthread_mutex_lock(&ls.routing_i_lock);
+ pthread_mutex_lock(&ls.instances.mtx);
- list_for_each(p, &ls.routing_instances) {
+ list_for_each(p, &ls.instances.list) {
struct routing_i * ri = list_entry(p, struct routing_i, next);
pff_flow_state_change(ri->pff, fd, up);
}
- pthread_mutex_unlock(&ls.routing_i_lock);
+ pthread_mutex_unlock(&ls.instances.mtx);
}
static void handle_event(void * self,
@@ -816,27 +866,27 @@ static void handle_event(void * self,
switch (event) {
case NOTIFY_DT_CONN_ADD:
- pthread_rwlock_rdlock(&ls.db_lock);
+ pthread_rwlock_rdlock(&ls.lock);
- pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.db_lock);
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &ls.lock);
- send_lsm(ipcpi.dt_addr, c->conn_info.addr, 0);
+ send_lsm(ls.addr, c->conn_info.addr, 0);
pthread_cleanup_pop(true);
- if (lsdb_add_nb(c->conn_info.addr, c->flow_info.fd, NB_DT))
- log_dbg("Failed to add neighbor to LSDB.");
+ if (lspb_add_nb(c->conn_info.addr, c->flow_info.fd, NB_DT))
+ log_dbg("Failed to add neighbor to Lspb.");
- if (lsdb_add_link(ipcpi.dt_addr, c->conn_info.addr, 0, &qs))
- log_dbg("Failed to add new adjacency to LSDB.");
+ if (lspb_add_link(ls.addr, c->conn_info.addr, 0, &qs))
+ log_dbg("Failed to add new adjacency to Lspb.");
break;
case NOTIFY_DT_CONN_DEL:
flow_event(c->flow_info.fd, false);
- if (lsdb_del_nb(c->conn_info.addr, c->flow_info.fd))
- log_dbg("Failed to delete neighbor from LSDB.");
+ if (lspb_del_nb(c->conn_info.addr, c->flow_info.fd))
+ log_dbg("Failed to delete neighbor from Lspb.");
- if (lsdb_del_link(ipcpi.dt_addr, c->conn_info.addr))
- log_dbg("Local link was not in LSDB.");
+ if (lspb_del_link(ls.addr, c->conn_info.addr))
+ log_dbg("Local link was not in Lspb.");
break;
case NOTIFY_DT_CONN_QOS:
log_dbg("QoS changes currently unsupported.");
@@ -851,15 +901,15 @@ static void handle_event(void * self,
fccntl(c->flow_info.fd, FLOWGFLAGS, &flags);
fccntl(c->flow_info.fd, FLOWSFLAGS, flags | FLOWFRNOPART);
fset_add(ls.mgmt_set, c->flow_info.fd);
- if (lsdb_add_nb(c->conn_info.addr, c->flow_info.fd, NB_MGMT))
- log_warn("Failed to add mgmt neighbor to LSDB.");
- /* replicate the entire lsdb */
- lsdb_replicate(c->flow_info.fd);
+ if (lspb_add_nb(c->conn_info.addr, c->flow_info.fd, NB_MGMT))
+ log_warn("Failed to add mgmt neighbor to Lspb.");
+ /* replicate the entire lspb */
+ lspb_replicate(c->flow_info.fd);
break;
case NOTIFY_MGMT_CONN_DEL:
fset_del(ls.mgmt_set, c->flow_info.fd);
- if (lsdb_del_nb(c->conn_info.addr, c->flow_info.fd))
- log_warn("Failed to delete mgmt neighbor from LSDB.");
+ if (lspb_del_nb(c->conn_info.addr, c->flow_info.fd))
+ log_warn("Failed to delete mgmt neighbor from Lspb.");
break;
default:
break;
@@ -886,11 +936,11 @@ struct routing_i * link_state_routing_i_create(struct pff * pff)
periodic_recalc_pff, tmp))
goto fail_pthread_create_lsupdate;
- pthread_mutex_lock(&ls.routing_i_lock);
+ pthread_mutex_lock(&ls.instances.mtx);
- list_add(&tmp->next, &ls.routing_instances);
+ list_add(&tmp->next, &ls.instances.list);
- pthread_mutex_unlock(&ls.routing_i_lock);
+ pthread_mutex_unlock(&ls.instances.mtx);
return tmp;
@@ -906,11 +956,11 @@ void link_state_routing_i_destroy(struct routing_i * instance)
{
assert(instance);
- pthread_mutex_lock(&ls.routing_i_lock);
+ pthread_mutex_lock(&ls.instances.mtx);
list_del(&instance->next);
- pthread_mutex_unlock(&ls.routing_i_lock);
+ pthread_mutex_unlock(&ls.instances.mtx);
pthread_cancel(instance->calculator);
@@ -921,96 +971,146 @@ void link_state_routing_i_destroy(struct routing_i * instance)
free(instance);
}
-int link_state_init(enum pol_routing pr)
+int link_state_start(void)
+{
+ if (notifier_reg(handle_event, NULL)) {
+ log_err("Failed to register link-state with notifier.");
+ goto fail_notifier_reg;
+ }
+
+ if (pthread_create(&ls.lsupdate, NULL, lsupdate, NULL)) {
+ log_err("Failed to create lsupdate thread.");
+ goto fail_pthread_create_lsupdate;
+ }
+
+ if (pthread_create(&ls.lsreader, NULL, lsreader, NULL)) {
+ log_err("Failed to create lsreader thread.");
+ goto fail_pthread_create_lsreader;
+ }
+
+ if (pthread_create(&ls.listener, NULL, ls_conn_handle, NULL)) {
+ log_err("Failed to create listener thread.");
+ goto fail_pthread_create_listener;
+ }
+
+ return 0;
+
+ fail_pthread_create_listener:
+ pthread_cancel(ls.lsreader);
+ pthread_join(ls.lsreader, NULL);
+ fail_pthread_create_lsreader:
+ pthread_cancel(ls.lsupdate);
+ pthread_join(ls.lsupdate, NULL);
+ fail_pthread_create_lsupdate:
+ notifier_unreg(handle_event);
+ fail_notifier_reg:
+ return -1;
+}
+
+void link_state_stop(void)
+{
+ pthread_cancel(ls.listener);
+ pthread_cancel(ls.lsreader);
+ pthread_cancel(ls.lsupdate);
+
+ pthread_join(ls.listener, NULL);
+ pthread_join(ls.lsreader, NULL);
+ pthread_join(ls.lsupdate, NULL);
+
+ notifier_unreg(handle_event);
+}
+
+
+int link_state_init(struct ls_config * conf,
+ enum pol_pff * pff_type)
{
struct conn_info info;
+ assert(conf != NULL);
+ assert(pff_type != NULL);
+
memset(&info, 0, sizeof(info));
+ ls.addr = addr_auth_address();
+
strcpy(info.comp_name, LS_COMP);
strcpy(info.protocol, LS_PROTO);
info.pref_version = 1;
info.pref_syntax = PROTO_GPB;
- info.addr = ipcpi.dt_addr;
+ info.addr = ls.addr;
- switch (pr) {
- case ROUTING_LINK_STATE:
- log_dbg("Using link state routing policy.");
+ ls.conf = *conf;
+
+ switch (conf->pol) {
+ case LS_SIMPLE:
+ *pff_type = PFF_SIMPLE;
ls.routing_algo = ROUTING_SIMPLE;
+ log_dbg("Using Link State Routing policy.");
break;
- case ROUTING_LINK_STATE_LFA:
- log_dbg("Using Loop-Free Alternates policy.");
+ case LS_LFA:
ls.routing_algo = ROUTING_LFA;
+ *pff_type = PFF_ALTERNATE;
+ log_dbg("Using Loop-Free Alternates policy.");
break;
- case ROUTING_LINK_STATE_ECMP:
- log_dbg("Using Equal-Cost Multipath policy.");
+ case LS_ECMP:
ls.routing_algo = ROUTING_ECMP;
+ *pff_type = PFF_MULTIPATH;
+ log_dbg("Using Equal-Cost Multipath policy.");
break;
default:
goto fail_graph;
}
+ log_dbg("LS update interval: %ld seconds.", ls.conf.t_update);
+ log_dbg("LS link timeout : %ld seconds.", ls.conf.t_timeo);
+ log_dbg("LS recalc interval: %ld seconds.", ls.conf.t_recalc);
+
ls.graph = graph_create();
if (ls.graph == NULL)
goto fail_graph;
- if (notifier_reg(handle_event, NULL))
- goto fail_notifier_reg;
-
- if (pthread_rwlock_init(&ls.db_lock, NULL))
- goto fail_db_lock_init;
+ if (pthread_rwlock_init(&ls.lock, NULL)) {
+ log_err("Failed to init lock.");
+ goto fail_lock_init;
+ }
- if (pthread_mutex_init(&ls.routing_i_lock, NULL))
+ if (pthread_mutex_init(&ls.instances.mtx, NULL)) {
+ log_err("Failed to init instances mutex.");
goto fail_routing_i_lock_init;
+ }
- if (connmgr_comp_init(COMPID_MGMT, &info))
+ if (connmgr_comp_init(COMPID_MGMT, &info)) {
+ log_err("Failed to init connmgr.");
goto fail_connmgr_comp_init;
+ }
ls.mgmt_set = fset_create();
- if (ls.mgmt_set == NULL)
+ if (ls.mgmt_set == NULL) {
+ log_err("Failed to create fset.");
goto fail_fset_create;
+ }
- list_head_init(&ls.db);
- list_head_init(&ls.nbs);
- list_head_init(&ls.routing_instances);
-
- if (pthread_create(&ls.lsupdate, NULL, lsupdate, NULL))
- goto fail_pthread_create_lsupdate;
-
- if (pthread_create(&ls.lsreader, NULL, lsreader, NULL))
- goto fail_pthread_create_lsreader;
-
- if (pthread_create(&ls.listener, NULL, ls_conn_handle, NULL))
- goto fail_pthread_create_listener;
+ list_head_init(&ls.db.list);
+ list_head_init(&ls.nbs.list);
+ list_head_init(&ls.instances.list);
- if (rib_reg(LSDB, &r_ops))
+ if (rib_reg(Lspb, &r_ops))
goto fail_rib_reg;
- ls.db_len = 0;
- ls.nbs_len = 0;
+ ls.db.len = 0;
+ ls.nbs.len = 0;
return 0;
fail_rib_reg:
- pthread_cancel(ls.listener);
- pthread_join(ls.listener, NULL);
- fail_pthread_create_listener:
- pthread_cancel(ls.lsreader);
- pthread_join(ls.lsreader, NULL);
- fail_pthread_create_lsreader:
- pthread_cancel(ls.lsupdate);
- pthread_join(ls.lsupdate, NULL);
- fail_pthread_create_lsupdate:
fset_destroy(ls.mgmt_set);
fail_fset_create:
connmgr_comp_fini(COMPID_MGMT);
fail_connmgr_comp_init:
- pthread_mutex_destroy(&ls.routing_i_lock);
+ pthread_mutex_destroy(&ls.instances.mtx);
fail_routing_i_lock_init:
- pthread_rwlock_destroy(&ls.db_lock);
- fail_db_lock_init:
- notifier_unreg(handle_event);
- fail_notifier_reg:
+ pthread_rwlock_destroy(&ls.lock);
+ fail_lock_init:
graph_destroy(ls.graph);
fail_graph:
return -1;
@@ -1021,17 +1121,7 @@ void link_state_fini(void)
struct list_head * p;
struct list_head * h;
- rib_unreg(LSDB);
-
- notifier_unreg(handle_event);
-
- pthread_cancel(ls.listener);
- pthread_cancel(ls.lsreader);
- pthread_cancel(ls.lsupdate);
-
- pthread_join(ls.listener, NULL);
- pthread_join(ls.lsreader, NULL);
- pthread_join(ls.lsupdate, NULL);
+ rib_unreg(Lspb);
fset_destroy(ls.mgmt_set);
@@ -1039,17 +1129,17 @@ void link_state_fini(void)
graph_destroy(ls.graph);
- pthread_rwlock_wrlock(&ls.db_lock);
+ pthread_rwlock_wrlock(&ls.lock);
- list_for_each_safe(p, h, &ls.db) {
+ list_for_each_safe(p, h, &ls.db.list) {
struct adjacency * a = list_entry(p, struct adjacency, next);
list_del(&a->next);
free(a);
}
- pthread_rwlock_unlock(&ls.db_lock);
+ pthread_rwlock_unlock(&ls.lock);
- pthread_rwlock_destroy(&ls.db_lock);
+ pthread_rwlock_destroy(&ls.lock);
- pthread_mutex_destroy(&ls.routing_i_lock);
+ pthread_mutex_destroy(&ls.instances.mtx);
}
diff --git a/src/ipcpd/unicast/routing/link-state.h b/src/ipcpd/unicast/routing/link-state.h
index d77d72df..69eb6781 100644
--- a/src/ipcpd/unicast/routing/link-state.h
+++ b/src/ipcpd/unicast/routing/link-state.h
@@ -28,10 +28,15 @@
#include "ops.h"
-int link_state_init(enum pol_routing pr);
+int link_state_init(struct ls_config * ls,
+ enum pol_pff * pff_type);
void link_state_fini(void);
+int link_state_start(void);
+
+void link_state_stop(void);
+
struct routing_i * link_state_routing_i_create(struct pff * pff);
void link_state_routing_i_destroy(struct routing_i * instance);
diff --git a/src/ipcpd/unicast/routing/ops.h b/src/ipcpd/unicast/routing/ops.h
index 8a79b7ec..4bf75c80 100644
--- a/src/ipcpd/unicast/routing/ops.h
+++ b/src/ipcpd/unicast/routing/ops.h
@@ -26,10 +26,15 @@
#include "pff.h"
struct routing_ops {
- int (* init)(enum pol_routing pr);
+ int (* init)(void * conf,
+ enum pol_pff * pff_type);
void (* fini)(void);
+ int (* start)(void);
+
+ void (* stop)(void);
+
struct routing_i * (* routing_i_create)(struct pff * pff);
void (* routing_i_destroy)(struct routing_i * instance);
diff --git a/src/ipcpd/unicast/routing/tests/CMakeLists.txt b/src/ipcpd/unicast/routing/tests/CMakeLists.txt
index d0652533..be2de72c 100644
--- a/src/ipcpd/unicast/routing/tests/CMakeLists.txt
+++ b/src/ipcpd/unicast/routing/tests/CMakeLists.txt
@@ -3,32 +3,32 @@ get_filename_component(CURRENT_SOURCE_PARENT_DIR
get_filename_component(CURRENT_BINARY_PARENT_DIR
${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+compute_test_prefix()
+
create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
# Add new tests here
graph_test.c
)
-add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests})
-target_link_libraries(${PARENT_DIR}_test ouroboros-common)
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests})
+
+target_include_directories(${PARENT_DIR}_test PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CURRENT_SOURCE_PARENT_DIR}
+ ${CURRENT_BINARY_PARENT_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src/ipcpd
+ ${CMAKE_BINARY_DIR}/src/ipcpd
+)
-add_dependencies(check ${PARENT_DIR}_test)
+disable_test_logging_for_target(${PARENT_DIR}_test)
+target_link_libraries(${PARENT_DIR}_test PRIVATE ouroboros-common)
-set(tests_to_run ${${PARENT_DIR}_tests})
-remove(tests_to_run test_suite.c)
+add_dependencies(build_tests ${PARENT_DIR}_test)
-foreach (test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(${test_name} ${C_TEST_PATH}/${PARENT_DIR}_test ${test_name})
-endforeach (test)
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
diff --git a/src/irmd/CMakeLists.txt b/src/irmd/CMakeLists.txt
index c9c2e553..d65635af 100644
--- a/src/irmd/CMakeLists.txt
+++ b/src/irmd/CMakeLists.txt
@@ -1,95 +1,65 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+# IRMd (IPC Resource Manager daemon) build configuration
+# Configuration options are in cmake/config/global.cmake and cmake/config/irmd.cmake
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-find_library(LIBTOML_LIBRARIES toml QUIET)
-if (LIBTOML_LIBRARIES)
- set(DISABLE_CONFIGFILE FALSE CACHE BOOL
- "Disable configuration file support")
- if (NOT DISABLE_CONFIGFILE)
- set(OUROBOROS_CONFIG_DIR /etc/ouroboros/ CACHE STRING
- "Configuration directory")
- set(OUROBOROS_CONFIG_FILE irmd.conf CACHE STRING
- "Name of the IRMd configuration file")
- set(HAVE_TOML TRUE)
- message(STATUS "Found TOML C99 library: " ${LIBTOML_LIBRARIES})
- message(STATUS "Configuration file support enabled")
- message(STATUS "Configuration directory: ${OUROBOROS_CONFIG_DIR}")
- set(INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
- configure_file("${CMAKE_SOURCE_DIR}/irmd.conf.in"
- "${CMAKE_BINARY_DIR}/irmd.conf.example" @ONLY)
- install(FILES "${CMAKE_BINARY_DIR}/irmd.conf.example"
- DESTINATION "${OUROBOROS_CONFIG_DIR}")
- unset(INSTALL_DIR)
- mark_as_advanced(LIBTOML_LIBRARIES)
- else ()
- message(STATUS "Configuration file support disabled by user")
- unset(OUROBOROS_CONFIG_FILE CACHE)
- unset(OUROBOROS_CONFIG_DIR CACHE)
- set(HAVE_TOML FALSE)
- endif ()
-else ()
- message(STATUS "Install tomlc99 for config file support")
- message(STATUS " https://github.com/cktan/tomlc99")
- set(LIBTOML_LIBRARIES "")
- unset(DISABLE_CONFIGFILE CACHE)
- unset(HAVE_TOML)
-endif ()
-
-set(IRMD_REQ_ARR_TIMEOUT 1000 CACHE STRING
- "Timeout for an application to respond to a new flow (ms)")
-
-set(BOOTSTRAP_TIMEOUT 5000 CACHE STRING
- "Timeout for an IPCP to bootstrap (ms)")
-set(ENROLL_TIMEOUT 20000 CACHE STRING
- "Timeout for an IPCP to enroll (ms)")
-set(REG_TIMEOUT 20000 CACHE STRING
- "Timeout for registering a name (ms)")
-set(QUERY_TIMEOUT 20000 CACHE STRING
- "Timeout to query a name with an IPCP (ms)")
-set(CONNECT_TIMEOUT 20000 CACHE STRING
- "Timeout to connect an IPCP to another IPCP (ms)")
-set(FLOW_ALLOC_TIMEOUT 20000 CACHE STRING
- "Timeout for a flow allocation response (ms)")
-set(IRMD_MIN_THREADS 8 CACHE STRING
- "Minimum number of worker threads in the IRMd")
-set(IRMD_ADD_THREADS 8 CACHE STRING
- "Number of extra threads to start when the IRMD faces thread starvation")
-set(IRMD_PKILL_TIMEOUT 30 CACHE STRING
- "Number of seconds to wait before sending SIGKILL to subprocesses on exit")
-set(IRMD_KILL_ALL_PROCESSES TRUE CACHE BOOL
- "Kill all processes on exit")
+# Generate and install configuration files if TOML support available
+# HAVE_TOML is set in cmake/dependencies/irmd/libtoml.cmake
+if(HAVE_TOML)
+ set(INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
+ configure_file("${CMAKE_SOURCE_DIR}/irmd.conf.in"
+ "${CMAKE_BINARY_DIR}/${OUROBOROS_CONFIG_FILE}.example" @ONLY)
+ configure_file("${CMAKE_SOURCE_DIR}/enc.conf.in"
+ "${CMAKE_BINARY_DIR}/enc.conf.example" @ONLY)
+ install(FILES "${CMAKE_BINARY_DIR}/${OUROBOROS_CONFIG_FILE}.example"
+ DESTINATION "${OUROBOROS_CONFIG_DIR}")
+ install(FILES "${CMAKE_BINARY_DIR}/enc.conf.example"
+ DESTINATION "${OUROBOROS_CONFIG_DIR}")
+ install(CODE "
+ if(NOT EXISTS \"${OUROBOROS_CONFIG_DIR}/${OUROBOROS_CONFIG_FILE}\")
+ file(WRITE \"${OUROBOROS_CONFIG_DIR}/${OUROBOROS_CONFIG_FILE}\" \"\")
+ endif()
+ ")
+ unset(INSTALL_DIR)
+endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
-set(SOURCE_FILES
- # Add source files here
+set(IRMD_SOURCES
ipcp.c
configfile.c
main.c
+ oap/io.c
+ oap/hdr.c
+ oap/auth.c
+ oap/srv.c
+ oap/cli.c
reg/flow.c
reg/ipcp.c
+ reg/pool.c
reg/proc.c
reg/prog.c
reg/name.c
reg/reg.c
- )
+)
+
+add_executable(irmd ${IRMD_SOURCES})
-add_executable (irmd ${SOURCE_FILES})
+target_include_directories(irmd PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include)
-target_link_libraries (irmd LINK_PUBLIC ouroboros-common
- ${LIBTOML_LIBRARIES})
+target_link_libraries(irmd PRIVATE ouroboros-common)
+if(HAVE_TOML)
+ target_link_libraries(irmd PRIVATE toml::toml)
+endif()
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(irmd -DCONFIG_OUROBOROS_DEBUG)
-endif ()
+ouroboros_target_debug_definitions(irmd)
install(TARGETS irmd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
-# Enable once irmd has tests
-#add_subdirectory(tests)
-add_subdirectory(reg)
+if(BUILD_TESTS)
+ add_subdirectory(oap/tests)
+ add_subdirectory(reg/tests)
+endif()
diff --git a/src/irmd/config.h.in b/src/irmd/config.h.in
index fa1156b9..e1072193 100644
--- a/src/irmd/config.h.in
+++ b/src/irmd/config.h.in
@@ -21,7 +21,8 @@
*/
-#define IPCP_UDP_EXEC "@IPCP_UDP_TARGET@"
+#define IPCP_UDP4_EXEC "@IPCP_UDP4_TARGET@"
+#define IPCP_UDP6_EXEC "@IPCP_UDP6_TARGET@"
#define IPCP_ETH_LLC_EXEC "@IPCP_ETH_LLC_TARGET@"
#define IPCP_ETH_DIX_EXEC "@IPCP_ETH_DIX_TARGET@"
#define IPCP_UNICAST_EXEC "@IPCP_UNICAST_TARGET@"
@@ -40,6 +41,8 @@
#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@
#define FLOW_DEALLOC_TIMEOUT @FLOW_DEALLOC_TIMEOUT@
+#define OAP_REPLAY_TIMER @OAP_REPLAY_TIMER@
+
#define BOOTSTRAP_TIMEOUT @BOOTSTRAP_TIMEOUT@
#define ENROLL_TIMEOUT @ENROLL_TIMEOUT@
#define REG_TIMEOUT @REG_TIMEOUT@
@@ -50,6 +53,7 @@
#define IRMD_MIN_THREADS @IRMD_MIN_THREADS@
#define IRMD_ADD_THREADS @IRMD_ADD_THREADS@
+#define SSM_PID_GSPP 0
#cmakedefine HAVE_FUSE
#ifdef HAVE_FUSE
@@ -62,36 +66,37 @@
#define OUROBOROS_CONFIG_FILE "@OUROBOROS_CONFIG_FILE@"
#endif
+#define OUROBOROS_SECURITY_DIR "@OUROBOROS_SECURITY_DIR@"
+#define OUROBOROS_CA_CRT_DIR "@OUROBOROS_CA_CRT_DIR@"
+#define OUROBOROS_SRV_CRT_DIR "@OUROBOROS_SRV_CRT_DIR@"
+#define OUROBOROS_CLI_CRT_DIR "@OUROBOROS_CLI_CRT_DIR@"
+#define OUROBOROS_CHAIN_DIR "@OUROBOROS_UNTRUSTED_DIR@"
+
#define IRMD_PKILL_TIMEOUT @IRMD_PKILL_TIMEOUT@
#cmakedefine IRMD_KILL_ALL_PROCESSES
#cmakedefine HAVE_LIBGCRYPT
+#cmakedefine HAVE_OPENSSL
+#ifdef HAVE_OPENSSL
+#cmakedefine HAVE_OPENSSL_PQC
+#endif
+#define IRMD_SECMEM_MAX @IRMD_SECMEM_MAX@
+#ifdef CONFIG_OUROBOROS_DEBUG
+#cmakedefine DEBUG_PROTO_OAP
+#endif
+
+#define _B ""
+#define _G ""
+#define RST ""
-#define O7S_ASCII_ART \
-"\n" \
-" ▄▄█████▄▄▄\n" \
-" ▄█▀▀ ▀▀███▄ " \
-"â–ˆ\n" \
-" ██ ▄▄▄ ▄███▄ " \
-"â–„ â–„ â–„ â–„â–„" \
-" â–„â–„ â–ˆ â–„â–„ " \
-" â–„â–„ â–„ â–„â–„ " \
-"â–„â–„ â–„â–„\n" \
-" ██ █ █ " \
-"[38;5;4m█████ █ █ " \
-"█▀ ▀ █ █" \
-" █▀ █ █ " \
-"█ █▀ ▀ █" \
-" █ ▀▄ ▀\n" \
-" ██ ▀▄▄▄▀ ▀█▀ " \
-"â–ˆ â–ˆ â–ˆ " \
-"█ █ █▄ █ " \
-"â–ˆ â–ˆ â–ˆ â–ˆ" \
-" █ ▄ ▀▄\n" \
-" █▄ █ ▀▀▀" \
-" ▀ ▀ ▀▀" \
-" ▀ ▀▀ ▀▀ " \
-"▀ ▀▀ ▀▀\n" \
-" ▀█▄▄▄▄▄▄▄▄▀\n" \
-" ▀▀▀▀▀▀\n" \
-"\n"
+#define O7S_ASCII_ART \
+RST "\n" \
+_B " ▄▄█████▄▄▄ \n" \
+_B " ▄█▀▀ ▀▀███▄ " _G " █ \n" \
+_B " ██ ▄▄▄ ▄███▄ " _G "▄ ▄ ▄ ▄ ▄▄ █▄▄ ▄▄ ▄ ▄ ▄▄ ▄▄ \n" \
+_B " ██ █ █ █████ " _G "█ █ █▀ ▀ █ █ █ █ █ █ █▀ ▀ █ █ ▀▄ ▀\n" \
+_B " ██ ▀▄▄▄▀ ▀█▀ " _G "█ █ █ █ █ █ █ █ █ █ █ █ ▄ ▀▄\n" \
+_B " █▄ █ " _G " ▀▀ ▀ ▀ ▀▀ ▀▀▀ ▀▀ ▀ ▀▀ ▀▀ \n" \
+_B " ▀█▄▄▄▄▄▄▄▄▀ \n" \
+_B " ▀▀▀▀▀▀ \n" \
+RST "\n"
diff --git a/src/irmd/configfile.c b/src/irmd/configfile.c
index 688c4ade..ce9fc8fc 100644
--- a/src/irmd/configfile.c
+++ b/src/irmd/configfile.c
@@ -47,8 +47,12 @@
#include <string.h>
#include <toml.h>
#include <arpa/inet.h>
+#ifdef __FreeBSD__
+#include <sys/socket.h>
+#endif
#define ERRBUFSZ 200
+#define DATUMSZ 256
static int toml_hash(toml_table_t * table,
struct layer_info * info)
@@ -157,14 +161,66 @@ static int toml_eth_dix(toml_table_t * table,
return 0;
}
-static int toml_udp(toml_table_t * table,
- struct ipcp_config * conf)
+static int toml_udp4(toml_table_t * table,
+ struct ipcp_config * conf)
+{
+ struct udp4_config * udp4;
+ toml_datum_t ip;
+ toml_datum_t port;
+ toml_datum_t dns;
+
+ *conf = udp4_default_conf;
+ udp4 = &conf->udp4;
+
+ ip = toml_string_in(table, "ip");
+ if (!ip.ok) {
+ log_err("No IP address specified!");
+ goto fail_ip;
+ }
+
+ if (inet_pton (AF_INET, ip.u.s, &udp4->ip_addr.s_addr) != 1) {
+ log_err("Failed to parse IPv4 address %s.", ip.u.s);
+ goto fail_addr;
+ }
+
+ port = toml_int_in(table, "port");
+ if (port.ok)
+ udp4->port = port.u.i;
+
+ dns = toml_string_in(table, "dns");
+ if (dns.ok) {
+ if (inet_pton(AF_INET, dns.u.s, &udp4->dns_addr.s_addr) < 0) {
+ log_err("Failed to parse DNS address %s.", ip.u.s);
+ goto fail_dns;
+ }
+
+ free(dns.u.s);
+ }
+
+ free(ip.u.s);
+
+ return 0;
+
+ fail_dns:
+ free(dns.u.s);
+ fail_addr:
+ free(ip.u.s);
+ fail_ip:
+ return -1;
+}
+
+static int toml_udp6(toml_table_t * table,
+ struct ipcp_config * conf)
{
+ struct in6_addr ip6;
+ struct in6_addr dns6;
toml_datum_t ip;
toml_datum_t port;
toml_datum_t dns;
- *conf = udp_default_conf;
+ *conf = udp6_default_conf;
+ ip6 = conf->udp6.ip_addr;
+ dns6 = conf->udp6.dns_addr;
ip = toml_string_in(table, "ip");
if (!ip.ok) {
@@ -172,18 +228,18 @@ static int toml_udp(toml_table_t * table,
goto fail_ip;
}
- if (inet_pton (AF_INET, ip.u.s, &conf->udp.ip_addr) != 1) {
+ if (inet_pton (AF_INET6, ip.u.s, &ip6.s6_addr) != 1) {
log_err("Failed to parse IPv4 address %s.", ip.u.s);
goto fail_addr;
}
port = toml_int_in(table, "port");
if (port.ok)
- conf->udp.port = port.u.i;
+ conf->udp6.port = port.u.i;
dns = toml_string_in(table, "dns");
if (dns.ok) {
- if (inet_pton(AF_INET, dns.u.s, &conf->udp.dns_addr) < 0) {
+ if (inet_pton(AF_INET6, dns.u.s, &dns6.s6_addr) < 0) {
log_err("Failed to parse DNS address %s.", ip.u.s);
goto fail_dns;
}
@@ -214,26 +270,160 @@ static int toml_broadcast(toml_table_t * table,
return 0;
}
+#define BETWEEN(a, b, c) ((a) >= (b) && (a) <= (c))
+#define DHT(conf, x) (conf)->dht.params.x
+static int toml_dir(toml_table_t * table,
+ struct dir_config * conf)
+{
+ toml_datum_t dir;
+ toml_datum_t alpha;
+ toml_datum_t t_expire;
+ toml_datum_t t_refresh;
+ toml_datum_t t_replicate;
+ toml_datum_t k;
+
+ dir = toml_string_in(table, "directory");
+ if (dir.ok) {
+ log_dbg("Found directory type: %s", dir.u.s);
+ if (strlen(dir.u.s) > DATUMSZ) {
+ log_err("Directory name too long: %s", dir.u.s);
+ free(dir.u.s);
+ return -1;
+ }
+ if (strcmp(dir.u.s, "DHT") == 0)
+ conf->pol = DIR_DHT;
+ else if (strcmp(dir.u.s, "dht") == 0)
+ conf->pol = DIR_DHT;
+ else {
+ log_err("Unknown directory type: %s", dir.u.s);
+ free(dir.u.s);
+ return -EINVAL;
+ }
+ free(dir.u.s);
+ }
+
+ switch(conf->pol) {
+ case DIR_DHT:
+ log_info("Using DHT directory policy.");
+ alpha = toml_int_in(table, "dht_alpha");
+ if (alpha.ok) {
+ if (!BETWEEN(alpha.u.i,
+ DHT_ALPHA_MIN, DHT_ALPHA_MAX)) {
+ log_err("Invalid alpha value: %ld",
+ (long) alpha.u.i);
+ return -EINVAL;
+ }
+ DHT(conf, alpha) = alpha.u.i;
+ }
+ t_expire = toml_int_in(table, "dht_t_expire");
+ if (t_expire.ok) {
+ if (!BETWEEN(t_expire.u.i,
+ DHT_T_EXPIRE_MIN, DHT_T_EXPIRE_MAX)) {
+ log_err("Invalid expire time: %ld",
+ (long) t_expire.u.i);
+ return -EINVAL;
+ }
+ DHT(conf, t_expire) = t_expire.u.i;
+ }
+ t_refresh = toml_int_in(table, "dht_t_refresh");
+ if (t_refresh.ok) {
+ if (!BETWEEN(t_refresh.u.i,
+ DHT_T_REFRESH_MIN, DHT_T_REFRESH_MAX)) {
+ log_err("Invalid refresh time: %ld",
+ (long) t_refresh.u.i);
+ return -EINVAL;
+ }
+ DHT(conf, t_refresh) = t_refresh.u.i;
+ }
+ t_replicate = toml_int_in(table, "dht_t_replicate");
+ if (t_replicate.ok) {
+ if (!BETWEEN(t_replicate.u.i,
+ DHT_T_REPLICATE_MIN, DHT_T_REPLICATE_MAX)) {
+ log_err("Invalid replication time: %ld",
+ (long) t_replicate.u.i);
+ return -EINVAL;
+ }
+ DHT(conf, t_replicate) = t_replicate.u.i;
+ }
+ k = toml_int_in(table, "dht_k");
+ if (k.ok) {
+ if (!BETWEEN(k.u.i, DHT_K_MIN, DHT_K_MAX)) {
+ log_err("Invalid replication factor: %ld",
+ (long) k.u.i);
+ return -EINVAL;
+ }
+ DHT(conf, k) = k.u.i;
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ return 0;
+}
+
static int toml_routing(toml_table_t * table,
struct dt_config * conf)
{
toml_datum_t routing;
+ toml_datum_t t_recalc;
+ toml_datum_t t_update;
+ toml_datum_t t_timeo;
routing = toml_string_in(table, "routing");
if (routing.ok) {
- if (strcmp(routing.u.s, "link-state") == 0)
- conf->routing_type = ROUTING_LINK_STATE;
- else if (strcmp(routing.u.s, "lfa") == 0)
- conf->routing_type = ROUTING_LINK_STATE_LFA;
- else if (strcmp(routing.u.s, "ecmp") == 0)
- conf->routing_type = ROUTING_LINK_STATE_ECMP;
- else
- conf->routing_type = ROUTING_INVALID;
+ if (strcmp(routing.u.s, "link-state") == 0) {
+ conf->routing.pol = ROUTING_LINK_STATE;
+ conf->routing.ls.pol = LS_SIMPLE;
+ } else if (strcmp(routing.u.s, "lfa") == 0) {
+ conf->routing.pol = ROUTING_LINK_STATE;
+ conf->routing.ls.pol = LS_LFA;
+ } else if (strcmp(routing.u.s, "ecmp") == 0) {
+ conf->routing.pol = ROUTING_LINK_STATE;
+ conf->routing.ls.pol = LS_ECMP;
+ } else {
+ conf->routing.pol = ROUTING_INVALID;
+ return -EINVAL;
+ }
free(routing.u.s);
}
- if (conf->routing_type == ROUTING_INVALID)
- return -1;
+ switch (conf->routing.pol) {
+ case ROUTING_LINK_STATE:
+ log_info("Using Link State routing policy.");
+ t_recalc = toml_int_in(table, "ls_t_recalc");
+ if (t_recalc.ok) {
+ if (t_recalc.u.i < 1) {
+ log_err("Invalid ls_t_recalc value: %ld",
+ (long) t_recalc.u.i);
+ return -EINVAL;
+ }
+ conf->routing.ls.t_recalc = t_recalc.u.i;
+ }
+ t_update = toml_int_in(table, "ls_t_update");
+ if (t_update.ok) {
+ if (t_update.u.i < 1) {
+ log_err("Invalid ls_t_update value: %ld",
+ (long) t_update.u.i);
+ return -EINVAL;
+ }
+ conf->routing.ls.t_update = t_update.u.i;
+ }
+ t_timeo = toml_int_in(table, "ls_t_timeo");
+ if (t_timeo.ok) {
+ if (t_timeo.u.i < 1) {
+ log_err("Invalid ls_t_timeo value: %ld",
+ (long) t_timeo.u.i);
+ return -EINVAL;
+ }
+ conf->routing.ls.t_timeo = t_timeo.u.i;
+ }
+ break;
+ default:
+ log_err("Invalid routing policy: %d", conf->routing.pol);
+ return -EINVAL;
+ }
return 0;
}
@@ -311,12 +501,12 @@ static int toml_dt(toml_table_t * table,
static int toml_unicast(toml_table_t * table,
struct ipcp_config * conf)
{
-
-
*conf = uni_default_conf;
- if (toml_hash(table, &conf->layer_info) < 0)
+ if (toml_dir(table, &conf->unicast.dir) < 0) {
+ log_err("Invalid directory configuration.");
return -1;
+ }
if (toml_dt(table, &conf->unicast.dt) < 0) {
log_err("Invalid DT configuration.");
@@ -333,6 +523,7 @@ static int toml_unicast(toml_table_t * table,
return -1;
}
+
return 0;
}
@@ -504,8 +695,11 @@ static int toml_ipcp(toml_table_t * table,
case IPCP_ETH_LLC:
ret = toml_eth_llc(table, conf);
break;
- case IPCP_UDP:
- ret = toml_udp(table, conf);
+ case IPCP_UDP4:
+ ret = toml_udp4(table, conf);
+ break;
+ case IPCP_UDP6:
+ ret = toml_udp6(table, conf);
break;
case IPCP_BROADCAST:
ret = toml_broadcast(table, conf);
@@ -563,7 +757,7 @@ static int toml_ipcp_list(toml_table_t * table,
}
info.type = type;
- strcpy(info.name,key);
+ strcpy(info.name, key);
conf.type = type;
ret = toml_ipcp(toml_table_in(table, key), &info, &conf);
@@ -691,18 +885,63 @@ static int toml_prog_list(toml_array_t * progs,
return ret;
}
+static int cp_chk_path(char * buf,
+ char * path)
+{
+ char * rp;
+
+ assert(path != NULL);
+
+ rp = realpath(path, NULL);
+ if (rp == NULL) {
+ log_err("Failed to check path %s: %s.", path, strerror(errno));
+ goto fail_rp;
+ }
+
+ if (strlen(rp) > NAME_PATH_SIZE) {
+ log_err("File path too long: %s.", rp);
+ goto fail_len;
+ }
+
+ strcpy(buf, rp);
+ free(rp);
+ free(path);
+
+ return 0;
+
+ fail_len:
+ free(rp);
+ fail_rp:
+ free(path);
+ return -1;
+}
+
static int toml_name(toml_table_t * table,
const char * name)
{
- toml_array_t * progs;
- toml_array_t * args;
- toml_datum_t lb;
+ toml_array_t * progs;
+ toml_array_t * args;
+ toml_datum_t lb;
+ toml_datum_t senc;
+ toml_datum_t scrt;
+ toml_datum_t skey;
+ toml_datum_t cenc;
+ toml_datum_t ccrt;
+ toml_datum_t ckey;
+
struct name_info info = {
.pol_lb = LB_SPILL
};
log_dbg("Found service name %s in configuration file.", name);
+ if (strlen(name) > NAME_SIZE) {
+ log_err("Name too long: %s", name);
+ return -1;
+ }
+
+ strcpy(info.name, name);
+
lb = toml_string_in(table, "lb");
if (lb.ok) {
if (strcmp(lb.u.s, "spill") == 0)
@@ -718,8 +957,29 @@ static int toml_name(toml_table_t * table,
log_err("Invalid load-balancing policy for %s.", name);
return -1;
}
+ senc = toml_string_in(table, "server_enc_file");
+ if (senc.ok && cp_chk_path(info.s.enc, senc.u.s) < 0)
+ return -1;
- strcpy(info.name, name);
+ scrt = toml_string_in(table, "server_crt_file");
+ if (scrt.ok && cp_chk_path(info.s.crt, scrt.u.s) < 0)
+ return -1;
+
+ skey = toml_string_in(table, "server_key_file");
+ if (skey.ok && cp_chk_path(info.s.key, skey.u.s) < 0)
+ return -1;
+
+ cenc = toml_string_in(table, "client_enc_file");
+ if (cenc.ok && cp_chk_path(info.c.enc, cenc.u.s) < 0)
+ return -1;
+
+ ccrt = toml_string_in(table, "client_crt_file");
+ if (ccrt.ok && cp_chk_path(info.c.crt, ccrt.u.s) < 0)
+ return -1;
+
+ ckey = toml_string_in(table, "client_key_file");
+ if (ckey.ok && cp_chk_path(info.c.key, ckey.u.s) < 0)
+ return -1;
if (name_create(&info) < 0) {
log_err("Failed to create name %s.", name);
@@ -762,23 +1022,24 @@ static int toml_toplevel(toml_table_t * table,
toml_table_t * subtable;
subtable = toml_table_in(table, key);
-
- if (strcmp(key, "local") == 0)
+ if (strcmp(key, "name") == 0)
+ return toml_name_list(subtable);
+ else if (strcmp(key, "local") == 0)
return toml_ipcp_list(subtable, IPCP_LOCAL);
else if (strcmp(key, "eth-dix") == 0)
return toml_ipcp_list(subtable, IPCP_ETH_DIX);
else if (strcmp(key, "eth-llc") == 0)
return toml_ipcp_list(subtable, IPCP_ETH_LLC);
- else if (strcmp(key, "udp") == 0)
- return toml_ipcp_list(subtable, IPCP_UDP);
+ else if (strcmp(key, "udp4") == 0)
+ return toml_ipcp_list(subtable, IPCP_UDP4);
+ else if (strcmp(key, "udp6") == 0)
+ return toml_ipcp_list(subtable, IPCP_UDP6);
else if (strcmp(key, "broadcast") == 0)
return toml_ipcp_list(subtable, IPCP_BROADCAST);
else if (strcmp(key, "unicast") == 0)
return toml_ipcp_list(subtable, IPCP_UNICAST);
- else if (strcmp(key, "name") == 0)
- return toml_name_list(subtable);
-
- log_err("Unkown toplevel key: %s.", key);
+ else
+ log_err("Unkown toplevel key: %s.", key);
return -1;
}
@@ -838,7 +1099,8 @@ int irm_configure(const char * path)
rp = realpath(path, NULL);
if (rp == NULL) {
- log_err("Failed to resolve path for %s", path);
+ log_err("Failed to check path for %s: %s.",
+ path, strerror(errno));
goto fail_resolve;
}
diff --git a/src/irmd/ipcp.c b/src/irmd/ipcp.c
index 5a9a79d3..d261fc57 100644
--- a/src/irmd/ipcp.c
+++ b/src/irmd/ipcp.c
@@ -83,28 +83,30 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid,
{
int sockfd;
uint8_t buf[SOCK_BUF_SIZE];
- char * sock_path;
+ char * spath;
ssize_t len;
- ipcp_msg_t * recv_msg;
struct timeval tv;
struct timespec tic;
struct timespec toc;
- bool dealloc = false;
+ bool may_fail = false;
if (kill(pid, 0) < 0)
return NULL;
- sock_path = ipcp_sock_path(pid);
- if (sock_path == NULL)
+ spath = sock_path(pid, IPCP_SOCK_PATH_PREFIX);
+ if (spath == NULL) {
+ log_err("Failed to get IPCP socket path for pid %d.", pid);
return NULL;
+ }
- sockfd = client_socket_open(sock_path);
+ sockfd = client_socket_open(spath);
if (sockfd < 0) {
- free(sock_path);
+ log_err("Failed to open client socket at %s.", spath);
+ free(spath);
return NULL;
}
- free(sock_path);
+ free(spath);
len = ipcp_msg__get_packed_size(msg);
if (len == 0 || len >= SOCK_BUF_SIZE) {
@@ -127,6 +129,7 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid,
tv.tv_usec = (REG_TIMEOUT % 1000) * 1000;
break;
case IPCP_MSG_CODE__IPCP_QUERY:
+ may_fail = true; /* name not always in Layer */
tv.tv_sec = QUERY_TIMEOUT / 1000;
tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000;
break;
@@ -139,7 +142,7 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid,
tv.tv_usec = (FLOW_ALLOC_TIMEOUT % 1000) * 1000;
break;
case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC:
- dealloc = true;
+ may_fail = true;
tv.tv_sec = 0; /* FIX DEALLOC: don't wait for dealloc */
tv.tv_usec = 500;
break;
@@ -167,17 +170,15 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid,
pthread_cleanup_pop(true); /* close socket */
if (len > 0)
- recv_msg = ipcp_msg__unpack(NULL, len, buf);
- else {
- if (errno == EAGAIN && !dealloc) {
- int diff = ts_diff_ms(&tic, &toc);
- log_warn("IPCP %s timed out after %d ms.",
- str_ipcp_cmd(msg->code), diff);
- }
- return NULL;
+ return ipcp_msg__unpack(NULL, len, buf);
+
+ if (errno == EAGAIN && !may_fail) {
+ int diff = ts_diff_ms(&toc, &tic);
+ log_warn("IPCP %s timed out after %d ms.",
+ str_ipcp_cmd(msg->code), diff);
}
- return recv_msg;
+ return NULL;
}
int ipcp_bootstrap(pid_t pid,
@@ -420,6 +421,8 @@ int ipcp_flow_join(const struct flow_info * flow,
msg.flow_id = flow->id;
msg.has_pid = true;
msg.pid = flow->n_pid;
+ msg.has_uid = true;
+ msg.uid = flow->uid;
msg.has_hash = true;
msg.hash.data = (uint8_t *) dst.data;
msg.hash.len = dst.len;
@@ -454,6 +457,8 @@ int ipcp_flow_alloc(const struct flow_info * flow,
msg.flow_id = flow->id;
msg.has_pid = true;
msg.pid = flow->n_pid;
+ msg.has_uid = true;
+ msg.uid = flow->uid;
msg.qosspec = qos_spec_s_to_msg(&flow->qs);
msg.has_hash = true;
msg.hash.data = (uint8_t *) dst.data;
@@ -494,9 +499,11 @@ int ipcp_flow_alloc_resp(const struct flow_info * flow,
msg.flow_id = flow->id;
msg.has_pid = true;
msg.pid = flow->n_pid;
+ msg.has_uid = true;
+ msg.uid = flow->uid;
msg.has_response = true;
msg.response = response;
- msg.has_pk = true;
+ msg.has_pk = response == 0;
msg.pk.data = data.data;
msg.pk.len = data.len;
diff --git a/src/irmd/irmd.h b/src/irmd/irmd.h
index cf8f6953..3e54904a 100644
--- a/src/irmd/irmd.h
+++ b/src/irmd/irmd.h
@@ -39,7 +39,7 @@ int connect_ipcp(pid_t pid,
const char * component,
qosspec_t qs);
-int name_create(const struct name_info * info);
+int name_create(struct name_info * info);
int name_reg(const char * name,
pid_t pid);
diff --git a/src/irmd/main.c b/src/irmd/main.c
index bc13fa7c..ccb16017 100644
--- a/src/irmd/main.c
+++ b/src/irmd/main.c
@@ -22,6 +22,7 @@
#if defined(__linux__) || defined(__CYGWIN__)
#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
#else
#define _POSIX_C_SOURCE 200809L
#endif
@@ -39,9 +40,11 @@
#include <ouroboros/list.h>
#include <ouroboros/lockfile.h>
#include <ouroboros/logs.h>
+#include <ouroboros/protobuf.h>
#include <ouroboros/pthread.h>
+#include <ouroboros/random.h>
#include <ouroboros/rib.h>
-#include <ouroboros/shm_rdrbuff.h>
+#include <ouroboros/ssm_pool.h>
#include <ouroboros/sockets.h>
#include <ouroboros/time.h>
#include <ouroboros/tpm.h>
@@ -50,9 +53,13 @@
#include "irmd.h"
#include "ipcp.h"
+#include "oap.h"
#include "reg/reg.h"
#include "configfile.h"
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
@@ -71,11 +78,13 @@
#define SHM_SAN_HOLDOFF 1000 /* ms */
#define IPCP_HASH_LEN(p) hash_len((p)->dir_hash_algo)
#define BIND_TIMEOUT 10 /* ms */
+#define TIMESYNC_SLACK 100 /* ms */
+#define OAP_SEEN_TIMER 20 /* s */
#define DEALLOC_TIME 300 /* s */
-#define MSGBUFSZ 2048
enum irm_state {
IRMD_NULL = 0,
+ IRMD_INIT,
IRMD_RUNNING,
IRMD_SHUTDOWN
};
@@ -94,7 +103,7 @@ struct {
char * cfg_file; /* configuration file path */
#endif
struct lockfile * lf; /* single irmd per system */
- struct shm_rdrbuff * rdrb; /* rdrbuff for packets */
+ struct ssm_pool * gspp; /* pool for packets */
int sockfd; /* UNIX socket */
@@ -173,8 +182,11 @@ static pid_t spawn_ipcp(struct ipcp_info * info)
case IPCP_BROADCAST:
exec_name = IPCP_BROADCAST_EXEC;
break;
- case IPCP_UDP:
- exec_name = IPCP_UDP_EXEC;
+ case IPCP_UDP4:
+ exec_name = IPCP_UDP4_EXEC;
+ break;
+ case IPCP_UDP6:
+ exec_name = IPCP_UDP6_EXEC;
break;
case IPCP_ETH_LLC:
exec_name = IPCP_ETH_LLC_EXEC;
@@ -217,7 +229,7 @@ static pid_t spawn_ipcp(struct ipcp_info * info)
}
info->pid = pid;
- info->state = IPCP_BOOT;
+ info->state = IPCP_INIT;
return 0;
}
@@ -312,7 +324,7 @@ int bootstrap_ipcp(pid_t pid,
goto fail;
}
- if (conf->type == IPCP_UDP)
+ if (conf->type == IPCP_UDP4 || conf->type == IPCP_UDP6)
conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5;
if (ipcp_bootstrap(pid, conf, &layer)) {
@@ -320,7 +332,7 @@ int bootstrap_ipcp(pid_t pid,
goto fail;
}
- info.state = IPCP_BOOTSTRAPPED;
+ info.state = IPCP_BOOT;
if (reg_set_layer_for_ipcp(&info, &layer) < 0) {
log_err("Failed to set layer info for IPCP.");
@@ -352,6 +364,8 @@ int enroll_ipcp(pid_t pid,
goto fail;
}
+ info.state = IPCP_BOOT;
+
if (reg_set_layer_for_ipcp(&info, &layer) < 0) {
log_err("Failed to set layer info for IPCP.");
goto fail;
@@ -425,6 +439,71 @@ static int disconnect_ipcp(pid_t pid,
return 0;
}
+static void name_update_sec_paths(struct name_info * info)
+{
+ char * srv_dir = OUROBOROS_SRV_CRT_DIR;
+ char * cli_dir = OUROBOROS_CLI_CRT_DIR;
+
+ assert(info != NULL);
+
+ if (strlen(info->s.enc) == 0)
+ sprintf(info->s.enc, "%s/%s/enc.conf", srv_dir, info->name);
+
+ if (strlen(info->s.crt) == 0)
+ sprintf(info->s.crt, "%s/%s/crt.pem", srv_dir, info->name);
+
+ if (strlen(info->s.key) == 0)
+ sprintf(info->s.key, "%s/%s/key.pem", srv_dir, info->name);
+
+ if (strlen(info->c.enc) == 0)
+ sprintf(info->c.enc, "%s/%s/enc.conf", cli_dir, info->name);
+
+ if (strlen(info->c.crt) == 0)
+ sprintf(info->c.crt, "%s/%s/crt.pem", cli_dir, info->name);
+
+ if (strlen(info->c.key) == 0)
+ sprintf(info->c.key, "%s/%s/key.pem", cli_dir, info->name);
+}
+
+int name_create(struct name_info * info)
+{
+ int ret;
+
+ assert(info != NULL);
+
+ name_update_sec_paths(info);
+
+ ret = reg_create_name(info);
+ if (ret == -EEXIST) {
+ log_info("Name %s already exists.", info->name);
+ return 0;
+ }
+
+ if (ret < 0) {
+ log_err("Failed to create name %s.", info->name);
+ return -1;
+ }
+
+ log_info("Created new name: %s.", info->name);
+
+ return 0;
+}
+
+static int name_destroy(const char * name)
+{
+
+ assert(name != NULL);
+
+ if (reg_destroy_name(name) < 0) {
+ log_err("Failed to destroy name %s.", name);
+ return -1;
+ }
+
+ log_info("Destroyed name: %s.", name);
+
+ return 0;
+}
+
int bind_program(char ** exec,
const char * name,
uint8_t flags)
@@ -448,10 +527,8 @@ int bind_program(char ** exec,
if (!reg_has_name(name)) {
ni.pol_lb = LB_SPILL;
strcpy(ni.name, name);
- if (reg_create_name(&ni) < 0) {
- log_err("Failed to create name %s.", name);
+ if (name_create(&ni) < 0)
goto fail_name;
- }
}
if (reg_bind_prog(name, exec, flags) < 0) {
@@ -497,10 +574,8 @@ int bind_process(pid_t pid,
if (!reg_has_name(name)) {
ni.pol_lb = LB_SPILL;
strcpy(ni.name, name);
- if (reg_create_name(&ni) < 0) {
- log_err("Failed to create name %s.", name);
+ if (name_create(&ni) < 0)
goto fail;
- }
}
if (reg_bind_proc(name, pid) < 0) {
@@ -529,18 +604,21 @@ static int unbind_program(const char * prog,
if (name == NULL) {
if (reg_destroy_prog(prog) < 0) {
log_err("Failed to unbind %s.", prog);
- return -1;
+ goto fail;
}
log_info("Program %s unbound.", prog);
} else {
if (reg_unbind_prog(name, prog) < 0) {
log_err("Failed to unbind %s from %s", prog, name);
- return -1;
+ goto fail;
}
log_info("Name %s unbound for %s.", name, prog);
}
return 0;
+
+ fail:
+ return -1;
}
static int unbind_process(pid_t pid,
@@ -549,18 +627,21 @@ static int unbind_process(pid_t pid,
if (name == NULL) {
if (reg_destroy_proc(pid) < 0) {
log_err("Failed to unbind %d.", pid);
- return -1;
+ goto fail;
}
log_info("Process %d unbound.", pid);
} else {
if (reg_unbind_proc(name, pid) < 0) {
log_err("Failed to unbind %d from %s", pid, name);
- return -1;
+ goto fail;
}
log_info("Name %s unbound for process %d.", name, pid);
}
return 0;
+
+ fail:
+ return -1;
}
static int list_ipcps(ipcp_list_msg_t *** ipcps,
@@ -581,43 +662,6 @@ static int list_ipcps(ipcp_list_msg_t *** ipcps,
return -1;
}
-int name_create(const struct name_info * info)
-{
- int ret;
-
- assert(info != NULL);
-
- ret = reg_create_name(info);
- if (ret == -EEXIST) {
- log_info("Name %s already exists.", info->name);
- return 0;
- }
-
- if (ret < 0) {
- log_err("Failed to create name %s.", info->name);
- return -1;
- }
-
- log_info("Created new name: %s.", info->name);
-
- return 0;
-}
-
-static int name_destroy(const char * name)
-{
-
- assert(name != NULL);
-
- if (reg_destroy_name(name) < 0) {
- log_err("Failed to destroy name %s.", name);
- return -1;
- }
-
- log_info("Destroyed name: %s.", name);
-
- return 0;
-}
-
static int list_names(name_info_msg_t *** names,
size_t * n_names)
{
@@ -669,8 +713,7 @@ int name_reg(const char * name,
if (ipcp_reg(pid, hash)) {
log_err("Could not register " HASH_FMT32 " with IPCP %d.",
HASH_VAL32(hash.data), pid);
- freebuf(hash);
- return -1;
+ goto fail_hash;
}
log_info("Registered %s with IPCP %d as " HASH_FMT32 ".",
@@ -679,6 +722,10 @@ int name_reg(const char * name,
freebuf(hash);
return 0;
+
+ fail_hash:
+ freebuf(hash);
+ return -1;
}
static int name_unreg(const char * name,
@@ -713,8 +760,7 @@ static int name_unreg(const char * name,
if (ipcp_unreg(pid, hash)) {
log_err("Could not unregister %s with IPCP %d.", name, pid);
- freebuf(hash);
- return -1;
+ goto fail_hash;
}
log_info("Unregistered %s from %d.", name, pid);
@@ -722,20 +768,53 @@ static int name_unreg(const char * name,
freebuf(hash);
return 0;
+
+ fail_hash:
+ freebuf(hash);
+ return -1;
+}
+
+static int get_peer_ids(int fd,
+ uid_t * uid,
+ gid_t * gid)
+{
+#if defined(__linux__)
+ struct ucred ucred;
+ socklen_t len;
+
+ len = sizeof(ucred);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0)
+ goto fail;
+
+ *uid = ucred.uid;
+ *gid = ucred.gid;
+#else
+ if (getpeereid(fd, uid, gid) < 0)
+ goto fail;
+#endif
+ return 0;
+ fail:
+ return -1;
}
static int proc_announce(const struct proc_info * info)
{
+ if (reg_prepare_pool(info->uid, info->gid) < 0) {
+ log_err("Failed to prepare pool for uid %d.", info->uid);
+ goto fail;
+ }
+
if (reg_create_proc(info) < 0) {
log_err("Failed to add process %d.", info->pid);
- goto fail_proc;
+ goto fail;
}
log_info("Process added: %d (%s).", info->pid, info->prog);
return 0;
- fail_proc:
+ fail:
return -1;
}
@@ -749,12 +828,6 @@ static int proc_exit(pid_t pid)
return 0;
}
-static void __cleanup_pkp(void * pkp)
-{
- if (pkp != NULL)
- crypt_dh_pkp_destroy(pkp);
-}
-
static void __cleanup_flow(void * flow)
{
reg_destroy_flow(((struct flow_info *) flow)->id);
@@ -762,65 +835,43 @@ static void __cleanup_flow(void * flow)
static int flow_accept(struct flow_info * flow,
buffer_t * data,
- struct timespec * abstime)
+ struct timespec * abstime,
+ struct crypt_sk * sk)
{
- uint8_t buf[MSGBUFSZ];
- buffer_t lpk; /* local public key */
- buffer_t rpk; /* remote public key */
- void * pkp; /* my public/private key pair */
- ssize_t key_len;
- uint8_t * s;
- int err;
+ buffer_t req_hdr;
+ buffer_t resp_hdr;
+ char name[NAME_SIZE + 1];
+ struct name_info info;
+ int err;
- /* piggyback of user data not yet implemented */
- assert(data != NULL && data->len == 0 && data->data == NULL);
+ assert(data != NULL && BUF_IS_EMPTY(data));
+
+ clrbuf(req_hdr);
+ clrbuf(resp_hdr);
if (!reg_has_proc(flow->n_pid)) {
log_err("Unknown process %d calling accept.", flow->n_pid);
err = -EINVAL;
- goto fail;
- }
-
- s = malloc(SYMMKEYSZ);
- if (s == NULL) {
- log_err("Failed to malloc symmkey.");
- err = -ENOMEM;
- goto fail;
- }
-
- key_len = crypt_dh_pkp_create(&pkp, buf);
- if (key_len < 0) {
- log_err("Failed to generate key pair.");
- err = -ECRYPT;
- goto fail_pkp;
+ goto fail_flow;
}
- lpk.data = buf;
- lpk.len = (size_t) key_len;
-
- log_dbg("Generated ephemeral keys for %d.", flow->n_pid);
-
if (reg_create_flow(flow) < 0) {
log_err("Failed to create flow.");
err = -EBADF;
goto fail_flow;
}
- if (reg_prepare_flow_accept(flow, &lpk) < 0) {
+ if (reg_prepare_flow_accept(flow) < 0) {
log_err("Failed to prepare accept.");
err = -EBADF;
goto fail_wait;
}
pthread_cleanup_push(__cleanup_flow, flow);
- pthread_cleanup_push(__cleanup_pkp, pkp);
- pthread_cleanup_push(free, s);
- err = reg_wait_flow_accepted(flow, &rpk, abstime);
+ err = reg_wait_flow_accepted(flow, &req_hdr, abstime);
pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
if (err == -ETIMEDOUT) {
log_err("Flow accept timed out.");
@@ -835,45 +886,55 @@ static int flow_accept(struct flow_info * flow,
assert(err == 0);
- if (flow->qs.cypher_s != 0) { /* crypto requested */
- if (crypt_dh_derive(pkp, rpk, s) < 0) {
- log_err("Failed to derive secret for %d.", flow->id);
- err = -ECRYPT;
- goto fail_derive;
- }
- freebuf(rpk);
- data->data = s;
- data->len = SYMMKEYSZ;
- s= NULL;
- } else {
- clrbuf(lpk);
+ if (reg_get_name_for_flow_id(name, flow->id) < 0) {
+ log_err("Failed to get name for flow %d.", flow->id);
+ err = -EIPCP;
+ goto fail_oap;
+ }
+
+ if (reg_get_name_info(name, &info) < 0) {
+ log_err("Failed to get name info for %s.", name);
+ err = -ENAME;
+ goto fail_oap;
}
- if (ipcp_flow_alloc_resp(flow, 0, lpk) < 0) {
+ log_dbg("IPCP %d accepting flow %d for %s.",
+ flow->n_pid, flow->id, name);
+
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+
+ err = oap_srv_process(&info, req_hdr, &resp_hdr, data, sk);
+ if (err < 0) {
+ log_err("OAP processing failed for %s.", name);
+ goto fail_oap;
+ }
+
+ if (ipcp_flow_alloc_resp(flow, 0, resp_hdr) < 0) {
log_err("Failed to respond to flow allocation.");
- err = -EIPCP;
- goto fail_alloc_resp;
+ goto fail_resp;
}
- crypt_dh_pkp_destroy(pkp);
- free(s);
+ log_info("Flow %d accepted by %d for %s (uid %d).",
+ flow->id, flow->n_pid, name, flow->uid);
+
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
return 0;
- fail_derive:
- freebuf(rpk);
- clrbuf(lpk);
- ipcp_flow_alloc_resp(flow, err, lpk);
- fail_alloc_resp:
- flow->state = FLOW_NULL;
+ fail_oap:
+ ipcp_flow_alloc_resp(flow, err, resp_hdr);
fail_wait:
reg_destroy_flow(flow->id);
fail_flow:
- crypt_dh_pkp_destroy(pkp);
- fail_pkp:
- free(s);
- fail:
return err;
+
+ fail_resp:
+ flow->state = FLOW_NULL;
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
+ reg_destroy_flow(flow->id);
+ return -EIPCP;
}
static int flow_join(struct flow_info * flow,
@@ -883,17 +944,20 @@ static int flow_join(struct flow_info * flow,
struct ipcp_info ipcp;
struct layer_info layer;
buffer_t hash;
- buffer_t pbuf = {NULL, 0}; /* nothing to piggyback */
+ buffer_t pbuf = BUF_INIT; /* nothing to piggyback */
int err;
- log_info("Allocating flow for %d to %s.", flow->n_pid, dst);
-
if (reg_create_flow(flow) < 0) {
log_err("Failed to create flow.");
err = -EBADF;
goto fail_flow;
}
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+
+ log_info("Allocating flow for %d to %s (uid %d).",
+ flow->n_pid, dst, flow->uid);
+
strcpy(layer.name, dst);
if (reg_get_ipcp_by_layer(&ipcp, &layer) < 0) {
log_err("Failed to get IPCP for layer %s.", dst);
@@ -901,6 +965,8 @@ static int flow_join(struct flow_info * flow,
goto fail_ipcp;
}
+ flow->n_1_pid = ipcp.pid;
+
hash.len = hash_len((enum hash_algo) layer.dir_hash_algo);
hash.data = malloc(hash.len);
if (hash.data == NULL) {
@@ -909,6 +975,8 @@ static int flow_join(struct flow_info * flow,
goto fail_ipcp;
}
+ str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, dst);
+
reg_prepare_flow_alloc(flow);
if (ipcp_flow_join(flow, hash)) {
@@ -936,6 +1004,7 @@ static int flow_join(struct flow_info * flow,
goto fail_alloc;
}
+ assert(pbuf.data == NULL && pbuf.len == 0);
assert(err == 0);
freebuf(hash);
@@ -1007,45 +1076,27 @@ static int get_ipcp_by_dst(const char * dst,
return err;
}
-static int flow_alloc(struct flow_info * flow,
- const char * dst,
+static int flow_alloc(const char * dst,
+ struct flow_info * flow,
buffer_t * data,
- struct timespec * abstime)
+ struct timespec * abstime,
+ struct crypt_sk * sk)
{
- uint8_t buf[MSGBUFSZ];
- buffer_t lpk ={NULL, 0}; /* local public key */
- buffer_t rpk; /* remote public key */
- void * pkp = NULL; /* my public/private key pair */
- uint8_t * s = NULL;
- buffer_t hash;
- int err;
- /* piggyback of user data not yet implemented */
- assert(data != NULL && data->len == 0 && data->data == NULL);
-
- log_info("Allocating flow for %d to %s.", flow->n_pid, dst);
-
-
- if (flow->qs.cypher_s > 0) {
- ssize_t key_len;
+ buffer_t req_hdr = BUF_INIT;
+ buffer_t resp_hdr = BUF_INIT;
+ buffer_t hash = BUF_INIT;
+ struct name_info info;
+ void * ctx;
+ int err;
- s = malloc(SYMMKEYSZ);
- if (s == NULL) {
- log_err("Failed to malloc symmetric key");
- err = -ENOMEM;
- goto fail_malloc;
- }
-
- key_len = crypt_dh_pkp_create(&pkp, buf);
- if (key_len < 0) {
- log_err("Failed to generate key pair.");
- err = -ECRYPT;
- goto fail_pkp;
- }
-
- lpk.data = buf;
- lpk.len = (size_t) key_len;
+ /* piggyback of user data not yet implemented */
+ assert(data != NULL && BUF_IS_EMPTY(data));
- log_dbg("Generated ephemeral keys for %d.", flow->n_pid);
+ /* Look up name_info for dst */
+ if (reg_get_name_info(dst, &info) < 0) {
+ log_err("Failed to get name info for %s.", dst);
+ err = -ENAME;
+ goto fail_flow;
}
if (reg_create_flow(flow) < 0) {
@@ -1054,81 +1105,85 @@ static int flow_alloc(struct flow_info * flow,
goto fail_flow;
}
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+
+ log_info("Allocating flow for %d to %s (uid %d).",
+ flow->n_pid, dst, flow->uid);
+
if (get_ipcp_by_dst(dst, &flow->n_1_pid, &hash) < 0) {
log_err("Failed to find IPCP for %s.", dst);
err = -EIPCP;
goto fail_ipcp;
}
- reg_prepare_flow_alloc(flow);
+ if (reg_prepare_flow_alloc(flow) < 0) {
+ log_err("Failed to prepare flow allocation.");
+ err = -EBADF;
+ goto fail_prepare;
+ }
- if (ipcp_flow_alloc(flow, hash, lpk)) {
+ if (oap_cli_prepare(&ctx, &info, &req_hdr, *data) < 0) {
+ log_err("Failed to prepare OAP request for %s.", dst);
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ if (ipcp_flow_alloc(flow, hash, req_hdr)) {
log_err("Flow allocation %d failed.", flow->id);
- err = -ENOTALLOC;
+ err = -EIPCP;
goto fail_alloc;
}
pthread_cleanup_push(__cleanup_flow, flow);
- pthread_cleanup_push(__cleanup_pkp, pkp);
pthread_cleanup_push(free, hash.data);
- pthread_cleanup_push(free, s);
- err = reg_wait_flow_allocated(flow, &rpk, abstime);
+ err = reg_wait_flow_allocated(flow, &resp_hdr, abstime);
pthread_cleanup_pop(false);
pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
if (err == -ETIMEDOUT) {
log_err("Flow allocation timed out.");
- goto fail_alloc;
+ goto fail_wait;
}
- if (err == -1) {
- log_dbg("Flow allocation terminated.");
- err = -EPIPE;
- goto fail_alloc;
- }
+ log_dbg("Response for flow %d to %s.", flow->id, dst);
- assert(err == 0);
+ if (err < 0) {
+ log_warn("Allocation rejected: %s (%d).", dst, err);
+ goto fail_peer;
+ }
- if (flow->qs.cypher_s != 0) { /* crypto requested */
- if (crypt_dh_derive(pkp, rpk, s) < 0) {
- log_err("Failed to derive secret for %d.", flow->id);
- err = -ECRYPT;
- goto fail_derive;
- }
- crypt_dh_pkp_destroy(pkp);
- freebuf(rpk);
- data->data = s;
- data->len = SYMMKEYSZ;
- s = NULL;
+ err = oap_cli_complete(ctx, &info, resp_hdr, data, sk);
+ if (err < 0) {
+ log_err("OAP completion failed for %s.", dst);
+ goto fail_complete;
}
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
freebuf(hash);
- free(s);
return 0;
- fail_derive:
- freebuf(rpk);
+ fail_complete:
+ ctx = NULL; /* freee'd on complete */
+ fail_peer:
flow->state = FLOW_DEALLOCATED;
+ fail_wait:
+ freebuf(resp_hdr);
fail_alloc:
+ freebuf(req_hdr);
+ oap_ctx_free(ctx);
+ fail_prepare:
freebuf(hash);
fail_ipcp:
reg_destroy_flow(flow->id);
fail_flow:
- if (flow->qs.cypher_s > 0)
- crypt_dh_pkp_destroy(pkp);
- fail_pkp:
- free(s);
- fail_malloc:
return err;
}
-static int wait_for_accept(enum hash_algo algo,
- const uint8_t * hash)
+static int wait_for_accept(const char * name)
{
struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT);
struct timespec abstime;
@@ -1138,25 +1193,23 @@ static int wait_for_accept(enum hash_algo algo,
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- ret = reg_wait_flow_accepting(algo, hash, &abstime);
+ ret = reg_wait_flow_accepting(name, &abstime);
if (ret == -ETIMEDOUT) {
- if (reg_get_exec(algo, hash, &exec) < 0) {
- log_dbg("No program bound to " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ if (reg_get_exec(name, &exec) < 0) {
+ log_dbg("No program bound for %s.", name);
goto fail;
}
- log_info("Autostarting %s.", exec[0]);
-
if (spawn_program(exec) < 0) {
- log_dbg("Failed to autostart " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ log_err("Failed to start %s for %s.", exec[0], name);
goto fail_spawn;
}
+ log_info("Starting %s for %s.", exec[0], name);
+
ts_add(&abstime, &timeo, &abstime);
- ret = reg_wait_flow_accepting(algo, hash, &abstime);
+ ret = reg_wait_flow_accepting(name, &abstime);
if (ret == -ETIMEDOUT)
goto fail_spawn;
@@ -1179,10 +1232,11 @@ static int flow_req_arr(struct flow_info * flow,
struct layer_info layer;
enum hash_algo algo;
int ret;
+ char name[NAME_SIZE + 1];
info.pid = flow->n_1_pid;
- log_info("Flow req arrived from IPCP %d for " HASH_FMT32 ".",
+ log_dbg("Flow req arrived from IPCP %d for " HASH_FMT32 ".",
info.pid, HASH_VAL32(hash));
if (reg_get_ipcp(&info, &layer) < 0) {
@@ -1193,10 +1247,17 @@ static int flow_req_arr(struct flow_info * flow,
algo = (enum hash_algo) layer.dir_hash_algo;
- ret = wait_for_accept(algo, hash);
+ if (reg_get_name_for_hash(name, algo, hash) < 0) {
+ log_warn("No name for " HASH_FMT32 ".", HASH_VAL32(hash));
+ ret = -ENAME;
+ goto fail;
+ }
+
+ log_info("Flow request arrived for %s.", name);
+
+ ret = wait_for_accept(name);
if (ret < 0) {
- log_err("No activeprocess for " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ log_err("No active process for %s.", name);
goto fail;
}
@@ -1218,9 +1279,9 @@ static int flow_alloc_reply(struct flow_info * flow,
int response,
buffer_t * data)
{
- flow->state = response ? FLOW_DEALLOCATED : FLOW_ALLOCATED;
+ flow->state = response != 0 ? FLOW_DEALLOCATED : FLOW_ALLOCATED;
- if (reg_respond_alloc(flow, data) < 0) {
+ if (reg_respond_alloc(flow, data, response) < 0) {
log_err("Failed to reply to flow %d.", flow->id);
flow->state = FLOW_DEALLOCATED;
return -EBADF;
@@ -1232,7 +1293,7 @@ static int flow_alloc_reply(struct flow_info * flow,
static int flow_dealloc(struct flow_info * flow,
struct timespec * ts)
{
- log_info("Deallocating flow %d for process %d (timeout: %zd s).",
+ log_info("Deallocating flow %d for process %d (timeout: %ld s).",
flow->id, flow->n_pid, ts->tv_sec);
reg_dealloc_flow(flow);
@@ -1308,25 +1369,29 @@ static void * acceptloop(void * o)
return (void *) 0;
}
-static void free_msg(void * o)
+static void __cleanup_irm_msg(void * o)
{
irm_msg__free_unpacked((irm_msg_t *) o, NULL);
}
-static irm_msg_t * do_command_msg(irm_msg_t * msg)
+static irm_msg_t * do_command_msg(irm_msg_t * msg,
+ int fd)
{
- struct ipcp_config conf;
- struct ipcp_info ipcp;
- struct flow_info flow;
- struct proc_info proc;
- struct name_info name;
- struct timespec * abstime;
- struct timespec max = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT);
- struct timespec now;
- struct timespec ts = TIMESPEC_INIT_S(0); /* static analysis */
- int res;
- irm_msg_t * ret_msg;
- buffer_t data;
+ struct ipcp_config conf;
+ struct ipcp_info ipcp;
+ struct flow_info flow;
+ struct proc_info proc;
+ struct name_info name;
+ struct crypt_sk sk;
+ uint8_t kbuf[SYMMKEYSZ]; /* stack buffer for OAP */
+ uint8_t * hbuf = NULL; /* heap copy for response */
+ struct timespec * abstime;
+ struct timespec max = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT);
+ struct timespec now;
+ struct timespec ts = TIMESPEC_INIT_S(0); /* static analysis */
+ int res;
+ irm_msg_t * ret_msg;
+ buffer_t data;
memset(&flow, 0, sizeof(flow));
@@ -1351,7 +1416,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
ret_msg->code = IRM_MSG_CODE__IRM_REPLY;
- pthread_cleanup_push(free_msg, ret_msg);
+ pthread_cleanup_push(__cleanup_irm_msg, ret_msg);
switch (msg->code) {
case IRM_MSG_CODE__IRM_CREATE_IPCP:
@@ -1380,7 +1445,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = disconnect_ipcp(msg->pid, msg->dst, msg->comp);
break;
case IRM_MSG_CODE__IRM_BIND_PROGRAM:
- /* Make exec NULL terminated instead of empty string terminated */
+ /* Terminate with NULL instead of "" */
free(msg->exec[msg->n_exec - 1]);
msg->exec[msg->n_exec - 1] = NULL;
res = bind_program(msg->exec, msg->name, msg->opts);
@@ -1391,7 +1456,11 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
case IRM_MSG_CODE__IRM_PROC_ANNOUNCE:
proc.pid = msg->pid;
strcpy(proc.prog, msg->prog);
- res = proc_announce(&proc);
+ res = get_peer_ids(fd, &proc.uid, &proc.gid);
+ if (res < 0)
+ log_err("Failed to get UID/GID for pid %d.", msg->pid);
+ else
+ res = proc_announce(&proc);
break;
case IRM_MSG_CODE__IRM_PROC_EXIT:
res = proc_exit(msg->pid);
@@ -1406,8 +1475,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = list_ipcps(&ret_msg->ipcps, &ret_msg->n_ipcps);
break;
case IRM_MSG_CODE__IRM_CREATE_NAME:
- strcpy(name.name, msg->names[0]->name);
- name.pol_lb = msg->names[0]->pol_lb;
+ name = name_info_msg_to_s(msg->name_info);
res = name_create(&name);
break;
case IRM_MSG_CODE__IRM_DESTROY_NAME:
@@ -1423,17 +1491,33 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = name_unreg(msg->name, msg->pid);
break;
case IRM_MSG_CODE__IRM_FLOW_ACCEPT:
+ tpm_wait_work(irmd.tpm);
data.len = msg->pk.len;
data.data = msg->pk.data;
msg->has_pk = false;
assert(data.len > 0 ? data.data != NULL : data.data == NULL);
flow = flow_info_msg_to_s(msg->flow_info);
- res = flow_accept(&flow, &data, abstime);
+ sk.key = kbuf;
+ res = flow_accept(&flow, &data, abstime, &sk);
if (res == 0) {
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
- ret_msg->has_symmkey = data.len != 0;
- ret_msg->symmkey.data = data.data;
- ret_msg->symmkey.len = data.len;
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
+ ret_msg->has_cipher_nid = true;
+ ret_msg->cipher_nid = sk.nid;
+ if (sk.nid != NID_undef) {
+ hbuf = malloc(SYMMKEYSZ);
+ if (hbuf == NULL) {
+ log_err("Failed to malloc key buf");
+ return NULL;
+ }
+
+ memcpy(hbuf, kbuf, SYMMKEYSZ);
+ ret_msg->sym_key.data = hbuf;
+ ret_msg->sym_key.len = SYMMKEYSZ;
+ ret_msg->has_sym_key = true;
+ }
}
break;
case IRM_MSG_CODE__IRM_FLOW_ALLOC:
@@ -1443,12 +1527,26 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
assert(data.len > 0 ? data.data != NULL : data.data == NULL);
flow = flow_info_msg_to_s(msg->flow_info);
abstime = abstime == NULL ? &max : abstime;
- res = flow_alloc(&flow, msg->dst, &data, abstime);
+ sk.key = kbuf;
+ res = flow_alloc(msg->dst, &flow, &data, abstime, &sk);
if (res == 0) {
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
- ret_msg->has_symmkey = data.len != 0;
- ret_msg->symmkey.data = data.data;
- ret_msg->symmkey.len = data.len;
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
+ ret_msg->has_cipher_nid = true;
+ ret_msg->cipher_nid = sk.nid;
+ if (sk.nid != NID_undef) {
+ hbuf = malloc(SYMMKEYSZ);
+ if (hbuf == NULL) {
+ log_err("Failed to malloc key buf");
+ return NULL;
+ }
+ memcpy(hbuf, kbuf, SYMMKEYSZ);
+ ret_msg->sym_key.data = hbuf;
+ ret_msg->sym_key.len = SYMMKEYSZ;
+ ret_msg->has_sym_key = true;
+ }
}
break;
case IRM_MSG_CODE__IRM_FLOW_JOIN:
@@ -1457,7 +1555,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
abstime = abstime == NULL ? &max : abstime;
res = flow_join(&flow, msg->dst, abstime);
if (res == 0)
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
break;
case IRM_MSG_CODE__IRM_FLOW_DEALLOC:
flow = flow_info_msg_to_s(msg->flow_info);
@@ -1502,14 +1600,16 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
else
ret_msg->result = res;
+ crypt_secure_clear(kbuf, SYMMKEYSZ);
+
return ret_msg;
}
static void * mainloop(void * o)
{
- int sfd;
- irm_msg_t * msg;
- buffer_t buffer;
+ int sfd;
+ irm_msg_t * msg;
+ buffer_t buffer;
(void) o;
@@ -1540,12 +1640,12 @@ static void * mainloop(void * o)
continue;
}
- tpm_dec(irmd.tpm);
+ tpm_begin_work(irmd.tpm);
pthread_cleanup_push(__cleanup_close_ptr, &sfd);
- pthread_cleanup_push(free_msg, msg);
+ pthread_cleanup_push(__cleanup_irm_msg, msg);
- ret_msg = do_command_msg(msg);
+ ret_msg = do_command_msg(msg, sfd);
pthread_cleanup_pop(true);
pthread_cleanup_pop(false);
@@ -1556,12 +1656,12 @@ static void * mainloop(void * o)
}
if (ret_msg->result == -EPIPE) {
- log_dbg("Terminated command: application closed socket.");
+ log_dbg("Terminated command: remote closed socket.");
goto fail;
}
if (ret_msg->result == -EIRMD) {
- log_dbg("Terminated command: IRMd not in running state.");
+ log_dbg("Terminated command: IRMd not running.");
goto fail;
}
@@ -1596,54 +1696,20 @@ static void * mainloop(void * o)
pthread_cleanup_pop(true);
pthread_cleanup_pop(true);
- tpm_inc(irmd.tpm);
+ tpm_end_work(irmd.tpm);
continue;
fail:
irm_msg__free_unpacked(ret_msg, NULL);
fail_msg:
close(sfd);
- tpm_inc(irmd.tpm);
+ tpm_end_work(irmd.tpm);
continue;
}
return (void *) 0;
}
-static void irm_fini(void)
-{
-#ifdef HAVE_FUSE
- struct timespec wait = TIMESPEC_INIT_MS(1);
- int retries = 5;
-#endif
- if (irmd_get_state() != IRMD_NULL)
- log_warn("Unsafe destroy.");
-
- tpm_destroy(irmd.tpm);
-
- close(irmd.sockfd);
-
- if (unlink(IRM_SOCK_PATH))
- log_dbg("Failed to unlink %s.", IRM_SOCK_PATH);
-
- if (irmd.rdrb != NULL)
- shm_rdrbuff_destroy(irmd.rdrb);
-
- if (irmd.lf != NULL)
- lockfile_destroy(irmd.lf);
-
- pthread_mutex_destroy(&irmd.cmd_lock);
- pthread_cond_destroy(&irmd.cmd_cond);
- pthread_rwlock_destroy(&irmd.state_lock);
-
-#ifdef HAVE_FUSE
- while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0)
- nanosleep(&wait, NULL);
- if (retries < 0)
- log_err("Failed to remove " FUSE_PREFIX);
-#endif
-}
-
#ifdef HAVE_FUSE
static void destroy_mount(char * mnt)
{
@@ -1672,7 +1738,7 @@ static void destroy_mount(char * mnt)
static int ouroboros_reset(void)
{
- shm_rdrbuff_purge();
+ ssm_pool_gspp_purge();
lockfile_destroy(irmd.lf);
return 0;
@@ -1693,10 +1759,8 @@ static void cleanup_pid(pid_t pid)
}
destroy_mount(mnt);
-
-#else
- (void) pid;
#endif
+ ssm_pool_reclaim_orphans(irmd.gspp, pid);
}
void * irm_sanitize(void * o)
@@ -1719,10 +1783,85 @@ void * irm_sanitize(void * o)
return (void *) 0;
}
+static int irm_load_store(char * dpath)
+{
+ struct stat st;
+ struct dirent * dent;
+ DIR * dir;
+ void * crt;
+
+ if (stat(dpath, &st) == -1) {
+ log_dbg("Store directory %s not found.", dpath);
+ return 0;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ log_err("%s is not a directory.", dpath);
+ goto fail_dir;
+ }
+
+ /* loop through files in directory and load certificates */
+ dir = opendir(dpath);
+ if (dir == NULL) {
+ log_err("Failed to open %s.", dpath);
+ goto fail_dir;
+ }
+
+ while ((dent = readdir(dir)) != NULL) {
+ char path[NAME_PATH_SIZE + 1];
+
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", dpath,
+ dent->d_name);
+
+ if (stat(path, &st) == -1) {
+ log_dbg("Failed to stat %s.", path);
+ continue;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ log_dbg("%s is not a regular file.", path);
+ goto fail_file;
+ }
+
+ if (crypt_load_crt_file(path, &crt) < 0) {
+ log_err("Failed to load certificate from %s.", path);
+ goto fail_file;
+ }
+
+ if (oap_auth_add_ca_crt(crt) < 0) {
+ log_err("Failed to add certificate from %s to store.",
+ path);
+ goto fail_crt_add;
+ }
+
+ log_dbg("Loaded certificate: %s.", path);
+
+ crypt_free_crt(crt);
+ }
+
+ closedir(dir);
+
+ log_info("Loaded certificates from %s.", dpath);
+
+ return 0;
+
+ fail_crt_add:
+ crypt_free_crt(crt);
+ fail_file:
+ closedir(dir);
+ fail_dir:
+ return -1;
+}
static int irm_init(void)
{
struct stat st;
+ struct group * grp;
+ gid_t gid;
pthread_condattr_t cattr;
#ifdef HAVE_FUSE
mode_t mask;
@@ -1808,17 +1947,45 @@ static int irm_init(void)
goto fail_sock_path;
}
- if ((irmd.rdrb = shm_rdrbuff_create()) == NULL) {
- log_err("Failed to create rdrbuff.");
- goto fail_rdrbuff;
+ grp = getgrnam("ouroboros");
+ if (grp == NULL) {
+ log_warn("ouroboros group not found, using gid %d.", getgid());
+ gid = getgid();
+ } else {
+ gid = grp->gr_gid;
+ }
+
+ irmd.gspp = ssm_pool_create(getuid(), gid);
+ if (irmd.gspp == NULL) {
+ log_err("Failed to create GSPP.");
+ goto fail_pool;
}
+ if (ssm_pool_mlock(irmd.gspp) < 0)
+ log_warn("Failed to mlock pool.");
+
irmd.tpm = tpm_create(IRMD_MIN_THREADS, IRMD_ADD_THREADS,
mainloop, NULL);
if (irmd.tpm == NULL) {
log_err("Failed to greate thread pool.");
goto fail_tpm_create;
}
+
+ if (oap_auth_init() < 0) {
+ log_err("Failed to initialize OAP module.");
+ goto fail_oap;
+ }
+
+ if (irm_load_store(OUROBOROS_CA_CRT_DIR) < 0) {
+ log_err("Failed to load CA certificates.");
+ goto fail_load_store;
+ }
+
+ if (irm_load_store(OUROBOROS_CHAIN_DIR) < 0) {
+ log_err("Failed to load intermediate certificates.");
+ goto fail_load_store;
+ }
+
#ifdef HAVE_FUSE
mask = umask(0);
@@ -1844,6 +2011,8 @@ static int irm_init(void)
gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
#endif
+ irmd_set_state(IRMD_INIT);
+
return 0;
#ifdef HAVE_LIBGCRYPT
@@ -1851,11 +2020,14 @@ static int irm_init(void)
#ifdef HAVE_FUSE
rmdir(FUSE_PREFIX);
#endif
- tpm_destroy(irmd.tpm);
#endif
+ fail_load_store:
+ oap_auth_fini();
+ fail_oap:
+ tpm_destroy(irmd.tpm);
fail_tpm_create:
- shm_rdrbuff_destroy(irmd.rdrb);
- fail_rdrbuff:
+ ssm_pool_destroy(irmd.gspp);
+ fail_pool:
close(irmd.sockfd);
fail_sock_path:
unlink(IRM_SOCK_PATH);
@@ -1872,6 +2044,57 @@ static int irm_init(void)
return -1;
}
+static void irm_fini(void)
+{
+ struct list_head * p;
+ struct list_head * h;
+#ifdef HAVE_FUSE
+ struct timespec wait = TIMESPEC_INIT_MS(1);
+ int retries = 5;
+#endif
+ if (irmd_get_state() != IRMD_INIT)
+ log_warn("Unsafe destroy.");
+
+ oap_auth_fini();
+
+ tpm_destroy(irmd.tpm);
+
+ close(irmd.sockfd);
+
+ if (unlink(IRM_SOCK_PATH))
+ log_dbg("Failed to unlink %s.", IRM_SOCK_PATH);
+
+ ssm_pool_destroy(irmd.gspp);
+
+ if (irmd.lf != NULL)
+ lockfile_destroy(irmd.lf);
+
+ pthread_mutex_lock(&irmd.cmd_lock);
+
+ list_for_each_safe(p, h, &irmd.cmds) {
+ struct cmd * cmd = list_entry(p, struct cmd, next);
+ list_del(&cmd->next);
+ close(cmd->fd);
+ free(cmd);
+ }
+
+ pthread_mutex_unlock(&irmd.cmd_lock);
+
+ pthread_mutex_destroy(&irmd.cmd_lock);
+ pthread_cond_destroy(&irmd.cmd_cond);
+ pthread_rwlock_destroy(&irmd.state_lock);
+
+#ifdef HAVE_FUSE
+ while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0)
+ nanosleep(&wait, NULL);
+ if (retries < 0)
+ log_err("Failed to remove " FUSE_PREFIX);
+#endif
+ assert(list_is_empty(&irmd.cmds));
+
+ irmd.state = IRMD_NULL;
+}
+
static void usage(void)
{
printf("Usage: irmd \n"
@@ -1885,11 +2108,11 @@ static void usage(void)
static int irm_start(void)
{
+ irmd_set_state(IRMD_RUNNING);
+
if (tpm_start(irmd.tpm))
goto fail_tpm_start;
- irmd_set_state(IRMD_RUNNING);
-
if (pthread_create(&irmd.irm_sanitize, NULL, irm_sanitize, NULL))
goto fail_irm_sanitize;
@@ -1904,9 +2127,9 @@ static int irm_start(void)
pthread_cancel(irmd.irm_sanitize);
pthread_join(irmd.irm_sanitize, NULL);
fail_irm_sanitize:
- irmd_set_state(IRMD_NULL);
tpm_stop(irmd.tpm);
fail_tpm_start:
+ irmd_set_state(IRMD_INIT);
return -1;
}
@@ -1947,7 +2170,7 @@ static void irm_stop(void)
tpm_stop(irmd.tpm);
- irmd_set_state(IRMD_NULL);
+ irmd_set_state(IRMD_INIT);
}
static void irm_argparse(int argc,
@@ -1998,8 +2221,8 @@ static void * kill_dash_nine(void * o)
slept += intv;
}
- log_dbg("I am become Death, destroyer of hung processes.");
-
+ log_dbg("I guess I’ll have to shut you down for good this time,");
+ log_dbg("already tried a SIGQUIT, so now it’s KILL DASH 9.");
#ifdef IRMD_KILL_ALL_PROCESSES
reg_kill_all_proc(SIGKILL);
nanosleep(&ts, NULL);
@@ -2058,7 +2281,7 @@ int main(int argc,
if (geteuid() != 0) {
printf("IPC Resource Manager must be run as root.\n");
- exit(EXIT_FAILURE);
+ goto fail_irm_init;
}
if (irm_init() < 0)
@@ -2069,6 +2292,11 @@ int main(int argc,
goto fail_reg;
}
+ if (crypt_secure_malloc_init(IRMD_SECMEM_MAX) < 0) {
+ log_err("Failed to initialize secure memory allocation.");
+ goto fail_reg;
+ }
+
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
if (irm_start() < 0)
@@ -2076,7 +2304,7 @@ int main(int argc,
#ifdef HAVE_TOML
if (irm_configure(irmd.cfg_file) < 0) {
- irmd_set_state(IRMD_NULL);
+ irmd_set_state(IRMD_SHUTDOWN);
ret = EXIT_FAILURE;
}
#endif
@@ -2088,6 +2316,8 @@ int main(int argc,
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+ crypt_secure_malloc_fini();
+
reg_clear();
reg_fini();
diff --git a/src/irmd/oap.c b/src/irmd/oap.c
new file mode 100644
index 00000000..085e06a3
--- /dev/null
+++ b/src/irmd/oap.c
@@ -0,0 +1,130 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Shared credential and configuration loading
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/*
+ * Shared credential and configuration loading helpers
+ */
+
+#ifndef OAP_TEST_MODE
+
+static bool file_exists(const char * path)
+{
+ struct stat s;
+
+ if (stat(path, &s) < 0 && errno == ENOENT) {
+ log_dbg("File %s does not exist.", path);
+ return false;
+ }
+
+ return true;
+}
+
+int load_credentials(const char * name,
+ const struct name_sec_paths * paths,
+ void ** pkp,
+ void ** crt)
+{
+ assert(paths != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ *pkp = NULL;
+ *crt = NULL;
+
+ if (!file_exists(paths->crt) || !file_exists(paths->key)) {
+ log_info("No authentication certificates for %s.", name);
+ return 0;
+ }
+
+ if (crypt_load_crt_file(paths->crt, crt) < 0) {
+ log_err("Failed to load %s for %s.", paths->crt, name);
+ goto fail_crt;
+ }
+
+ if (crypt_load_privkey_file(paths->key, pkp) < 0) {
+ log_err("Failed to load %s for %s.", paths->key, name);
+ goto fail_key;
+ }
+
+ log_info("Loaded authentication certificates for %s.", name);
+
+ return 0;
+
+ fail_key:
+ crypt_free_crt(*crt);
+ *crt = NULL;
+ fail_crt:
+ return -EAUTH;
+}
+
+int load_kex_config(const char * name,
+ const char * path,
+ struct sec_config * cfg)
+{
+ assert(name != NULL);
+ assert(cfg != NULL);
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ /* Load encryption config */
+ if (!file_exists(path))
+ log_dbg("No encryption %s for %s.", path, name);
+
+ if (load_sec_config_file(cfg, path) < 0) {
+ log_warn("Failed to load %s for %s.", path, name);
+ return -1;
+ }
+
+ if (!IS_KEX_ALGO_SET(cfg)) {
+ log_info("Key exchange not configured for %s.", name);
+ return 0;
+ }
+
+ if (cfg->c.nid == NID_undef || crypt_nid_to_str(cfg->c.nid) == NULL) {
+ log_err("Invalid cipher NID %d for %s.", cfg->c.nid, name);
+ return -ECRYPT;
+ }
+
+ log_info("Encryption enabled for %s.", name);
+
+ return 0;
+}
+
+#endif /* OAP_TEST_MODE */
diff --git a/src/irmd/oap.h b/src/irmd/oap.h
new file mode 100644
index 00000000..25c07408
--- /dev/null
+++ b/src/irmd/oap.h
@@ -0,0 +1,67 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Ouroboros Allocation Protocol (OAP) Component
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_OAP_H
+#define OUROBOROS_IRMD_OAP_H
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/flow.h>
+#include <ouroboros/name.h>
+#include <ouroboros/utils.h>
+
+/* OAP authentication state (in oap/auth.c) */
+int oap_auth_init(void);
+
+void oap_auth_fini(void);
+
+int oap_auth_add_ca_crt(void * crt);
+
+/*
+* Prepare OAP request header for server, returns context
+* Passes client data for srv, returns srv data for client
+*/
+int oap_cli_prepare(void ** ctx,
+ const struct name_info * info,
+ buffer_t * req_buf,
+ buffer_t data);
+
+/*
+ * Server processes header, creates response header, returns secret key.
+ * data is in/out: input=srv data to send, output=cli data received.
+ */
+int oap_srv_process(const struct name_info * info,
+ buffer_t req_buf,
+ buffer_t * rsp_buf,
+ buffer_t * data,
+ struct crypt_sk * sk);
+
+/* Complete OAP, returns secret key and server data, frees ctx */
+int oap_cli_complete(void * ctx,
+ const struct name_info * info,
+ buffer_t rsp_buf,
+ buffer_t * data,
+ struct crypt_sk * sk);
+
+/* Free OAP state (on failure before complete) */
+void oap_ctx_free(void * ctx);
+
+#endif /* OUROBOROS_IRMD_OAP_H */
diff --git a/src/irmd/oap/auth.c b/src/irmd/oap/auth.c
new file mode 100644
index 00000000..cea7b7a0
--- /dev/null
+++ b/src/irmd/oap/auth.c
@@ -0,0 +1,252 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Authentication, replay detection, and validation
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/list.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/pthread.h>
+#include <ouroboros/time.h>
+
+#include "config.h"
+
+#include "auth.h"
+#include "hdr.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct oap_replay_entry {
+ struct list_head next;
+ uint64_t timestamp;
+ uint8_t id[OAP_ID_SIZE];
+};
+
+static struct {
+ struct auth_ctx * ca_ctx;
+ struct {
+ struct list_head list;
+ pthread_mutex_t mtx;
+ } replay;
+} oap_auth;
+
+int oap_auth_init(void)
+{
+ oap_auth.ca_ctx = auth_create_ctx();
+ if (oap_auth.ca_ctx == NULL) {
+ log_err("Failed to create OAP auth context.");
+ goto fail_ctx;
+ }
+
+ list_head_init(&oap_auth.replay.list);
+
+ if (pthread_mutex_init(&oap_auth.replay.mtx, NULL)) {
+ log_err("Failed to init OAP replay mutex.");
+ goto fail_mtx;
+ }
+
+ return 0;
+
+ fail_mtx:
+ auth_destroy_ctx(oap_auth.ca_ctx);
+ fail_ctx:
+ return -1;
+}
+
+void oap_auth_fini(void)
+{
+ struct list_head * p;
+ struct list_head * h;
+
+ pthread_mutex_lock(&oap_auth.replay.mtx);
+
+ list_for_each_safe(p, h, &oap_auth.replay.list) {
+ struct oap_replay_entry * e;
+ e = list_entry(p, struct oap_replay_entry, next);
+ list_del(&e->next);
+ free(e);
+ }
+
+ pthread_mutex_unlock(&oap_auth.replay.mtx);
+ pthread_mutex_destroy(&oap_auth.replay.mtx);
+
+ auth_destroy_ctx(oap_auth.ca_ctx);
+}
+
+int oap_auth_add_ca_crt(void * crt)
+{
+ return auth_add_crt_to_store(oap_auth.ca_ctx, crt);
+}
+
+#define TIMESYNC_SLACK 100 /* ms */
+#define ID_IS_EQUAL(id1, id2) (memcmp(id1, id2, OAP_ID_SIZE) == 0)
+int oap_check_hdr(const struct oap_hdr * hdr)
+{
+ struct list_head * p;
+ struct list_head * h;
+ struct timespec now;
+ struct oap_replay_entry * new;
+ uint64_t stamp;
+ uint64_t cur;
+ uint8_t * id;
+ ssize_t delta;
+
+ assert(hdr != NULL);
+
+ stamp = hdr->timestamp;
+ id = hdr->id.data;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+
+ cur = TS_TO_UINT64(now);
+
+ delta = (ssize_t)(cur - stamp) / MILLION;
+ if (delta < -TIMESYNC_SLACK) {
+ log_err_id(id, "OAP header from %zd ms into future.", -delta);
+ goto fail_stamp;
+ }
+
+ if (delta > OAP_REPLAY_TIMER * 1000) {
+ log_err_id(id, "OAP header too old (%zd ms).", delta);
+ goto fail_stamp;
+ }
+
+ new = malloc(sizeof(*new));
+ if (new == NULL) {
+ log_err_id(id, "Failed to allocate memory for OAP element.");
+ goto fail_stamp;
+ }
+
+ pthread_mutex_lock(&oap_auth.replay.mtx);
+
+ list_for_each_safe(p, h, &oap_auth.replay.list) {
+ struct oap_replay_entry * e;
+ e = list_entry(p, struct oap_replay_entry, next);
+ if (cur > e->timestamp + OAP_REPLAY_TIMER * BILLION) {
+ list_del(&e->next);
+ free(e);
+ continue;
+ }
+
+ if (e->timestamp == stamp && ID_IS_EQUAL(e->id, id)) {
+ log_warn_id(id, "OAP header already known.");
+ goto fail_replay;
+ }
+ }
+
+ memcpy(new->id, id, OAP_ID_SIZE);
+ new->timestamp = stamp;
+
+ list_add_tail(&new->next, &oap_auth.replay.list);
+
+ pthread_mutex_unlock(&oap_auth.replay.mtx);
+
+ return 0;
+
+ fail_replay:
+ pthread_mutex_unlock(&oap_auth.replay.mtx);
+ free(new);
+ fail_stamp:
+ return -EAUTH;
+}
+
+int oap_auth_peer(char * name,
+ const struct oap_hdr * local_hdr,
+ const struct oap_hdr * peer_hdr)
+{
+ void * crt;
+ void * pk;
+ buffer_t sign; /* Signed region */
+ uint8_t * id = peer_hdr->id.data;
+
+ assert(name != NULL);
+ assert(local_hdr != NULL);
+ assert(peer_hdr != NULL);
+
+ if (memcmp(peer_hdr->id.data, local_hdr->id.data, OAP_ID_SIZE) != 0) {
+ log_err_id(id, "OAP ID mismatch in flow allocation.");
+ goto fail_check;
+ }
+
+ if (peer_hdr->crt.len == 0) {
+ log_dbg_id(id, "No crt provided.");
+ name[0] = '\0';
+ return 0;
+ }
+
+ if (crypt_load_crt_der(peer_hdr->crt, &crt) < 0) {
+ log_err_id(id, "Failed to load crt.");
+ goto fail_check;
+ }
+
+ log_dbg_id(id, "Loaded peer crt.");
+
+ if (crypt_get_pubkey_crt(crt, &pk) < 0) {
+ log_err_id(id, "Failed to get pubkey from crt.");
+ goto fail_crt;
+ }
+
+ log_dbg_id(id, "Got public key from crt.");
+
+ if (auth_verify_crt(oap_auth.ca_ctx, crt) < 0) {
+ log_err_id(id, "Failed to verify peer with CA store.");
+ goto fail_crt;
+ }
+
+ log_dbg_id(id, "Successfully verified peer crt.");
+
+ sign = peer_hdr->hdr;
+ sign.len -= peer_hdr->sig.len;
+
+ if (auth_verify_sig(pk, peer_hdr->md_nid, sign, peer_hdr->sig) < 0) {
+ log_err_id(id, "Failed to verify signature.");
+ goto fail_check_sig;
+ }
+
+ if (crypt_get_crt_name(crt, name) < 0) {
+ log_warn_id(id, "Failed to extract name from certificate.");
+ name[0] = '\0';
+ }
+
+ crypt_free_key(pk);
+ crypt_free_crt(crt);
+
+ log_dbg_id(id, "Successfully authenticated peer.");
+
+ return 0;
+
+ fail_check_sig:
+ crypt_free_key(pk);
+ fail_crt:
+ crypt_free_crt(crt);
+ fail_check:
+ return -EAUTH;
+}
diff --git a/src/irmd/oap/auth.h b/src/irmd/oap/auth.h
new file mode 100644
index 00000000..07c33a23
--- /dev/null
+++ b/src/irmd/oap/auth.h
@@ -0,0 +1,35 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Authentication functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_OAP_AUTH_H
+#define OUROBOROS_IRMD_OAP_AUTH_H
+
+#include "hdr.h"
+
+int oap_check_hdr(const struct oap_hdr * hdr);
+
+/* name is updated with the peer's certificate name if available */
+int oap_auth_peer(char * name,
+ const struct oap_hdr * local_hdr,
+ const struct oap_hdr * peer_hdr);
+
+#endif /* OUROBOROS_IRMD_OAP_AUTH_H */
diff --git a/src/irmd/oap/cli.c b/src/irmd/oap/cli.c
new file mode 100644
index 00000000..ea2a25d1
--- /dev/null
+++ b/src/irmd/oap/cli.c
@@ -0,0 +1,553 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Client-side processing
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/random.h>
+
+#include "config.h"
+
+#include "auth.h"
+#include "hdr.h"
+#include "io.h"
+#include "../oap.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Client context between oap_cli_prepare and oap_cli_complete */
+struct oap_cli_ctx {
+ uint8_t __id[OAP_ID_SIZE];
+ buffer_t id;
+ uint8_t kex_buf[MSGBUFSZ];
+ uint8_t req_hash[MAX_HASH_SIZE];
+ size_t req_hash_len;
+ int req_md_nid;
+ struct sec_config kcfg;
+ struct oap_hdr local_hdr;
+ void * pkp; /* Ephemeral keypair */
+ uint8_t * key; /* For client-encap KEM */
+};
+
+#define OAP_CLI_CTX_INIT(s) \
+ do { s->id.len = OAP_ID_SIZE; s->id.data = s->__id; } while (0)
+
+/* Client-side credential loading, mocked in tests */
+
+#ifdef OAP_TEST_MODE
+extern int load_cli_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+extern int load_cli_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+extern int load_server_kem_pk(const char * name,
+ struct sec_config * cfg,
+ buffer_t * buf);
+#else
+
+int load_cli_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt)
+{
+ assert(info != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ return load_credentials(info->name, &info->c, pkp, crt);
+}
+
+int load_cli_kex_config(const struct name_info * info,
+ struct sec_config * cfg)
+{
+ assert(info != NULL);
+ assert(cfg != NULL);
+
+ return load_kex_config(info->name, info->c.enc, cfg);
+}
+
+int load_server_kem_pk(const char * name,
+ struct sec_config * cfg,
+ buffer_t * pk)
+{
+ char path[PATH_MAX];
+ const char * ext;
+
+ assert(name != NULL);
+ assert(cfg != NULL);
+ assert(pk != NULL);
+
+ ext = IS_HYBRID_KEM(cfg->x.str) ? "raw" : "pem";
+
+ snprintf(path, sizeof(path),
+ OUROBOROS_CLI_CRT_DIR "/%s/kex.srv.pub.%s", name, ext);
+
+ if (IS_HYBRID_KEM(cfg->x.str)) {
+ if (crypt_load_pubkey_raw_file(path, pk) < 0) {
+ log_err("Failed to load %s pubkey from %s.", ext, path);
+ return -1;
+ }
+ } else {
+ if (crypt_load_pubkey_file_to_der(path, pk) < 0) {
+ log_err("Failed to load %s pubkey from %s.", ext, path);
+ return -1;
+ }
+ }
+
+ log_dbg("Loaded %s pubkey from %s (%zu bytes).", ext, path, pk->len);
+
+ return 0;
+}
+
+#endif /* OAP_TEST_MODE */
+
+static int do_client_kex_prepare_dhe(struct oap_cli_ctx * s)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ buffer_t * kex = &s->local_hdr.kex;
+ uint8_t * id = s->id.data;
+ ssize_t len;
+
+ /* Generate ephemeral keypair, send PK */
+ len = kex_pkp_create(kcfg, &s->pkp, kex->data);
+ if (len < 0) {
+ log_err_id(id, "Failed to generate DHE keypair.");
+ return -ECRYPT;
+ }
+
+ kex->len = (size_t) len;
+ log_dbg_id(id, "Generated ephemeral %s keys (%zd bytes).",
+ kcfg->x.str, len);
+
+ return 0;
+}
+
+static int do_client_kex_prepare_kem_encap(const char * server_name,
+ struct oap_cli_ctx * s)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ buffer_t * kex = &s->local_hdr.kex;
+ uint8_t * id = s->id.data;
+ buffer_t server_pk = BUF_INIT;
+ uint8_t key_buf[SYMMKEYSZ];
+ ssize_t len;
+
+ if (load_server_kem_pk(server_name, kcfg, &server_pk) < 0) {
+ log_err_id(id, "Failed to load server KEM pk.");
+ return -ECRYPT;
+ }
+
+ if (IS_HYBRID_KEM(kcfg->x.str))
+ len = kex_kem_encap_raw(server_pk, kex->data,
+ kcfg->k.nid, key_buf);
+ else
+ len = kex_kem_encap(server_pk, kex->data,
+ kcfg->k.nid, key_buf);
+
+ freebuf(server_pk);
+
+ if (len < 0) {
+ log_err_id(id, "Failed to encapsulate KEM.");
+ return -ECRYPT;
+ }
+
+ kex->len = (size_t) len;
+ log_dbg_id(id, "Client encaps: CT len=%zd.", len);
+
+ /* Store derived key */
+ s->key = crypt_secure_malloc(SYMMKEYSZ);
+ if (s->key == NULL) {
+ log_err_id(id, "Failed to allocate secure key.");
+ return -ENOMEM;
+ }
+ memcpy(s->key, key_buf, SYMMKEYSZ);
+ crypt_secure_clear(key_buf, SYMMKEYSZ);
+
+ return 0;
+}
+
+static int do_client_kex_prepare_kem_decap(struct oap_cli_ctx * s)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ buffer_t * kex = &s->local_hdr.kex;
+ uint8_t * id = s->id.data;
+ ssize_t len;
+
+ /* Server encaps: generate keypair, send PK */
+ len = kex_pkp_create(kcfg, &s->pkp, kex->data);
+ if (len < 0) {
+ log_err_id(id, "Failed to generate KEM keypair.");
+ return -ECRYPT;
+ }
+
+ kex->len = (size_t) len;
+ log_dbg_id(id, "Client PK for server encaps (%zd bytes).", len);
+
+ return 0;
+}
+
+static int do_client_kex_prepare(const char * server_name,
+ struct oap_cli_ctx * s)
+{
+ struct sec_config * kcfg = &s->kcfg;
+
+ if (!IS_KEX_ALGO_SET(kcfg))
+ return 0;
+
+ if (IS_KEM_ALGORITHM(kcfg->x.str)) {
+ if (kcfg->x.mode == KEM_MODE_CLIENT_ENCAP)
+ return do_client_kex_prepare_kem_encap(server_name, s);
+ else
+ return do_client_kex_prepare_kem_decap(s);
+ }
+
+ return do_client_kex_prepare_dhe(s);
+}
+
+int oap_cli_prepare(void ** ctx,
+ const struct name_info * info,
+ buffer_t * req_buf,
+ buffer_t data)
+{
+ struct oap_cli_ctx * s;
+ void * pkp = NULL;
+ void * crt = NULL;
+ ssize_t ret;
+
+ assert(ctx != NULL);
+ assert(info != NULL);
+ assert(req_buf != NULL);
+
+ clrbuf(*req_buf);
+ *ctx = NULL;
+
+ /* Allocate ctx to carry between prepare and complete */
+ s = malloc(sizeof(*s));
+ if (s == NULL) {
+ log_err("Failed to allocate OAP client ctx.");
+ return -ENOMEM;
+ }
+
+ memset(s, 0, sizeof(*s));
+ OAP_CLI_CTX_INIT(s);
+
+ /* Generate session ID */
+ if (random_buffer(s->__id, OAP_ID_SIZE) < 0) {
+ log_err("Failed to generate OAP session ID.");
+ goto fail_id;
+ }
+
+ log_dbg_id(s->id.data, "Preparing OAP request for %s.", info->name);
+
+ /* Load client credentials */
+ if (load_cli_credentials(info, &pkp, &crt) < 0) {
+ log_err_id(s->id.data, "Failed to load credentials for %s.",
+ info->name);
+ goto fail_id;
+ }
+
+ /* Load KEX config */
+ if (load_cli_kex_config(info, &s->kcfg) < 0) {
+ log_err_id(s->id.data, "Failed to load KEX config for %s.",
+ info->name);
+ goto fail_kex;
+ }
+
+ log_dbg_id(s->id.data, "KEX config: algo=%s, mode=%s, cipher=%s.",
+ s->kcfg.x.str != NULL ? s->kcfg.x.str : "none",
+ s->kcfg.x.mode == KEM_MODE_CLIENT_ENCAP ? "client-encap" :
+ s->kcfg.x.mode == KEM_MODE_SERVER_ENCAP ? "server-encap" :
+ "none",
+ s->kcfg.c.str != NULL ? s->kcfg.c.str : "none");
+
+ oap_hdr_init(&s->local_hdr, s->id, s->kex_buf, data, s->kcfg.c.nid);
+
+ if (do_client_kex_prepare(info->name, s) < 0) {
+ log_err_id(s->id.data, "Failed to prepare client KEX.");
+ goto fail_kex;
+ }
+
+ if (oap_hdr_encode(&s->local_hdr, pkp, crt, &s->kcfg,
+ (buffer_t) BUF_INIT, NID_undef)) {
+ log_err_id(s->id.data, "Failed to create OAP request header.");
+ goto fail_hdr;
+ }
+
+ debug_oap_hdr_snd(&s->local_hdr);
+
+ /* Compute and store hash of request for verification in complete */
+ s->req_md_nid = s->kcfg.d.nid != NID_undef ? s->kcfg.d.nid : NID_sha384;
+ ret = md_digest(s->req_md_nid, s->local_hdr.hdr, s->req_hash);
+ if (ret < 0) {
+ log_err_id(s->id.data, "Failed to hash request.");
+ goto fail_hash;
+ }
+ s->req_hash_len = (size_t) ret;
+
+ /* Transfer ownership of request buffer */
+ *req_buf = s->local_hdr.hdr;
+ clrbuf(s->local_hdr.hdr);
+
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+
+ *ctx = s;
+
+ log_dbg_id(s->id.data, "OAP request prepared for %s.", info->name);
+
+ return 0;
+
+ fail_hash:
+ fail_hdr:
+ crypt_secure_free(s->key, SYMMKEYSZ);
+ crypt_free_key(s->pkp);
+ fail_kex:
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+ fail_id:
+ free(s);
+ return -ECRYPT;
+}
+
+void oap_ctx_free(void * ctx)
+{
+ struct oap_cli_ctx * s = ctx;
+
+ if (s == NULL)
+ return;
+
+ oap_hdr_fini(&s->local_hdr);
+
+ if (s->pkp != NULL)
+ crypt_free_key(s->pkp);
+
+ if (s->key != NULL)
+ crypt_secure_free(s->key, SYMMKEYSZ);
+
+ memset(s, 0, sizeof(*s));
+ free(s);
+}
+
+static int do_client_kex_complete_kem(struct oap_cli_ctx * s,
+ const struct oap_hdr * peer_hdr,
+ struct crypt_sk * sk)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ uint8_t * id = s->id.data;
+ uint8_t key_buf[SYMMKEYSZ];
+
+ if (kcfg->x.mode == KEM_MODE_SERVER_ENCAP) {
+ buffer_t ct;
+
+ if (peer_hdr->kex.len == 0) {
+ log_err_id(id, "Server did not send KEM CT.");
+ return -ECRYPT;
+ }
+
+ ct.data = peer_hdr->kex.data;
+ ct.len = peer_hdr->kex.len;
+
+ if (kex_kem_decap(s->pkp, ct, kcfg->k.nid, key_buf) < 0) {
+ log_err_id(id, "Failed to decapsulate KEM.");
+ return -ECRYPT;
+ }
+
+ log_dbg_id(id, "Client decapsulated server CT.");
+
+ } else if (kcfg->x.mode == KEM_MODE_CLIENT_ENCAP) {
+ /* Key already derived during prepare */
+ memcpy(sk->key, s->key, SYMMKEYSZ);
+ sk->nid = kcfg->c.nid;
+ log_info_id(id, "Negotiated %s + %s.", kcfg->x.str,
+ kcfg->c.str);
+ return 0;
+ }
+
+ memcpy(sk->key, key_buf, SYMMKEYSZ);
+ sk->nid = kcfg->c.nid;
+ crypt_secure_clear(key_buf, SYMMKEYSZ);
+
+ log_info_id(id, "Negotiated %s + %s.", kcfg->x.str, kcfg->c.str);
+
+ return 0;
+}
+
+static int do_client_kex_complete_dhe(struct oap_cli_ctx * s,
+ const struct oap_hdr * peer_hdr,
+ struct crypt_sk * sk)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ uint8_t * id = s->id.data;
+ uint8_t key_buf[SYMMKEYSZ];
+
+ /* DHE: derive from server's public key */
+ if (peer_hdr->kex.len == 0) {
+ log_err_id(id, "Server did not send DHE public key.");
+ return -ECRYPT;
+ }
+
+ if (kex_dhe_derive(kcfg, s->pkp, peer_hdr->kex, key_buf) < 0) {
+ log_err_id(id, "Failed to derive DHE secret.");
+ return -ECRYPT;
+ }
+
+ log_dbg_id(id, "DHE: derived shared secret.");
+
+ memcpy(sk->key, key_buf, SYMMKEYSZ);
+ sk->nid = kcfg->c.nid;
+ crypt_secure_clear(key_buf, SYMMKEYSZ);
+
+ log_info_id(id, "Negotiated %s + %s.", kcfg->x.str, kcfg->c.str);
+
+ return 0;
+}
+
+
+static int do_client_kex_complete(struct oap_cli_ctx * s,
+ const struct oap_hdr * peer_hdr,
+ struct crypt_sk * sk)
+{
+ struct sec_config * kcfg = &s->kcfg;
+ uint8_t * id = s->id.data;
+
+ if (!IS_KEX_ALGO_SET(kcfg))
+ return 0;
+
+ /* Accept server's cipher choice */
+ if (peer_hdr->cipher_str == NULL) {
+ log_err_id(id, "Server did not provide cipher.");
+ return -ECRYPT;
+ }
+
+ SET_KEX_CIPHER(kcfg, peer_hdr->cipher_str);
+ if (crypt_validate_nid(kcfg->c.nid) < 0) {
+ log_err_id(id, "Server cipher '%s' not supported.",
+ peer_hdr->cipher_str);
+ return -ENOTSUP;
+ }
+
+ log_dbg_id(id, "Accepted server cipher %s.", peer_hdr->cipher_str);
+
+ /* Derive shared secret */
+ if (IS_KEM_ALGORITHM(kcfg->x.str))
+ return do_client_kex_complete_kem(s, peer_hdr, sk);
+
+ return do_client_kex_complete_dhe(s, peer_hdr, sk);
+}
+
+int oap_cli_complete(void * ctx,
+ const struct name_info * info,
+ buffer_t rsp_buf,
+ buffer_t * data,
+ struct crypt_sk * sk)
+{
+ struct oap_cli_ctx * s = ctx;
+ struct oap_hdr peer_hdr;
+ char peer[NAME_SIZE + 1];
+ uint8_t * id;
+
+ assert(ctx != NULL);
+ assert(info != NULL);
+ assert(data != NULL);
+ assert(sk != NULL);
+
+ sk->nid = NID_undef;
+
+ clrbuf(*data);
+
+ memset(&peer_hdr, 0, sizeof(peer_hdr));
+
+ id = s->id.data;
+
+ log_dbg_id(id, "Completing OAP for %s.", info->name);
+
+ /* Decode response header using client's md_nid for hash length */
+ if (oap_hdr_decode(&peer_hdr, rsp_buf, s->req_md_nid) < 0) {
+ log_err_id(id, "Failed to decode OAP response header.");
+ goto fail_oap;
+ }
+
+ debug_oap_hdr_rcv(&peer_hdr);
+
+ /* Verify response ID matches request */
+ if (memcmp(peer_hdr.id.data, id, OAP_ID_SIZE) != 0) {
+ log_err_id(id, "OAP response ID mismatch.");
+ goto fail_oap;
+ }
+
+ /* Authenticate server */
+ if (oap_auth_peer(peer, &s->local_hdr, &peer_hdr) < 0) {
+ log_err_id(id, "Failed to authenticate server.");
+ goto fail_oap;
+ }
+
+ /* Verify request hash in authenticated response */
+ if (peer_hdr.req_hash.len == 0) {
+ log_err_id(id, "Response missing req_hash.");
+ goto fail_oap;
+ }
+
+ if (memcmp(peer_hdr.req_hash.data, s->req_hash, s->req_hash_len) != 0) {
+ log_err_id(id, "Response req_hash mismatch.");
+ goto fail_oap;
+ }
+
+ /* Verify peer certificate name matches expected destination */
+ if (peer_hdr.crt.len > 0 && strcmp(peer, info->name) != 0) {
+ log_err_id(id, "Peer crt for '%s' does not match '%s'.",
+ peer, info->name);
+ goto fail_oap;
+ }
+
+ /* Complete key exchange */
+ if (do_client_kex_complete(s, &peer_hdr, sk) < 0) {
+ log_err_id(id, "Failed to complete key exchange.");
+ goto fail_oap;
+ }
+
+ /* Copy piggybacked data from server response */
+ if (oap_hdr_copy_data(&peer_hdr, data) < 0) {
+ log_err_id(id, "Failed to copy server data.");
+ goto fail_oap;
+ }
+
+ log_info_id(id, "OAP completed for %s.", info->name);
+
+ oap_ctx_free(s);
+
+ return 0;
+
+ fail_oap:
+ oap_ctx_free(s);
+ return -ECRYPT;
+}
diff --git a/src/irmd/oap/hdr.c b/src/irmd/oap/hdr.c
new file mode 100644
index 00000000..cdff7ab6
--- /dev/null
+++ b/src/irmd/oap/hdr.c
@@ -0,0 +1,456 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Header encoding, decoding, and debugging
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/endian.h>
+#include <ouroboros/hash.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/rib.h>
+#include <ouroboros/time.h>
+
+#include "config.h"
+
+#include "hdr.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+int oap_hdr_decode(struct oap_hdr * oap_hdr,
+ buffer_t hdr,
+ int req_md_nid)
+{
+ off_t offset;
+ uint16_t kex_len;
+ uint16_t ciph_nid;
+ size_t crt_len;
+ size_t data_len;
+ size_t hash_len;
+ size_t sig_len;
+
+ assert(oap_hdr != NULL);
+ memset(oap_hdr, 0, sizeof(*oap_hdr));
+
+ if (hdr.len < OAP_HDR_MIN_SIZE)
+ goto fail_decode;
+
+ /* Parse fixed header (36 bytes) */
+ oap_hdr->id.data = hdr.data;
+ oap_hdr->id.len = OAP_ID_SIZE;
+
+ offset = OAP_ID_SIZE;
+
+ oap_hdr->timestamp = ntoh64(*(uint64_t *)(hdr.data + offset));
+ offset += sizeof(uint64_t);
+
+ /* cipher NID */
+ ciph_nid = ntoh16(*(uint16_t *)(hdr.data + offset));
+ oap_hdr->nid = ciph_nid;
+ oap_hdr->cipher_str = crypt_nid_to_str(ciph_nid);
+ offset += sizeof(uint16_t);
+
+ /* kdf NID */
+ oap_hdr->kdf_nid = ntoh16(*(uint16_t *)(hdr.data + offset));
+ oap_hdr->kdf_str = md_nid_to_str(oap_hdr->kdf_nid);
+ offset += sizeof(uint16_t);
+
+ /* md NID (signature hash) */
+ oap_hdr->md_nid = ntoh16(*(uint16_t *)(hdr.data + offset));
+ oap_hdr->md_str = md_nid_to_str(oap_hdr->md_nid);
+ offset += sizeof(uint16_t);
+
+ /* Validate NIDs: NID_undef is valid at parse time, else must be known.
+ * Note: md_nid=NID_undef only valid for PQC; enforced at sign/verify.
+ */
+ if (ciph_nid != NID_undef && crypt_validate_nid(ciph_nid) < 0)
+ goto fail_decode;
+ if (oap_hdr->kdf_nid != NID_undef &&
+ md_validate_nid(oap_hdr->kdf_nid) < 0)
+ goto fail_decode;
+ if (oap_hdr->md_nid != NID_undef &&
+ md_validate_nid(oap_hdr->md_nid) < 0)
+ goto fail_decode;
+
+ /* crt_len */
+ crt_len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset));
+ offset += sizeof(uint16_t);
+
+ /* kex_len + flags */
+ kex_len = ntoh16(*(uint16_t *)(hdr.data + offset));
+ oap_hdr->kex.len = (size_t) (kex_len & OAP_KEX_LEN_MASK);
+ oap_hdr->kex_flags.fmt = (kex_len & OAP_KEX_FMT_BIT) ? 1 : 0;
+ oap_hdr->kex_flags.role = (kex_len & OAP_KEX_ROLE_BIT) ? 1 : 0;
+ offset += sizeof(uint16_t);
+
+ /* data_len */
+ data_len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset));
+ offset += sizeof(uint16_t);
+
+ /* Response includes req_hash when md_nid is set */
+ hash_len = (req_md_nid != NID_undef) ?
+ (size_t) md_len(req_md_nid) : 0;
+
+ /* Validate total length */
+ if (hdr.len < (size_t) offset + crt_len + oap_hdr->kex.len +
+ data_len + hash_len)
+ goto fail_decode;
+
+ /* Derive sig_len from remaining bytes */
+ sig_len = hdr.len - offset - crt_len - oap_hdr->kex.len -
+ data_len - hash_len;
+
+ /* Unsigned packets must not have trailing bytes */
+ if (crt_len == 0 && sig_len != 0)
+ goto fail_decode;
+
+ /* Parse variable fields */
+ oap_hdr->crt.data = hdr.data + offset;
+ oap_hdr->crt.len = crt_len;
+ offset += crt_len;
+
+ oap_hdr->kex.data = hdr.data + offset;
+ offset += oap_hdr->kex.len;
+
+ oap_hdr->data.data = hdr.data + offset;
+ oap_hdr->data.len = data_len;
+ offset += data_len;
+
+ oap_hdr->req_hash.data = hdr.data + offset;
+ oap_hdr->req_hash.len = hash_len;
+ offset += hash_len;
+
+ oap_hdr->sig.data = hdr.data + offset;
+ oap_hdr->sig.len = sig_len;
+
+ oap_hdr->hdr = hdr;
+
+ return 0;
+
+ fail_decode:
+ memset(oap_hdr, 0, sizeof(*oap_hdr));
+ return -1;
+}
+
+void oap_hdr_fini(struct oap_hdr * oap_hdr)
+{
+ assert(oap_hdr != NULL);
+
+ freebuf(oap_hdr->hdr);
+ memset(oap_hdr, 0, sizeof(*oap_hdr));
+}
+
+int oap_hdr_copy_data(const struct oap_hdr * hdr,
+ buffer_t * out)
+{
+ assert(hdr != NULL);
+ assert(out != NULL);
+
+ if (hdr->data.len == 0) {
+ clrbuf(*out);
+ return 0;
+ }
+
+ out->data = malloc(hdr->data.len);
+ if (out->data == NULL)
+ return -ENOMEM;
+
+ memcpy(out->data, hdr->data.data, hdr->data.len);
+ out->len = hdr->data.len;
+
+ return 0;
+}
+
+void oap_hdr_init(struct oap_hdr * hdr,
+ buffer_t id,
+ uint8_t * kex_buf,
+ buffer_t data,
+ uint16_t nid)
+{
+ assert(hdr != NULL);
+ assert(id.data != NULL && id.len == OAP_ID_SIZE);
+
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->id = id;
+ hdr->kex.data = kex_buf;
+ hdr->kex.len = 0;
+ hdr->data = data;
+ hdr->nid = nid;
+}
+
+int oap_hdr_encode(struct oap_hdr * hdr,
+ void * pkp,
+ void * crt,
+ struct sec_config * kcfg,
+ buffer_t req_hash,
+ int req_md_nid)
+{
+ struct timespec now;
+ uint64_t stamp;
+ buffer_t out;
+ buffer_t der = BUF_INIT;
+ buffer_t sig = BUF_INIT;
+ buffer_t sign;
+ uint16_t len;
+ uint16_t ciph_nid;
+ uint16_t kdf_nid;
+ uint16_t md_nid;
+ uint16_t kex_len;
+ off_t offset;
+
+ assert(hdr != NULL);
+ assert(hdr->id.data != NULL && hdr->id.len == OAP_ID_SIZE);
+ assert(kcfg != NULL);
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ stamp = hton64(TS_TO_UINT64(now));
+
+ if (crt != NULL && crypt_crt_der(crt, &der) < 0)
+ goto fail_der;
+
+ ciph_nid = hton16(hdr->nid);
+ kdf_nid = hton16(kcfg->k.nid);
+ md_nid = hton16(kcfg->d.nid);
+
+ /* Build kex_len with flags */
+ kex_len = (uint16_t) hdr->kex.len;
+ if (hdr->kex.len > 0 && IS_KEM_ALGORITHM(kcfg->x.str)) {
+ if (IS_HYBRID_KEM(kcfg->x.str))
+ kex_len |= OAP_KEX_FMT_BIT;
+ if (kcfg->x.mode == KEM_MODE_CLIENT_ENCAP)
+ kex_len |= OAP_KEX_ROLE_BIT;
+ }
+ kex_len = hton16(kex_len);
+
+ /* Fixed header (36 bytes) + variable fields + req_hash (if auth) */
+ out.len = OAP_HDR_MIN_SIZE + der.len + hdr->kex.len + hdr->data.len +
+ req_hash.len;
+
+ out.data = malloc(out.len);
+ if (out.data == NULL)
+ goto fail_out;
+
+ offset = 0;
+
+ /* id (16 bytes) */
+ memcpy(out.data + offset, hdr->id.data, hdr->id.len);
+ offset += hdr->id.len;
+
+ /* timestamp (8 bytes) */
+ memcpy(out.data + offset, &stamp, sizeof(stamp));
+ offset += sizeof(stamp);
+
+ /* cipher_nid (2 bytes) */
+ memcpy(out.data + offset, &ciph_nid, sizeof(ciph_nid));
+ offset += sizeof(ciph_nid);
+
+ /* kdf_nid (2 bytes) */
+ memcpy(out.data + offset, &kdf_nid, sizeof(kdf_nid));
+ offset += sizeof(kdf_nid);
+
+ /* md_nid (2 bytes) */
+ memcpy(out.data + offset, &md_nid, sizeof(md_nid));
+ offset += sizeof(md_nid);
+
+ /* crt_len (2 bytes) */
+ len = hton16((uint16_t) der.len);
+ memcpy(out.data + offset, &len, sizeof(len));
+ offset += sizeof(len);
+
+ /* kex_len + flags (2 bytes) */
+ memcpy(out.data + offset, &kex_len, sizeof(kex_len));
+ offset += sizeof(kex_len);
+
+ /* data_len (2 bytes) */
+ len = hton16((uint16_t) hdr->data.len);
+ memcpy(out.data + offset, &len, sizeof(len));
+ offset += sizeof(len);
+
+ /* Fixed header complete (36 bytes) */
+ assert((size_t) offset == OAP_HDR_MIN_SIZE);
+
+ /* certificate (variable) */
+ if (der.len != 0)
+ memcpy(out.data + offset, der.data, der.len);
+ offset += der.len;
+
+ /* kex data (variable) */
+ if (hdr->kex.len != 0)
+ memcpy(out.data + offset, hdr->kex.data, hdr->kex.len);
+ offset += hdr->kex.len;
+
+ /* data (variable) */
+ if (hdr->data.len != 0)
+ memcpy(out.data + offset, hdr->data.data, hdr->data.len);
+ offset += hdr->data.len;
+
+ /* req_hash (variable, only for authenticated responses) */
+ if (req_hash.len != 0)
+ memcpy(out.data + offset, req_hash.data, req_hash.len);
+ offset += req_hash.len;
+
+ assert((size_t) offset == out.len);
+
+ /* Sign the entire header (fixed + variable, excluding signature) */
+ sign.data = out.data;
+ sign.len = out.len;
+
+ if (pkp != NULL && auth_sign(pkp, kcfg->d.nid, sign, &sig) < 0)
+ goto fail_sig;
+
+ hdr->hdr = out;
+
+ /* Append signature */
+ if (sig.len > 0) {
+ hdr->hdr.len += sig.len;
+ hdr->hdr.data = realloc(out.data, hdr->hdr.len);
+ if (hdr->hdr.data == NULL)
+ goto fail_realloc;
+
+ memcpy(hdr->hdr.data + offset, sig.data, sig.len);
+ clrbuf(out);
+ }
+
+ if (oap_hdr_decode(hdr, hdr->hdr, req_md_nid) < 0)
+ goto fail_decode;
+
+ freebuf(der);
+ freebuf(sig);
+
+ return 0;
+
+ fail_decode:
+ oap_hdr_fini(hdr);
+ fail_realloc:
+ freebuf(sig);
+ fail_sig:
+ freebuf(out);
+ fail_out:
+ freebuf(der);
+ fail_der:
+ return -1;
+}
+
+#ifdef DEBUG_PROTO_OAP
+static void debug_oap_hdr(const struct oap_hdr * hdr)
+{
+ assert(hdr);
+
+ if (hdr->crt.len > 0)
+ log_proto(" crt: [%zu bytes]", hdr->crt.len);
+ else
+ log_proto(" crt: <none>");
+
+ if (hdr->kex.len > 0)
+ log_proto(" Key Exchange Data: [%zu bytes] [%s]",
+ hdr->kex.len, hdr->kex_flags.role ?
+ "Client encaps" : "Server encaps");
+ else
+ log_proto(" Ephemeral Public Key: <none>");
+
+ if (hdr->cipher_str != NULL)
+ log_proto(" Cipher: %s", hdr->cipher_str);
+ else
+ log_proto(" Cipher: <none>");
+
+ if (hdr->kdf_str != NULL)
+ log_proto(" KDF: HKDF-%s", hdr->kdf_str);
+ else
+ log_proto(" KDF: <none>");
+
+ if (hdr->md_str != NULL)
+ log_proto(" Digest: %s", hdr->md_str);
+ else
+ log_proto(" Digest: <none>");
+
+ if (hdr->data.len > 0)
+ log_proto(" Data: [%zu bytes]", hdr->data.len);
+ else
+ log_proto(" Data: <none>");
+
+ if (hdr->req_hash.len > 0)
+ log_proto(" Req Hash: [%zu bytes]", hdr->req_hash.len);
+ else
+ log_proto(" Req Hash: <none>");
+
+ if (hdr->sig.len > 0)
+ log_proto(" Signature: [%zu bytes]", hdr->sig.len);
+ else
+ log_proto(" Signature: <none>");
+}
+#endif
+
+void debug_oap_hdr_rcv(const struct oap_hdr * hdr)
+{
+#ifdef DEBUG_PROTO_OAP
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ time_t stamp;
+
+ assert(hdr);
+
+ stamp = (time_t) hdr->timestamp / BILLION;
+
+ tm = gmtime(&stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+
+ log_proto("OAP_HDR [" HASH_FMT64 " @ %s ] <--",
+ HASH_VAL64(hdr->id.data), tmstr);
+
+ debug_oap_hdr(hdr);
+#else
+ (void) hdr;
+#endif
+}
+
+void debug_oap_hdr_snd(const struct oap_hdr * hdr)
+{
+#ifdef DEBUG_PROTO_OAP
+ struct tm * tm;
+ char tmstr[RIB_TM_STRLEN];
+ time_t stamp;
+
+ assert(hdr);
+
+ stamp = (time_t) hdr->timestamp / BILLION;
+
+ tm = gmtime(&stamp);
+ strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm);
+
+ log_proto("OAP_HDR [" HASH_FMT64 " @ %s ] -->",
+ HASH_VAL64(hdr->id.data), tmstr);
+
+ debug_oap_hdr(hdr);
+#else
+ (void) hdr;
+#endif
+}
diff --git a/src/irmd/oap/hdr.h b/src/irmd/oap/hdr.h
new file mode 100644
index 00000000..f603b169
--- /dev/null
+++ b/src/irmd/oap/hdr.h
@@ -0,0 +1,159 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Header definitions and functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_OAP_HDR_H
+#define OUROBOROS_IRMD_OAP_HDR_H
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/utils.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define OAP_ID_SIZE (16)
+#define OAP_HDR_MIN_SIZE (OAP_ID_SIZE + sizeof(uint64_t) + 6 * sizeof(uint16_t))
+
+#define OAP_KEX_FMT_BIT 0x8000 /* bit 15: 0=X.509 DER, 1=Raw */
+#define OAP_KEX_ROLE_BIT 0x4000 /* bit 14: 0=Server encaps, 1=Client encaps */
+#define OAP_KEX_LEN_MASK 0x3FFF /* bits 0-13: Length (0-16383 bytes) */
+
+#define OAP_KEX_ROLE(hdr) (hdr->kex_flags.role)
+#define OAP_KEX_FMT(hdr) (hdr->kex_flags.fmt)
+
+#define OAP_KEX_IS_X509_FMT(hdr) (((hdr)->kex_flags.fmt) == 0)
+#define OAP_KEX_IS_RAW_FMT(hdr) (((hdr)->kex_flags.fmt) == 1)
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+
+ * | | |
+ * + + |
+ * | | |
+ * + id (128 bits) + |
+ * | Unique flow allocation ID | |
+ * + + |
+ * | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | | |
+ * + timestamp (64 bits) + |
+ * | UTC nanoseconds since epoch | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | cipher_nid (16 bits) | kdf_nid (16 bits) | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | md_nid (16 bits) | crt_len (16 bits) | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * |F|R| kex_len (14 bits) | data_len (16 bits) | | Signed
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Region
+ * | | |
+ * + certificate (variable) + |
+ * | X.509 certificate, DER encoded | |
+ * + + |
+ * | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | | |
+ * + kex_data (variable) + |
+ * | public key (DER/raw) or ciphertext (KEM) | |
+ * + + |
+ * | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | | |
+ * + data (variable) + |
+ * | Piggybacked application data | |
+ * + + |
+ * | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | | |
+ * + req_hash (variable, response only) + |
+ * | H(request) using req md_nid / sha384 | |
+ * | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+
+ * | |
+ * + signature (variable) +
+ * | DSA signature over signed region |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * cipher_nid: NID value for symmetric cipher (0 = none)
+ * kdf_nid: NID value for KDF function (0 = none)
+ * md_nid: NID value for signature hash (0 = PQC/no signature)
+ *
+ * kex_len field bit layout:
+ * F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid
+ * R (bit 14): Role - 0 = Server encaps, 1 = Client encaps
+ * (R is ignored for non-KEM algorithms)
+ * Bits 0-13: Length (0-16383 bytes)
+ *
+ * Request: sig_len = total - 36 - crt_len - kex_len - data_len
+ * Response: sig_len = total - 36 - crt_len - kex_len - data_len - hash_len
+ * where hash_len = md_len(req_md_nid / sha384)
+ */
+
+/* Parsed OAP header - buffers pointing to a single memory region */
+struct oap_hdr {
+ const char * cipher_str;
+ const char * kdf_str;
+ const char * md_str;
+ uint64_t timestamp;
+ uint16_t nid;
+ uint16_t kdf_nid;
+ uint16_t md_nid;
+ struct {
+ bool fmt; /* Format */
+ bool role; /* Role */
+ } kex_flags;
+ buffer_t id;
+ buffer_t crt;
+ buffer_t kex;
+ buffer_t data;
+ buffer_t req_hash; /* H(request) - response only */
+ buffer_t sig;
+ buffer_t hdr;
+};
+
+
+void oap_hdr_init(struct oap_hdr * hdr,
+ buffer_t id,
+ uint8_t * kex_buf,
+ buffer_t data,
+ uint16_t nid);
+
+void oap_hdr_fini(struct oap_hdr * oap_hdr);
+
+int oap_hdr_encode(struct oap_hdr * hdr,
+ void * pkp,
+ void * crt,
+ struct sec_config * kcfg,
+ buffer_t req_hash,
+ int req_md_nid);
+
+int oap_hdr_decode(struct oap_hdr * hdr,
+ buffer_t buf,
+ int req_md_nid);
+
+void debug_oap_hdr_rcv(const struct oap_hdr * hdr);
+
+void debug_oap_hdr_snd(const struct oap_hdr * hdr);
+
+int oap_hdr_copy_data(const struct oap_hdr * hdr,
+ buffer_t * out);
+
+#endif /* OUROBOROS_IRMD_OAP_HDR_H */
diff --git a/src/irmd/oap/internal.h b/src/irmd/oap/internal.h
new file mode 100644
index 00000000..8363e3a2
--- /dev/null
+++ b/src/irmd/oap/internal.h
@@ -0,0 +1,133 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP internal definitions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_OAP_INTERNAL_H
+#define OUROBOROS_IRMD_OAP_INTERNAL_H
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/list.h>
+#include <ouroboros/name.h>
+#include <ouroboros/pthread.h>
+#include <ouroboros/utils.h>
+
+#include "hdr.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * Authentication functions (auth.c)
+ */
+int oap_check_hdr(const struct oap_hdr * hdr);
+
+int oap_auth_peer(char * name,
+ const struct oap_hdr * local_hdr,
+ const struct oap_hdr * peer_hdr);
+
+/*
+ * Key exchange functions (kex.c)
+ */
+int oap_negotiate_cipher(const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg);
+
+/*
+ * Credential loading (oap.c) - shared between client and server
+ */
+#ifndef OAP_TEST_MODE
+int load_credentials(const char * name,
+ const struct name_sec_paths * paths,
+ void ** pkp,
+ void ** crt);
+
+int load_kex_config(const char * name,
+ const char * path,
+ struct sec_config * cfg);
+#endif
+
+/*
+ * Server functions (srv.c)
+ */
+#ifndef OAP_TEST_MODE
+int load_srv_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+
+int load_srv_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+
+int load_server_kem_keypair(const char * name,
+ struct sec_config * cfg,
+ void ** pkp);
+#else
+extern int load_srv_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+extern int load_srv_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+extern int load_server_kem_keypair(const char * name,
+ struct sec_config * cfg,
+ void ** pkp);
+#endif
+
+int do_server_kex(const struct name_info * info,
+ struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ buffer_t * kex,
+ struct crypt_sk * sk);
+
+/*
+ * Client functions (cli.c)
+ */
+#ifndef OAP_TEST_MODE
+int load_cli_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+
+int load_cli_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+
+int load_server_kem_pk(const char * name,
+ struct sec_config * cfg,
+ buffer_t * pk);
+#else
+extern int load_cli_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+extern int load_cli_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+extern int load_server_kem_pk(const char * name,
+ struct sec_config * cfg,
+ buffer_t * pk);
+#endif
+
+int oap_client_kex_prepare(struct sec_config * kcfg,
+ buffer_t server_pk,
+ buffer_t * kex,
+ uint8_t * key,
+ void ** ephemeral_pkp);
+
+int oap_client_kex_complete(const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ void * pkp,
+ uint8_t * key);
+
+#endif /* OUROBOROS_IRMD_OAP_INTERNAL_H */
diff --git a/src/irmd/oap/io.c b/src/irmd/oap/io.c
new file mode 100644
index 00000000..e4189d4d
--- /dev/null
+++ b/src/irmd/oap/io.c
@@ -0,0 +1,132 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - File I/O for credentials and configuration
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+
+#include "config.h"
+
+#include "io.h"
+
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/*
+ * Shared credential and configuration loading helpers
+ */
+
+#ifndef OAP_TEST_MODE
+
+static bool file_exists(const char * path)
+{
+ struct stat s;
+
+ if (stat(path, &s) < 0 && errno == ENOENT) {
+ log_dbg("File %s does not exist.", path);
+ return false;
+ }
+
+ return true;
+}
+
+int load_credentials(const char * name,
+ const struct name_sec_paths * paths,
+ void ** pkp,
+ void ** crt)
+{
+ assert(paths != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ *pkp = NULL;
+ *crt = NULL;
+
+ if (!file_exists(paths->crt) || !file_exists(paths->key)) {
+ log_info("No authentication certificates for %s.", name);
+ return 0;
+ }
+
+ if (crypt_load_crt_file(paths->crt, crt) < 0) {
+ log_err("Failed to load %s for %s.", paths->crt, name);
+ goto fail_crt;
+ }
+
+ if (crypt_load_privkey_file(paths->key, pkp) < 0) {
+ log_err("Failed to load %s for %s.", paths->key, name);
+ goto fail_key;
+ }
+
+ log_info("Loaded authentication certificates for %s.", name);
+
+ return 0;
+
+ fail_key:
+ crypt_free_crt(*crt);
+ *crt = NULL;
+ fail_crt:
+ return -EAUTH;
+}
+
+int load_kex_config(const char * name,
+ const char * path,
+ struct sec_config * cfg)
+{
+ assert(name != NULL);
+ assert(cfg != NULL);
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ /* Load encryption config */
+ if (!file_exists(path))
+ log_dbg("No encryption %s for %s.", path, name);
+
+ if (load_sec_config_file(cfg, path) < 0) {
+ log_warn("Failed to load %s for %s.", path, name);
+ return -1;
+ }
+
+ if (!IS_KEX_ALGO_SET(cfg)) {
+ log_info("Key exchange not configured for %s.", name);
+ return 0;
+ }
+
+ if (cfg->c.nid == NID_undef || crypt_nid_to_str(cfg->c.nid) == NULL) {
+ log_err("Invalid cipher NID %d for %s.", cfg->c.nid, name);
+ return -ECRYPT;
+ }
+
+ log_info("Encryption enabled for %s.", name);
+
+ return 0;
+}
+
+#endif /* OAP_TEST_MODE */
diff --git a/src/irmd/oap/io.h b/src/irmd/oap/io.h
new file mode 100644
index 00000000..a31ddf85
--- /dev/null
+++ b/src/irmd/oap/io.h
@@ -0,0 +1,40 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Credential and configuration file I/O
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_OAP_IO_H
+#define OUROBOROS_IRMD_OAP_IO_H
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/name.h>
+
+#ifndef OAP_TEST_MODE
+int load_credentials(const char * name,
+ const struct name_sec_paths * paths,
+ void ** pkp,
+ void ** crt);
+
+int load_kex_config(const char * name,
+ const char * path,
+ struct sec_config * cfg);
+#endif
+
+#endif /* OUROBOROS_IRMD_OAP_IO_H */
diff --git a/src/irmd/oap/srv.c b/src/irmd/oap/srv.c
new file mode 100644
index 00000000..c5a4453f
--- /dev/null
+++ b/src/irmd/oap/srv.c
@@ -0,0 +1,462 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OAP - Server-side processing
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #define _DEFAULT_SOURCE
+#else
+ #define _POSIX_C_SOURCE 200809L
+#endif
+
+#define OUROBOROS_PREFIX "irmd/oap"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+
+#include "config.h"
+
+#include "auth.h"
+#include "hdr.h"
+#include "io.h"
+#include "oap.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef OAP_TEST_MODE
+extern int load_srv_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt);
+extern int load_srv_kex_config(const struct name_info * info,
+ struct sec_config * cfg);
+extern int load_server_kem_keypair(const char * name,
+ bool raw_fmt,
+ void ** pkp);
+#else
+
+int load_srv_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt)
+{
+ assert(info != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ return load_credentials(info->name, &info->s, pkp, crt);
+}
+
+int load_srv_kex_config(const struct name_info * info,
+ struct sec_config * cfg)
+{
+ assert(info != NULL);
+ assert(cfg != NULL);
+
+ return load_kex_config(info->name, info->s.enc, cfg);
+}
+
+int load_server_kem_keypair(const char * name,
+ bool raw_fmt,
+ void ** pkp)
+{
+ char path[PATH_MAX];
+ const char * ext;
+
+ assert(name != NULL);
+ assert(pkp != NULL);
+
+ ext = raw_fmt ? "raw" : "pem";
+
+ snprintf(path, sizeof(path),
+ OUROBOROS_SRV_CRT_DIR "/%s/kex.key.%s", name, ext);
+
+ if (raw_fmt) {
+ if (crypt_load_privkey_raw_file(path, pkp) < 0) {
+ log_err("Failed to load %s keypair from %s.",
+ ext, path);
+ return -ECRYPT;
+ }
+ } else {
+ if (crypt_load_privkey_file(path, pkp) < 0) {
+ log_err("Failed to load %s keypair from %s.",
+ ext, path);
+ return -ECRYPT;
+ }
+ }
+
+ log_dbg("Loaded server KEM keypair from %s.", path);
+ return 0;
+}
+
+#endif /* OAP_TEST_MODE */
+
+static int get_algo_from_peer_key(const struct oap_hdr * peer_hdr,
+ char * algo_buf)
+{
+ uint8_t * id = peer_hdr->id.data;
+ int ret;
+
+ if (OAP_KEX_IS_RAW_FMT(peer_hdr)) {
+ ret = kex_get_algo_from_pk_raw(peer_hdr->kex, algo_buf);
+ if (ret < 0) {
+ log_err_id(id, "Failed to get algo from raw key.");
+ return -ECRYPT;
+ }
+ } else {
+ ret = kex_get_algo_from_pk_der(peer_hdr->kex, algo_buf);
+ if (ret < 0) {
+ log_err_id(id, "Failed to get algo from DER key.");
+ return -ECRYPT;
+ }
+ }
+
+ return 0;
+}
+
+static int negotiate_kex(const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg)
+{
+ uint8_t * id = peer_hdr->id.data;
+
+ if (kcfg->c.nid == NID_undef) {
+ if (peer_hdr->cipher_str != NULL) {
+ SET_KEX_CIPHER(kcfg, peer_hdr->cipher_str);
+ if (kcfg->c.nid == NID_undef) {
+ log_err_id(id, "Unsupported cipher '%s'.",
+ peer_hdr->cipher_str);
+ return -ENOTSUP;
+ }
+ log_dbg_id(id, "Peer requested cipher %s.",
+ peer_hdr->cipher_str);
+ } else {
+ log_err_id(id, "Encryption requested, no cipher.");
+ return -ECRYPT;
+ }
+ } else {
+ log_dbg_id(id, "Using local cipher %s.", kcfg->c.str);
+ }
+
+ /* Negotiate KDF - server overrides client if configured */
+ if (kcfg->k.nid != NID_undef) {
+ log_dbg_id(id, "Using local KDF %s.",
+ md_nid_to_str(kcfg->k.nid));
+ } else if (peer_hdr->kdf_nid != NID_undef) {
+ if (md_validate_nid(peer_hdr->kdf_nid) == 0) {
+ kcfg->k.nid = peer_hdr->kdf_nid;
+ log_dbg_id(id, "Using peer KDF %s.",
+ md_nid_to_str(peer_hdr->kdf_nid));
+ } else {
+ log_err_id(id, "Unsupported KDF NID %d.",
+ peer_hdr->kdf_nid);
+ return -ENOTSUP;
+ }
+ }
+
+ if (IS_KEX_ALGO_SET(kcfg))
+ log_info_id(id, "Negotiated %s + %s.",
+ kcfg->x.str, kcfg->c.str);
+ else
+ log_info_id(id, "No key exchange.");
+
+ return 0;
+}
+
+static int do_server_kem_decap(const struct name_info * info,
+ const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ struct crypt_sk * sk)
+{
+ buffer_t ct;
+ void * server_pkp = NULL;
+ int ret;
+ uint8_t * id = peer_hdr->id.data;
+
+ ret = load_server_kem_keypair(info->name,
+ peer_hdr->kex_flags.fmt,
+ &server_pkp);
+ if (ret < 0)
+ return ret;
+
+ ct.data = peer_hdr->kex.data;
+ ct.len = peer_hdr->kex.len;
+
+ ret = kex_kem_decap(server_pkp, ct, kcfg->k.nid, sk->key);
+
+ crypt_free_key(server_pkp);
+
+ if (ret < 0) {
+ log_err_id(id, "Failed to decapsulate KEM.");
+ return -ECRYPT;
+ }
+
+ log_dbg_id(id, "Client encaps: decapsulated CT.");
+
+ return 0;
+}
+
+static int do_server_kem_encap(const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ buffer_t * kex,
+ struct crypt_sk * sk)
+{
+ buffer_t client_pk;
+ ssize_t ct_len;
+ uint8_t * id = peer_hdr->id.data;
+
+ client_pk.data = peer_hdr->kex.data;
+ client_pk.len = peer_hdr->kex.len;
+
+ if (IS_HYBRID_KEM(kcfg->x.str))
+ ct_len = kex_kem_encap_raw(client_pk, kex->data,
+ kcfg->k.nid, sk->key);
+ else
+ ct_len = kex_kem_encap(client_pk, kex->data,
+ kcfg->k.nid, sk->key);
+
+ if (ct_len < 0) {
+ log_err_id(id, "Failed to encapsulate KEM.");
+ return -ECRYPT;
+ }
+
+ kex->len = (size_t) ct_len;
+
+ log_dbg_id(id, "Server encaps: generated CT, len=%zd.", ct_len);
+
+ return 0;
+}
+
+static int do_server_kex_kem(const struct name_info * info,
+ struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ buffer_t * kex,
+ struct crypt_sk * sk)
+{
+ int ret;
+
+ kcfg->x.mode = peer_hdr->kex_flags.role;
+
+ if (kcfg->x.mode == KEM_MODE_CLIENT_ENCAP) {
+ ret = do_server_kem_decap(info, peer_hdr, kcfg, sk);
+ kex->len = 0;
+ } else {
+ ret = do_server_kem_encap(peer_hdr, kcfg, kex, sk);
+ }
+
+ return ret;
+}
+
+static int do_server_kex_dhe(const struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ buffer_t * kex,
+ struct crypt_sk * sk)
+{
+ ssize_t key_len;
+ void * epkp;
+ int ret;
+ uint8_t * id = peer_hdr->id.data;
+
+ key_len = kex_pkp_create(kcfg, &epkp, kex->data);
+ if (key_len < 0) {
+ log_err_id(id, "Failed to generate key pair.");
+ return -ECRYPT;
+ }
+
+ kex->len = (size_t) key_len;
+
+ log_dbg_id(id, "Generated %s ephemeral keys.", kcfg->x.str);
+
+ ret = kex_dhe_derive(kcfg, epkp, peer_hdr->kex, sk->key);
+ if (ret < 0) {
+ log_err_id(id, "Failed to derive secret.");
+ kex_pkp_destroy(epkp);
+ return -ECRYPT;
+ }
+
+ kex_pkp_destroy(epkp);
+
+ return 0;
+}
+
+int do_server_kex(const struct name_info * info,
+ struct oap_hdr * peer_hdr,
+ struct sec_config * kcfg,
+ buffer_t * kex,
+ struct crypt_sk * sk)
+{
+ char algo_buf[KEX_ALGO_BUFSZ];
+ uint8_t * id;
+
+ id = peer_hdr->id.data;
+
+ /* No KEX data from client */
+ if (peer_hdr->kex.len == 0) {
+ if (IS_KEX_ALGO_SET(kcfg)) {
+ log_warn_id(id, "KEX requested without info.");
+ return -ECRYPT;
+ }
+ return 0;
+ }
+
+ if (negotiate_kex(peer_hdr, kcfg) < 0)
+ return -ECRYPT;
+
+ if (OAP_KEX_ROLE(peer_hdr) != KEM_MODE_CLIENT_ENCAP) {
+ /* Server encapsulation or DHE: extract algo from DER PK */
+ if (get_algo_from_peer_key(peer_hdr, algo_buf) < 0)
+ return -ECRYPT;
+
+ SET_KEX_ALGO(kcfg, algo_buf);
+ }
+
+ /* Dispatch based on algorithm type */
+ if (IS_KEM_ALGORITHM(kcfg->x.str))
+ return do_server_kex_kem(info, peer_hdr, kcfg, kex, sk);
+ else
+ return do_server_kex_dhe(peer_hdr, kcfg, kex, sk);
+}
+
+int oap_srv_process(const struct name_info * info,
+ buffer_t req_buf,
+ buffer_t * rsp_buf,
+ buffer_t * data,
+ struct crypt_sk * sk)
+{
+ struct oap_hdr peer_hdr;
+ struct oap_hdr local_hdr;
+ struct sec_config kcfg;
+ uint8_t kex_buf[MSGBUFSZ];
+ uint8_t hash_buf[MAX_HASH_SIZE];
+ buffer_t req_hash = BUF_INIT;
+ ssize_t hash_ret;
+ char cli_name[NAME_SIZE + 1]; /* TODO */
+ uint8_t * id;
+ void * pkp = NULL;
+ void * crt = NULL;
+ int req_md_nid;
+
+ assert(info != NULL);
+ assert(rsp_buf != NULL);
+ assert(data != NULL);
+ assert(sk != NULL);
+
+ sk->nid = NID_undef;
+
+ memset(&peer_hdr, 0, sizeof(peer_hdr));
+ memset(&local_hdr, 0, sizeof(local_hdr));
+ clrbuf(*rsp_buf);
+
+ log_dbg("Processing OAP request for %s.", info->name);
+
+ /* Load server credentials */
+ if (load_srv_credentials(info, &pkp, &crt) < 0) {
+ log_err("Failed to load security keys for %s.", info->name);
+ goto fail_cred;
+ }
+
+ /* Load KEX config */
+ if (load_srv_kex_config(info, &kcfg) < 0) {
+ log_err("Failed to load KEX config for %s.", info->name);
+ goto fail_kex;
+ }
+
+ sk->nid = kcfg.c.nid;
+
+ /* Decode incoming header (NID_undef = request, no hash) */
+ if (oap_hdr_decode(&peer_hdr, req_buf, NID_undef) < 0) {
+ log_err("Failed to decode OAP header.");
+ goto fail_auth;
+ }
+
+ debug_oap_hdr_rcv(&peer_hdr);
+
+ id = peer_hdr.id.data; /* Logging */
+
+ /* Check for replay */
+ if (oap_check_hdr(&peer_hdr) < 0) {
+ log_err_id(id, "OAP header failed replay check.");
+ goto fail_auth;
+ }
+
+ /* Authenticate client before processing KEX data */
+ oap_hdr_init(&local_hdr, peer_hdr.id, kex_buf, *data, NID_undef);
+
+ if (oap_auth_peer(cli_name, &local_hdr, &peer_hdr) < 0) {
+ log_err_id(id, "Failed to authenticate client.");
+ goto fail_auth;
+ }
+
+ if (do_server_kex(info, &peer_hdr, &kcfg, &local_hdr.kex, sk) < 0)
+ goto fail_kex;
+
+ /* Build response header with hash of client request */
+ local_hdr.nid = sk->nid;
+
+ /* Use client's md_nid, defaulting to SHA-384 for PQC */
+ req_md_nid = peer_hdr.md_nid != NID_undef ? peer_hdr.md_nid : NID_sha384;
+
+ /* Compute request hash using client's md_nid */
+ hash_ret = md_digest(req_md_nid, req_buf, hash_buf);
+ if (hash_ret < 0) {
+ log_err_id(id, "Failed to hash request.");
+ goto fail_auth;
+ }
+ req_hash.data = hash_buf;
+ req_hash.len = (size_t) hash_ret;
+
+ if (oap_hdr_encode(&local_hdr, pkp, crt, &kcfg,
+ req_hash, req_md_nid) < 0) {
+ log_err_id(id, "Failed to create OAP response header.");
+ goto fail_auth;
+ }
+
+ debug_oap_hdr_snd(&local_hdr);
+
+ if (oap_hdr_copy_data(&peer_hdr, data) < 0) {
+ log_err_id(id, "Failed to copy client data.");
+ goto fail_data;
+ }
+
+ /* Transfer ownership of response buffer */
+ *rsp_buf = local_hdr.hdr;
+
+ log_info_id(id, "OAP request processed for %s.", info->name);
+
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+
+ return 0;
+
+ fail_data:
+ oap_hdr_fini(&local_hdr);
+ fail_auth:
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+ fail_cred:
+ return -EAUTH;
+
+ fail_kex:
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+ return -ECRYPT;
+}
diff --git a/src/irmd/oap/tests/CMakeLists.txt b/src/irmd/oap/tests/CMakeLists.txt
new file mode 100644
index 00000000..2bf23821
--- /dev/null
+++ b/src/irmd/oap/tests/CMakeLists.txt
@@ -0,0 +1,64 @@
+get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+
+get_filename_component(OAP_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" DIRECTORY)
+get_filename_component(OAP_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" DIRECTORY)
+get_filename_component(IRMD_SOURCE_DIR "${OAP_SOURCE_DIR}" DIRECTORY)
+get_filename_component(IRMD_BINARY_DIR "${OAP_BINARY_DIR}" DIRECTORY)
+
+compute_test_prefix()
+
+create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
+ # Add new tests here
+ oap_test.c
+)
+
+create_test_sourcelist(${PARENT_DIR}_pqc_tests test_suite_pqc.c
+ # PQC-specific tests
+ oap_test_pqc.c
+)
+
+# OAP test needs io.c compiled with OAP_TEST_MODE
+set(OAP_TEST_SOURCES
+ ${OAP_SOURCE_DIR}/io.c
+ ${OAP_SOURCE_DIR}/hdr.c
+ ${OAP_SOURCE_DIR}/auth.c
+ ${OAP_SOURCE_DIR}/srv.c
+ ${OAP_SOURCE_DIR}/cli.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/common.c
+)
+
+# Regular test executable (ECDSA)
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests} ${OAP_TEST_SOURCES})
+set_source_files_properties(${OAP_TEST_SOURCES}
+ PROPERTIES COMPILE_DEFINITIONS "OAP_TEST_MODE"
+)
+
+disable_test_logging_for_target(${PARENT_DIR}_test)
+target_link_libraries(${PARENT_DIR}_test ouroboros-irm)
+target_include_directories(${PARENT_DIR}_test PRIVATE
+ ${IRMD_SOURCE_DIR}
+ ${IRMD_BINARY_DIR}
+)
+
+# PQC test executable (ML-DSA)
+add_executable(${PARENT_DIR}_pqc_test ${${PARENT_DIR}_pqc_tests} ${OAP_TEST_SOURCES})
+set_source_files_properties(${OAP_TEST_SOURCES}
+ TARGET_DIRECTORY ${PARENT_DIR}_pqc_test
+ PROPERTIES COMPILE_DEFINITIONS "OAP_TEST_MODE"
+)
+
+disable_test_logging_for_target(${PARENT_DIR}_pqc_test)
+target_link_libraries(${PARENT_DIR}_pqc_test ouroboros-irm)
+target_include_directories(${PARENT_DIR}_pqc_test PRIVATE
+ ${IRMD_SOURCE_DIR}
+ ${IRMD_BINARY_DIR}
+)
+
+add_dependencies(build_tests ${PARENT_DIR}_test ${PARENT_DIR}_pqc_test)
+
+# Regular tests
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
+
+# PQC tests
+ouroboros_register_tests(TARGET ${PARENT_DIR}_pqc_test TESTS ${${PARENT_DIR}_pqc_tests})
diff --git a/src/irmd/oap/tests/common.c b/src/irmd/oap/tests/common.c
new file mode 100644
index 00000000..0a1af100
--- /dev/null
+++ b/src/irmd/oap/tests/common.c
@@ -0,0 +1,457 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Common test helper functions for OAP tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "common.h"
+
+#include <ouroboros/crypt.h>
+
+#include "oap.h"
+
+#include <string.h>
+#include <stdio.h>
+
+int load_srv_kex_config(const struct name_info * info,
+ struct sec_config * cfg)
+{
+ (void) info;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ if (test_cfg.srv.kex == NID_undef)
+ return 0;
+
+ SET_KEX_ALGO_NID(cfg, test_cfg.srv.kex);
+ SET_KEX_CIPHER_NID(cfg, test_cfg.srv.cipher);
+ SET_KEX_KDF_NID(cfg, test_cfg.srv.kdf);
+ SET_KEX_DIGEST_NID(cfg, test_cfg.srv.md);
+ SET_KEX_KEM_MODE(cfg, test_cfg.srv.kem_mode);
+
+ return 0;
+}
+
+int load_cli_kex_config(const struct name_info * info,
+ struct sec_config * cfg)
+{
+ (void) info;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ if (test_cfg.cli.kex == NID_undef)
+ return 0;
+
+ SET_KEX_ALGO_NID(cfg, test_cfg.cli.kex);
+ SET_KEX_CIPHER_NID(cfg, test_cfg.cli.cipher);
+ SET_KEX_KDF_NID(cfg, test_cfg.cli.kdf);
+ SET_KEX_DIGEST_NID(cfg, test_cfg.cli.md);
+ SET_KEX_KEM_MODE(cfg, test_cfg.cli.kem_mode);
+
+ return 0;
+}
+
+int load_srv_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt)
+{
+ (void) info;
+
+ *pkp = NULL;
+ *crt = NULL;
+
+ if (!test_cfg.srv.auth)
+ return 0;
+
+ return mock_load_credentials(pkp, crt);
+}
+
+int load_cli_credentials(const struct name_info * info,
+ void ** pkp,
+ void ** crt)
+{
+ (void) info;
+
+ *pkp = NULL;
+ *crt = NULL;
+
+ if (!test_cfg.cli.auth)
+ return 0;
+
+ return mock_load_credentials(pkp, crt);
+}
+
+int oap_test_setup(struct oap_test_ctx * ctx,
+ const char * root_ca_str,
+ const char * im_ca_str)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ strcpy(ctx->srv.info.name, "test-1.unittest.o7s");
+ strcpy(ctx->cli.info.name, "test-1.unittest.o7s");
+
+ if (oap_auth_init() < 0) {
+ printf("Failed to init OAP.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_crt_str(root_ca_str, &ctx->root_ca) < 0) {
+ printf("Failed to load root CA cert.\n");
+ goto fail_root_ca;
+ }
+
+ if (crypt_load_crt_str(im_ca_str, &ctx->im_ca) < 0) {
+ printf("Failed to load intermediate CA cert.\n");
+ goto fail_im_ca;
+ }
+
+ if (oap_auth_add_ca_crt(ctx->root_ca) < 0) {
+ printf("Failed to add root CA cert to store.\n");
+ goto fail_add_ca;
+ }
+
+ if (oap_auth_add_ca_crt(ctx->im_ca) < 0) {
+ printf("Failed to add intermediate CA cert to store.\n");
+ goto fail_add_ca;
+ }
+
+ return 0;
+
+ fail_add_ca:
+ crypt_free_crt(ctx->im_ca);
+ fail_im_ca:
+ crypt_free_crt(ctx->root_ca);
+ fail_root_ca:
+ oap_auth_fini();
+ fail_init:
+ memset(ctx, 0, sizeof(*ctx));
+ return -1;
+}
+
+void oap_test_teardown(struct oap_test_ctx * ctx)
+{
+ struct crypt_sk res;
+ buffer_t dummy = BUF_INIT;
+
+ if (ctx->cli.state != NULL) {
+ res.key = ctx->cli.key;
+ oap_cli_complete(ctx->cli.state, &ctx->cli.info, dummy,
+ &ctx->data, &res);
+ ctx->cli.state = NULL;
+ }
+
+ freebuf(ctx->data);
+ freebuf(ctx->resp_hdr);
+ freebuf(ctx->req_hdr);
+
+ crypt_free_crt(ctx->im_ca);
+ crypt_free_crt(ctx->root_ca);
+
+ oap_auth_fini();
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+int oap_cli_prepare_ctx(struct oap_test_ctx * ctx)
+{
+ return oap_cli_prepare(&ctx->cli.state, &ctx->cli.info, &ctx->req_hdr,
+ ctx->data);
+}
+
+int oap_srv_process_ctx(struct oap_test_ctx * ctx)
+{
+ struct crypt_sk res = { .nid = NID_undef, .key = ctx->srv.key };
+ int ret;
+
+ ret = oap_srv_process(&ctx->srv.info, ctx->req_hdr,
+ &ctx->resp_hdr, &ctx->data, &res);
+ if (ret == 0)
+ ctx->srv.nid = res.nid;
+
+ return ret;
+}
+
+int oap_cli_complete_ctx(struct oap_test_ctx * ctx)
+{
+ struct crypt_sk res = { .nid = NID_undef, .key = ctx->cli.key };
+ int ret;
+
+ ret = oap_cli_complete(ctx->cli.state, &ctx->cli.info, ctx->resp_hdr,
+ &ctx->data, &res);
+ ctx->cli.state = NULL;
+
+ if (ret == 0)
+ ctx->cli.nid = res.nid;
+
+ return ret;
+}
+
+int roundtrip_auth_only(const char * root_ca,
+ const char * im_ca_str)
+{
+ struct oap_test_ctx ctx;
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca, im_ca_str) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_cli_complete_ctx(&ctx) < 0) {
+ printf("Client complete failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.cli.nid != NID_undef || ctx.srv.nid != NID_undef) {
+ printf("Cipher should not be set for auth-only.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int roundtrip_kex_only(void)
+{
+ struct name_info cli_info;
+ struct name_info srv_info;
+ struct crypt_sk res;
+ uint8_t cli_key[SYMMKEYSZ];
+ uint8_t srv_key[SYMMKEYSZ];
+ int cli_nid;
+ int srv_nid;
+ buffer_t req_hdr = BUF_INIT;
+ buffer_t resp_hdr = BUF_INIT;
+ buffer_t data = BUF_INIT;
+ void * cli_state = NULL;
+
+ TEST_START();
+
+ memset(&cli_info, 0, sizeof(cli_info));
+ memset(&srv_info, 0, sizeof(srv_info));
+
+ strcpy(cli_info.name, "test-1.unittest.o7s");
+ strcpy(srv_info.name, "test-1.unittest.o7s");
+
+ if (oap_auth_init() < 0) {
+ printf("Failed to init OAP.\n");
+ goto fail;
+ }
+
+ if (oap_cli_prepare(&cli_state, &cli_info, &req_hdr,
+ data) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ res.key = srv_key;
+
+ if (oap_srv_process(&srv_info, req_hdr, &resp_hdr, &data, &res) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ srv_nid = res.nid;
+
+ res.key = cli_key;
+
+ if (oap_cli_complete(cli_state, &cli_info, resp_hdr, &data, &res) < 0) {
+ printf("Client complete failed.\n");
+ cli_state = NULL;
+ goto fail_cleanup;
+ }
+
+ cli_nid = res.nid;
+ cli_state = NULL;
+
+ if (memcmp(cli_key, srv_key, SYMMKEYSZ) != 0) {
+ printf("Client and server keys do not match!\n");
+ goto fail_cleanup;
+ }
+
+ if (cli_nid == NID_undef || srv_nid == NID_undef) {
+ printf("Cipher should be set for kex-only.\n");
+ goto fail_cleanup;
+ }
+
+ freebuf(resp_hdr);
+ freebuf(req_hdr);
+ oap_auth_fini();
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ if (cli_state != NULL) {
+ res.key = cli_key;
+ oap_cli_complete(cli_state, &cli_info, resp_hdr, &data, &res);
+ }
+ freebuf(resp_hdr);
+ freebuf(req_hdr);
+ oap_auth_fini();
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int corrupted_request(const char * root_ca,
+ const char * im_ca_str)
+{
+ struct oap_test_ctx ctx;
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca, im_ca_str) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Corrupt the request */
+ if (ctx.req_hdr.len > 100) {
+ ctx.req_hdr.data[50] ^= 0xFF;
+ ctx.req_hdr.data[51] ^= 0xAA;
+ ctx.req_hdr.data[52] ^= 0x55;
+ }
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject corrupted request.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int corrupted_response(const char * root_ca,
+ const char * im_ca_str)
+{
+ struct oap_test_ctx ctx;
+ struct crypt_sk res;
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca, im_ca_str) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Corrupt the response */
+ if (ctx.resp_hdr.len > 100) {
+ ctx.resp_hdr.data[50] ^= 0xFF;
+ ctx.resp_hdr.data[51] ^= 0xAA;
+ ctx.resp_hdr.data[52] ^= 0x55;
+ }
+
+ res.key = ctx.cli.key;
+
+ if (oap_cli_complete(ctx.cli.state, &ctx.cli.info, ctx.resp_hdr,
+ &ctx.data, &res) == 0) {
+ printf("Client should reject corrupted response.\n");
+ ctx.cli.state = NULL;
+ goto fail_cleanup;
+ }
+
+ ctx.cli.state = NULL;
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int truncated_request(const char * root_ca,
+ const char * im_ca_str)
+{
+ struct oap_test_ctx ctx;
+ size_t orig_len;
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca, im_ca_str) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Truncate the request buffer */
+ orig_len = ctx.req_hdr.len;
+ ctx.req_hdr.len = orig_len / 2;
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject truncated request.\n");
+ ctx.req_hdr.len = orig_len;
+ goto fail_cleanup;
+ }
+
+ ctx.req_hdr.len = orig_len;
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
diff --git a/src/irmd/oap/tests/common.h b/src/irmd/oap/tests/common.h
new file mode 100644
index 00000000..d4b6733a
--- /dev/null
+++ b/src/irmd/oap/tests/common.h
@@ -0,0 +1,100 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Common test helper functions for OAP tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef IRMD_TESTS_COMMON_H
+#define IRMD_TESTS_COMMON_H
+
+#include <ouroboros/utils.h>
+#include <ouroboros/flow.h>
+#include <ouroboros/name.h>
+#include <test/test.h>
+
+#include <stdbool.h>
+
+/* Per-side security configuration for tests */
+struct test_sec_cfg {
+ int kex; /* KEX algorithm NID */
+ int cipher; /* Cipher NID for encryption */
+ int kdf; /* KDF NID for key derivation */
+ int md; /* Digest NID for signatures */
+ int kem_mode; /* KEM encapsulation mode (0 for ECDH) */
+ bool auth; /* Use authentication (certificates) */
+};
+
+/* Test configuration - set by each test before running roundtrip */
+extern struct test_cfg {
+ struct test_sec_cfg srv;
+ struct test_sec_cfg cli;
+} test_cfg;
+
+/* Each test file defines this with its own certificates */
+extern int mock_load_credentials(void ** pkp,
+ void ** crt);
+
+/* Per-side test context */
+struct oap_test_side {
+ struct name_info info;
+ struct flow_info flow;
+ uint8_t key[SYMMKEYSZ];
+ int nid;
+ void * state;
+};
+
+/* Test context - holds all common state for OAP tests */
+struct oap_test_ctx {
+ struct oap_test_side srv;
+ struct oap_test_side cli;
+
+ buffer_t req_hdr;
+ buffer_t resp_hdr;
+ buffer_t data;
+ void * root_ca;
+ void * im_ca;
+};
+
+int oap_test_setup(struct oap_test_ctx * ctx,
+ const char * root_ca_str,
+ const char * im_ca_str);
+
+void oap_test_teardown(struct oap_test_ctx * ctx);
+
+int oap_cli_prepare_ctx(struct oap_test_ctx * ctx);
+
+int oap_srv_process_ctx(struct oap_test_ctx * ctx);
+
+int oap_cli_complete_ctx(struct oap_test_ctx * ctx);
+
+int roundtrip_auth_only(const char * root_ca,
+ const char * im_ca_str);
+
+int roundtrip_kex_only(void);
+
+int corrupted_request(const char * root_ca,
+ const char * im_ca_str);
+
+int corrupted_response(const char * root_ca,
+ const char * im_ca_str);
+
+int truncated_request(const char * root_ca,
+ const char * im_ca_str);
+
+#endif /* IRMD_TESTS_COMMON_H */
diff --git a/src/irmd/oap/tests/oap_test.c b/src/irmd/oap/tests/oap_test.c
new file mode 100644
index 00000000..70f0a248
--- /dev/null
+++ b/src/irmd/oap/tests/oap_test.c
@@ -0,0 +1,951 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Unit tests of Ouroboros Allocation Protocol (OAP)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+ #ifndef _DEFAULT_SOURCE
+ #define _DEFAULT_SOURCE
+ #endif
+#else
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include "config.h"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/endian.h>
+#include <ouroboros/flow.h>
+#include <ouroboros/name.h>
+#include <ouroboros/random.h>
+#include <ouroboros/time.h>
+
+#include <test/test.h>
+#include <test/certs.h>
+
+#include "oap.h"
+#include "common.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
+#define AUTH true
+#define NO_AUTH false
+
+extern const uint16_t kex_supported_nids[];
+extern const uint16_t md_supported_nids[];
+
+struct test_cfg test_cfg;
+
+/* Mock load - called by load_*_credentials in common.c */
+int mock_load_credentials(void ** pkp,
+ void ** crt)
+{
+ *crt = NULL;
+
+ if (crypt_load_privkey_str(server_pkp_ec, pkp) < 0)
+ goto fail_privkey;
+
+ if (crypt_load_crt_str(signed_server_crt_ec, crt) < 0)
+ goto fail_crt;
+
+ return 0;
+
+ fail_crt:
+ crypt_free_key(*pkp);
+ fail_privkey:
+ *pkp = NULL;
+ return -1;
+}
+
+/* Stub KEM functions - ECDSA tests don't use KEM */
+int load_server_kem_keypair(__attribute__((unused)) const char * name,
+ __attribute__((unused)) bool raw_fmt,
+ __attribute__((unused)) void ** pkp)
+{
+ return -1;
+}
+
+int load_server_kem_pk(__attribute__((unused)) const char * name,
+ __attribute__((unused)) struct sec_config * cfg,
+ __attribute__((unused)) buffer_t * pk)
+{
+ return -1;
+}
+
+static void test_default_cfg(void)
+{
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server: X25519, AES-256-GCM, SHA-256, with auth */
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_aes_256_gcm;
+ test_cfg.srv.kdf = NID_sha256;
+ test_cfg.srv.md = NID_sha256;
+ test_cfg.srv.auth = AUTH;
+
+ /* Client: same KEX/cipher/kdf/md, no auth */
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+}
+
+static int test_oap_auth_init_fini(void)
+{
+ TEST_START();
+
+ if (oap_auth_init() < 0) {
+ printf("Failed to init OAP.\n");
+ goto fail;
+ }
+
+ oap_auth_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_roundtrip(int kex)
+{
+ struct oap_test_ctx ctx;
+ const char * kex_str = kex_nid_to_str(kex);
+
+ TEST_START("(%s)", kex_str);
+
+ test_default_cfg();
+ test_cfg.srv.kex = kex;
+ test_cfg.cli.kex = kex;
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_cli_complete_ctx(&ctx) < 0) {
+ printf("Client complete failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (memcmp(ctx.cli.key, ctx.srv.key, SYMMKEYSZ) != 0) {
+ printf("Client and server keys do not match!\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.cli.nid == NID_undef || ctx.srv.nid == NID_undef) {
+ printf("Cipher not set in flow.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS("(%s)", kex_str);
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL("(%s)", kex_str);
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_roundtrip_auth_only(void)
+{
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server: auth only, no encryption */
+ test_cfg.srv.md = NID_sha256;
+ test_cfg.srv.auth = AUTH;
+
+ /* Client: no auth, no encryption */
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+
+ return roundtrip_auth_only(root_ca_crt_ec, im_ca_crt_ec);
+}
+
+static int test_oap_roundtrip_kex_only(void)
+{
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server: KEX only, no auth */
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_aes_256_gcm;
+ test_cfg.srv.kdf = NID_sha256;
+ test_cfg.srv.md = NID_sha256;
+ test_cfg.srv.auth = NO_AUTH;
+
+ /* Client: KEX only, no auth */
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+
+ return roundtrip_kex_only();
+}
+
+static int test_oap_piggyback_data(void)
+{
+ struct oap_test_ctx ctx;
+ const char * cli_data_str = "client_data";
+ const char * srv_data_str = "server_data";
+ buffer_t srv_data = BUF_INIT;
+
+ TEST_START();
+
+ test_default_cfg();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ /* Client prepares request with piggybacked data */
+ ctx.data.len = strlen(cli_data_str);
+ ctx.data.data = malloc(ctx.data.len);
+ if (ctx.data.data == NULL)
+ goto fail_cleanup;
+ memcpy(ctx.data.data, cli_data_str, ctx.data.len);
+
+ if (oap_cli_prepare_ctx(&ctx) < 0)
+ goto fail_cleanup;
+
+ /* Set server's response data (ctx.data will take cli data) */
+ srv_data.len = strlen(srv_data_str);
+ srv_data.data = (uint8_t *) srv_data_str;
+
+ freebuf(ctx.data);
+ ctx.data.data = srv_data.data;
+ ctx.data.len = srv_data.len;
+ srv_data.data = NULL;
+ srv_data.len = 0;
+
+ if (oap_srv_process_ctx(&ctx) < 0)
+ goto fail_cleanup;
+
+ /* Verify server received client's piggybacked data */
+ if (ctx.data.len != strlen(cli_data_str) ||
+ memcmp(ctx.data.data, cli_data_str, ctx.data.len) != 0) {
+ printf("Server did not receive correct client data.\n");
+ goto fail_cleanup;
+ }
+
+ freebuf(ctx.data);
+
+ if (oap_cli_complete_ctx(&ctx) < 0)
+ goto fail_cleanup;
+
+ /* Verify client received server's piggybacked data */
+ if (ctx.data.len != strlen(srv_data_str) ||
+ memcmp(ctx.data.data, srv_data_str, ctx.data.len) != 0) {
+ printf("Client did not receive correct server data.\n");
+ goto fail_cleanup;
+ }
+
+ /* Free the copied data */
+ free(ctx.data.data);
+ ctx.data.data = NULL;
+ ctx.data.len = 0;
+
+ if (memcmp(ctx.cli.key, ctx.srv.key, SYMMKEYSZ) != 0) {
+ printf("Client and server keys do not match!\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ freebuf(srv_data);
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_corrupted_request(void)
+{
+ test_default_cfg();
+ test_cfg.cli.auth = AUTH;
+
+ return corrupted_request(root_ca_crt_ec, im_ca_crt_ec);
+}
+
+static int test_oap_corrupted_response(void)
+{
+ test_default_cfg();
+
+ return corrupted_response(root_ca_crt_ec, im_ca_crt_ec);
+}
+
+static int test_oap_truncated_request(void)
+{
+ test_default_cfg();
+
+ return truncated_request(root_ca_crt_ec, im_ca_crt_ec);
+}
+
+/* After ID (16), timestamp (8), cipher_nid (2), kdf_nid (2), md (2) */
+#define OAP_CERT_LEN_OFFSET 30
+static int test_oap_inflated_length_field(void)
+{
+ struct oap_test_ctx ctx;
+ uint16_t fake;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.req_hdr.len < OAP_CERT_LEN_OFFSET + 2) {
+ printf("Request too short for test.\n");
+ goto fail_cleanup;
+ }
+
+ /* Set cert length to claim more bytes than packet contains */
+ fake = hton16(60000);
+ memcpy(ctx.req_hdr.data + OAP_CERT_LEN_OFFSET, &fake, sizeof(fake));
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject inflated length field.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Attacker claims cert is smaller - causes misparse of subsequent fields */
+static int test_oap_deflated_length_field(void)
+{
+ struct oap_test_ctx ctx;
+ uint16_t fake;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.req_hdr.len < OAP_CERT_LEN_OFFSET + 2) {
+ printf("Request too short for test.\n");
+ goto fail_cleanup;
+ }
+
+ /* Set cert length to claim fewer bytes - will misparse rest */
+ fake = hton16(1);
+ memcpy(ctx.req_hdr.data + OAP_CERT_LEN_OFFSET, &fake, sizeof(fake));
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject deflated length field.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Header field offsets for byte manipulation */
+#define OAP_CIPHER_NID_OFFSET 24
+#define OAP_KEX_LEN_OFFSET 32
+
+/* Server rejects request when cipher NID set but no KEX data provided */
+static int test_oap_nid_without_kex(void)
+{
+ struct oap_test_ctx ctx;
+ uint16_t cipher_nid;
+ uint16_t zero = 0;
+
+ TEST_START();
+
+ /* Configure unsigned KEX-only mode */
+ memset(&test_cfg, 0, sizeof(test_cfg));
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_aes_256_gcm;
+ test_cfg.srv.kdf = NID_sha256;
+ test_cfg.srv.md = NID_sha256;
+ test_cfg.srv.auth = NO_AUTH;
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Tamper: keep cipher_nid but set kex_len=0, truncate KEX data */
+ cipher_nid = hton16(NID_aes_256_gcm);
+ memcpy(ctx.req_hdr.data + OAP_CIPHER_NID_OFFSET, &cipher_nid,
+ sizeof(cipher_nid));
+ memcpy(ctx.req_hdr.data + OAP_KEX_LEN_OFFSET, &zero, sizeof(zero));
+ ctx.req_hdr.len = 36; /* Fixed header only, no KEX data */
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject cipher NID without KEX data.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Server rejects OAP request with unsupported cipher NID */
+static int test_oap_unsupported_nid(void)
+{
+ struct oap_test_ctx ctx;
+ uint16_t bad_nid;
+
+ TEST_START();
+
+ /* Configure unsigned KEX-only mode */
+ memset(&test_cfg, 0, sizeof(test_cfg));
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_aes_256_gcm;
+ test_cfg.srv.kdf = NID_sha256;
+ test_cfg.srv.md = NID_sha256;
+ test_cfg.srv.auth = NO_AUTH;
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Tamper: set cipher_nid to unsupported value */
+ bad_nid = hton16(9999);
+ memcpy(ctx.req_hdr.data + OAP_CIPHER_NID_OFFSET, &bad_nid,
+ sizeof(bad_nid));
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject unsupported cipher NID.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_roundtrip_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ /* Skip KEM algorithms - they're tested in oap_test_pqc */
+ if (IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_oap_roundtrip(kex_supported_nids[i]);
+ }
+
+ return ret;
+}
+
+/* Cipher negotiation - client should accept server's chosen cipher */
+static int test_oap_cipher_mismatch(void)
+{
+ struct oap_test_ctx ctx;
+
+ TEST_START();
+
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server: ChaCha20-Poly1305, SHA3-256, SHA-384 */
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_chacha20_poly1305;
+ test_cfg.srv.kdf = NID_sha3_256;
+ test_cfg.srv.md = NID_sha384;
+ test_cfg.srv.auth = AUTH;
+
+ /* Client: AES-256-GCM, SHA-256, SHA-256 */
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = NID_sha256;
+ test_cfg.cli.auth = NO_AUTH;
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_cli_complete_ctx(&ctx) < 0) {
+ printf("Client complete failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Verify: both should have the server's chosen cipher and KDF */
+ if (ctx.srv.nid != test_cfg.srv.cipher) {
+ printf("Server cipher mismatch: expected %s, got %s\n",
+ crypt_nid_to_str(test_cfg.srv.cipher),
+ crypt_nid_to_str(ctx.srv.nid));
+ goto fail_cleanup;
+ }
+
+ if (ctx.cli.nid != test_cfg.srv.cipher) {
+ printf("Client cipher mismatch: expected %s, got %s\n",
+ crypt_nid_to_str(test_cfg.srv.cipher),
+ crypt_nid_to_str(ctx.cli.nid));
+ goto fail_cleanup;
+ }
+
+ /* Parse response header to check negotiated KDF */
+ if (ctx.resp_hdr.len > 26) {
+ uint16_t resp_kdf_nid;
+ /* KDF NID at offset 26: ID(16) + ts(8) + cipher(2) */
+ resp_kdf_nid = ntoh16(*(uint16_t *)(ctx.resp_hdr.data + 26));
+
+ if (resp_kdf_nid != test_cfg.srv.kdf) {
+ printf("Response KDF mismatch: expected %s, got %s\n",
+ md_nid_to_str(test_cfg.srv.kdf),
+ md_nid_to_str(resp_kdf_nid));
+ goto fail_cleanup;
+ }
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Test roundtrip with different signature digest algorithms */
+static int test_oap_roundtrip_md(int md)
+{
+ struct oap_test_ctx ctx;
+ const char * md_str = md_nid_to_str(md);
+
+ TEST_START("(%s)", md_str ? md_str : "default");
+
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server: auth + KEX with specified md */
+ test_cfg.srv.kex = NID_X25519;
+ test_cfg.srv.cipher = NID_aes_256_gcm;
+ test_cfg.srv.kdf = NID_sha256;
+ test_cfg.srv.md = md;
+ test_cfg.srv.auth = AUTH;
+
+ /* Client: no auth */
+ test_cfg.cli.kex = NID_X25519;
+ test_cfg.cli.cipher = NID_aes_256_gcm;
+ test_cfg.cli.kdf = NID_sha256;
+ test_cfg.cli.md = md;
+ test_cfg.cli.auth = NO_AUTH;
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_cli_complete_ctx(&ctx) < 0) {
+ printf("Client complete failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (memcmp(ctx.cli.key, ctx.srv.key, SYMMKEYSZ) != 0) {
+ printf("Client and server keys do not match!\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS("(%s)", md_str ? md_str : "default");
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL("(%s)", md_str ? md_str : "default");
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_roundtrip_md_all(void)
+{
+ int ret = 0;
+ int i;
+
+ /* Test with default (0) */
+ ret |= test_oap_roundtrip_md(0);
+
+ /* Test with all supported digest NIDs */
+ for (i = 0; md_supported_nids[i] != NID_undef; i++)
+ ret |= test_oap_roundtrip_md(md_supported_nids[i]);
+
+ return ret;
+}
+
+/* Timestamp is at offset 16 (after the 16-byte ID) */
+#define OAP_TIMESTAMP_OFFSET 16
+/* Test that packets with outdated timestamps are rejected */
+static int test_oap_outdated_packet(void)
+{
+ struct oap_test_ctx ctx;
+ struct timespec old_ts;
+ uint64_t old_stamp;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.req_hdr.len < OAP_TIMESTAMP_OFFSET + sizeof(uint64_t)) {
+ printf("Request too short for test.\n");
+ goto fail_cleanup;
+ }
+
+ /* Set timestamp to 30 seconds in the past (> 20s replay timer) */
+ clock_gettime(CLOCK_REALTIME, &old_ts);
+ old_ts.tv_sec -= OAP_REPLAY_TIMER + 10;
+ old_stamp = hton64(TS_TO_UINT64(old_ts));
+ memcpy(ctx.req_hdr.data + OAP_TIMESTAMP_OFFSET, &old_stamp,
+ sizeof(old_stamp));
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject outdated packet.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Test that packets from the future are rejected */
+static int test_oap_future_packet(void)
+{
+ struct oap_test_ctx ctx;
+ struct timespec future_ts;
+ uint64_t future_stamp;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.req_hdr.len < OAP_TIMESTAMP_OFFSET + sizeof(uint64_t)) {
+ printf("Request too short for test.\n");
+ goto fail_cleanup;
+ }
+
+ /* Set timestamp to 1 second in the future (> 100ms slack) */
+ clock_gettime(CLOCK_REALTIME, &future_ts);
+ future_ts.tv_sec += 1;
+ future_stamp = hton64(TS_TO_UINT64(future_ts));
+ memcpy(ctx.req_hdr.data + OAP_TIMESTAMP_OFFSET, &future_stamp,
+ sizeof(future_stamp));
+
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject future packet.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Test that replayed packets (same ID + timestamp) are rejected */
+static int test_oap_replay_packet(void)
+{
+ struct oap_test_ctx ctx;
+ buffer_t saved_req;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Save the request for replay */
+ saved_req.len = ctx.req_hdr.len;
+ saved_req.data = malloc(saved_req.len);
+ if (saved_req.data == NULL) {
+ printf("Failed to allocate saved request.\n");
+ goto fail_cleanup;
+ }
+ memcpy(saved_req.data, ctx.req_hdr.data, saved_req.len);
+
+ /* First request should succeed */
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("First request should succeed.\n");
+ free(saved_req.data);
+ goto fail_cleanup;
+ }
+
+ /* Free response from first request before replay */
+ freebuf(ctx.resp_hdr);
+
+ /* Restore the saved request for replay */
+ freebuf(ctx.req_hdr);
+ ctx.req_hdr = saved_req;
+
+ /* Replayed request should fail */
+ if (oap_srv_process_ctx(&ctx) == 0) {
+ printf("Server should reject replayed packet.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+/* Test that client rejects server with wrong certificate name */
+static int test_oap_server_name_mismatch(void)
+{
+ struct oap_test_ctx ctx;
+
+ test_default_cfg();
+
+ TEST_START();
+
+ if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0)
+ goto fail;
+
+ /* Set client's expected name to something different from cert name */
+ strcpy(ctx.cli.info.name, "wrong.server.name");
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ /* Client should reject due to name mismatch */
+ if (oap_cli_complete_ctx(&ctx) == 0) {
+ printf("Client should reject server with wrong cert name.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown(&ctx);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown(&ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int oap_test(int argc,
+ char **argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_oap_auth_init_fini();
+
+#ifdef HAVE_OPENSSL
+ ret |= test_oap_roundtrip_auth_only();
+ ret |= test_oap_roundtrip_kex_only();
+ ret |= test_oap_piggyback_data();
+
+ ret |= test_oap_roundtrip_all();
+ ret |= test_oap_roundtrip_md_all();
+
+ ret |= test_oap_corrupted_request();
+ ret |= test_oap_corrupted_response();
+ ret |= test_oap_truncated_request();
+ ret |= test_oap_inflated_length_field();
+ ret |= test_oap_deflated_length_field();
+ ret |= test_oap_nid_without_kex();
+ ret |= test_oap_unsupported_nid();
+
+ ret |= test_oap_cipher_mismatch();
+
+ ret |= test_oap_outdated_packet();
+ ret |= test_oap_future_packet();
+ ret |= test_oap_replay_packet();
+ ret |= test_oap_server_name_mismatch();
+#else
+ (void) test_oap_roundtrip_auth_only;
+ (void) test_oap_roundtrip_kex_only;
+ (void) test_oap_piggyback_data;
+ (void) test_oap_roundtrip;
+ (void) test_oap_roundtrip_all;
+ (void) test_oap_roundtrip_md;
+ (void) test_oap_roundtrip_md_all;
+ (void) test_oap_corrupted_request;
+ (void) test_oap_corrupted_response;
+ (void) test_oap_truncated_request;
+ (void) test_oap_inflated_length_field;
+ (void) test_oap_deflated_length_field;
+ (void) test_oap_nid_without_kex;
+ (void) test_oap_unsupported_nid;
+ (void) test_oap_cipher_mismatch;
+ (void) test_oap_outdated_packet;
+ (void) test_oap_future_packet;
+ (void) test_oap_replay_packet;
+ (void) test_oap_server_name_mismatch;
+
+ ret = TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/irmd/oap/tests/oap_test_pqc.c b/src/irmd/oap/tests/oap_test_pqc.c
new file mode 100644
index 00000000..ed51a6b4
--- /dev/null
+++ b/src/irmd/oap/tests/oap_test_pqc.c
@@ -0,0 +1,363 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Unit tests of OAP post-quantum key exchange
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include "config.h"
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/flow.h>
+#include <ouroboros/name.h>
+#include <ouroboros/random.h>
+#include <test/test.h>
+
+#include <test/certs_pqc.h>
+
+#include "oap.h"
+#include "common.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
+#define CLI_AUTH 1
+#define NO_CLI_AUTH 0
+#define CLI_ENCAP KEM_MODE_CLIENT_ENCAP
+#define SRV_ENCAP KEM_MODE_SERVER_ENCAP
+
+extern const uint16_t kex_supported_nids[];
+extern const uint16_t md_supported_nids[];
+
+static int get_random_kdf(void)
+{
+ static int idx = 0;
+ int count;
+
+ if (md_supported_nids[0] == NID_undef)
+ return NID_undef;
+
+ for (count = 0; md_supported_nids[count] != NID_undef; count++)
+ ;
+
+ return md_supported_nids[(idx++) % count];
+}
+
+struct test_cfg test_cfg;
+
+/* KEM keypair storage for tests (server-side keypair for KEM modes) */
+static void * test_kem_pkp = NULL; /* Private key pair */
+static uint8_t test_kem_pk[4096]; /* Public key buffer */
+static size_t test_kem_pk_len = 0;
+
+/* Mock load - called by load_*_credentials in common.c */
+int mock_load_credentials(void ** pkp,
+ void ** crt)
+{
+ *pkp = NULL;
+ *crt = NULL;
+
+ if (crypt_load_privkey_str(server_pkp_ml, pkp) < 0)
+ return -1;
+
+ if (crypt_load_crt_str(signed_server_crt_ml, crt) < 0) {
+ crypt_free_key(*pkp);
+ *pkp = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int load_server_kem_keypair(const char * name,
+ bool raw_fmt,
+ void ** pkp)
+{
+#ifdef HAVE_OPENSSL
+ struct sec_config local_cfg;
+ ssize_t pk_len;
+
+ (void) name;
+ (void) raw_fmt;
+
+ /*
+ * Uses reference counting. The caller will call
+ * EVP_PKEY_free which decrements the count.
+ */
+ if (test_kem_pkp != NULL) {
+ if (EVP_PKEY_up_ref((EVP_PKEY *)test_kem_pkp) != 1)
+ return -1;
+
+ *pkp = test_kem_pkp;
+ return 0;
+ }
+
+ /*
+ * Generate a new KEM keypair from test_cfg.srv.kex.
+ */
+ memset(&local_cfg, 0, sizeof(local_cfg));
+ if (test_cfg.srv.kex == NID_undef)
+ goto fail;
+
+ SET_KEX_ALGO_NID(&local_cfg, test_cfg.srv.kex);
+
+ pk_len = kex_pkp_create(&local_cfg, &test_kem_pkp, test_kem_pk);
+ if (pk_len < 0)
+ goto fail;
+
+ test_kem_pk_len = (size_t) pk_len;
+
+ if (EVP_PKEY_up_ref((EVP_PKEY *)test_kem_pkp) != 1)
+ goto fail_ref;
+
+ *pkp = test_kem_pkp;
+
+ return 0;
+ fail_ref:
+ kex_pkp_destroy(test_kem_pkp);
+ test_kem_pkp = NULL;
+ test_kem_pk_len = 0;
+ fail:
+ return -1;
+
+#else
+ (void) name;
+ (void) raw_fmt;
+ (void) pkp;
+ return -1;
+#endif
+}
+
+int load_server_kem_pk(const char * name,
+ struct sec_config * cfg,
+ buffer_t * pk)
+{
+ ssize_t len;
+
+ (void) name;
+
+ if (test_kem_pk_len > 0) {
+ pk->data = malloc(test_kem_pk_len);
+ if (pk->data == NULL)
+ return -1;
+ memcpy(pk->data, test_kem_pk, test_kem_pk_len);
+ pk->len = test_kem_pk_len;
+ return 0;
+ }
+
+ /* Generate keypair on demand if not already done */
+ len = kex_pkp_create(cfg, &test_kem_pkp, test_kem_pk);
+ if (len < 0)
+ return -1;
+
+ test_kem_pk_len = (size_t) len;
+ pk->data = malloc(test_kem_pk_len);
+ if (pk->data == NULL)
+ return -1;
+ memcpy(pk->data, test_kem_pk, test_kem_pk_len);
+ pk->len = test_kem_pk_len;
+
+ return 0;
+}
+
+static void reset_kem_state(void)
+{
+ if (test_kem_pkp != NULL) {
+ kex_pkp_destroy(test_kem_pkp);
+ test_kem_pkp = NULL;
+ }
+ test_kem_pk_len = 0;
+}
+
+static void test_cfg_init(int kex,
+ int cipher,
+ int kdf,
+ int kem_mode,
+ bool cli_auth)
+{
+ memset(&test_cfg, 0, sizeof(test_cfg));
+
+ /* Server config */
+ test_cfg.srv.kex = kex;
+ test_cfg.srv.cipher = cipher;
+ test_cfg.srv.kdf = kdf;
+ test_cfg.srv.kem_mode = kem_mode;
+ test_cfg.srv.auth = true;
+
+ /* Client config */
+ test_cfg.cli.kex = kex;
+ test_cfg.cli.cipher = cipher;
+ test_cfg.cli.kdf = kdf;
+ test_cfg.cli.kem_mode = kem_mode;
+ test_cfg.cli.auth = cli_auth;
+}
+
+static int oap_test_setup_kem(struct oap_test_ctx * ctx,
+ const char * root_ca,
+ const char * im_ca)
+{
+ reset_kem_state();
+ return oap_test_setup(ctx, root_ca, im_ca);
+}
+
+static void oap_test_teardown_kem(struct oap_test_ctx * ctx)
+{
+ oap_test_teardown(ctx);
+}
+
+static int test_oap_roundtrip_auth_only(void)
+{
+ test_cfg_init(NID_undef, NID_undef, NID_undef, 0, false);
+
+ return roundtrip_auth_only(root_ca_crt_ml, im_ca_crt_ml);
+}
+
+static int test_oap_corrupted_request(void)
+{
+ test_cfg_init(NID_MLKEM768, NID_aes_256_gcm, get_random_kdf(),
+ SRV_ENCAP, CLI_AUTH);
+
+ return corrupted_request(root_ca_crt_ml, im_ca_crt_ml);
+}
+
+static int test_oap_corrupted_response(void)
+{
+ test_cfg_init(NID_MLKEM768, NID_aes_256_gcm, get_random_kdf(),
+ SRV_ENCAP, NO_CLI_AUTH);
+
+ return corrupted_response(root_ca_crt_ml, im_ca_crt_ml);
+}
+
+static int test_oap_truncated_request(void)
+{
+ test_cfg_init(NID_MLKEM768, NID_aes_256_gcm, get_random_kdf(),
+ SRV_ENCAP, NO_CLI_AUTH);
+
+ return truncated_request(root_ca_crt_ml, im_ca_crt_ml);
+}
+
+static int test_oap_roundtrip_kem(int kex,
+ int kem_mode)
+{
+ struct oap_test_ctx ctx;
+ const char * kex_str = kex_nid_to_str(kex);
+ const char * mode_str = kem_mode == CLI_ENCAP ? "cli" : "srv";
+
+ test_cfg_init(kex, NID_aes_256_gcm, get_random_kdf(),
+ kem_mode, NO_CLI_AUTH);
+
+ TEST_START("(%s, %s encaps)", kex_str, mode_str);
+
+ if (oap_test_setup_kem(&ctx, root_ca_crt_ml, im_ca_crt_ml) < 0)
+ goto fail;
+
+ if (oap_cli_prepare_ctx(&ctx) < 0) {
+ printf("Client prepare failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_srv_process_ctx(&ctx) < 0) {
+ printf("Server process failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (oap_cli_complete_ctx(&ctx) < 0) {
+ printf("Client complete failed.\n");
+ goto fail_cleanup;
+ }
+
+ if (memcmp(ctx.cli.key, ctx.srv.key, SYMMKEYSZ) != 0) {
+ printf("Client and server keys do not match!\n");
+ goto fail_cleanup;
+ }
+
+ if (ctx.cli.nid == NID_undef ||
+ ctx.srv.nid == NID_undef) {
+ printf("Cipher not set in flow.\n");
+ goto fail_cleanup;
+ }
+
+ oap_test_teardown_kem(&ctx);
+
+ TEST_SUCCESS("(%s, %s encaps)", kex_str, mode_str);
+ return TEST_RC_SUCCESS;
+
+ fail_cleanup:
+ oap_test_teardown_kem(&ctx);
+ fail:
+ TEST_FAIL("(%s, %s encaps)", kex_str, mode_str);
+ return TEST_RC_FAIL;
+}
+
+static int test_oap_roundtrip_kem_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (!IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_oap_roundtrip_kem(kex_supported_nids[i], SRV_ENCAP);
+ ret |= test_oap_roundtrip_kem(kex_supported_nids[i], CLI_ENCAP);
+ }
+
+ return ret;
+}
+
+int oap_test_pqc(int argc,
+ char **argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+#ifdef HAVE_OPENSSL_PQC
+ ret |= test_oap_roundtrip_auth_only();
+
+ ret |= test_oap_roundtrip_kem_all();
+
+ ret |= test_oap_corrupted_request();
+ ret |= test_oap_corrupted_response();
+ ret |= test_oap_truncated_request();
+#else
+ (void) test_oap_roundtrip_auth_only;
+ (void) test_oap_roundtrip_kem;
+ (void) test_oap_roundtrip_kem_all;
+ (void) test_oap_corrupted_request;
+ (void) test_oap_corrupted_response;
+ (void) test_oap_truncated_request;
+
+ ret = TEST_RC_SKIP;
+#endif
+
+ return ret;
+}
diff --git a/src/irmd/reg/CMakeLists.txt b/src/irmd/reg/CMakeLists.txt
deleted file mode 100644
index ff9d2e99..00000000
--- a/src/irmd/reg/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-add_subdirectory(tests)
diff --git a/src/irmd/reg/flow.c b/src/irmd/reg/flow.c
index 4d091b23..52b03e61 100644
--- a/src/irmd/reg/flow.c
+++ b/src/irmd/reg/flow.c
@@ -66,11 +66,11 @@ struct reg_flow * reg_flow_create(const struct flow_info * info)
static void destroy_rbuffs(struct reg_flow * flow)
{
if (flow->n_rb != NULL)
- shm_rbuff_destroy(flow->n_rb);
+ ssm_rbuff_destroy(flow->n_rb);
flow->n_rb = NULL;
if (flow->n_1_rb != NULL)
- shm_rbuff_destroy(flow->n_1_rb);
+ ssm_rbuff_destroy(flow->n_1_rb);
flow->n_1_rb = NULL;
}
@@ -103,22 +103,28 @@ static int create_rbuffs(struct reg_flow * flow,
assert(flow != NULL);
assert(info != NULL);
- flow->n_rb = shm_rbuff_create(info->n_pid, info->id);
+ flow->n_rb = ssm_rbuff_create(info->n_pid, info->id);
if (flow->n_rb == NULL)
goto fail_n_rb;
+ if (ssm_rbuff_mlock(flow->n_rb) < 0)
+ log_warn("Failed to mlock n_rb for flow %d.", info->id);
+
assert(flow->info.n_1_pid == 0);
assert(flow->n_1_rb == NULL);
flow->info.n_1_pid = info->n_1_pid;
- flow->n_1_rb = shm_rbuff_create(info->n_1_pid, info->id);
+ flow->n_1_rb = ssm_rbuff_create(info->n_1_pid, info->id);
if (flow->n_1_rb == NULL)
goto fail_n_1_rb;
+ if (ssm_rbuff_mlock(flow->n_1_rb) < 0)
+ log_warn("Failed to mlock n_1_rb for flow %d.", info->id);
+
return 0;
fail_n_1_rb:
- shm_rbuff_destroy(flow->n_rb);
+ ssm_rbuff_destroy(flow->n_rb);
fail_n_rb:
return -ENOMEM;
}
@@ -172,6 +178,7 @@ int reg_flow_update(struct reg_flow * flow,
}
flow->info.state = info->state;
+ flow->info.uid = info->uid;
*info = flow->info;
diff --git a/src/irmd/reg/flow.h b/src/irmd/reg/flow.h
index 75ada971..b671d486 100644
--- a/src/irmd/reg/flow.h
+++ b/src/irmd/reg/flow.h
@@ -25,24 +25,28 @@
#include <ouroboros/list.h>
#include <ouroboros/flow.h>
+#include <ouroboros/name.h>
#include <ouroboros/pthread.h>
#include <ouroboros/qos.h>
-#include <ouroboros/shm_rbuff.h>
+#include <ouroboros/ssm_rbuff.h>
#include <ouroboros/utils.h>
#include <sys/types.h>
#include <time.h>
struct reg_flow {
- struct list_head next;
+ struct list_head next;
- struct flow_info info;
+ struct flow_info info;
+ int response;
- buffer_t data;
- struct timespec t0;
+ buffer_t data;
+ struct timespec t0;
- struct shm_rbuff * n_rb;
- struct shm_rbuff * n_1_rb;
+ char name[NAME_SIZE + 1];
+
+ struct ssm_rbuff * n_rb;
+ struct ssm_rbuff * n_1_rb;
};
struct reg_flow * reg_flow_create(const struct flow_info * info);
diff --git a/src/irmd/reg/ipcp.c b/src/irmd/reg/ipcp.c
index 6580cb5b..74ec4939 100644
--- a/src/irmd/reg/ipcp.c
+++ b/src/irmd/reg/ipcp.c
@@ -40,7 +40,7 @@ struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info)
struct reg_ipcp * ipcp;
assert(info != NULL);
- assert(info->state == IPCP_BOOT);
+ assert(info->state == IPCP_INIT);
ipcp = malloc(sizeof(*ipcp));
if (ipcp == NULL) {
@@ -54,7 +54,7 @@ struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info)
list_head_init(&ipcp->next);
ipcp->info = *info;
- ipcp->info.state = IPCP_BOOT;
+ ipcp->info.state = IPCP_INIT;
strcpy(ipcp->layer.name, "Not enrolled.");
@@ -77,7 +77,6 @@ void reg_ipcp_update(struct reg_ipcp * ipcp,
const struct ipcp_info * info)
{
assert(ipcp != NULL);
- assert(info->state != IPCP_INIT);
ipcp->info = *info;
}
@@ -86,7 +85,7 @@ void reg_ipcp_set_layer(struct reg_ipcp * ipcp,
const struct layer_info * info)
{
assert(ipcp != NULL);
- assert(ipcp->info.state == IPCP_OPERATIONAL);
+ assert(ipcp->info.state == IPCP_BOOT);
ipcp->layer = *info;
}
diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c
index 1ac939a5..4e609711 100644
--- a/src/irmd/reg/name.c
+++ b/src/irmd/reg/name.c
@@ -66,15 +66,14 @@ struct reg_name * reg_name_create(const struct name_info * info)
goto fail_malloc;
}
+ memset(name, 0, sizeof(*name));
+
list_head_init(&name->next);
- list_head_init(&name->progs);
- list_head_init(&name->procs);
- list_head_init(&name->active);
+ list_head_init(&name->progs.list);
+ list_head_init(&name->procs.list);
+ list_head_init(&name->active.list);
- name->info = *info;
- name->n_progs = 0;
- name->n_procs = 0;
- name->n_active = 0;
+ name->info = *info;
return name;
@@ -88,13 +87,13 @@ void reg_name_destroy(struct reg_name * name)
assert(list_is_empty(&name->next));
- assert(name->n_progs == 0);
- assert(name->n_procs == 0);
- assert(name->n_active == 0);
+ assert(name->progs.len == 0);
+ assert(name->procs.len == 0);
+ assert(name->active.len == 0);
- assert(list_is_empty(&name->progs));
- assert(list_is_empty(&name->procs));
- assert(list_is_empty(&name->active));
+ assert(list_is_empty(&name->progs.list));
+ assert(list_is_empty(&name->procs.list));
+ assert(list_is_empty(&name->active.list));
free(name);
}
@@ -107,7 +106,7 @@ static struct proc_entry * __reg_name_get_active(const struct reg_name * name,
assert(name != NULL);
assert(pid > 0);
- list_for_each(p, &name->active) {
+ list_for_each(p, &name->active.list) {
struct proc_entry * entry;
entry = list_entry(p, struct proc_entry, next);
if (entry->pid == pid)
@@ -123,13 +122,13 @@ static void __reg_name_del_all_active(struct reg_name * name,
struct list_head * p;
struct list_head * h;
- list_for_each_safe(p, h, &name->active) {
+ list_for_each_safe(p, h, &name->active.list) {
struct proc_entry * entry;
entry = list_entry(p, struct proc_entry, next);
if (entry->pid == pid) {
list_del(&entry->next);
free(entry);
- name->n_active--;
+ --name->active.len;
}
}
}
@@ -142,7 +141,7 @@ static struct proc_entry * __reg_name_get_proc(const struct reg_name * name,
assert(name != NULL);
assert(pid > 0);
- list_for_each(p, &name->procs) {
+ list_for_each(p, &name->procs.list) {
struct proc_entry * entry;
entry = list_entry(p, struct proc_entry, next);
if (entry->pid == pid)
@@ -160,7 +159,7 @@ static struct prog_entry * __reg_name_get_prog(const struct reg_name * name,
assert(name != NULL);
assert(prog != NULL);
- list_for_each(p, &name->progs) {
+ list_for_each(p, &name->progs.list) {
struct prog_entry * entry;
entry = list_entry(p, struct prog_entry, next);
if (strcmp(entry->exec[0], prog) == 0)
@@ -195,16 +194,16 @@ int reg_name_add_active(struct reg_name * name,
switch (name->info.pol_lb) {
case LB_RR: /* Round robin policy. */
- list_add_tail(&entry->next, &name->active);
+ list_add_tail(&entry->next, &name->active.list);
break;
case LB_SPILL: /* Keep accepting flows on the current process */
- list_add(&entry->next, &name->active);
+ list_add(&entry->next, &name->active.list);
break;
default:
goto fail_unreachable;
}
- name->n_active++;
+ ++name->active.len;
return 0;
@@ -226,19 +225,23 @@ void reg_name_del_active(struct reg_name * name,
list_del(&entry->next);
- name->n_active--;
+ --name->active.len;
free(entry);
}
pid_t reg_name_get_active(struct reg_name * name)
{
+ struct proc_entry * e;
+
assert(name != NULL);
- if (list_is_empty(&name->active))
+ if (list_is_empty(&name->active.list))
return -1;
- return list_first_entry(&name->active, struct proc_entry, next)->pid;
+ e = list_first_entry(&name->active.list, struct proc_entry, next);
+
+ return e->pid;
}
int reg_name_add_proc(struct reg_name * name,
@@ -259,9 +262,9 @@ int reg_name_add_proc(struct reg_name * name,
entry->pid = pid;
- list_add(&entry->next, &name->procs);
+ list_add(&entry->next, &name->procs.list);
- name->n_procs++;
+ ++name->procs.len;
return 0;
@@ -287,7 +290,7 @@ void reg_name_del_proc(struct reg_name * name,
free(entry);
- name->n_procs--;
+ --name->procs.len;
assert(__reg_name_get_proc(name, pid) == NULL);
}
@@ -296,8 +299,7 @@ bool reg_name_has_proc(const struct reg_name * name,
pid_t pid)
{
return __reg_name_get_proc(name, pid) != NULL;
-} char ** exec;
-
+}
int reg_name_add_prog(struct reg_name * name,
char ** exec)
@@ -322,11 +324,11 @@ int reg_name_add_prog(struct reg_name * name,
goto fail_exec;
}
- list_add(&entry->next, &name->progs);
+ list_add(&entry->next, &name->progs.list);
log_dbg("Add prog %s to name %s.", exec[0], name->info.name);
- name->n_progs++;
+ ++name->progs.len;
return 0;
@@ -352,7 +354,7 @@ void reg_name_del_prog(struct reg_name * name,
__free_prog_entry(entry);
- name->n_progs--;
+ --name->progs.len;
assert(__reg_name_get_prog(name, prog) == NULL);
}
@@ -368,8 +370,12 @@ bool reg_name_has_prog(const struct reg_name * name,
char ** reg_name_get_exec(const struct reg_name * name)
{
- if (list_is_empty(&name->progs))
+ struct prog_entry * e;
+
+ if (list_is_empty(&name->progs.list))
return NULL;
- return list_first_entry(&name->progs, struct prog_entry, next)->exec;
+ e = list_first_entry(&name->progs.list, struct prog_entry, next);
+
+ return e->exec;
}
diff --git a/src/irmd/reg/name.h b/src/irmd/reg/name.h
index 97ca7f04..30a64e1c 100644
--- a/src/irmd/reg/name.h
+++ b/src/irmd/reg/name.h
@@ -33,14 +33,25 @@ struct reg_name {
struct name_info info;
- struct list_head progs; /* autostart programs for this name */
- size_t n_progs; /* number of programs */
-
- struct list_head procs; /* processes bound to this name */
- size_t n_procs; /* number of processes */
-
- struct list_head active; /* processes actively calling accept */
- size_t n_active; /* number of processes accepting */
+ struct {
+ void * key;
+ void * crt;
+ } cache;
+
+ struct {
+ struct list_head list;
+ size_t len;
+ } progs; /* autostart programs for this name */
+
+ struct {
+ struct list_head list;
+ size_t len;
+ } procs; /* processes bound to this name */
+
+ struct {
+ struct list_head list;
+ size_t len;
+ } active; /* processes actively calling accept */
};
struct reg_name * reg_name_create(const struct name_info * info);
@@ -74,5 +85,4 @@ pid_t reg_name_get_active(struct reg_name * name);
void reg_name_del_active(struct reg_name * name,
pid_t proc);
-
#endif /* OUROBOROS_IRMD_REG_NAME_H */
diff --git a/src/irmd/reg/pool.c b/src/irmd/reg/pool.c
new file mode 100644
index 00000000..fd983db8
--- /dev/null
+++ b/src/irmd/reg/pool.c
@@ -0,0 +1,101 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * The IPC Resource Manager - Registry - Per-User Pools
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#define OUROBOROS_PREFIX "reg/pool"
+
+#include <ouroboros/logs.h>
+#include <ouroboros/ssm_pool.h>
+
+#include "pool.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+struct reg_pool * reg_pool_create(uid_t uid,
+ gid_t gid)
+{
+ struct reg_pool * pool;
+
+ pool = malloc(sizeof(*pool));
+ if (pool == NULL) {
+ log_err("Failed to malloc pool.");
+ goto fail_malloc;
+ }
+
+ pool->ssm = ssm_pool_create(uid, gid);
+ if (pool->ssm == NULL) {
+ log_err("Failed to create PUP for uid %d.", uid);
+ goto fail_ssm;
+ }
+
+ list_head_init(&pool->next);
+ pool->uid = uid;
+ pool->gid = gid;
+ pool->refcount = 1;
+
+ log_dbg("Created PUP for uid %d gid %d.", uid, gid);
+
+ return pool;
+
+ fail_ssm:
+ free(pool);
+ fail_malloc:
+ return NULL;
+}
+
+void reg_pool_destroy(struct reg_pool * pool)
+{
+ assert(pool != NULL);
+ assert(pool->refcount == 0);
+
+ log_dbg("Destroying PUP for uid %d.", pool->uid);
+
+ ssm_pool_destroy(pool->ssm);
+
+ assert(list_is_empty(&pool->next));
+
+ free(pool);
+}
+
+void reg_pool_ref(struct reg_pool * pool)
+{
+ assert(pool != NULL);
+ assert(pool->refcount > 0);
+
+ pool->refcount++;
+
+ log_dbg("PUP uid %d refcount++ -> %zu.", pool->uid, pool->refcount);
+}
+
+int reg_pool_unref(struct reg_pool * pool)
+{
+ assert(pool != NULL);
+ assert(pool->refcount > 0);
+
+ pool->refcount--;
+
+ log_dbg("PUP uid %d refcount-- -> %zu.", pool->uid, pool->refcount);
+
+ return pool->refcount == 0 ? 0 : 1;
+}
diff --git a/src/irmd/reg/pool.h b/src/irmd/reg/pool.h
new file mode 100644
index 00000000..576f491c
--- /dev/null
+++ b/src/irmd/reg/pool.h
@@ -0,0 +1,48 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * The IPC Resource Manager - Registry - Per-User Pools
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_IRMD_REG_POOL_H
+#define OUROBOROS_IRMD_REG_POOL_H
+
+#include <ouroboros/list.h>
+#include <ouroboros/ssm_pool.h>
+
+#include <sys/types.h>
+
+struct reg_pool {
+ struct list_head next;
+ uid_t uid;
+ gid_t gid;
+ size_t refcount;
+ struct ssm_pool * ssm;
+};
+
+struct reg_pool * reg_pool_create(uid_t uid,
+ gid_t gid);
+
+void reg_pool_destroy(struct reg_pool * pool);
+
+void reg_pool_ref(struct reg_pool * pool);
+
+int reg_pool_unref(struct reg_pool * pool);
+
+#endif /* OUROBOROS_IRMD_REG_POOL_H */
diff --git a/src/irmd/reg/proc.c b/src/irmd/reg/proc.c
index 9bbdf0eb..b97dcf2d 100644
--- a/src/irmd/reg/proc.c
+++ b/src/irmd/reg/proc.c
@@ -25,6 +25,7 @@
#define OUROBOROS_PREFIX "reg/proc"
#include <ouroboros/logs.h>
+#include <ouroboros/utils.h>
#include "proc.h"
@@ -75,7 +76,9 @@ struct reg_proc * reg_proc_create(const struct proc_info * info)
goto fail_malloc;
}
- proc->set = shm_flow_set_create(info->pid);
+ memset(proc, 0, sizeof(*proc));
+
+ proc->set = ssm_flow_set_create(info->pid);
if (proc->set == NULL) {
log_err("Failed to create flow set for %d.", info->pid);
goto fail_set;
@@ -99,7 +102,7 @@ void reg_proc_destroy(struct reg_proc * proc)
{
assert(proc != NULL);
- shm_flow_set_destroy(proc->set);
+ ssm_flow_set_destroy(proc->set);
__reg_proc_clear_names(proc);
@@ -181,3 +184,10 @@ bool reg_proc_has_name(const struct reg_proc * proc,
{
return __reg_proc_get_name(proc, name) != NULL;
}
+
+bool reg_proc_is_privileged(const struct reg_proc * proc)
+{
+ assert(proc != NULL);
+
+ return is_ouroboros_member_uid(proc->info.uid);
+}
diff --git a/src/irmd/reg/proc.h b/src/irmd/reg/proc.h
index 99f74fef..be4c1161 100644
--- a/src/irmd/reg/proc.h
+++ b/src/irmd/reg/proc.h
@@ -25,17 +25,17 @@
#include <ouroboros/list.h>
#include <ouroboros/proc.h>
-#include <ouroboros/shm_flow_set.h>
+#include <ouroboros/ssm_flow_set.h>
struct reg_proc {
struct list_head next;
struct proc_info info;
- struct list_head names; /* names for which process accepts flows */
- size_t n_names; /* number of names */
+ struct list_head names; /* process accepts flows for names */
+ size_t n_names; /* number of names */
- struct shm_flow_set * set;
+ struct ssm_flow_set * set;
};
struct reg_proc * reg_proc_create(const struct proc_info * info);
@@ -53,4 +53,6 @@ void reg_proc_del_name(struct reg_proc * proc,
bool reg_proc_has_name(const struct reg_proc * proc,
const char * name);
+bool reg_proc_is_privileged(const struct reg_proc * proc);
+
#endif /* OUROBOROS_IRMD_REG_PROC_H */
diff --git a/src/irmd/reg/reg.c b/src/irmd/reg/reg.c
index d95a4722..e89b492b 100644
--- a/src/irmd/reg/reg.c
+++ b/src/irmd/reg/reg.c
@@ -28,12 +28,14 @@ The IPC Resource Manager - Registry
#include <ouroboros/errno.h>
#include <ouroboros/list.h>
#include <ouroboros/logs.h>
+#include <ouroboros/protobuf.h>
#include <ouroboros/pthread.h>
#include "reg.h"
#include "flow.h"
#include "ipcp.h"
#include "name.h"
+#include "pool.h"
#include "proc.h"
#include "prog.h"
@@ -46,6 +48,7 @@ The IPC Resource Manager - Registry
struct {
struct bmp * flow_ids; /* flow_ids for flows */
+
struct list_head flows; /* flow information */
size_t n_flows; /* number of flows */
@@ -55,6 +58,9 @@ struct {
struct list_head names; /* registered names known */
size_t n_names; /* number of names */
+ struct list_head pools; /* per-user pools */
+ size_t n_pools; /* number of pools */
+
struct list_head procs; /* processes */
size_t n_procs; /* number of processes */
@@ -151,16 +157,23 @@ static struct reg_ipcp * __reg_get_ipcp_by_layer(const char * layer)
return NULL;
}
-static struct list_head * __reg_after_ipcp(pid_t pid)
+
+static struct list_head * __reg_after_ipcp(const struct ipcp_info * info)
{
struct list_head * p;
- assert(pid > 0);
+ assert(info != NULL);
list_for_each(p, &reg.ipcps) {
struct reg_ipcp * entry;
entry = list_entry(p, struct reg_ipcp, next);
- if (entry->info.pid > pid)
+ if (entry->info.type < info->type)
+ continue;
+
+ if (entry->info.type > info->type)
+ break;
+
+ if (entry->info.pid > info->pid)
break;
}
@@ -183,41 +196,17 @@ static struct reg_name * __reg_get_name(const char * name)
return NULL;
}
-static struct reg_name * __reg_get_name_by_hash(enum hash_algo algo,
- const uint8_t * hash)
-{
- struct list_head * p;
- uint8_t * thash;
- size_t len;
-
- len = hash_len(algo);
-
- thash = malloc(len);
- if (thash == NULL)
- return NULL;
-
- list_for_each(p, &reg.names) {
- struct reg_name * n = list_entry(p, struct reg_name, next);
- str_hash(algo, thash, n->info.name);
- if (memcmp(thash, hash, len) == 0) {
- free(thash);
- return n;
- }
- }
-
- free(thash);
-
- return NULL;
-}
-
-static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo,
- const uint8_t * hash)
+static int __reg_get_pending_flow_id(const char * name)
{
struct reg_name * entry;
struct reg_flow * flow;
pid_t pid;
- entry =__reg_get_name_by_hash(algo, hash);
+ assert(name != NULL);
+ assert(strlen(name) > 0);
+ assert(strlen(name) < NAME_SIZE + 1);
+
+ entry =__reg_get_name(name);
if (entry == NULL)
return -ENAME;
@@ -226,7 +215,10 @@ static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo,
return -EAGAIN;
flow = __reg_get_accept_flow(pid);
- assert(flow != NULL);
+ if (flow == NULL) /* compiler barks, this can't be NULL */
+ return -EAGAIN;
+
+ strcpy(flow->name, name);
return flow->info.id;
}
@@ -247,6 +239,20 @@ static struct list_head * __reg_after_name(const char * name)
return p;
}
+static struct reg_pool * __reg_get_pool(uid_t uid)
+{
+ struct list_head * p;
+
+ list_for_each(p, &reg.pools) {
+ struct reg_pool * entry;
+ entry = list_entry(p, struct reg_pool, next);
+ if (entry->uid == uid)
+ return entry;
+ }
+
+ return NULL;
+}
+
static struct reg_proc * __reg_get_proc(pid_t pid)
{
struct list_head * p;
@@ -388,30 +394,17 @@ static struct reg_prog * __reg_get_prog(const char * name)
return NULL;
}
-static char ** __reg_get_exec(enum hash_algo algo,
- const uint8_t * hash)
+static char ** __reg_get_exec(const char * name)
{
struct list_head * p;
- uint8_t * buf;
-
- buf = malloc(hash_len(algo));
- if (buf == NULL) {
- log_err("Failed to malloc hash buffer.");
- return NULL;
- }
list_for_each(p, &reg.names) {
struct reg_name * entry;
entry = list_entry(p, struct reg_name, next);
- str_hash(algo, buf, entry->info.name);
- if (memcmp(buf, hash, hash_len(algo)) == 0) {
- free(buf);
+ if (strcmp(entry->info.name, name) == 0)
return reg_name_get_exec(entry);
- }
}
- free(buf);
-
return NULL;
}
@@ -565,6 +558,7 @@ int reg_init(void)
list_head_init(&reg.flows);
list_head_init(&reg.ipcps);
list_head_init(&reg.names);
+ list_head_init(&reg.pools);
list_head_init(&reg.procs);
list_head_init(&reg.progs);
list_head_init(&reg.spawned);
@@ -614,6 +608,23 @@ void reg_clear(void)
reg.n_procs--;
}
+ list_for_each_safe(p, h, &reg.pools) {
+ struct reg_pool * entry;
+ entry = list_entry(p, struct reg_pool, next);
+ list_del(&entry->next);
+ entry->refcount = 0; /* Force destroy during cleanup */
+ reg_pool_destroy(entry);
+ reg.n_pools--;
+ }
+
+ list_for_each_safe(p, h, &reg.flows) {
+ struct reg_flow * entry;
+ entry = list_entry(p, struct reg_flow, next);
+ list_del(&entry->next);
+ reg_flow_destroy(entry);
+ reg.n_flows--;
+ }
+
list_for_each_safe(p, h, &reg.names) {
struct reg_name * entry;
entry = list_entry(p, struct reg_name, next);
@@ -630,14 +641,6 @@ void reg_clear(void)
reg.n_ipcps--;
}
- list_for_each_safe(p, h, &reg.flows) {
- struct reg_flow * entry;
- entry = list_entry(p, struct reg_flow, next);
- list_del(&entry->next);
- reg_flow_destroy(entry);
- reg.n_flows--;
- }
-
pthread_mutex_unlock(&reg.mtx);
}
@@ -646,6 +649,7 @@ void reg_fini(void)
assert(list_is_empty(&reg.spawned));
assert(list_is_empty(&reg.progs));
assert(list_is_empty(&reg.procs));
+ assert(list_is_empty(&reg.pools));
assert(list_is_empty(&reg.names));
assert(list_is_empty(&reg.ipcps));
assert(list_is_empty(&reg.flows));
@@ -653,6 +657,7 @@ void reg_fini(void)
assert(reg.n_spawned == 0);
assert(reg.n_progs == 0);
assert(reg.n_procs == 0);
+ assert(reg.n_pools == 0);
assert(reg.n_names == 0);
assert(reg.n_ipcps == 0);
assert(reg.n_flows == 0);
@@ -757,7 +762,7 @@ int reg_create_ipcp(const struct ipcp_info * info)
assert(info != NULL);
assert(info->pid != 0);
- assert(info->state == IPCP_BOOT);
+ assert(info->state == IPCP_INIT);
pthread_mutex_lock(&reg.mtx);
@@ -780,7 +785,7 @@ int reg_create_ipcp(const struct ipcp_info * info)
entry->pid = info->pid;
- list_add(&ipcp->next, __reg_after_ipcp(info->pid));
+ list_add_tail(&ipcp->next, __reg_after_ipcp(info));
list_add(&entry->next, __reg_after_spawned(info->pid));
reg.n_ipcps++;
@@ -848,11 +853,11 @@ static int __get_ipcp_info(ipcp_list_msg_t ** msg,
(*msg)->name = strdup(ipcp->info.name);
if ((*msg)->name == NULL)
- goto fail_name;
+ goto fail_msg;
(*msg)->layer = strdup(ipcp->layer.name);
if ((*msg)->layer == NULL)
- goto fail_layer;
+ goto fail_msg;
(*msg)->pid = ipcp->info.pid;
(*msg)->type = ipcp->info.type;
@@ -860,10 +865,8 @@ static int __get_ipcp_info(ipcp_list_msg_t ** msg,
return 0;
- fail_layer:
- free((*msg)->name);
- fail_name:
- free(*msg);
+ fail_msg:
+ ipcp_list_msg__free_unpacked(*msg, NULL);
*msg = NULL;
fail:
return -1;
@@ -876,10 +879,8 @@ int reg_list_ipcps(ipcp_list_msg_t *** ipcps)
pthread_mutex_lock(&reg.mtx);
- if (reg.n_ipcps == 0) {
- *ipcps = NULL;
+ if (reg.n_ipcps == 0)
goto finish;
- }
*ipcps = malloc(reg.n_ipcps * sizeof(**ipcps));
if (*ipcps == NULL) {
@@ -890,24 +891,19 @@ int reg_list_ipcps(ipcp_list_msg_t *** ipcps)
list_for_each(p, &reg.ipcps) {
struct reg_ipcp * entry;
entry = list_entry(p, struct reg_ipcp, next);
- if (__get_ipcp_info(&((*ipcps)[i]), entry) < 0) {
- log_err("Failed to create ipcp list info.");
+ if (__get_ipcp_info(&(*ipcps)[i], entry) < 0)
goto fail;
- }
- ++i;
+ i++;
}
-
- assert(i == (int) reg.n_ipcps);
finish:
pthread_mutex_unlock(&reg.mtx);
return i;
fail:
- while (i > 0)
- ipcp_list_msg__free_unpacked((*ipcps)[--i], NULL);
-
+ while (i-- > 0)
+ ipcp_list_msg__free_unpacked((*ipcps)[i], NULL);
free(*ipcps);
fail_malloc:
pthread_mutex_unlock(&reg.mtx);
@@ -993,28 +989,84 @@ bool reg_has_name(const char * name)
return ret;
}
-static int __get_name_info(name_info_msg_t ** msg,
- struct reg_name * n)
+int reg_get_name_info(const char * name,
+ struct name_info * info)
{
- *msg = malloc(sizeof(**msg));
- if (*msg == NULL)
- goto fail;
+ struct reg_name * n;
- name_info_msg__init(*msg);
+ assert(name != NULL);
+ assert(info != NULL);
- (*msg)->name = strdup(n->info.name);
- if ((*msg)->name == NULL)
- goto fail_name;
+ pthread_mutex_lock(&reg.mtx);
+
+ n = __reg_get_name(name);
+ if (n == NULL) {
+ log_err("Name %s does not exist.", name);
+ goto no_name;
+ }
- (*msg)->pol_lb = n->info.pol_lb;
+ *info = n->info;
+
+ pthread_mutex_unlock(&reg.mtx);
return 0;
- fail_name:
- free(*msg);
- *msg = NULL;
- fail:
- return -1;
+ no_name:
+ pthread_mutex_unlock(&reg.mtx);
+ return -ENOENT;
+
+}
+
+int reg_get_name_for_hash(char * buf,
+ enum hash_algo algo,
+ const uint8_t * hash)
+{
+ struct list_head * p;
+ uint8_t * thash;
+ size_t len;
+ char * name = NULL;
+
+ len = hash_len(algo);
+
+ thash = malloc(len);
+ if (thash == NULL)
+ return -ENOMEM;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ list_for_each(p, &reg.names) {
+ struct reg_name * n = list_entry(p, struct reg_name, next);
+ str_hash(algo, thash, n->info.name);
+ if (memcmp(thash, hash, len) == 0) {
+ name = n->info.name;
+ break;
+ }
+ }
+
+ if (name != NULL)
+ strcpy(buf, name);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ free(thash);
+
+ return name == NULL ? -ENOENT : 0;
+}
+
+int reg_get_name_for_flow_id(char * buf,
+ int flow_id)
+{
+ struct reg_flow * f;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ f = __reg_get_flow(flow_id);
+ if (f != NULL)
+ strcpy(buf, f->name);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return f == NULL ? -ENOENT : 0;
}
int reg_list_names(name_info_msg_t *** names)
@@ -1036,24 +1088,31 @@ int reg_list_names(name_info_msg_t *** names)
list_for_each(p, &reg.names) {
struct reg_name * entry;
entry = list_entry(p, struct reg_name, next);
- if (__get_name_info(&((*names)[i]), entry) < 0) {
+ (*names)[i] = name_info_s_to_msg(&entry->info);
+ if ((*names)[i] == NULL) {
log_err("Failed to create name list info.");
goto fail;
}
-
- ++i;
+ /* wipe security info to avoid huge messages */
+ free((*names)[i]->scrt);
+ (*names)[i]->scrt = NULL;
+ free((*names)[i]->skey);
+ (*names)[i]->skey = NULL;
+ free((*names)[i]->ccrt);
+ (*names)[i]->ccrt = NULL;
+ free((*names)[i]->ckey);
+ (*names)[i]->ckey = NULL;
+
+ i++;
}
-
- assert(i == (int) reg.n_names);
finish:
pthread_mutex_unlock(&reg.mtx);
return i;
fail:
- while (i > 0)
- name_info_msg__free_unpacked((*names)[--i], NULL);
-
+ while (i-- > 0)
+ name_info_msg__free_unpacked((*names)[i], NULL);
free(*names);
fail_malloc:
pthread_mutex_unlock(&reg.mtx);
@@ -1061,6 +1120,35 @@ int reg_list_names(name_info_msg_t *** names)
return -ENOMEM;
}
+int reg_prepare_pool(uid_t uid,
+ gid_t gid)
+{
+ struct reg_pool * pool;
+
+ if (is_ouroboros_member_uid(uid))
+ return 0;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ pool = __reg_get_pool(uid);
+ if (pool == NULL) {
+ pool = reg_pool_create(uid, gid);
+ if (pool == NULL) {
+ log_err("Failed to create pool for uid %d.", uid);
+ pthread_mutex_unlock(&reg.mtx);
+ return -1;
+ }
+ list_add(&pool->next, &reg.pools);
+ reg.n_pools++;
+ }
+
+ reg_pool_ref(pool);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return 0;
+}
+
int reg_create_proc(const struct proc_info * info)
{
struct reg_proc * proc;
@@ -1071,13 +1159,13 @@ int reg_create_proc(const struct proc_info * info)
if (__reg_get_proc(info->pid) != NULL) {
log_err("Process %d already exists.", info->pid);
- goto fail_proc;
+ goto fail;
}
proc = reg_proc_create(info);
if (proc == NULL) {
log_err("Failed to create process %d.", info->pid);
- goto fail_proc;
+ goto fail;
}
__reg_proc_update_names(proc);
@@ -1092,7 +1180,7 @@ int reg_create_proc(const struct proc_info * info)
return 0;
- fail_proc:
+ fail:
pthread_mutex_unlock(&reg.mtx);
return -1;
}
@@ -1100,6 +1188,7 @@ int reg_create_proc(const struct proc_info * info)
int reg_destroy_proc(pid_t pid)
{
struct reg_proc * proc;
+ struct reg_pool * pool = NULL;
struct pid_entry * spawn;
struct reg_ipcp * ipcp;
@@ -1107,11 +1196,18 @@ int reg_destroy_proc(pid_t pid)
proc = __reg_get_proc(pid);
if (proc != NULL) {
+ if (!is_ouroboros_member_uid(proc->info.uid))
+ pool = __reg_get_pool(proc->info.uid);
list_del(&proc->next);
reg.n_procs--;
reg_proc_destroy(proc);
__reg_del_proc_from_names(pid);
__reg_cancel_flows_for_proc(pid);
+ if (pool != NULL && reg_pool_unref(pool) == 0) {
+ list_del(&pool->next);
+ reg.n_pools--;
+ reg_pool_destroy(pool);
+ }
}
spawn = __reg_get_spawned(pid);
@@ -1146,6 +1242,38 @@ bool reg_has_proc(pid_t pid)
return ret;
}
+bool reg_is_proc_privileged(pid_t pid)
+{
+ struct reg_proc * proc;
+ bool ret = false;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ proc = __reg_get_proc(pid);
+ if (proc != NULL)
+ ret = reg_proc_is_privileged(proc);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return ret;
+}
+
+uid_t reg_get_proc_uid(pid_t pid)
+{
+ struct reg_proc * proc;
+ uid_t ret = 0;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ proc = __reg_get_proc(pid);
+ if (proc != NULL && !is_ouroboros_member_uid(proc->info.uid))
+ ret = proc->info.uid;
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return ret;
+}
+
void reg_kill_all_proc(int signal)
{
pthread_mutex_lock(&reg.mtx);
@@ -1419,19 +1547,18 @@ bool reg_has_prog(const char * name)
return ret;
}
-int reg_get_exec(enum hash_algo algo,
- const uint8_t * hash,
- char *** prog)
+int reg_get_exec(const char * name,
+ char *** prog)
{
char ** exec;
int ret = 0;
- assert(hash != NULL);
+ assert(name != NULL);
assert(prog != NULL);
pthread_mutex_lock(&reg.mtx);
- exec = __reg_get_exec(algo, hash);
+ exec = __reg_get_exec(name);
if (exec == NULL) {
ret = -EPERM;
goto finish;
@@ -1444,12 +1571,9 @@ int reg_get_exec(enum hash_algo algo,
goto finish;
}
- pthread_mutex_unlock(&reg.mtx);
-
- return 0;
-
finish:
pthread_mutex_unlock(&reg.mtx);
+
return ret;
}
@@ -1557,8 +1681,7 @@ int reg_set_layer_for_ipcp(struct ipcp_info * info,
struct reg_ipcp * ipcp;
assert(info != NULL);
- assert(info->state > IPCP_BOOT);
- assert(info->state < IPCP_SHUTDOWN);
+ assert(info->state == IPCP_BOOT);
pthread_mutex_lock(&reg.mtx);
@@ -1690,7 +1813,7 @@ int reg_wait_flow_allocated(struct flow_info * info,
stop = true;
break;
case FLOW_DEALLOCATED:
- ret = -1;
+ ret = flow->response;
stop = true;
break;
default:
@@ -1722,7 +1845,8 @@ int reg_wait_flow_allocated(struct flow_info * info,
}
int reg_respond_alloc(struct flow_info * info,
- buffer_t * pbuf)
+ buffer_t * pbuf,
+ int response)
{
struct reg_flow * flow;
@@ -1755,7 +1879,9 @@ int reg_respond_alloc(struct flow_info * info,
if (reg_flow_update(flow, info) < 0) {
log_err("Failed to create flow structs.");
goto fail_flow;
- };
+ }
+
+ flow->response = response;
if (info->state == FLOW_ALLOCATED)
reg_flow_set_data(flow, pbuf);
@@ -1771,8 +1897,7 @@ int reg_respond_alloc(struct flow_info * info,
return -1;
}
-int reg_prepare_flow_accept(struct flow_info * info,
- buffer_t * pbuf)
+int reg_prepare_flow_accept(struct flow_info * info)
{
struct reg_flow * flow;
int ret;
@@ -1790,7 +1915,7 @@ int reg_prepare_flow_accept(struct flow_info * info,
ret = reg_flow_update(flow, info);
- reg_flow_set_data(flow, pbuf);
+ pthread_cond_broadcast(&reg.cond);
pthread_mutex_unlock(&reg.mtx);
@@ -1824,8 +1949,6 @@ int reg_wait_flow_accepted(struct flow_info * info,
assert(flow != NULL);
assert(info->id == flow->info.id);
assert(info->n_pid == flow->info.n_pid);
- assert(info->state == flow->info.state);
- assert(flow->info.state == FLOW_ACCEPT_PENDING);
if (__reg_add_active_proc(info->n_pid) < 0) {
log_err("Failed to mark pid %d active.", info->n_pid);
@@ -1883,13 +2006,12 @@ int reg_wait_flow_accepted(struct flow_info * info,
return -1;
}
-int reg_wait_flow_accepting(enum hash_algo algo,
- const uint8_t * hash,
+int reg_wait_flow_accepting(const char * name,
const struct timespec * abstime)
{
int ret;
- assert(hash != NULL);
+ assert(name != NULL);
assert(abstime != NULL);
pthread_mutex_lock(&reg.mtx);
@@ -1897,7 +2019,7 @@ int reg_wait_flow_accepting(enum hash_algo algo,
pthread_cleanup_push(__cleanup_mutex_unlock, &reg.mtx);
while (true) {
- ret = __reg_get_pending_flow_id_for_hash(algo, hash);
+ ret = __reg_get_pending_flow_id(name);
if (ret != -EAGAIN)
break;
@@ -1915,7 +2037,6 @@ int reg_respond_accept(struct flow_info * info,
buffer_t * pbuf)
{
struct reg_flow * flow;
- buffer_t temp;
assert(info != NULL);
assert(info->state == FLOW_ALLOCATED);
@@ -1933,11 +2054,8 @@ int reg_respond_accept(struct flow_info * info,
info->n_pid = flow->info.n_pid;
- if (info->qs.cypher_s > 0) {
- reg_flow_get_data(flow, &temp);
- reg_flow_set_data(flow, pbuf);
- *pbuf = temp;
- }
+ reg_flow_set_data(flow, pbuf);
+ clrbuf(pbuf);
if (reg_flow_update(flow, info) < 0) {
log_err("Failed to create flow structs.");
@@ -1970,12 +2088,14 @@ void reg_dealloc_flow(struct flow_info * info)
assert(flow != NULL);
assert(flow->data.data == NULL);
assert(flow->data.len == 0);
-
assert(flow->info.state == FLOW_ALLOCATED);
+
flow->info.state = FLOW_DEALLOC_PENDING;
info->state = FLOW_DEALLOC_PENDING;
info->n_1_pid = flow->info.n_1_pid;
+ memset(flow->name, 0, sizeof(flow->name));
+
reg_flow_update(flow, info);
pthread_mutex_unlock(&reg.mtx);
@@ -2043,7 +2163,7 @@ int reg_wait_ipcp_boot(struct ipcp_info * info,
int ret;
bool stop = false;
- assert(info->state == IPCP_BOOT);
+ assert(info->state == IPCP_INIT);
pthread_mutex_lock(&reg.mtx);
@@ -2063,16 +2183,18 @@ int reg_wait_ipcp_boot(struct ipcp_info * info,
ret = -1;
stop = true;
break;
+ case IPCP_BOOT:
+ /* FALLTHRU*/
case IPCP_OPERATIONAL:
ret = 0;
stop = true;
break;
- case IPCP_BOOT:
+ case IPCP_INIT:
ret = -__timedwait(&reg.cond, &reg.mtx, abstime);
break;
default:
assert(false);
- continue; /* Shut up static analyzer. */
+ break; /* Shut up static analyzer. */
}
ipcp = __reg_get_ipcp(info->pid);
diff --git a/src/irmd/reg/reg.h b/src/irmd/reg/reg.h
index 17dfcc32..77264fde 100644
--- a/src/irmd/reg/reg.h
+++ b/src/irmd/reg/reg.h
@@ -31,6 +31,8 @@
#include <ouroboros/time.h>
#include <ouroboros/utils.h>
+#include "pool.h"
+
int reg_init(void);
void reg_clear(void);
@@ -50,6 +52,13 @@ int reg_destroy_proc(pid_t pid);
bool reg_has_proc(pid_t pid);
+bool reg_is_proc_privileged(pid_t pid);
+
+int reg_prepare_pool(uid_t uid,
+ gid_t gid);
+
+uid_t reg_get_proc_uid(pid_t pid);
+
void reg_kill_all_proc(int signal);
pid_t reg_get_dead_proc(void);
@@ -90,6 +99,16 @@ int reg_destroy_name(const char * name);
bool reg_has_name(const char * name);
+int reg_get_name_info(const char * name,
+ struct name_info * info);
+
+int reg_get_name_for_hash(char * buf,
+ enum hash_algo algo,
+ const uint8_t * hash);
+
+int reg_get_name_for_flow_id(char * buf,
+ int flow_id);
+
/* TODO don't rely on protobuf here */
int reg_list_names(name_info_msg_t *** names);
@@ -99,9 +118,8 @@ int reg_destroy_prog(const char * name);
bool reg_has_prog(const char * name);
-int reg_get_exec(enum hash_algo algo,
- const uint8_t * hash,
- char *** exec);
+int reg_get_exec(const char * name,
+ char *** exec);
int reg_bind_prog(const char * name,
char ** exec,
@@ -117,17 +135,16 @@ int reg_wait_flow_allocated(struct flow_info * info,
const struct timespec * abstime);
int reg_respond_alloc(struct flow_info * info,
- buffer_t * pbuf);
+ buffer_t * pbuf,
+ int response);
-int reg_prepare_flow_accept(struct flow_info * info,
- buffer_t * pbuf);
+int reg_prepare_flow_accept(struct flow_info * info);
int reg_wait_flow_accepted(struct flow_info * info,
buffer_t * pbuf,
const struct timespec * abstime);
-int reg_wait_flow_accepting(enum hash_algo algo,
- const uint8_t * hash,
+int reg_wait_flow_accepting(const char * name,
const struct timespec * abstime);
int reg_respond_accept(struct flow_info * info,
diff --git a/src/irmd/reg/tests/CMakeLists.txt b/src/irmd/reg/tests/CMakeLists.txt
index bc1354ed..e8521545 100644
--- a/src/irmd/reg/tests/CMakeLists.txt
+++ b/src/irmd/reg/tests/CMakeLists.txt
@@ -1,7 +1,9 @@
-get_filename_component(tmp ".." ABSOLUTE)
-get_filename_component(src_folder "${tmp}" NAME)
+get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
-create_test_sourcelist(${src_folder}_tests test_suite.c
+compute_test_prefix()
+
+create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
# Add new tests here
flow_test.c
ipcp_test.c
@@ -11,19 +13,21 @@ create_test_sourcelist(${src_folder}_tests test_suite.c
reg_test.c
)
-add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests})
-target_link_libraries(${src_folder}_test ouroboros-common)
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests})
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(${src_folder}_test -DCONFIG_OUROBOROS_DEBUG)
-endif ()
+target_include_directories(${PARENT_DIR}_test PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src/irmd
+ ${CMAKE_BINARY_DIR}/src/irmd
+)
-add_dependencies(check ${src_folder}_test)
+disable_test_logging_for_target(${PARENT_DIR}_test)
+target_link_libraries(${PARENT_DIR}_test PRIVATE ouroboros-common)
+ouroboros_target_debug_definitions(${PARENT_DIR}_test)
-set(tests_to_run ${${src_folder}_tests})
-remove(tests_to_run test_suite.c)
+add_dependencies(build_tests ${PARENT_DIR}_test)
-foreach(test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(irmd/reg/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name})
-endforeach(test)
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
diff --git a/src/irmd/reg/tests/flow_test.c b/src/irmd/reg/tests/flow_test.c
index f9d23fd1..2066c811 100644
--- a/src/irmd/reg/tests/flow_test.c
+++ b/src/irmd/reg/tests/flow_test.c
@@ -22,13 +22,13 @@
#include "../flow.c"
-#include <ouroboros/test.h>
+#include <test/test.h>
#include <string.h>
#define TEST_DATA "testpiggybackdata"
-static int test_reg_flow_create(void)
+static int test_reg_flow_create_destroy(void)
{
struct reg_flow * f;
@@ -51,10 +51,10 @@ static int test_reg_flow_create(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_flow_create_no_id(void) {
@@ -67,7 +67,7 @@ static int test_reg_flow_create_no_id(void) {
reg_flow_create(&info); /* assert fail */
- return 0;
+ return TEST_RC_SUCCESS;
}
static int test_reg_flow_create_no_pid(void) {
@@ -80,7 +80,7 @@ static int test_reg_flow_create_no_pid(void) {
reg_flow_create(&info); /* assert fail */
- return 0;
+ return TEST_RC_SUCCESS;
}
static int test_reg_flow_create_has_n_1_pid(void) {
@@ -94,7 +94,7 @@ static int test_reg_flow_create_has_n_1_pid(void) {
reg_flow_create(&info); /* assert fail */
- return 0;
+ return TEST_RC_SUCCESS;
}
static int test_reg_flow_create_wrong_state(void) {
@@ -108,7 +108,7 @@ static int test_reg_flow_create_wrong_state(void) {
reg_flow_create(&info); /* assert fail */
- return 0;
+ return TEST_RC_SUCCESS;
}
static int test_reg_flow_create_has_mpl(void) {
@@ -123,7 +123,7 @@ static int test_reg_flow_create_has_mpl(void) {
reg_flow_create(&info); /* assert fail */
- return 0;
+ return TEST_RC_SUCCESS;
}
static int test_reg_flow_update(void)
@@ -163,10 +163,10 @@ static int test_reg_flow_update(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_flow_update_wrong_id(void)
@@ -199,10 +199,10 @@ static int test_reg_flow_update_wrong_id(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_flow_assert_fails(void)
@@ -210,15 +210,10 @@ static int test_reg_flow_assert_fails(void)
int ret = 0;
ret |= test_assert_fail(test_reg_flow_create_no_id);
-
ret |= test_assert_fail(test_reg_flow_create_no_pid);
-
ret |= test_assert_fail(test_reg_flow_create_has_n_1_pid);
-
ret |= test_assert_fail(test_reg_flow_create_wrong_state);
-
ret |= test_assert_fail(test_reg_flow_create_has_mpl);
-
ret |= test_assert_fail(test_reg_flow_update_wrong_id);
return ret;
@@ -237,7 +232,7 @@ static int test_flow_data(void)
char * data;
buffer_t buf;
- buffer_t rcv = {NULL, 0};
+ buffer_t rcv = {0, NULL};
TEST_START();
@@ -267,11 +262,11 @@ static int test_flow_data(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
free(data);
TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
int flow_test(int argc,
@@ -282,12 +277,9 @@ int flow_test(int argc,
(void) argc;
(void) argv;
- ret |= test_reg_flow_create();
-
+ ret |= test_reg_flow_create_destroy();
ret |= test_reg_flow_update();
-
ret |= test_reg_flow_assert_fails();
-
ret |= test_flow_data();
return ret;
diff --git a/src/irmd/reg/tests/ipcp_test.c b/src/irmd/reg/tests/ipcp_test.c
index fb8ba71b..6ab6443d 100644
--- a/src/irmd/reg/tests/ipcp_test.c
+++ b/src/irmd/reg/tests/ipcp_test.c
@@ -20,7 +20,7 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#include <ouroboros/test.h>
+#include <test/test.h>
#include "../ipcp.c"
@@ -31,7 +31,7 @@ static int test_reg_ipcp_create(void)
struct reg_ipcp * ipcp;
struct ipcp_info info = {
.pid = TEST_PID,
- .state = IPCP_BOOT
+ .state = IPCP_INIT
};
struct layer_info layer = {
.name = "testlayer",
@@ -51,7 +51,7 @@ static int test_reg_ipcp_create(void)
goto fail;
}
- ipcp->info.state = IPCP_OPERATIONAL;
+ ipcp->info.state = IPCP_BOOT;
reg_ipcp_set_layer(ipcp, &layer);
@@ -60,11 +60,6 @@ static int test_reg_ipcp_create(void)
goto fail;
}
- if (ipcp->info.state != IPCP_OPERATIONAL) {
- printf("IPCP state was not set.\n");
- goto fail;
- }
-
reg_ipcp_destroy(ipcp);
TEST_SUCCESS();
diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c
index 48f132e9..5b42875e 100644
--- a/src/irmd/reg/tests/name_test.c
+++ b/src/irmd/reg/tests/name_test.c
@@ -20,8 +20,11 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
+
#include "../name.c"
+#include <test/test.h>
+
#define TEST_PID 65534
#define TEST_PROG "/usr/bin/testprog"
#define TEST_NAME "testservicename"
@@ -34,6 +37,8 @@ static int test_reg_name_create(void)
.pol_lb = LB_RR,
};
+ TEST_START();
+
n = reg_name_create(&info);
if (n == NULL) {
printf("Failed to create name %s.\n", info.name);
@@ -42,9 +47,12 @@ static int test_reg_name_create(void)
reg_name_destroy(n);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_reg_name_add_proc(void)
@@ -55,6 +63,8 @@ static int test_reg_name_add_proc(void)
.pol_lb = LB_RR,
};
+ TEST_START();
+
n = reg_name_create(&info);
if (n == NULL) {
printf("Failed to create name %s.\n", info.name);
@@ -66,8 +76,8 @@ static int test_reg_name_add_proc(void)
goto fail;
}
- if (n->n_procs != 1) {
- printf("n_procs not updated.\n");
+ if (n->procs.len != 1) {
+ printf("Proc not added to list.\n");
goto fail;
}
@@ -78,16 +88,19 @@ static int test_reg_name_add_proc(void)
reg_name_del_proc(n, TEST_PID);
- if (n->n_procs != 0) {
- printf("n_procs not updated.\n");
+ if (n->procs.len != 0) {
+ printf("Proc not removed from list.\n");
goto fail;
}
reg_name_destroy(n);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_reg_name_add_prog(void)
@@ -100,6 +113,8 @@ static int test_reg_name_add_prog(void)
char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL};
+ TEST_START();
+
n = reg_name_create(&info);
if (n == NULL) {
printf("Failed to create name %s.\n", info.name);
@@ -111,8 +126,8 @@ static int test_reg_name_add_prog(void)
goto fail;
}
- if (n->n_progs != 1) {
- printf("n_progs not updated.\n");
+ if (n->progs.len != 1) {
+ printf("Prog not added to list.\n");
goto fail;
}
@@ -123,16 +138,19 @@ static int test_reg_name_add_prog(void)
reg_name_del_prog(n, TEST_PROG);
- if (n->n_progs != 0) {
- printf("n_progs not updated.\n");
+ if (n->progs.len != 0) {
+ printf("Prog not removed from list.\n");
goto fail;
}
reg_name_destroy(n);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_reg_name_add_active(enum pol_balance lb)
@@ -144,6 +162,8 @@ static int test_reg_name_add_active(enum pol_balance lb)
.pol_lb = lb,
};
+ TEST_START();
+
n = reg_name_create(&info);
if (n == NULL) {
printf("Failed to create name %s.\n", info.name);
@@ -175,8 +195,8 @@ static int test_reg_name_add_active(enum pol_balance lb)
goto fail;
}
- if (n->n_active != 1) {
- printf("n_active not updated.\n");
+ if (n->active.len != 1) {
+ printf("Active list not updated.\n");
goto fail;
}
@@ -206,13 +226,13 @@ static int test_reg_name_add_active(enum pol_balance lb)
goto fail;
}
- if (n->n_procs != 3) {
- printf("n_procs not updated.\n");
+ if (n->procs.len != 3) {
+ printf("Procs list not updated.\n");
goto fail;
}
- if (n->n_active != 4) {
- printf("n_active not updated.\n");
+ if (n->active.len != 4) {
+ printf("Active list not updated.\n");
goto fail;
}
@@ -243,41 +263,39 @@ static int test_reg_name_add_active(enum pol_balance lb)
reg_name_del_proc(n, TEST_PID);
- if (n->n_procs != 0) {
- printf("n_procs not updated.\n");
+ if (n->procs.len != 0) {
+ printf("Procs list not cleared.\n");
goto fail;
}
- if (n->n_active != 0) {
- printf("n_active not updated.\n");
+ if (n->active.len != 0) {
+ printf("Active list not cleared.\n");
goto fail;
}
reg_name_destroy(n);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
-
int name_test(int argc,
char ** argv)
{
- int res = 0;
+ int rc = 0;
(void) argc;
(void) argv;
- res |= test_reg_name_create();
-
- res |= test_reg_name_add_proc();
-
- res |= test_reg_name_add_prog();
-
- res |= test_reg_name_add_active(LB_RR);
-
- res |= test_reg_name_add_active(LB_SPILL);
+ rc |= test_reg_name_create();
+ rc |= test_reg_name_add_proc();
+ rc |= test_reg_name_add_prog();
+ rc |= test_reg_name_add_active(LB_RR);
+ rc |= test_reg_name_add_active(LB_SPILL);
- return res;
+ return rc;
}
diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c
index 5c9dd865..c4e689f0 100644
--- a/src/irmd/reg/tests/proc_test.c
+++ b/src/irmd/reg/tests/proc_test.c
@@ -22,16 +22,24 @@
#include "../proc.c"
+#include <test/test.h>
+
#define TEST_PID 65534
#define TEST_PROG "usr/bin/testprog"
-static int test_reg_proc_create(void)
+#define TEST_PROC { \
+ .pid = TEST_PID, \
+ .prog = TEST_PROG, \
+ .uid = getuid(), \
+ .gid = getgid() \
+}
+
+static int test_reg_proc_create_destroy(void)
{
struct reg_proc * proc;
- struct proc_info info = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info info = TEST_PROC;
+
+ TEST_START();
proc = reg_proc_create(&info);
if (proc == NULL) {
@@ -41,21 +49,23 @@ static int test_reg_proc_create(void)
reg_proc_destroy(proc);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_reg_proc_add_name(void)
{
struct reg_proc * proc;
- struct proc_info info = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info info = TEST_PROC;
char * name = "testname";
+ TEST_START();
+
proc = reg_proc_create(&info);
if (proc == NULL) {
printf("Failed to create proc.\n");
@@ -86,9 +96,12 @@ static int test_reg_proc_add_name(void)
reg_proc_destroy(proc);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
int proc_test(int argc,
@@ -99,8 +112,7 @@ int proc_test(int argc,
(void) argc;
(void) argv;
- res |= test_reg_proc_create();
-
+ res |= test_reg_proc_create_destroy();
res |= test_reg_proc_add_name();
return res;
diff --git a/src/irmd/reg/tests/prog_test.c b/src/irmd/reg/tests/prog_test.c
index 5e6931d8..3900e7d7 100644
--- a/src/irmd/reg/tests/prog_test.c
+++ b/src/irmd/reg/tests/prog_test.c
@@ -22,8 +22,9 @@
#include "../prog.c"
-#define TEST_PROG "usr/bin/testprog"
+#include <test/test.h>
+#define TEST_PROG "usr/bin/testprog"
static int test_reg_prog_create(void)
{
@@ -32,6 +33,8 @@ static int test_reg_prog_create(void)
.name = TEST_PROG
};
+ TEST_START();
+
prog = reg_prog_create(&info);
if (prog == NULL) {
printf("Failed to create prog.\n");
@@ -40,9 +43,12 @@ static int test_reg_prog_create(void)
reg_prog_destroy(prog);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_reg_prog_add_name(void)
@@ -54,6 +60,8 @@ static int test_reg_prog_add_name(void)
char * name = "testname";
+ TEST_START();
+
prog = reg_prog_create(&info);
if (prog == NULL) {
printf("Failed to create prog.\n");
@@ -84,9 +92,12 @@ static int test_reg_prog_add_name(void)
reg_prog_destroy(prog);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
- return -1;
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
int prog_test(int argc,
@@ -98,7 +109,6 @@ int prog_test(int argc,
(void) argv;
ret |= test_reg_prog_create();
-
ret |= test_reg_prog_add_name();
return ret;
diff --git a/src/irmd/reg/tests/reg_test.c b/src/irmd/reg/tests/reg_test.c
index c341c297..f7a4de8e 100644
--- a/src/irmd/reg/tests/reg_test.c
+++ b/src/irmd/reg/tests/reg_test.c
@@ -21,9 +21,11 @@
*/
+#include "../pool.c"
+#undef OUROBOROS_PREFIX
#include "../reg.c"
-#include <ouroboros/test.h>
+#include <test/test.h>
#define TEST_PID 3666
#define TEST_N_1_PID 3999
@@ -35,8 +37,14 @@
#define TEST_DATA "testpbufdata"
#define TEST_DATA2 "testpbufdata2"
#define TEST_LAYER "testlayer"
+#define TEST_PROC_INFO { \
+ .pid = TEST_PID, \
+ .prog = TEST_PROG, \
+ .uid = 0, \
+ .gid = 0 \
+}
#define REG_TEST_FAIL() \
- do { TEST_FAIL(); memset(&reg, 0, sizeof(reg)); } while(0)
+ do { TEST_FAIL(); reg_clear(); return TEST_RC_FAIL;} while(0)
static int test_reg_init(void)
{
@@ -51,10 +59,10 @@ static int test_reg_init(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_create_flow(void)
@@ -105,18 +113,17 @@ static int test_reg_create_flow(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_allocate_flow_timeout(void)
{
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_MS(1);
- buffer_t pbuf;
- buffer_t rbuf = {NULL, 0};
+ buffer_t rbuf = BUF_INIT;
struct flow_info info = {
.n_pid = TEST_PID,
@@ -125,14 +132,6 @@ static int test_reg_allocate_flow_timeout(void)
TEST_START();
- pbuf.data = (uint8_t *) strdup(TEST_DATA);;
- if (pbuf.data == NULL) {
- printf("Failed to strdup data.\n");
- goto fail;
- }
-
- pbuf.len = strlen((char *) pbuf.data) + 1;
-
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
@@ -147,7 +146,7 @@ static int test_reg_allocate_flow_timeout(void)
goto fail;
}
- if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ if (reg_prepare_flow_accept(&info) < 0) {
printf("Failed to prepare flow for accept.\n");
goto fail;
}
@@ -162,12 +161,6 @@ static int test_reg_allocate_flow_timeout(void)
goto fail;
}
- if (pbuf.data == NULL) {
- printf("Flow data was updated on timeout.");
- goto fail;
- }
-
- freebuf(pbuf);
reg_destroy_flow(info.id);
if (reg.n_flows != 0) {
@@ -179,16 +172,19 @@ static int test_reg_allocate_flow_timeout(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static void * test_flow_respond_alloc(void * o)
{
struct flow_info * info = (struct flow_info *) o;
- buffer_t pbuf = {NULL, 0};
+ buffer_t pbuf = BUF_INIT;
+ int response;
+
+ response = (info->state == FLOW_ALLOCATED) ? 0 : -1;
if (info->state == FLOW_ALLOCATED) {
pbuf.data = (uint8_t *) strdup(TEST_DATA2);
@@ -199,7 +195,7 @@ static void * test_flow_respond_alloc(void * o)
pbuf.len = strlen((char *) pbuf.data) + 1;
}
- reg_respond_alloc(info, &pbuf);
+ reg_respond_alloc(info, &pbuf, response);
return (void *) 0;
fail:
@@ -220,13 +216,6 @@ static void * test_flow_respond_accept(void * o)
reg_respond_accept(info, &pbuf);
- if (info->qs.cypher_s == 0) {
- freebuf(pbuf);
- } else if (strcmp((char *) pbuf.data, TEST_DATA) != 0) {
- printf("Data was not passed correctly.\n");
- goto fail;
- }
-
return (void *) 0;
fail:
return (void *) -1;
@@ -237,8 +226,7 @@ static int test_reg_accept_flow_success(void)
pthread_t thr;
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_S(1);
- buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)};
- buffer_t rbuf = {NULL, 0};
+ buffer_t rbuf = BUF_INIT;
struct flow_info info = {
.n_pid = TEST_PID,
@@ -247,7 +235,7 @@ static int test_reg_accept_flow_success(void)
struct flow_info n_1_info = {
.n_1_pid = TEST_N_1_PID,
- .qs = qos_data_crypt,
+ .qs = qos_data,
.state = FLOW_ALLOCATED /* RESPONSE SUCCESS */
};
@@ -267,7 +255,7 @@ static int test_reg_accept_flow_success(void)
goto fail;
}
- if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ if (reg_prepare_flow_accept(&info) < 0) {
printf("Failed to prepare flow for accept.\n");
goto fail;
}
@@ -277,8 +265,11 @@ static int test_reg_accept_flow_success(void)
pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info);
- if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) {
+ if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0) {
printf("Flow allocation failed.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_flow(info.id);
+ reg_fini();
goto fail;
}
@@ -321,10 +312,10 @@ static int test_reg_accept_flow_success(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_accept_flow_success_no_crypt(void)
@@ -332,8 +323,7 @@ static int test_reg_accept_flow_success_no_crypt(void)
pthread_t thr;
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_S(1);
- buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)};
- buffer_t rbuf = {NULL, 0};
+ buffer_t rbuf = BUF_INIT;
struct flow_info info = {
.n_pid = TEST_PID,
@@ -362,7 +352,7 @@ static int test_reg_accept_flow_success_no_crypt(void)
goto fail;
}
- if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ if (reg_prepare_flow_accept(&info) < 0) {
printf("Failed to prepare flow for accept.\n");
goto fail;
}
@@ -374,6 +364,9 @@ static int test_reg_accept_flow_success_no_crypt(void)
if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) {
printf("Flow allocation failed.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_flow(info.id);
+ reg_fini();
goto fail;
}
@@ -389,10 +382,7 @@ static int test_reg_accept_flow_success_no_crypt(void)
goto fail;
}
- if (strcmp((char *) rbuf.data, TEST_DATA) != 0) {
- printf("Data was updated.\n");
- goto fail;
- }
+ freebuf(rbuf);
n_1_info.state = FLOW_DEALLOCATED;
@@ -416,16 +406,16 @@ static int test_reg_accept_flow_success_no_crypt(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_allocate_flow_fail(void)
{
- buffer_t buf = {NULL, 0};
+ buffer_t buf = BUF_INIT;
pthread_t thr;
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_S(1);
@@ -470,6 +460,9 @@ static int test_reg_allocate_flow_fail(void)
if (reg_wait_flow_allocated(&info, &buf, &abstime) == 0 ) {
printf("Flow allocation succeeded.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_flow(info.id);
+ reg_fini();
goto fail;
}
@@ -486,26 +479,22 @@ static int test_reg_allocate_flow_fail(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_flow(void) {
- int ret = 0;
-
- ret |= test_reg_create_flow();
+ int rc = 0;
- ret |= test_reg_allocate_flow_timeout();
+ rc |= test_reg_create_flow();
+ rc |= test_reg_allocate_flow_timeout();
+ rc |= test_reg_accept_flow_success();
+ rc |= test_reg_accept_flow_success_no_crypt();
+ rc |= test_reg_allocate_flow_fail();
- ret |= test_reg_accept_flow_success();
-
- ret |= test_reg_accept_flow_success_no_crypt();
-
- ret |= test_reg_allocate_flow_fail();
-
- return ret;
+ return rc;
}
static int test_reg_create_ipcp(void)
@@ -513,7 +502,7 @@ static int test_reg_create_ipcp(void)
struct ipcp_info info = {
.name = TEST_IPCP,
.pid = TEST_PID,
- .state = IPCP_BOOT /* set by spawn_ipcp */
+ .state = IPCP_INIT /* set by spawn_ipcp */
};
TEST_START();
@@ -552,10 +541,130 @@ static int test_reg_create_ipcp(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
+ fail:
+ REG_TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_reg_list_ipcps(void)
+{
+ ipcp_list_msg_t ** ipcps;
+ int i;
+ ssize_t len;
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < 10; i++) {
+ struct ipcp_info info = {
+ .pid = TEST_PID + i,
+ .state = IPCP_INIT /* set by spawn_ipcp */
+ };
+
+ sprintf(info.name, "%s%d", TEST_IPCP, i);
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp %d.\n", i);
+ goto fail;
+ }
+ }
+
+ len = reg_list_ipcps(&ipcps);
+ if (len < 0) {
+ printf("Failed to list ipcps.\n");
+ goto fail;
+ }
+
+ if (len != 10) {
+ printf("Failed to list all ipcps.\n");
+ goto fail;
+ }
+
+ while (len-- > 0)
+ ipcp_list_msg__free_unpacked(ipcps[len], NULL);
+ free(ipcps);
+
+ for (i = 0; i < 10; i++)
+ reg_destroy_proc(TEST_PID + i);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
+}
+
+static int test_insert_ipcps(void)
+{
+ ipcp_list_msg_t ** ipcps;
+ struct ipcp_info info;
+ size_t i;
+ size_t len;
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < 100; i++) {
+ sprintf(info.name, "%s-%zd", TEST_IPCP, i);
+ info.pid = TEST_PID + rand() % 10000;
+ info.type = rand() % IPCP_INVALID;
+ info.state = IPCP_INIT; /* set by spawn_ipcp */
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp %s.\n", info.name);
+ goto fail;
+ }
+ }
+
+ len = reg_list_ipcps(&ipcps);
+ if (len != 100) {
+ printf("Failed to list all ipcps.\n");
+ goto fail;
+ }
+
+ for (i = 1; i < len; i++) {
+ if (ipcps[i]->type < ipcps[i - 1]->type) {
+ printf("IPCPS not sorted by type.\n");
+ goto fail;
+ }
+
+ if (ipcps[i]->type != ipcps[i - 1]->type)
+ continue;
+
+ /* allow occasional duplicate PID in test */
+ if (ipcps[i]->pid < ipcps[i - 1]->pid) {
+ printf("IPCPS not sorted by pid.\n");
+ goto fail;
+ }
+ }
+
+ while (len-- > 0)
+ ipcp_list_msg__free_unpacked(ipcps[len], NULL);
+ free(ipcps);
+
+ reg_clear();
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+fail:
+ REG_TEST_FAIL();
+ return TEST_RC_FAIL;
}
static int test_set_layer(void)
@@ -564,7 +673,7 @@ static int test_set_layer(void)
struct ipcp_info info = {
.name = TEST_IPCP,
.pid = TEST_PID,
- .state = IPCP_BOOT /* set by spawn_ipcp */
+ .state = IPCP_INIT /* set by spawn_ipcp */
};
struct layer_info layer = {
.name = TEST_LAYER,
@@ -588,8 +697,9 @@ static int test_set_layer(void)
}
ipcp = __reg_get_ipcp(info.pid);
- ipcp->info.state = IPCP_OPERATIONAL;
- info.state = IPCP_ENROLLED;
+
+ ipcp->info.state = IPCP_BOOT;
+ info.state = IPCP_BOOT;
reg_set_layer_for_ipcp(&info, &layer);
@@ -614,21 +724,22 @@ static int test_set_layer(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_ipcp(void)
{
- int ret = 0;
+ int rc = 0;
- ret |= test_reg_create_ipcp();
+ rc |= test_reg_create_ipcp();
+ rc |= test_reg_list_ipcps();
+ rc |= test_insert_ipcps();
+ rc |= test_set_layer();
- ret |= test_set_layer();
-
- return ret;
+ return rc;
}
static int test_reg_create_name(void)
@@ -674,27 +785,82 @@ static int test_reg_create_name(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
+ fail:
+ REG_TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_reg_list_names(void)
+{
+ name_info_msg_t ** names;
+ int i;
+ ssize_t len;
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < 10; i++) {
+ struct name_info info = {
+ .pol_lb = LB_RR
+ };
+
+ sprintf(info.name, "%s%d", TEST_NAME, i);
+
+ if (reg_create_name(&info) < 0) {
+ printf("Failed to create name %d.\n", i);
+ goto fail;
+ }
+ }
+
+ len = reg_list_names(&names);
+ if (len < 0) {
+ printf("Failed to list names.\n");
+ goto fail;
+ }
+
+ if (len != 10) {
+ printf("Failed to list all names.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < len; i++)
+ name_info_msg__free_unpacked(names[i], NULL);
+ free(names);
+
+ for (i = 0; i < 10; i++) {
+ char name[NAME_MAX];
+ sprintf(name, "%s%d", TEST_NAME, i);
+ reg_destroy_name(name);
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_name(void)
{
- int ret = 0;
+ int rc = 0;
- ret |= test_reg_create_name();
+ rc |= test_reg_create_name();
+ rc |= test_reg_list_names();
- return ret;
+ return rc;
}
static int test_reg_create_proc(void)
{
- struct proc_info info = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info info = TEST_PROC_INFO;
TEST_START();
@@ -732,19 +898,19 @@ static int test_reg_create_proc(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_proc(void)
{
- int ret = 0;
+ int rc = 0;
- ret |= test_reg_create_proc();
+ rc |= test_reg_create_proc();
- return ret;
+ return rc;
}
static int test_reg_spawned(void)
@@ -785,10 +951,10 @@ static int test_reg_spawned(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_create_prog(void)
@@ -833,27 +999,24 @@ static int test_reg_create_prog(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_reg_prog(void)
{
- int ret = 0;
+ int rc = 0;
- ret |= test_reg_create_prog();
+ rc |= test_reg_create_prog();
- return ret;
+ return rc;
}
static int test_bind_proc(void)
{
- struct proc_info pinfo = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info pinfo = TEST_PROC_INFO;
struct name_info ninfo = {
.name = TEST_NAME,
@@ -900,10 +1063,10 @@ static int test_bind_proc(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_bind_prog(void)
@@ -989,10 +1152,10 @@ static int test_bind_prog(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_inherit_prog(void)
@@ -1006,10 +1169,7 @@ static int test_inherit_prog(void)
.name = TEST_PROG
};
- struct proc_info procinfo = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info procinfo = TEST_PROC_INFO;
char * exec[] = { TEST_PROG, NULL};
@@ -1060,10 +1220,10 @@ static int test_inherit_prog(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_accepting_timeout(void)
@@ -1071,7 +1231,6 @@ static int test_wait_accepting_timeout(void)
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_MS(1);
int flow_id;
- uint8_t hash[64];
struct name_info ninfo = {
.name = TEST_NAME,
.pol_lb = LB_RR
@@ -1089,12 +1248,10 @@ static int test_wait_accepting_timeout(void)
goto fail;
}
- str_hash(HASH_SHA3_256, hash, ninfo.name);
-
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ flow_id = reg_wait_flow_accepting(ninfo.name, &abstime);
if (flow_id != -ETIMEDOUT) {
printf("Wait accept did not time out: %d.\n", flow_id);
goto fail;
@@ -1106,10 +1263,10 @@ static int test_wait_accepting_timeout(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_accepting_fail_name(void)
@@ -1117,7 +1274,6 @@ static int test_wait_accepting_fail_name(void)
struct timespec abstime;
struct timespec timeo = TIMESPEC_INIT_S(1);
int flow_id;
- uint8_t hash[64];
TEST_START();
@@ -1128,11 +1284,10 @@ static int test_wait_accepting_fail_name(void)
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- str_hash(HASH_SHA3_256, hash, "C0FF33");
- flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ flow_id = reg_wait_flow_accepting(TEST_NAME, &abstime);
if (flow_id != -ENAME) {
- printf("Wait accept did not fail on name: %d.\n", flow_id);
+ printf("Wait accept did not fail: %d.\n", flow_id);
goto fail;
}
@@ -1140,22 +1295,19 @@ static int test_wait_accepting_fail_name(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static void * test_call_flow_accept(void * o)
{
struct timespec abstime;
- struct timespec timeo = TIMESPEC_INIT_MS(1);
- buffer_t pbuf = {NULL, 0};
+ struct timespec timeo = TIMESPEC_INIT_MS(10);
+ buffer_t pbuf = BUF_INIT;
- struct proc_info pinfo = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info pinfo = TEST_PROC_INFO;
struct flow_info info = {
.n_pid = pinfo.pid,
@@ -1179,16 +1331,21 @@ static void * test_call_flow_accept(void * o)
info.state = FLOW_ACCEPT_PENDING;
+ reg_prepare_flow_accept(&info);
+
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- reg_prepare_flow_accept(&info, &pbuf);
-
if (reg_wait_flow_accepted(&info, &pbuf, &abstime) != -ETIMEDOUT) {
printf("Wait allocated did not timeout.\n");
goto fail;
}
+ if (reg_unbind_proc((char *) o, pinfo.pid) < 0) {
+ printf("Failed to unbind proc.\n");
+ goto fail;
+ }
+
reg_destroy_flow(info.id);
reg_destroy_proc(pinfo.pid);
@@ -1200,15 +1357,15 @@ static void * test_call_flow_accept(void * o)
static int test_wait_accepting_success(void)
{
struct timespec abstime;
- struct timespec timeo = TIMESPEC_INIT_S(1);
- int flow_id;
+ struct timespec timeo = TIMESPEC_INIT_S(10);
pthread_t thr;
- uint8_t hash[64];
+ int flow_id;
struct name_info ninfo = {
.name = TEST_NAME,
.pol_lb = LB_RR
};
+
TEST_START();
if (reg_init()) {
@@ -1226,11 +1383,12 @@ static int test_wait_accepting_success(void)
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- str_hash(HASH_SHA3_256, hash, ninfo.name);
-
- flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ flow_id = reg_wait_flow_accepting(ninfo.name, &abstime);
if (flow_id < 0) {
- printf("Wait accept did not return a flow id: %d.", flow_id);
+ printf("Wait accept did not return a flow id: %d.\n", flow_id);
+ pthread_join(thr, NULL);
+ reg_destroy_name(TEST_NAME);
+ reg_fini();
goto fail;
}
@@ -1242,23 +1400,21 @@ static int test_wait_accepting_success(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_accepting(void)
{
- int ret = 0;
-
- ret |= test_wait_accepting_timeout();
-
- ret |= test_wait_accepting_fail_name();
+ int rc = 0;
- ret |= test_wait_accepting_success();
+ rc |= test_wait_accepting_timeout();
+ rc |= test_wait_accepting_fail_name();
+ rc |= test_wait_accepting_success();
- return ret;
+ return rc;
}
static int test_wait_ipcp_boot_timeout(void)
@@ -1268,7 +1424,7 @@ static int test_wait_ipcp_boot_timeout(void)
struct ipcp_info info = {
.name = TEST_IPCP,
.pid = TEST_PID,
- .state = IPCP_BOOT /* set by spawn_ipcp */
+ .state = IPCP_INIT /* set by spawn_ipcp */
};
TEST_START();
@@ -1300,10 +1456,10 @@ static int test_wait_ipcp_boot_timeout(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static void * test_ipcp_respond(void * o)
@@ -1318,12 +1474,12 @@ static void * test_ipcp_respond(void * o)
static int test_wait_ipcp_boot_fail(void)
{
struct timespec abstime;
- struct timespec timeo = TIMESPEC_INIT_S(1);
+ struct timespec timeo = TIMESPEC_INIT_S(10);
pthread_t thr;
struct ipcp_info info = {
.name = TEST_IPCP,
.pid = TEST_PID,
- .state = IPCP_BOOT /* set by spawn_ipcp */
+ .state = IPCP_INIT /* set by spawn_ipcp */
};
struct ipcp_info resp_info = {
.name = TEST_IPCP,
@@ -1348,10 +1504,13 @@ static int test_wait_ipcp_boot_fail(void)
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- info.state = IPCP_BOOT;
+ info.state = IPCP_INIT;
if (reg_wait_ipcp_boot(&info, &abstime) == 0) {
printf("IPCP boot reported success.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_proc(info.pid);
+ reg_fini();
goto fail;
}
@@ -1371,21 +1530,21 @@ static int test_wait_ipcp_boot_fail(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_ipcp_boot_success(void)
{
pthread_t thr;
struct timespec abstime;
- struct timespec timeo = TIMESPEC_INIT_S(1);
+ struct timespec timeo = TIMESPEC_INIT_S(10);
struct ipcp_info info = {
.name = TEST_IPCP,
.pid = TEST_PID,
- .state = IPCP_BOOT /* set by spawn_ipcp */
+ .state = IPCP_INIT /* set by spawn_ipcp */
};
struct ipcp_info resp_info = {
.name = TEST_IPCP,
@@ -1410,10 +1569,13 @@ static int test_wait_ipcp_boot_success(void)
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- info.state = IPCP_BOOT;
+ info.state = IPCP_INIT;
if (reg_wait_ipcp_boot(&info, &abstime) < 0) {
printf("IPCP boot failed.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_proc(info.pid);
+ reg_fini();
goto fail;
}
@@ -1421,6 +1583,8 @@ static int test_wait_ipcp_boot_success(void)
if (info.state != IPCP_OPERATIONAL) {
printf("IPCP boot succeeded in non-operational state.\n");
+ reg_destroy_proc(info.pid);
+ reg_fini();
goto fail;
}
@@ -1433,23 +1597,21 @@ static int test_wait_ipcp_boot_success(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_ipcp_boot(void)
{
- int ret = 0;
-
- ret |= test_wait_ipcp_boot_timeout();
+ int rc = 0;
- ret |= test_wait_ipcp_boot_fail();
+ rc |= test_wait_ipcp_boot_timeout();
+ rc |= test_wait_ipcp_boot_fail();
+ rc |= test_wait_ipcp_boot_success();
- ret |= test_wait_ipcp_boot_success();
-
- return ret;
+ return rc;
}
static int test_wait_proc_timeout(void)
@@ -1477,10 +1639,10 @@ static int test_wait_proc_timeout(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static void * test_proc(void * o)
@@ -1495,12 +1657,9 @@ static void * test_proc(void * o)
static int test_wait_proc_success(void)
{
struct timespec abstime;
- struct timespec timeo = TIMESPEC_INIT_S(1);
+ struct timespec timeo = TIMESPEC_INIT_S(10);
pthread_t thr;
- struct proc_info info = {
- .pid = TEST_PID,
- .prog = TEST_PROG
- };
+ struct proc_info info = TEST_PROC_INFO;
TEST_START();
@@ -1516,6 +1675,9 @@ static int test_wait_proc_success(void)
if (reg_wait_proc(info.pid, &abstime) < 0) {
printf("Waiting for proc failed.\n");
+ pthread_join(thr, NULL);
+ reg_destroy_proc(info.pid);
+ reg_fini();
goto fail;
}
@@ -1527,57 +1689,43 @@ static int test_wait_proc_success(void)
TEST_SUCCESS();
- return 0;
+ return TEST_RC_SUCCESS;
fail:
REG_TEST_FAIL();
- return -1;
+ return TEST_RC_FAIL;
}
static int test_wait_proc(void)
{
- int ret = 0;
+ int rc = 0;
- ret |= test_wait_proc_timeout();
+ rc |= test_wait_proc_timeout();
+ rc |= test_wait_proc_success();
- ret |= test_wait_proc_success();
-
- return ret;
+ return rc;
}
-
int reg_test(int argc,
char ** argv)
{
- int ret = 0;
+ int rc = 0;
(void) argc;
(void) argv;
- ret |= test_reg_init();
-
- ret |= test_reg_flow();
-
- ret |= test_reg_ipcp();
-
- ret |= test_reg_name();
-
- ret |= test_reg_proc();
-
- ret |= test_reg_prog();
-
- ret |= test_reg_spawned();
-
- ret |= test_bind_proc();
-
- ret |= test_bind_prog();
-
- ret |= test_inherit_prog();
-
- ret |= test_wait_accepting();
-
- ret |= test_wait_ipcp_boot();
-
- ret |= test_wait_proc();
-
- return ret;
+ rc |= test_reg_init();
+ rc |= test_reg_flow();
+ rc |= test_reg_ipcp();
+ rc |= test_reg_name();
+ rc |= test_reg_proc();
+ rc |= test_reg_prog();
+ rc |= test_reg_spawned();
+ rc |= test_bind_proc();
+ rc |= test_bind_prog();
+ rc |= test_inherit_prog();
+ rc |= test_wait_accepting();
+ rc |= test_wait_ipcp_boot();
+ rc |= test_wait_proc();
+
+ return rc;
}
diff --git a/src/irmd/tests/CMakeLists.txt b/src/irmd/tests/CMakeLists.txt
deleted file mode 100644
index e005d194..00000000
--- a/src/irmd/tests/CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-get_filename_component(tmp ".." ABSOLUTE)
-get_filename_component(src_folder "${tmp}" NAME)
-
-create_test_sourcelist(${src_folder}_tests test_suite.c
- # Add new tests here
-)
-
-add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests})
-target_link_libraries(${src_folder}_test ouroboros-common)
-
-add_dependencies(check ${src_folder}_test)
-
-set(tests_to_run ${${src_folder}_tests})
-remove(tests_to_run test_suite.c)
-
-foreach(test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(irmd/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name})
-endforeach(test)
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index a6d7ac98..c4306b00 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -1,253 +1,18 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
+# Ouroboros libraries build configuration
+# Configuration options are in cmake/config/lib.cmake
protobuf_generate_c(MODEL_PROTO_SRCS MODEL_PROTO_HDRS
- pb/model.proto)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/model.proto")
protobuf_generate_c(IPCP_CONFIG_PROTO_SRCS IPCP_CONFIG_PROTO_HDRS
- pb/ipcp_config.proto)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/ipcp_config.proto")
protobuf_generate_c(ENROLL_PROTO_SRCS ENROLL_PROTO_HDRS
- pb/enroll.proto)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/enroll.proto")
protobuf_generate_c(CEP_PROTO_SRCS CEP_PROTO_HDRS
- pb/cep.proto)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/cep.proto")
protobuf_generate_c(IRM_PROTO_SRCS IRM_PROTO_HDRS
- pb/irm.proto)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/irm.proto")
protobuf_generate_c(IPCP_PROTO_SRCS IPCP_PROTO_HDRS
- pb/ipcp.proto)
-
-if (NOT APPLE)
- find_library(LIBRT_LIBRARIES rt)
- if (NOT LIBRT_LIBRARIES)
- message(FATAL_ERROR "Could not find librt")
- endif ()
-else ()
- set(LIBRT_LIBRARIES "")
-endif ()
-
-find_library(LIBPTHREAD_LIBRARIES pthread)
-if (NOT LIBPTHREAD_LIBRARIES)
- message(FATAL_ERROR "Could not find libpthread")
-endif ()
-
-include(CheckSymbolExists)
-list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L)
-list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__XSI_VISIBLE=500)
-list(APPEND CMAKE_REQUIRED_LIBRARIES pthread)
-check_symbol_exists(pthread_mutexattr_setrobust pthread.h HAVE_ROBUST_MUTEX)
-
-if (HAVE_ROBUST_MUTEX)
- set(DISABLE_ROBUST_MUTEXES FALSE CACHE BOOL "Disable robust mutex support")
- if (NOT DISABLE_ROBUST_MUTEXES)
- message(STATUS "Robust mutex support enabled")
- set(HAVE_ROBUST_MUTEX TRUE)
- else ()
- message(STATUS "Robust mutex support disabled by user")
- unset(HAVE_ROBUST_MUTEX)
- endif ()
-endif ()
-
-find_library(FUSE_LIBRARIES fuse QUIET)
-if (FUSE_LIBRARIES)
- #FIXME: Check for version >= 2.6
- set(DISABLE_FUSE FALSE CACHE BOOL "Disable FUSE support")
- if (NOT DISABLE_FUSE)
- message(STATUS "FUSE support enabled")
- set(FUSE_PREFIX "/tmp/ouroboros" CACHE STRING
- "Mountpoint for RIB filesystem")
- set(HAVE_FUSE TRUE CACHE INTERNAL "")
- else ()
- message(STATUS "FUSE support disabled by user")
- unset(HAVE_FUSE CACHE)
- endif ()
-else ()
- message(STATUS "Install FUSE version > 2.6 to enable RIB access")
-endif ()
-
-if (NOT HAVE_FUSE)
- set(FUSE_LIBRARIES "")
- set(FUSE_INCLUDE_DIR "")
-endif ()
-
-mark_as_advanced(FUSE_LIBRARIES)
-
-find_library(LIBGCRYPT_LIBRARIES gcrypt QUIET)
-if (LIBGCRYPT_LIBRARIES)
- find_path(LIBGCRYPT_INCLUDE_DIR gcrypt.h
- HINTS /usr/include /usr/local/include)
- if (LIBGCRYPT_INCLUDE_DIR)
- file(STRINGS ${LIBGCRYPT_INCLUDE_DIR}/gcrypt.h GCSTR
- REGEX "^#define GCRYPT_VERSION ")
- string(REGEX REPLACE "^#define GCRYPT_VERSION \"(.*)\".*$" "\\1"
- GCVER "${GCSTR}")
- if (NOT GCVER VERSION_LESS "1.7.0")
- set(DISABLE_LIBGCRYPT FALSE CACHE BOOL "Disable libgcrypt support")
- if (NOT DISABLE_LIBGCRYPT)
- message(STATUS "libgcrypt support enabled")
- set(HAVE_LIBGCRYPT TRUE CACHE INTERNAL "")
- else ()
- message(STATUS "libgcrypt support disabled by user")
- unset(HAVE_LIBGCRYPT CACHE)
- endif()
- else ()
- message(STATUS "Install version >= \"1.7.0\" to enable libgcrypt support "
- "(found version \"${GCVER}\")")
- endif()
- endif ()
-endif ()
-
-if (NOT HAVE_LIBGCRYPT)
- set(LIBGCRYPT_LIBRARIES "")
- set(LIBGCRYPT_INCLUDE_DIR "")
-endif ()
-
-find_package(OpenSSL QUIET)
-if (OPENSSL_FOUND)
- set(HAVE_OPENSSL_RNG TRUE)
- if (OPENSSL_VERSION VERSION_LESS "1.1.0")
- message(STATUS "Install version >= \"1.1.0\" to enable OpenSSL support "
- "(found version \"${OPENSSL_VERSION}\")")
- else ()
- set(DISABLE_OPENSSL FALSE CACHE BOOL "Disable OpenSSL support")
- if (NOT DISABLE_OPENSSL)
- message(STATUS "OpenSSL support enabled")
- set(HAVE_OPENSSL TRUE)
- else()
- message(STATUS "OpenSSL support disabled")
- unset(HAVE_OPENSSL)
- endif()
- endif ()
-endif ()
-
-if (NOT HAVE_OPENSSL_RNG)
- set(OPENSSL_INCLUDE_DIR "")
- set(OPENSSL_LIBRARIES "")
- set(OPENSSL_CRYPTO_LIBRARY "")
-endif ()
-
-if (APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
- set(SYS_RND_HDR "")
-else ()
- find_path(SYS_RND_HDR sys/random.h PATH /usr/include/ /usr/local/include/)
- if (SYS_RND_HDR)
- message(STATUS "Found sys/random.h in ${SYS_RND_HDR}")
- set(HAVE_SYS_RANDOM TRUE)
- else ()
- set(SYS_RND_HDR "")
- unset(HAVE_SYS_RANDOM)
- endif ()
-endif()
-
-if (NOT ((CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") OR APPLE OR
- HAVE_SYS_RANDOM OR HAVE_OPENSSL_RNG OR HAVE_LIBGCRYPT))
- message(FATAL_ERROR "No secure random generator found, "
- "please install libgcrypt (> 1.7.0) or OpenSSL")
-endif ()
-
-mark_as_advanced(LIBRT_LIBRARIES LIBPTHREAD_LIBRARIES
- LIBGCRYPT_LIBRARIES OPENSSL_LIBRARIES OPENSSL_CRYPTO_LIBRARY
- SYS_RND_INCLUDE_DIR LIBGCRYPT_INCLUDE_DIR SYS_RND_HDR)
-
-set(SHM_BUFFER_SIZE 16384 CACHE STRING
- "Number of blocks in packet buffer, must be a power of 2")
-set(SHM_RBUFF_SIZE 1024 CACHE STRING
- "Number of blocks in rbuff buffer, must be a power of 2")
-set(SYS_MAX_FLOWS 10240 CACHE STRING
- "Maximum number of total flows for this system")
-set(PROG_MAX_FLOWS 4096 CACHE STRING
- "Maximum number of flows in an application")
-set(PROG_RES_FDS 64 CACHE STRING
- "Number of reserved flow descriptors per application")
-set(PROG_MAX_FQUEUES 32 CACHE STRING
- "Maximum number of flow sets per application")
-set(DU_BUFF_HEADSPACE 256 CACHE STRING
- "Bytes of headspace to reserve for future headers")
-set(DU_BUFF_TAILSPACE 32 CACHE STRING
- "Bytes of tailspace to reserve for future tails")
-if (NOT APPLE)
- set(PTHREAD_COND_CLOCK "CLOCK_MONOTONIC" CACHE STRING
- "Clock to use for condition variable timing")
-else ()
- set(PTHREAD_COND_CLOCK "CLOCK_REALTIME" CACHE INTERNAL
- "Clock to use for condition variable timing")
-endif ()
-set(SOCKET_TIMEOUT 500 CACHE STRING
- "Default timeout for responses from IPCPs (ms)")
-set(SHM_PREFIX "ouroboros" CACHE STRING
- "String to prepend to POSIX shared memory filenames")
-set(SHM_RBUFF_PREFIX "/${SHM_PREFIX}.rbuff." CACHE INTERNAL
- "Prefix for rbuff POSIX shared memory filenames")
-set(SHM_LOCKFILE_NAME "/${SHM_PREFIX}.lockfile" CACHE INTERNAL
- "Filename for the POSIX shared memory lockfile")
-set(SHM_FLOW_SET_PREFIX "/${SHM_PREFIX}.set." CACHE INTERNAL
- "Prefix for the POSIX shared memory flow set")
-set(SHM_RDRB_NAME "/${SHM_PREFIX}.rdrb" CACHE INTERNAL
- "Name for the main POSIX shared memory buffer")
-set(SHM_RDRB_BLOCK_SIZE "sysconf(_SC_PAGESIZE)" CACHE STRING
- "Packet buffer block size, multiple of pagesize for performance")
-set(SHM_RDRB_MULTI_BLOCK TRUE CACHE BOOL
- "Packet buffer multiblock packet support")
-set(SHM_RBUFF_LOCKLESS FALSE CACHE BOOL
- "Enable shared memory lockless rbuff support")
-set(QOS_DISABLE_CRC TRUE CACHE BOOL
- "Ignores ber setting on all QoS cubes")
-set(DELTA_T_MPL 60 CACHE STRING
- "Maximum packet lifetime (s)")
-set(DELTA_T_ACK 10 CACHE STRING
- "Maximum time to acknowledge a packet (s)")
-set(DELTA_T_RTX 120 CACHE STRING
- "Maximum time to retransmit a packet (s)")
-set(FRCT_REORDER_QUEUE_SIZE 256 CACHE STRING
- "Size of the reordering queue, must be a power of 2")
-set(FRCT_START_WINDOW 64 CACHE STRING
- "Start window, must be a power of 2")
-set(FRCT_LINUX_RTT_ESTIMATOR TRUE CACHE BOOL
- "Use Linux RTT estimator formula instead of the TCP RFC formula")
-set(FRCT_RTO_MDEV_MULTIPLIER 2 CACHE STRING
- "Multiplier for deviation term in the RTO: RTO = sRTT + (mdev << X)")
-set(FRCT_RTO_INC_FACTOR 0 CACHE STRING
- "Divisor for RTO increase after timeout: RTO += RTX >> X, 0: Karn/Partridge")
-set(FRCT_RTO_MIN 250 CACHE STRING
- "Minimum Retransmission Timeout (RTO) for FRCT (us)")
-set(FRCT_TICK_TIME 5000 CACHE STRING
- "Tick time for FRCT activity (retransmission, acknowledgments) (us)")
-set(RXM_BUFFER_ON_HEAP FALSE CACHE BOOL
- "Store packets for retransmission on the heap instead of in packet buffer")
-set(RXM_BLOCKING TRUE CACHE BOOL
- "Use blocking writes for retransmission")
-set(RXM_MIN_RESOLUTION 20 CACHE STRING
- "Minimum retransmission delay (ns), as a power to 2")
-set(RXM_WHEEL_MULTIPLIER 4 CACHE STRING
- "Factor for retransmission wheel levels as a power to 2")
-set(RXM_WHEEL_LEVELS 3 CACHE STRING
- "Number of levels in the retransmission wheel")
-set(RXM_WHEEL_SLOTS_PER_LEVEL 256 CACHE STRING
- "Number of slots per level in the retransmission wheel, must be a power of 2")
-set(ACK_WHEEL_SLOTS 256 CACHE STRING
- "Number of slots in the acknowledgment wheel, must be a power of 2")
-set(ACK_WHEEL_RESOLUTION 18 CACHE STRING
- "Minimum acknowledgment delay (ns), as a power to 2")
-
-if (HAVE_FUSE)
- set(PROC_FLOW_STATS TRUE CACHE BOOL
- "Enable flow statistics tracking for application flows")
- if (PROC_FLOW_STATS)
- message(STATUS "Application flow statistics enabled")
- else ()
- message(STATUS "Application flow statistics disabled")
- endif ()
-endif ()
-
-set(SOURCE_FILES_DEV
- # Add source files here
- cep.c
- dev.c
- )
-
-set(SOURCE_FILES_IRM
- irm.c
-)
+ "${CMAKE_CURRENT_SOURCE_DIR}/pb/ipcp.proto")
set(SOURCE_FILES_COMMON
bitmap.c
@@ -267,60 +32,129 @@ set(SOURCE_FILES_COMMON
serdes-irm.c
serdes-oep.c
sha3.c
- shm_flow_set.c
- shm_rbuff.c
- shm_rdrbuff.c
+ ssm/flow_set.c
+ ssm/rbuff.c
+ ssm/pool.c
sockets.c
tpm.c
utils.c
)
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
- "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
+if(HAVE_OPENSSL)
+ list(APPEND SOURCE_FILES_COMMON crypt/openssl.c)
+endif()
-add_library(ouroboros-common SHARED ${SOURCE_FILES_COMMON} ${IRM_PROTO_SRCS}
- ${IPCP_PROTO_SRCS} ${IPCP_CONFIG_PROTO_SRCS} ${MODEL_PROTO_SRCS}
+add_library(ouroboros-common SHARED
+ ${SOURCE_FILES_COMMON}
+ ${IRM_PROTO_SRCS}
+ ${IPCP_PROTO_SRCS}
+ ${IPCP_CONFIG_PROTO_SRCS}
+ ${MODEL_PROTO_SRCS}
${ENROLL_PROTO_SRCS})
-add_library(ouroboros-dev SHARED ${SOURCE_FILES_DEV} ${CEP_PROTO_SRCS})
-
-add_library(ouroboros-irm SHARED ${SOURCE_FILES_IRM})
-
set_target_properties(ouroboros-common PROPERTIES
VERSION ${PACKAGE_VERSION}
SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR})
+
+ouroboros_target_debug_definitions(ouroboros-common)
+
+target_include_directories(ouroboros-common
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ PRIVATE
+ ${SYS_RND_HDR}
+ ${APPLE_INCLUDE_DIRS})
+
+target_link_libraries(ouroboros-common
+ PRIVATE
+ ${LIBRT_LIBRARIES}
+ Threads::Threads
+ PUBLIC
+ ProtobufC::ProtobufC)
+
+if(HAVE_OPENSSL)
+ target_link_libraries(ouroboros-common PUBLIC OpenSSL::Crypto)
+endif()
+
+if(HAVE_LIBGCRYPT)
+ target_link_libraries(ouroboros-common PUBLIC Gcrypt::Gcrypt)
+endif()
+
+if(HAVE_FUSE)
+ target_link_libraries(ouroboros-common PRIVATE Fuse::Fuse)
+endif()
+
+install(TARGETS ouroboros-common
+ EXPORT OuroborosTargets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+set(SOURCE_FILES_DEV
+ cep.c
+ dev.c
+)
+
+add_library(ouroboros-dev SHARED
+ ${SOURCE_FILES_DEV}
+ ${CEP_PROTO_SRCS})
+
set_target_properties(ouroboros-dev PROPERTIES
VERSION ${PACKAGE_VERSION}
SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR})
+
+ouroboros_target_debug_definitions(ouroboros-dev)
+
+target_include_directories(ouroboros-dev
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${SYS_RND_HDR})
+
+target_link_libraries(ouroboros-dev PUBLIC ouroboros-common)
+
+install(TARGETS ouroboros-dev
+ EXPORT OuroborosTargets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+add_library(ouroboros-irm SHARED irm.c)
+
set_target_properties(ouroboros-irm PROPERTIES
VERSION ${PACKAGE_VERSION}
SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR})
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ouroboros-common -DCONFIG_OUROBOROS_DEBUG)
- add_compile_flags(ouroboros-dev -DCONFIG_OUROBOROS_DEBUG)
- add_compile_flags(ouroboros-irm -DCONFIG_OUROBOROS_DEBUG)
-endif ()
-
-target_link_libraries(ouroboros-common ${LIBRT_LIBRARIES}
- ${LIBPTHREAD_LIBRARIES} ${PROTOBUF_C_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}
- ${LIBGCRYPT_LIBRARIES} ${FUSE_LIBRARIES})
+ouroboros_target_debug_definitions(ouroboros-irm)
-target_link_libraries(ouroboros-dev ouroboros-common)
-target_link_libraries(ouroboros-irm ouroboros-common)
+target_include_directories(ouroboros-irm
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${SYS_RND_HDR})
-install(TARGETS ouroboros-common LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
-install(TARGETS ouroboros-dev LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
-install(TARGETS ouroboros-irm LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+target_link_libraries(ouroboros-irm PUBLIC ouroboros-common)
-target_include_directories(ouroboros-common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
- ${SYS_RND_HDR} ${LIBGCRYPT_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
+install(TARGETS ouroboros-irm
+ EXPORT OuroborosTargets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
-target_include_directories(ouroboros-dev PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
- ${SYS_RND_HDR} ${LIBGCRYPT_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
-target_include_directories(ouroboros-irm PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
- ${SYS_RND_HDR} ${LIBGCRYPT_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ssm/ssm.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/ssm.h" @ONLY)
-add_subdirectory(tests)
+if(BUILD_TESTS)
+ add_subdirectory(tests)
+ add_subdirectory(ssm/tests)
+endif()
diff --git a/src/lib/config.h.in b/src/lib/config.h.in
index 604038b4..6065ac41 100644
--- a/src/lib/config.h.in
+++ b/src/lib/config.h.in
@@ -21,28 +21,26 @@
*/
#cmakedefine HAVE_SYS_RANDOM
+#cmakedefine HAVE_EXPLICIT_BZERO
#cmakedefine HAVE_LIBGCRYPT
#cmakedefine HAVE_OPENSSL
-
#ifdef HAVE_OPENSSL
+#cmakedefine HAVE_OPENSSL_PQC
#define HAVE_ENCRYPTION
+#define SECMEM_GUARD @SECMEM_GUARD@
#endif
+#define PROC_SECMEM_MAX @PROC_SECMEM_MAX@
+
+#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@
-#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@
+#cmakedefine QOS_DISABLE_CRC
+#cmakedefine HAVE_OPENSSL_RNG
-#cmakedefine SHM_RBUFF_LOCKLESS
-#cmakedefine SHM_RDRB_MULTI_BLOCK
-#cmakedefine QOS_DISABLE_CRC
-#cmakedefine HAVE_OPENSSL_RNG
+#define SHM_LOCKFILE_NAME "@SHM_LOCKFILE_NAME@"
+#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@
-#define SHM_RBUFF_PREFIX "@SHM_RBUFF_PREFIX@"
-#define SHM_LOCKFILE_NAME "@SHM_LOCKFILE_NAME@"
-#define SHM_FLOW_SET_PREFIX "@SHM_FLOW_SET_PREFIX@"
-#define SHM_RDRB_NAME "@SHM_RDRB_NAME@"
-#define SHM_RDRB_BLOCK_SIZE @SHM_RDRB_BLOCK_SIZE@
-#define SHM_BUFFER_SIZE @SHM_BUFFER_SIZE@
-#define SHM_RBUFF_SIZE @SHM_RBUFF_SIZE@
-#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@
+#define TPM_DEBUG_REPORT_INTERVAL @TPM_DEBUG_REPORT_INTERVAL@
+#define TPM_DEBUG_ABORT_TIMEOUT @TPM_DEBUG_ABORT_TIMEOUT@
#if defined(__linux__) || (defined(__MACH__) && !defined(__APPLE__))
/* Avoid a bug in robust mutex implementation of glibc 2.25 */
@@ -66,9 +64,6 @@
#define PROG_RES_FDS @PROG_RES_FDS@
#define PROG_MAX_FQUEUES @PROG_MAX_FQUEUES@
-#define DU_BUFF_HEADSPACE @DU_BUFF_HEADSPACE@
-#define DU_BUFF_TAILSPACE @DU_BUFF_TAILSPACE@
-
/* Default Delta-t parameters */
#cmakedefine FRCT_LINUX_RTT_ESTIMATOR
#define DELT_A (@DELTA_T_ACK@) /* ns */
@@ -93,3 +88,5 @@
#define ACKQ_SLOTS (@ACK_WHEEL_SLOTS@)
#define ACKQ_RES (@ACK_WHEEL_RESOLUTION@) /* 2^N ns */
+
+#define KEY_ROTATION_BIT (@KEY_ROTATION_BIT@) /* Bit for key rotation */
diff --git a/src/lib/crypt.c b/src/lib/crypt.c
index ad679501..8c29cbb3 100644
--- a/src/lib/crypt.c
+++ b/src/lib/crypt.c
@@ -1,8 +1,7 @@
/*
* Ouroboros - Copyright (C) 2016 - 2024
*
- * Elliptic curve Diffie-Hellman key exchange and
- * AES encryption for flows using OpenSSL
+ * Cryptographic operations
*
* Dimitri Staessens <dimitri@ouroboros.rocks>
* Sander Vrijders <sander@ouroboros.rocks>
@@ -20,436 +19,1073 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#endif
+
#include <config.h>
-#include <ouroboros/crypt.h>
#include <ouroboros/errno.h>
+#include <ouroboros/random.h>
+#include <ouroboros/crypt.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include "crypt/openssl.h"
+#endif
#include <assert.h>
+#include <stdio.h>
#include <string.h>
-
+#include <sys/stat.h>
+
+struct nid_map {
+ uint16_t nid;
+ const char * name;
+};
+
+static const struct nid_map cipher_nid_map[] = {
+ {NID_aes_128_gcm, "aes-128-gcm"},
+ {NID_aes_192_gcm, "aes-192-gcm"},
+ {NID_aes_256_gcm, "aes-256-gcm"},
+ {NID_chacha20_poly1305, "chacha20-poly1305"},
+ {NID_aes_128_ctr, "aes-128-ctr"},
+ {NID_aes_192_ctr, "aes-192-ctr"},
+ {NID_aes_256_ctr, "aes-256-ctr"},
+ {NID_undef, NULL}
+};
+
+const uint16_t crypt_supported_nids[] = {
+#ifdef HAVE_OPENSSL
+ NID_aes_128_gcm,
+ NID_aes_192_gcm,
+ NID_aes_256_gcm,
+ NID_chacha20_poly1305,
+ NID_aes_128_ctr,
+ NID_aes_192_ctr,
+ NID_aes_256_ctr,
+#endif
+ NID_undef
+};
+
+static const struct nid_map kex_nid_map[] = {
+ {NID_X9_62_prime256v1, "prime256v1"},
+ {NID_secp384r1, "secp384r1"},
+ {NID_secp521r1, "secp521r1"},
+ {NID_X25519, "X25519"},
+ {NID_X448, "X448"},
+ {NID_ffdhe2048, "ffdhe2048"},
+ {NID_ffdhe3072, "ffdhe3072"},
+ {NID_ffdhe4096, "ffdhe4096"},
+ {NID_MLKEM512, "ML-KEM-512"},
+ {NID_MLKEM768, "ML-KEM-768"},
+ {NID_MLKEM1024, "ML-KEM-1024"},
+ {NID_X25519MLKEM768, "X25519MLKEM768"},
+ {NID_X448MLKEM1024, "X448MLKEM1024"},
+ {NID_undef, NULL}
+};
+
+const uint16_t kex_supported_nids[] = {
#ifdef HAVE_OPENSSL
+ NID_X9_62_prime256v1,
+ NID_secp384r1,
+ NID_secp521r1,
+ NID_X25519,
+ NID_X448,
+ NID_ffdhe2048,
+ NID_ffdhe3072,
+ NID_ffdhe4096,
+#ifdef HAVE_OPENSSL_PQC
+ NID_MLKEM512,
+ NID_MLKEM768,
+ NID_MLKEM1024,
+ NID_X25519MLKEM768,
+ NID_X448MLKEM1024,
+#endif
+#endif
+ NID_undef
+};
+
+static const struct nid_map md_nid_map[] = {
+ {NID_sha256, "sha256"},
+ {NID_sha384, "sha384"},
+ {NID_sha512, "sha512"},
+ {NID_sha3_256, "sha3-256"},
+ {NID_sha3_384, "sha3-384"},
+ {NID_sha3_512, "sha3-512"},
+ {NID_blake2b512, "blake2b512"},
+ {NID_blake2s256, "blake2s256"},
+ {NID_undef, NULL}
+};
+
+const uint16_t md_supported_nids[] = {
+#ifdef HAVE_OPENSSL
+ NID_sha256,
+ NID_sha384,
+ NID_sha512,
+ NID_sha3_256,
+ NID_sha3_384,
+ NID_sha3_512,
+ NID_blake2b512,
+ NID_blake2s256,
+#endif
+ NID_undef
+};
-#include <ouroboros/hash.h>
-#include <ouroboros/random.h>
+struct crypt_ctx {
+ void * ctx; /* Encryption context */
+};
-#include <openssl/evp.h>
-#include <openssl/ec.h>
-#include <openssl/pem.h>
+struct auth_ctx {
+ void * store;
+};
-#include <openssl/bio.h>
+static int parse_kex_value(const char * value,
+ struct sec_config * cfg)
+{
+ SET_KEX_ALGO(cfg, value);
+ if (cfg->x.nid == NID_undef)
+ return -ENOTSUP;
-#define IVSZ 16
-/* SYMMKEYSZ defined in dev.c */
+ return 0;
+}
-/*
- * Derive the common secret from
- * your public key pair (kp)
- * the remote public key (pub).
- * Store it in a preallocated buffer (s).
- */
-static int __openssl_ecdh_derive_secret(EVP_PKEY * kp,
- EVP_PKEY * pub,
- uint8_t * s)
+/* not in header, but non-static for unit testing */
+int parse_sec_config(struct sec_config * cfg,
+ FILE * fp)
{
- EVP_PKEY_CTX * ctx;
- int ret;
- uint8_t * secret;
- size_t secret_len;
+ char line[256];
+ char * equals;
+ char * key;
+ char * value;
+
+ assert(cfg != NULL);
+ assert(fp != NULL);
+
+ /* Set defaults */
+ SET_KEX_ALGO_NID(cfg, NID_X9_62_prime256v1);
+ cfg->x.mode = KEM_MODE_SERVER_ENCAP;
+ SET_KEX_KDF_NID(cfg, NID_sha256);
+ SET_KEX_CIPHER_NID(cfg, NID_aes_256_gcm);
+ SET_KEX_DIGEST_NID(cfg, NID_sha256);
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ char * trimmed;
+
+ /* Skip comments and empty lines */
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /* Check for 'none' keyword */
+ trimmed = trim_whitespace(line);
+ if (strcmp(trimmed, "none") == 0) {
+ memset(cfg, 0, sizeof(*cfg));
+ return 0;
+ }
+
+ /* Find the = separator */
+ equals = strchr(line, '=');
+ if (equals == NULL)
+ continue;
+
+ /* Split into key and value */
+ *equals = '\0';
+ key = trim_whitespace(line);
+ value = trim_whitespace(equals + 1);
+
+ /* Parse key exchange field */
+ if (strcmp(key, "kex") == 0) {
+ if (parse_kex_value(value, cfg) < 0)
+ return -EINVAL;
+ } else if (strcmp(key, "cipher") == 0) {
+ SET_KEX_CIPHER(cfg, value);
+ if (cfg->c.nid == NID_undef)
+ return -EINVAL;
+ } else if (strcmp(key, "kdf") == 0) {
+ SET_KEX_KDF(cfg, value);
+ if (cfg->k.nid == NID_undef)
+ return -EINVAL;
+ } else if (strcmp(key, "digest") == 0) {
+ SET_KEX_DIGEST(cfg, value);
+ if (cfg->d.nid == NID_undef)
+ return -EINVAL;
+ } else if (strcmp(key, "kem_mode") == 0) {
+ if (strcmp(value, "server") == 0) {
+ cfg->x.mode = KEM_MODE_SERVER_ENCAP;
+ } else if (strcmp(value, "client") == 0) {
+ cfg->x.mode = KEM_MODE_CLIENT_ENCAP;
+ } else {
+ return -EINVAL;
+ }
+ }
+ }
- ctx = EVP_PKEY_CTX_new(kp, NULL);
- if (ctx == NULL)
- goto fail_new;
+ return 0;
+}
- ret = EVP_PKEY_derive_init(ctx);
- if (ret != 1)
- goto fail_ctx;
+/* Parse key exchange config from file */
+int load_sec_config_file(struct sec_config * cfg,
+ const char * path)
+{
+ FILE * fp;
+ int ret;
- ret = EVP_PKEY_derive_set_peer(ctx, pub);
- if (ret != 1)
- goto fail_ctx;
+ assert(cfg != NULL);
+ assert(path != NULL);
- ret = EVP_PKEY_derive(ctx, NULL, &secret_len);
- if (ret != 1)
- goto fail_ctx;
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ /* File doesn't exist - disable encryption */
+ CLEAR_KEX_ALGO(cfg);
+ return 0;
+ }
- if (secret_len < SYMMKEYSZ)
- goto fail_ctx;
+ ret = parse_sec_config(cfg, fp);
- secret = OPENSSL_malloc(secret_len);
- if (secret == NULL)
- goto fail_ctx;
+ fclose(fp);
+
+ return ret;
+}
+
+int kex_pkp_create(struct sec_config * cfg,
+ void ** pkp,
+ uint8_t * pk)
+{
+#ifdef HAVE_OPENSSL
+ assert(cfg != NULL);
+ assert(pkp != NULL);
+
+ *pkp = NULL;
- ret = EVP_PKEY_derive(ctx, secret, &secret_len);
- if (ret != 1)
- goto fail_derive;
+ if (cfg->x.str == NULL || kex_validate_nid(cfg->x.nid) < 0)
+ return -ENOTSUP;
- /* Hash the secret for use as AES key. */
- mem_hash(HASH_SHA3_256, s, secret, secret_len);
+ return openssl_pkp_create(cfg->x.str, (EVP_PKEY **) pkp, pk);
+#else
+ (void) cfg;
+ (void) pkp;
+ (void) pk;
- OPENSSL_free(secret);
- EVP_PKEY_CTX_free(ctx);
+ *pkp = NULL;
return 0;
+#endif
+}
+
+void kex_pkp_destroy(void * pkp)
+{
+ if (pkp == NULL)
+ return;
+#ifdef HAVE_OPENSSL
+ openssl_pkp_destroy((EVP_PKEY *) pkp);
+#else
+ (void) pkp;
+
+ return;
+#endif
+}
+
+int kex_dhe_derive(struct sec_config * cfg,
+ void * pkp,
+ buffer_t pk,
+ uint8_t * s)
+{
+ assert(cfg != NULL);
+
+ if (kex_validate_nid(cfg->x.nid) < 0)
+ return -ENOTSUP;
+
+#ifdef HAVE_OPENSSL
+ return openssl_dhe_derive((EVP_PKEY *) pkp, pk, cfg->k.nid, s);
+#else
+ (void) pkp;
+ (void) pk;
+
+ memset(s, 0, SYMMKEYSZ);
- fail_derive:
- OPENSSL_free(secret);
- fail_ctx:
- EVP_PKEY_CTX_free(ctx);
- fail_new:
return -ECRYPT;
+#endif
}
-static int __openssl_ecdh_gen_key(void ** kp)
+ssize_t kex_kem_encap(buffer_t pk,
+ uint8_t * ct,
+ int kdf,
+ uint8_t * s)
{
- EVP_PKEY_CTX * ctx = NULL;
- EVP_PKEY_CTX * kctx = NULL;
- EVP_PKEY * params = NULL;
- int ret;
+#ifdef HAVE_OPENSSL
+ return openssl_kem_encap(pk, ct, kdf, s);
+#else
+ (void) pk;
+ (void) ct;
+ (void) kdf;
- ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
- if (ctx == NULL)
- goto fail_new_id;
+ memset(s, 0, SYMMKEYSZ);
- ret = EVP_PKEY_paramgen_init(ctx);
- if (ret != 1)
- goto fail_paramgen;
+ return -ECRYPT;
+#endif
+}
- ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1);
- if (ret != 1)
- goto fail_paramgen;
+ssize_t kex_kem_encap_raw(buffer_t pk,
+ uint8_t * ct,
+ int kdf,
+ uint8_t * s)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_kem_encap_raw(pk, ct, kdf, s);
+#else
+ (void) pk;
+ (void) ct;
+ (void) kdf;
- ret = EVP_PKEY_paramgen(ctx, &params);
- if (ret != 1)
- goto fail_paramgen;
+ memset(s, 0, SYMMKEYSZ);
- kctx = EVP_PKEY_CTX_new(params, NULL);
- if (kctx == NULL)
- goto fail_keygen_init;
+ return -ECRYPT;
+#endif
+}
- ret = EVP_PKEY_keygen_init(kctx);
- if (ret != 1)
- goto fail_keygen;
+int kex_kem_decap(void * pkp,
+ buffer_t ct,
+ int kdf,
+ uint8_t * s)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_kem_decap((EVP_PKEY *) pkp, ct, kdf, s);
+#else
+ (void) pkp;
+ (void) ct;
+ (void) kdf;
- ret = EVP_PKEY_keygen(kctx, (EVP_PKEY **) kp);
- if (ret != 1)
- goto fail_keygen;
+ memset(s, 0, SYMMKEYSZ);
- EVP_PKEY_free(params);
- EVP_PKEY_CTX_free(kctx);
- EVP_PKEY_CTX_free(ctx);
+ return -ECRYPT;
+#endif
+}
- return 0;
+int kex_get_algo_from_pk_der(buffer_t pk,
+ char * algo)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_get_algo_from_pk_der(pk, algo);
+#else
+ (void) pk;
+ algo[0] = '\0';
- fail_keygen:
- EVP_PKEY_CTX_free(kctx);
- fail_keygen_init:
- EVP_PKEY_free(params);
- fail_paramgen:
- EVP_PKEY_CTX_free(ctx);
- fail_new_id:
return -ECRYPT;
+#endif
}
-static ssize_t openssl_ecdh_pkp_create(void ** pkp,
- uint8_t * pk)
+int kex_get_algo_from_pk_raw(buffer_t pk,
+ char * algo)
{
- uint8_t * pos;
- ssize_t len;
+#ifdef HAVE_OPENSSL
+ return openssl_get_algo_from_pk_raw(pk, algo);
+#else
+ (void) pk;
+ algo[0] = '\0';
- assert(pkp != NULL);
- assert(*pkp == NULL);
- assert(pk != NULL);
+ return -ECRYPT;
+#endif
+}
- if (__openssl_ecdh_gen_key(pkp) < 0)
- return -ECRYPT;
+int kex_validate_algo(const char * algo)
+{
+ if (algo == NULL)
+ return -EINVAL;
- assert(*pkp != NULL);
+ /* Use NID validation instead of string array */
+ return kex_validate_nid(kex_str_to_nid(algo));
+}
+
+int crypt_validate_nid(int nid)
+{
+ const struct nid_map * p;
- pos = pk; /* i2d_PUBKEY increments the pointer, don't use buf! */
- len = i2d_PUBKEY(*pkp, &pos);
- if (len < 0) {
- EVP_PKEY_free(*pkp);
- return -ECRYPT;
+ if (nid == NID_undef)
+ return -EINVAL;
+
+ for (p = cipher_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return 0;
}
- return len;
+ return -ENOTSUP;
}
-static void openssl_ecdh_pkp_destroy(void * pkp)
+
+const char * crypt_nid_to_str(uint16_t nid)
{
- EVP_PKEY_free((EVP_PKEY *) pkp);
+ const struct nid_map * p;
+
+ for (p = cipher_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return p->name;
+ }
+
+ return NULL;
}
-static int openssl_ecdh_derive(void * pkp,
- buffer_t pk,
- uint8_t * s)
+uint16_t crypt_str_to_nid(const char * cipher)
{
- uint8_t * pos;
- EVP_PKEY * pub;
+ const struct nid_map * p;
- pos = pk.data; /* d2i_PUBKEY increments the pointer, don't use key ptr! */
- pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len);
- if (pub == NULL)
- return -ECRYPT;
+ if (cipher == NULL)
+ return NID_undef;
- if (__openssl_ecdh_derive_secret(pkp, pub, s) < 0) {
- EVP_PKEY_free(pub);
- return -ECRYPT;
+ /* fast, check if cipher pointer is in the map */
+ for (p = cipher_nid_map; p->name != NULL; p++) {
+ if (cipher == p->name)
+ return p->nid;
}
- EVP_PKEY_free(pub);
+ for (p = cipher_nid_map; p->name != NULL; p++) {
+ if (strcmp(p->name, cipher) == 0)
+ return p->nid;
+ }
- return 0;
+ return NID_undef;
}
-/*
- * AES encryption calls. If FRCT is disabled, we should generate a
- * 128-bit random IV and append it to the packet. If the flow is
- * reliable, we could initialize the context once, and consider the
- * stream a single encrypted message to avoid initializing the
- * encryption context for each packet.
- */
+const char * kex_nid_to_str(uint16_t nid)
+{
+ const struct nid_map * p;
+
+ for (p = kex_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return p->name;
+ }
-static int openssl_encrypt(void * ctx,
- uint8_t * key,
- struct shm_du_buff * sdb)
+ return NULL;
+}
+
+uint16_t kex_str_to_nid(const char * algo)
{
- uint8_t * out;
- uint8_t * in;
- uint8_t * head;
- uint8_t iv[IVSZ];
- int in_sz;
- int out_sz;
- int tmp_sz;
- int ret;
+ const struct nid_map * p;
+
+ if (algo == NULL)
+ return NID_undef;
- in = shm_du_buff_head(sdb);
- in_sz = shm_du_buff_tail(sdb) - in;
+ /* Fast path: check if algo pointer is in the map */
+ for (p = kex_nid_map; p->name != NULL; p++) {
+ if (algo == p->name)
+ return p->nid;
+ }
+
+ /* Slow path: string comparison */
+ for (p = kex_nid_map; p->name != NULL; p++) {
+ if (strcmp(p->name, algo) == 0)
+ return p->nid;
+ }
- assert(in_sz > 0);
+ return NID_undef;
+}
+
+int kex_validate_nid(int nid)
+{
+ const struct nid_map * p;
- if (random_buffer(iv, IVSZ) < 0)
- goto fail_iv;
+ if (nid == NID_undef)
+ return -EINVAL;
- out = malloc(in_sz + EVP_MAX_BLOCK_LENGTH);
- if (out == NULL)
- goto fail_iv;
+ for (p = kex_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return 0;
+ }
- EVP_CIPHER_CTX_reset(ctx);
+ return -ENOTSUP;
+}
- ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
- if (ret != 1)
- goto fail_encrypt_init;
+const char * md_nid_to_str(uint16_t nid)
+{
+ const struct nid_map * p;
- ret = EVP_EncryptUpdate(ctx, out, &tmp_sz, in, in_sz);
- if (ret != 1)
- goto fail_encrypt;
+ for (p = md_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return p->name;
+ }
- out_sz = tmp_sz;
- ret = EVP_EncryptFinal_ex(ctx, out + tmp_sz, &tmp_sz);
- if (ret != 1)
- goto fail_encrypt;
+ return NULL;
+}
- out_sz += tmp_sz;
+uint16_t md_str_to_nid(const char * kdf)
+{
+ const struct nid_map * p;
- EVP_CIPHER_CTX_cleanup(ctx);
+ if (kdf == NULL)
+ return NID_undef;
- assert(out_sz >= in_sz);
+ /* Fast path: check if kdf pointer is in the map */
+ for (p = md_nid_map; p->name != NULL; p++) {
+ if (kdf == p->name)
+ return p->nid;
+ }
- head = shm_du_buff_head_alloc(sdb, IVSZ);
- if (head == NULL)
- goto fail_encrypt;
+ /* Slow path: string comparison */
+ for (p = md_nid_map; p->name != NULL; p++) {
+ if (strcmp(p->name, kdf) == 0)
+ return p->nid;
+ }
- if (shm_du_buff_tail_alloc(sdb, out_sz - in_sz) == NULL)
- goto fail_tail_alloc;
+ return NID_undef;
+}
- memcpy(head, iv, IVSZ);
- memcpy(in, out, out_sz);
+int md_validate_nid(int nid)
+{
+ const struct nid_map * p;
- free(out);
+ if (nid == NID_undef)
+ return -EINVAL;
- return 0;
+ for (p = md_nid_map; p->name != NULL; p++) {
+ if (p->nid == nid)
+ return 0;
+ }
+
+ return -ENOTSUP;
+}
+
+/* Hash length now returned by md_digest() */
+
+int crypt_encrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out)
+{
+ assert(ctx != NULL);
+ assert(ctx->ctx != NULL);
+
+#ifdef HAVE_OPENSSL
+ return openssl_encrypt(ctx->ctx, in, out);
+#else
+ (void) ctx;
+ (void) in;
+ (void) out;
- fail_tail_alloc:
- shm_du_buff_head_release(sdb, IVSZ);
- fail_encrypt:
- EVP_CIPHER_CTX_cleanup(ctx);
- fail_encrypt_init:
- free(out);
- fail_iv:
return -ECRYPT;
+#endif
}
-static int openssl_decrypt(void * ctx,
- uint8_t * key,
- struct shm_du_buff * sdb)
+int crypt_decrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out)
{
- uint8_t * in;
- uint8_t * out;
- uint8_t iv[IVSZ];
- int ret;
- int out_sz;
- int in_sz;
- int tmp_sz;
+ assert(ctx != NULL);
+ assert(ctx->ctx != NULL);
- in_sz = shm_du_buff_len(sdb);
- if (in_sz < IVSZ)
- return -ECRYPT;
+#ifdef HAVE_OPENSSL
+ return openssl_decrypt(ctx->ctx, in, out);
+#else
+ (void) ctx;
+ (void) in;
+ (void) out;
- in = shm_du_buff_head_release(sdb, IVSZ);
+ return -ECRYPT;
+#endif
+}
- memcpy(iv, in, IVSZ);
+struct crypt_ctx * crypt_create_ctx(struct crypt_sk * sk)
+{
+ struct crypt_ctx * crypt;
- in = shm_du_buff_head(sdb);
- in_sz = shm_du_buff_tail(sdb) - in;
+ if (crypt_validate_nid(sk->nid) != 0)
+ return NULL;
- out = malloc(in_sz);
- if (out == NULL)
- goto fail_malloc;
+ crypt = malloc(sizeof(*crypt));
+ if (crypt == NULL)
+ goto fail_crypt;
- EVP_CIPHER_CTX_reset(ctx);
+ memset(crypt, 0, sizeof(*crypt));
- ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
- if (ret != 1)
- goto fail_decrypt_init;
+#ifdef HAVE_OPENSSL
+ crypt->ctx = openssl_crypt_create_ctx(sk);
+ if (crypt->ctx == NULL)
+ goto fail_ctx;
+#endif
+ return crypt;
+#ifdef HAVE_OPENSSL
+ fail_ctx:
+ free(crypt);
+#endif
+ fail_crypt:
+ return NULL;
+}
- ret = EVP_DecryptUpdate(ctx, out, &tmp_sz, in, in_sz);
- if (ret != 1)
- goto fail_decrypt;
+void crypt_destroy_ctx(struct crypt_ctx * crypt)
+{
+ if (crypt == NULL)
+ return;
- out_sz = tmp_sz;
+#ifdef HAVE_OPENSSL
+ assert(crypt->ctx != NULL);
+ openssl_crypt_destroy_ctx(crypt->ctx);
+#else
+ assert(crypt->ctx == NULL);
+#endif
+ free(crypt);
+}
- ret = EVP_DecryptFinal_ex(ctx, out + tmp_sz, &tmp_sz);
- if (ret != 1)
- goto fail_decrypt;
+int crypt_get_ivsz(struct crypt_ctx * ctx)
+{
+ if (ctx == NULL)
+ return -EINVAL;
- out_sz += tmp_sz;
+#ifdef HAVE_OPENSSL
+ assert(ctx->ctx != NULL);
+ return openssl_crypt_get_ivsz(ctx->ctx);
+#else
+ assert(ctx->ctx == NULL);
+ return -ENOTSUP;
+#endif
+}
- assert(out_sz <= in_sz);
+int crypt_get_tagsz(struct crypt_ctx * ctx)
+{
+ if (ctx == NULL)
+ return -EINVAL;
- shm_du_buff_tail_release(sdb, in_sz - out_sz);
+#ifdef HAVE_OPENSSL
+ assert(ctx->ctx != NULL);
+ return openssl_crypt_get_tagsz(ctx->ctx);
+#else
+ assert(ctx->ctx == NULL);
+ return -ENOTSUP;
+#endif
+}
- memcpy(in, out, out_sz);
+int crypt_load_privkey_file(const char * path,
+ void ** key)
+{
+ *key = NULL;
- free(out);
+#ifdef HAVE_OPENSSL
+ return openssl_load_privkey_file(path, key);
+#else
+ (void) path;
return 0;
+#endif
+}
- fail_decrypt:
- EVP_CIPHER_CTX_cleanup(ctx);
- fail_decrypt_init:
- free(out);
- fail_malloc:
- return -ECRYPT;
+int crypt_load_privkey_str(const char * str,
+ void ** key)
+{
+ *key = NULL;
+#ifdef HAVE_OPENSSL
+ return openssl_load_privkey_str(str, key);
+#else
+ (void) str;
+
+ return 0;
+#endif
}
-static int openssl_crypt_init(void ** ctx)
+int crypt_load_pubkey_str(const char * str,
+ void ** key)
{
- *ctx = EVP_CIPHER_CTX_new();
- if (*ctx == NULL)
- return -ECRYPT;
+ *key = NULL;
+
+#ifdef HAVE_OPENSSL
+ return openssl_load_pubkey_str(str, key);
+#else
+ (void) str;
return 0;
+#endif
}
-static void openssl_crypt_fini(void * ctx)
+int crypt_load_pubkey_file(const char * path,
+ void ** key)
{
- EVP_CIPHER_CTX_free(ctx);
+ *key = NULL;
+
+#ifdef HAVE_OPENSSL
+ return openssl_load_pubkey_file(path, key);
+#else
+ (void) path;
+
+ return 0;
+#endif
}
-#endif /* HAVE_OPENSSL */
+int crypt_load_pubkey_file_to_der(const char * path,
+ buffer_t * buf)
+{
+ assert(buf != NULL);
+
+#ifdef HAVE_OPENSSL
+ return openssl_load_pubkey_file_to_der(path, buf);
+#else
+ (void) path;
+
+ buf->data = NULL;
+ buf->len = 0;
+ return 0;
+#endif
+}
-int crypt_dh_pkp_create(void ** pkp,
- uint8_t * pk)
+int crypt_load_pubkey_raw_file(const char * path,
+ buffer_t * buf)
{
+ assert(buf != NULL);
+
#ifdef HAVE_OPENSSL
- assert(pkp != NULL);
- *pkp = NULL;
- return openssl_ecdh_pkp_create(pkp, pk);
+ return openssl_load_pubkey_raw_file(path, buf);
#else
- (void) pkp;
- (void) pk;
+ (void) path;
- *pkp = NULL;
+ buf->data = NULL;
+ buf->len = 0;
+ return 0;
+#endif
+}
+
+int crypt_load_privkey_raw_file(const char * path,
+ void ** key)
+{
+ *key = NULL;
+
+#ifdef HAVE_OPENSSL
+ return openssl_load_privkey_raw_file(path, key);
+#else
+ (void) path;
return 0;
#endif
}
-void crypt_dh_pkp_destroy(void * pkp)
+int crypt_cmp_key(const void * key1,
+ const void * key2)
{
#ifdef HAVE_OPENSSL
- openssl_ecdh_pkp_destroy(pkp);
+ return openssl_cmp_key((const EVP_PKEY *) key1,
+ (const EVP_PKEY *) key2);
#else
- (void) pkp;
- return;
+ (void) key1;
+ (void) key2;
+
+ return 0;
+#endif
+}
+
+void crypt_free_key(void * key)
+{
+ if (key == NULL)
+ return;
+
+#ifdef HAVE_OPENSSL
+ openssl_free_key((EVP_PKEY *) key);
#endif
}
-int crypt_dh_derive(void * pkp,
- buffer_t pk,
- uint8_t * s)
+int crypt_load_crt_file(const char * path,
+ void ** crt)
{
+ assert(crt != NULL);
+
+ *crt = NULL;
+
#ifdef HAVE_OPENSSL
- return openssl_ecdh_derive(pkp, pk, s);
+ return openssl_load_crt_file(path, crt);
#else
- (void) pkp;
- (void) pk;
+ (void) path;
- memset(s, 0, SYMMKEYSZ);
+ return 0;
+#endif
+}
- return -ECRYPT;
+int crypt_load_crt_str(const char * str,
+ void ** crt)
+{
+ assert(crt != NULL);
+
+ *crt = NULL;
+
+#ifdef HAVE_OPENSSL
+ return openssl_load_crt_str(str, crt);
+#else
+ (void) str;
+
+ return 0;
#endif
}
-int crypt_encrypt(struct crypt_info * info,
- struct shm_du_buff * sdb)
+int crypt_load_crt_der(const buffer_t buf,
+ void ** crt)
{
- if (info->flags == 0)
- return 0;
+ assert(crt != NULL);
+#ifdef HAVE_OPENSSL
+ return openssl_load_crt_der(buf, crt);
+#else
+ *crt = NULL;
+
+ (void) buf;
+
+ return 0;
+#endif
+}
+
+int crypt_get_pubkey_crt(void * crt,
+ void ** pk)
+{
+ assert(crt != NULL);
+ assert(pk != NULL);
#ifdef HAVE_OPENSSL
- return openssl_encrypt(info->ctx, info->key, sdb);
+ return openssl_get_pubkey_crt(crt, pk);
#else
- (void) sdb;
+ (void) crt;
+
+ clrbuf(*pk);
return 0;
#endif
}
-int crypt_decrypt(struct crypt_info * info,
- struct shm_du_buff * sdb)
+void crypt_free_crt(void * crt)
{
- if (info->flags == 0)
- return 0;
+ if (crt == NULL)
+ return;
+#ifdef HAVE_OPENSSL
+ openssl_free_crt(crt);
+#endif
+}
+int crypt_crt_str(const void * crt,
+ char * buf)
+{
#ifdef HAVE_OPENSSL
- return openssl_decrypt(info->ctx, info->key, sdb);
+ return openssl_crt_str(crt, buf);
#else
- (void) sdb;
+ (void) crt;
+ (void) buf;
- return -ECRYPT;
+ return 0;
+#endif
+}
+
+int crypt_crt_der(const void * crt,
+ buffer_t * buf)
+{
+ assert(crt != NULL);
+ assert(buf != NULL);
+
+#ifdef HAVE_OPENSSL
+ return openssl_crt_der(crt, buf);
+#else
+ (void) crt;
+
+ clrbuf(*buf);
+
+ return 0;
+#endif
+}
+
+int crypt_check_crt_name(void * crt,
+ const char * name)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_check_crt_name(crt, name);
+#else
+ (void) crt;
+ (void) name;
+
+ return 0;
#endif
}
-int crypt_init(struct crypt_info * info)
+int crypt_get_crt_name(void * crt,
+ char * name)
{
#ifdef HAVE_OPENSSL
- return openssl_crypt_init(&info->ctx);
+ return openssl_get_crt_name(crt, name);
#else
- info->ctx = NULL;
+ (void) crt;
+ (void) name;
+
+ return 0;
+#endif
+}
+
+struct auth_ctx * auth_create_ctx(void)
+{
+ struct auth_ctx * ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto fail_malloc;
+
+ memset(ctx, 0, sizeof(*ctx));
+#ifdef HAVE_OPENSSL
+ ctx->store = openssl_auth_create_store();
+ if (ctx->store == NULL)
+ goto fail_store;
+#endif
+ return ctx;
+#ifdef HAVE_OPENSSL
+ fail_store:
+ free(ctx);
+#endif
+ fail_malloc:
+ return NULL;
+}
+
+void auth_destroy_ctx(struct auth_ctx * ctx)
+{
+ if (ctx == NULL)
+ return;
+#ifdef HAVE_OPENSSL
+ openssl_auth_destroy_store(ctx->store);
+#endif
+ free(ctx);
+}
+
+int auth_add_crt_to_store(struct auth_ctx * ctx,
+ void * crt)
+{
+ assert(ctx != NULL);
+ assert(crt != NULL);
+
+#ifdef HAVE_OPENSSL
+ return openssl_auth_add_crt_to_store(ctx->store, crt);
+#else
+ (void) ctx;
+ (void) crt;
+
+ return 0;
+#endif
+}
+
+int auth_verify_crt(struct auth_ctx * ctx,
+ void * crt)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_verify_crt(ctx->store, crt);
+#else
+ (void) ctx;
+ (void) crt;
+
+ return 0;
+#endif
+}
+
+int auth_sign(void * pkp,
+ int md_nid,
+ buffer_t msg,
+ buffer_t * sig)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_sign((EVP_PKEY *) pkp, md_nid, msg, sig);
+#else
+ (void) pkp;
+ (void) md_nid;
+ (void) msg;
+ (void) sig;
+
+ clrbuf(*sig);
+
return 0;
#endif
}
-void crypt_fini(struct crypt_info * info)
+int auth_verify_sig(void * pk,
+ int md_nid,
+ buffer_t msg,
+ buffer_t sig)
{
#ifdef HAVE_OPENSSL
- openssl_crypt_fini(info->ctx);
+ return openssl_verify_sig((EVP_PKEY *) pk, md_nid, msg, sig);
#else
- (void) info;
- assert(info->ctx == NULL);
+ (void) pk;
+ (void) md_nid;
+ (void) msg;
+ (void) sig;
+
+ return 0;
+#endif
+}
+
+ssize_t md_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_md_digest(md_nid, in, out);
+#else
+ (void) md_nid;
+ (void) in;
+ (void) out;
+
+ return -1;
+#endif
+}
+
+ssize_t md_len(int md_nid)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_md_len(md_nid);
+#else
+ (void) md_nid;
+ return -1;
+#endif
+}
+
+int crypt_secure_malloc_init(size_t max)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_secure_malloc_init(max, SECMEM_GUARD);
+#else
+ (void) max;
+ return 0;
+#endif
+}
+
+void crypt_secure_malloc_fini(void)
+{
+#ifdef HAVE_OPENSSL
+ openssl_secure_malloc_fini();
+#endif
+}
+
+void * crypt_secure_malloc(size_t size)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_secure_malloc(size);
+#else
+ return malloc(size);
+#endif
+}
+
+void crypt_secure_free(void * ptr,
+ size_t size)
+{
+ if (ptr == NULL)
+ return;
+
+#ifdef HAVE_OPENSSL
+ openssl_secure_free(ptr, size);
+#else
+ memset(ptr, 0, size);
+ free(ptr);
+#endif
+}
+
+void crypt_secure_clear(void * ptr,
+ size_t size)
+{
+ volatile uint8_t * p;
+
+ if (ptr == NULL)
+ return;
+
+#ifdef HAVE_OPENSSL
+ (void) p;
+ openssl_secure_clear(ptr, size);
+#elif defined(HAVE_EXPLICIT_BZERO)
+ (void) p;
+ explicit_bzero(ptr, size);
+#else /* best effort to avoid optimizing out */
+ p = ptr;
+ while (size-- > 0)
+ *p++ = 0;
#endif
}
diff --git a/src/lib/crypt/openssl.c b/src/lib/crypt/openssl.c
new file mode 100644
index 00000000..232aa6c9
--- /dev/null
+++ b/src/lib/crypt/openssl.c
@@ -0,0 +1,1880 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OpenSSL based cryptographic operations
+ * Elliptic curve Diffie-Hellman key exchange
+ * AES encryption
+ # Authentication
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <config.h>
+
+#include <ouroboros/errno.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/hash.h>
+#include <ouroboros/random.h>
+#include <ouroboros/utils.h>
+
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/kdf.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/provider.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#define IS_EC_GROUP(str) (strcmp(str, "EC") == 0)
+#define IS_DH_GROUP(str) (strcmp(str, "DH") == 0)
+
+#define HKDF_INFO_DHE "o7s-ossl-dhe"
+#define HKDF_INFO_ENCAP "o7s-ossl-encap"
+#define HKDF_INFO_ROTATION "o7s-key-rotation"
+#define HKDF_SALT_LEN 32 /* SHA-256 output size */
+
+struct ossl_crypt_ctx {
+ EVP_CIPHER_CTX * evp_ctx;
+ const EVP_CIPHER * cipher;
+ int ivsz;
+ int tagsz;
+
+ struct {
+ uint8_t * cur; /* current key */
+ uint8_t * prv; /* rotated key */
+ } keys;
+
+ struct {
+ uint32_t cntr; /* counter */
+ uint32_t mask; /* phase mask */
+ uint32_t age; /* counter within epoch */
+ uint8_t phase; /* current key phase */
+ uint8_t salt[HKDF_SALT_LEN];
+ } rot; /* rotation logic */
+};
+
+struct kdf_info {
+ buffer_t secret;
+ int nid;
+ buffer_t salt;
+ buffer_t info;
+ buffer_t key;
+};
+
+/* Key rotation macros */
+#define HAS_PHASE_BIT_TOGGLED(ctx) \
+ (((ctx)->rot.cntr & (ctx)->rot.mask) != \
+ (((ctx)->rot.cntr - 1) & (ctx)->rot.mask))
+
+#define HAS_GRACE_EXPIRED(ctx) \
+ ((ctx)->rot.age >= ((ctx)->rot.mask >> 1))
+
+#define ROTATION_TOO_RECENT(ctx) \
+ ((ctx)->rot.age < ((ctx)->rot.mask - ((ctx)->rot.mask >> 2)))
+
+/* Convert hash NID to OpenSSL digest name string for HKDF */
+static const char * hash_nid_to_digest_name(int nid)
+{
+ const EVP_MD * md;
+ const char * name;
+
+ md = EVP_get_digestbynid(nid);
+ if (md == NULL)
+ return "SHA256"; /* fallback to SHA-256 */
+
+ name = EVP_MD_get0_name(md);
+ if (name == NULL)
+ return "SHA256"; /* fallback to SHA-256 */
+
+ return name;
+}
+
+/* Extract public key bytes from a key pair for salt derivation */
+static int get_pk_bytes_from_key(EVP_PKEY * key,
+ buffer_t * pk)
+{
+ const char * name;
+ int ret;
+
+ assert(key != NULL);
+ assert(pk != NULL);
+
+ name = EVP_PKEY_get0_type_name(key);
+ if (name == NULL)
+ goto fail_name;
+
+ if (IS_HYBRID_KEM(name)) {
+ pk->len = EVP_PKEY_get1_encoded_public_key(key, &pk->data);
+ if (pk->len == 0)
+ goto fail_name;
+ } else {
+ /* Pure ML-KEM: use DER encoding to match encap */
+ pk->data = NULL;
+ ret = i2d_PUBKEY(key, &pk->data);
+ if (ret <= 0)
+ goto fail_name;
+ pk->len = (size_t) ret;
+ }
+
+ return 0;
+ fail_name:
+ return -ECRYPT;
+}
+
+/* Derive salt from public key bytes by hashing them */
+static int derive_salt_from_pk_bytes(buffer_t pk,
+ uint8_t * salt,
+ size_t salt_len)
+{
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ unsigned hash_len;
+
+ assert(pk.data != NULL);
+ assert(salt != NULL);
+
+ if (EVP_Digest(pk.data, pk.len, hash, &hash_len,
+ EVP_sha256(), NULL) != 1)
+ goto fail_digest;
+
+ memcpy(salt, hash, salt_len < hash_len ? salt_len : hash_len);
+
+ return 0;
+ fail_digest:
+ return -ECRYPT;
+}
+
+/* Derive salt from two public key byte buffers (DHE) in canonical order */
+static int derive_salt_from_pk_bytes_dhe(buffer_t local,
+ buffer_t remote,
+ uint8_t * salt,
+ size_t salt_len)
+{
+ uint8_t * concat;
+ size_t concat_len;
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ unsigned hash_len;
+ size_t min_len;
+ int cmp;
+
+ assert(local.data != NULL);
+ assert(remote.data != NULL);
+ assert(salt != NULL);
+
+ concat_len = local.len + remote.len;
+ concat = OPENSSL_malloc(concat_len);
+ if (concat == NULL)
+ goto fail_malloc;
+
+ /* Canonical order: compare and concatenate smaller first */
+ min_len = local.len < remote.len ? local.len : remote.len;
+ cmp = memcmp(local.data, remote.data, min_len);
+ if (cmp < 0 || (cmp == 0 && local.len < remote.len)) {
+ memcpy(concat, local.data, local.len);
+ memcpy(concat + local.len, remote.data, remote.len);
+ } else {
+ memcpy(concat, remote.data, remote.len);
+ memcpy(concat + remote.len, local.data, local.len);
+ }
+
+ if (EVP_Digest(concat, concat_len, hash, &hash_len,
+ EVP_sha256(), NULL) != 1)
+ goto fail_digest;
+
+ OPENSSL_free(concat);
+
+ memcpy(salt, hash, salt_len < hash_len ? salt_len : hash_len);
+
+ return 0;
+ fail_digest:
+ OPENSSL_free(concat);
+ fail_malloc:
+ return -ECRYPT;
+}
+
+/* Derive key using HKDF */
+#define OPc_u_str OSSL_PARAM_construct_utf8_string
+#define OPc_o_str OSSL_PARAM_construct_octet_string
+static int derive_key_hkdf(struct kdf_info * ki)
+{
+ EVP_KDF * kdf;
+ EVP_KDF_CTX * kctx;
+ OSSL_PARAM params[5];
+ const char * digest;
+ int idx;
+
+ digest = hash_nid_to_digest_name(ki->nid);
+
+ kdf = EVP_KDF_fetch(NULL, "HKDF", NULL);
+ if (kdf == NULL)
+ goto fail_fetch;
+
+ kctx = EVP_KDF_CTX_new(kdf);
+ if (kctx == NULL)
+ goto fail_ctx;
+
+ idx = 0;
+ params[idx++] = OPc_u_str("digest", (char *) digest, 0);
+ params[idx++] = OPc_o_str("key", ki->secret.data, ki->secret.len);
+ params[idx++] = OPc_o_str("salt", ki->salt.data, ki->salt.len);
+ params[idx++] = OPc_o_str("info", ki->info.data, ki->info.len);
+
+ params[idx] = OSSL_PARAM_construct_end();
+
+ if (EVP_KDF_derive(kctx, ki->key.data, ki->key.len, params) != 1)
+ goto fail_derive;
+
+ EVP_KDF_CTX_free(kctx);
+ EVP_KDF_free(kdf);
+
+ return 0;
+
+ fail_derive:
+ EVP_KDF_CTX_free(kctx);
+ fail_ctx:
+ EVP_KDF_free(kdf);
+ fail_fetch:
+ return -ECRYPT;
+}
+
+/* Key rotation helper functions implementation */
+static int should_rotate_key_rx(struct ossl_crypt_ctx * ctx,
+ uint8_t rx_phase)
+{
+ assert(ctx != NULL);
+
+ /* Phase must have changed */
+ if (rx_phase == ctx->rot.phase)
+ return 0;
+
+ if (ROTATION_TOO_RECENT(ctx))
+ return 0;
+
+ return 1;
+}
+
+static int rotate_key(struct ossl_crypt_ctx * ctx)
+{
+ struct kdf_info ki;
+ uint8_t * tmp;
+
+ assert(ctx != NULL);
+
+ /* Swap keys - move current to prev */
+ tmp = ctx->keys.prv;
+ ctx->keys.prv = ctx->keys.cur;
+
+ if (tmp != NULL) {
+ /* Reuse old prev_key memory for new key */
+ ctx->keys.cur = tmp;
+ } else {
+ /* First rotation - allocate new memory */
+ ctx->keys.cur = OPENSSL_secure_malloc(SYMMKEYSZ);
+ if (ctx->keys.cur == NULL)
+ return -ECRYPT;
+ }
+
+ /* Derive new key from previous key using HKDF */
+ ki.secret.data = ctx->keys.prv;
+ ki.secret.len = SYMMKEYSZ;
+ ki.nid = NID_sha256;
+ ki.salt.data = ctx->rot.salt;
+ ki.salt.len = HKDF_SALT_LEN;
+ ki.info.data = (uint8_t *) HKDF_INFO_ROTATION;
+ ki.info.len = strlen(HKDF_INFO_ROTATION);
+ ki.key.data = ctx->keys.cur;
+ ki.key.len = SYMMKEYSZ;
+
+ if (derive_key_hkdf(&ki) != 0)
+ return -ECRYPT;
+
+ ctx->rot.age = 0;
+ ctx->rot.phase = !ctx->rot.phase;
+
+ return 0;
+}
+
+static void cleanup_old_key(struct ossl_crypt_ctx * ctx)
+{
+ assert(ctx != NULL);
+
+ if (ctx->keys.prv == NULL)
+ return;
+
+ if (!HAS_GRACE_EXPIRED(ctx))
+ return;
+
+ OPENSSL_secure_clear_free(ctx->keys.prv, SYMMKEYSZ);
+ ctx->keys.prv = NULL;
+}
+
+static int try_decrypt(struct ossl_crypt_ctx * ctx,
+ uint8_t * key,
+ uint8_t * iv,
+ uint8_t * input,
+ int in_sz,
+ uint8_t * out,
+ int * out_sz)
+{
+ uint8_t * tag;
+ int tmp_sz;
+ int ret;
+
+ tag = input + in_sz;
+
+ EVP_CIPHER_CTX_reset(ctx->evp_ctx);
+
+ ret = EVP_DecryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL, key, iv);
+ if (ret != 1)
+ return -1;
+
+ if (ctx->tagsz > 0) {
+ ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_SET_TAG,
+ ctx->tagsz, tag);
+ if (ret != 1)
+ return -1;
+ }
+
+ ret = EVP_DecryptUpdate(ctx->evp_ctx, out, &tmp_sz, input, in_sz);
+ if (ret != 1)
+ return -1;
+
+ *out_sz = tmp_sz;
+
+ ret = EVP_DecryptFinal_ex(ctx->evp_ctx, out + tmp_sz, &tmp_sz);
+ if (ret != 1)
+ return -1;
+
+ *out_sz += tmp_sz;
+
+ return 0;
+}
+
+/*
+ * Derive the common secret from
+ * - your public key pair (pkp)
+ * - the remote public key bytes (remote_pk).
+ * Store it in a preallocated buffer (s).
+ */
+static int __openssl_dhe_derive(EVP_PKEY * pkp,
+ EVP_PKEY * pub,
+ buffer_t remote_pk,
+ int kdf,
+ uint8_t * s)
+{
+ EVP_PKEY_CTX * ctx;
+ struct kdf_info ki;
+ buffer_t local_pk;
+ int ret;
+ uint8_t * secret;
+ size_t secret_len;
+ uint8_t salt_buf[HKDF_SALT_LEN];
+
+ /* Extract local public key bytes */
+ local_pk.data = NULL;
+ ret = i2d_PUBKEY(pkp, &local_pk.data);
+ if (ret <= 0)
+ goto fail_local;
+ local_pk.len = (size_t) ret;
+
+ /* Derive salt from both public keys */
+ if (derive_salt_from_pk_bytes_dhe(local_pk, remote_pk, salt_buf,
+ HKDF_SALT_LEN) < 0)
+ goto fail_salt;
+
+ ctx = EVP_PKEY_CTX_new(pkp, NULL);
+ if (ctx == NULL)
+ goto fail_salt;
+
+ ret = EVP_PKEY_derive_init(ctx);
+ if (ret != 1)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive_set_peer(ctx, pub);
+ if (ret != 1)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive(ctx, NULL, &secret_len);
+ if (ret != 1)
+ goto fail_ctx;
+
+ if (secret_len < SYMMKEYSZ)
+ goto fail_ctx;
+
+ secret = OPENSSL_malloc(secret_len);
+ if (secret == NULL)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive(ctx, secret, &secret_len);
+ if (ret != 1)
+ goto fail_derive;
+
+ ki.nid = kdf;
+ ki.secret.len = secret_len;
+ ki.secret.data = secret;
+ ki.info.len = strlen(HKDF_INFO_DHE);
+ ki.info.data = (uint8_t *) HKDF_INFO_DHE;
+ ki.key.len = SYMMKEYSZ;
+ ki.key.data = s;
+ ki.salt.len = HKDF_SALT_LEN;
+ ki.salt.data = salt_buf;
+
+ /* Derive symmetric key from shared secret using HKDF */
+ ret = derive_key_hkdf(&ki);
+
+ OPENSSL_free(secret);
+ EVP_PKEY_CTX_free(ctx);
+ OPENSSL_free(local_pk.data);
+
+ if (ret != 0)
+ return ret;
+
+ return 0;
+ fail_derive:
+ OPENSSL_free(secret);
+ fail_ctx:
+ EVP_PKEY_CTX_free(ctx);
+ fail_salt:
+ OPENSSL_free(local_pk.data);
+ fail_local:
+ return -ECRYPT;
+}
+
+static int __openssl_dhe_gen_key(const char * algo,
+ EVP_PKEY ** kp)
+{
+ EVP_PKEY_CTX * ctx = NULL;
+ EVP_PKEY_CTX * kctx = NULL;
+ EVP_PKEY * params = NULL;
+ int nid;
+ int type;
+ int ret;
+
+ assert(algo != NULL);
+ assert(kp != NULL);
+
+ nid = OBJ_txt2nid(algo);
+ if (nid == NID_undef)
+ return -ECRYPT;
+
+ /* X25519 and X448: direct keygen context */
+ if (nid == EVP_PKEY_X25519 || nid == EVP_PKEY_X448) {
+ kctx = EVP_PKEY_CTX_new_id(nid, NULL);
+ if (kctx == NULL)
+ goto fail_kctx;
+
+ goto keygen;
+ }
+ /* EC and FFDHE: parameter generation first */
+ type = (strncmp(algo, "ffdhe", 5) == 0) ? EVP_PKEY_DH : EVP_PKEY_EC;
+
+ ctx = EVP_PKEY_CTX_new_id(type, NULL);
+ if (ctx == NULL)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_paramgen_init(ctx);
+ if (ret != 1)
+ goto fail_paramgen;
+
+ if (type == EVP_PKEY_EC)
+ ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid);
+ else /* EVP_PKEY_DH */
+ ret = EVP_PKEY_CTX_set_dh_nid(ctx, nid);
+
+ if (ret != 1)
+ goto fail_paramgen;
+
+ ret = EVP_PKEY_paramgen(ctx, &params);
+ if (ret != 1)
+ goto fail_paramgen;
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (kctx == NULL)
+ goto fail_kctx;
+
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ keygen:
+ ret = EVP_PKEY_keygen_init(kctx);
+ if (ret != 1)
+ goto fail_keygen;
+
+ ret = EVP_PKEY_keygen(kctx, kp);
+ if (ret != 1)
+ goto fail_keygen;
+
+ EVP_PKEY_CTX_free(kctx);
+
+ return 0;
+
+ fail_keygen:
+ EVP_PKEY_CTX_free(kctx);
+ return -ECRYPT;
+ fail_kctx:
+ if (params != NULL)
+ EVP_PKEY_free(params);
+ fail_paramgen:
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+ fail_ctx:
+ return -ECRYPT;
+}
+
+static int __openssl_kem_gen_key(const char * algo,
+ EVP_PKEY ** kp)
+{
+ EVP_PKEY_CTX * kctx;
+ int ret;
+
+ assert(algo != NULL);
+ assert(kp != NULL);
+
+ /* PQC KEM (ML-KEM-512, ML-KEM-768, ML-KEM-1024) or hybrid */
+ kctx = EVP_PKEY_CTX_new_from_name(NULL, algo, NULL);
+ if (kctx == NULL)
+ goto fail_kctx;
+
+ ret = EVP_PKEY_keygen_init(kctx);
+ if (ret != 1)
+ goto fail_keygen;
+
+ ret = EVP_PKEY_keygen(kctx, kp);
+ if (ret != 1)
+ goto fail_keygen;
+
+ EVP_PKEY_CTX_free(kctx);
+
+ return 0;
+
+ fail_keygen:
+ EVP_PKEY_CTX_free(kctx);
+ fail_kctx:
+ return -ECRYPT;
+}
+
+/* Determine hybrid KEM algorithm from raw key/ciphertext length */
+static const char * __openssl_hybrid_algo_from_len(size_t len)
+{
+ switch(len) {
+ case X25519MLKEM768_PKSZ:
+ return "X25519MLKEM768";
+ case X25519MLKEM768_CTSZ:
+ return "X25519MLKEM768";
+ case X448MLKEM1024_PKSZ:
+ return "X448MLKEM1024";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static int __openssl_kex_gen_key(const char * algo,
+ EVP_PKEY ** kp)
+{
+ assert(algo != NULL);
+ assert(kp != NULL);
+
+ /* Dispatch based on algorithm name prefix */
+ if (IS_KEM_ALGORITHM(algo))
+ return __openssl_kem_gen_key(algo, kp);
+
+ return __openssl_dhe_gen_key(algo, kp);
+}
+
+ssize_t openssl_pkp_create(const char * algo,
+ EVP_PKEY ** pkp,
+ uint8_t * pk)
+{
+ uint8_t * pos;
+ buffer_t raw;
+ ssize_t len;
+
+ assert(algo != NULL);
+ assert(pkp != NULL);
+ assert(*pkp == NULL);
+ assert(pk != NULL);
+
+ if (__openssl_kex_gen_key(algo, pkp) < 0)
+ goto fail_key;
+
+ if (IS_HYBRID_KEM(algo)) { /* Raw encode hybrid KEM */
+ raw.len = EVP_PKEY_get1_encoded_public_key(*pkp, &raw.data);
+ if (raw.len == 0)
+ goto fail_pubkey;
+
+ memcpy(pk, raw.data, raw.len);
+ OPENSSL_free(raw.data);
+
+ return (ssize_t) raw.len;
+ } else { /* DER encode standard algorithms */
+ pos = pk; /* i2d_PUBKEY increments the pointer, don't use pk! */
+ len = i2d_PUBKEY(*pkp, &pos);
+ if (len < 0)
+ goto fail_pubkey;
+
+ return len;
+ }
+ fail_pubkey:
+ EVP_PKEY_free(*pkp);
+ fail_key:
+ return -ECRYPT;
+}
+
+/* Common KEM encapsulation - pub key and salt already prepared */
+static ssize_t __openssl_kem_encap(EVP_PKEY * pub,
+ uint8_t * salt,
+ uint8_t * ct,
+ int kdf,
+ uint8_t * s)
+{
+ EVP_PKEY_CTX * ctx;
+ struct kdf_info ki;
+ uint8_t * secret;
+ size_t secret_len;
+ size_t ct_len;
+ int ret;
+
+ ctx = EVP_PKEY_CTX_new(pub, NULL);
+ if (ctx == NULL)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_encapsulate_init(ctx, NULL);
+ if (ret != 1)
+ goto fail_encap;
+
+ /* Get required lengths */
+ ret = EVP_PKEY_encapsulate(ctx, NULL, &ct_len, NULL, &secret_len);
+ if (ret != 1 || ct_len > MSGBUFSZ)
+ goto fail_encap;
+
+ /* Allocate buffer for secret */
+ secret = OPENSSL_malloc(secret_len);
+ if (secret == NULL)
+ goto fail_encap;
+
+ /* Perform encapsulation */
+ ret = EVP_PKEY_encapsulate(ctx, ct, &ct_len, secret, &secret_len);
+ if (ret != 1)
+ goto fail_secret;
+
+ ki.secret.len = secret_len;
+ ki.secret.data = secret;
+ ki.nid = kdf;
+ ki.info.len = strlen(HKDF_INFO_ENCAP);
+ ki.info.data = (uint8_t *) HKDF_INFO_ENCAP;
+ ki.key.len = SYMMKEYSZ;
+ ki.key.data = s;
+ ki.salt.len = HKDF_SALT_LEN;
+ ki.salt.data = salt;
+
+ /* Derive symmetric key from shared secret using HKDF */
+ ret = derive_key_hkdf(&ki);
+
+ OPENSSL_free(secret);
+ EVP_PKEY_CTX_free(ctx);
+
+ if (ret != 0)
+ return -ECRYPT;
+
+ return (ssize_t) ct_len;
+
+ fail_secret:
+ OPENSSL_free(secret);
+ fail_encap:
+ EVP_PKEY_CTX_free(ctx);
+ fail_ctx:
+ return -ECRYPT;
+}
+
+/* ML-KEM encapsulation - DER-encoded public key */
+ssize_t openssl_kem_encap(buffer_t pk,
+ uint8_t * ct,
+ int kdf,
+ uint8_t * s)
+{
+ EVP_PKEY * pub;
+ uint8_t * pos;
+ uint8_t salt[HKDF_SALT_LEN];
+ ssize_t ret;
+
+ assert(pk.data != NULL);
+ assert(ct != NULL);
+ assert(s != NULL);
+
+ if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0)
+ goto fail_salt;
+
+ pos = pk.data;
+ pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len);
+ if (pub == NULL)
+ goto fail_salt;
+
+ ret = __openssl_kem_encap(pub, salt, ct, kdf, s);
+
+ EVP_PKEY_free(pub);
+
+ return ret;
+ fail_salt:
+ return -ECRYPT;
+}
+
+/* Hybrid KEM encapsulation: raw-encoded public key */
+ssize_t openssl_kem_encap_raw(buffer_t pk,
+ uint8_t * ct,
+ int kdf,
+ uint8_t * s)
+{
+ EVP_PKEY * pub;
+ const char * algo;
+ uint8_t salt[HKDF_SALT_LEN];
+ ssize_t ret;
+
+ assert(pk.data != NULL);
+ assert(ct != NULL);
+ assert(s != NULL);
+
+ if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0)
+ goto fail_salt;
+
+ algo = __openssl_hybrid_algo_from_len(pk.len);
+ if (algo == NULL)
+ goto fail_salt;
+
+ pub = EVP_PKEY_new_raw_public_key_ex(NULL, algo, NULL,
+ pk.data, pk.len);
+ if (pub == NULL)
+ goto fail_salt;
+
+ ret = __openssl_kem_encap(pub, salt, ct, kdf, s);
+
+ EVP_PKEY_free(pub);
+
+ return ret;
+ fail_salt:
+ return -ECRYPT;
+}
+
+/* KEM decapsulation - used by party that generated the keypair */
+int openssl_kem_decap(EVP_PKEY * priv,
+ buffer_t ct,
+ int kdf,
+ uint8_t * s)
+{
+ EVP_PKEY_CTX * ctx;
+ struct kdf_info ki;
+ buffer_t pk;
+ uint8_t * secret;
+ size_t secret_len;
+ int ret;
+ uint8_t salt[HKDF_SALT_LEN];
+
+ /* Extract public key bytes from private key */
+ if (get_pk_bytes_from_key(priv, &pk) < 0)
+ goto fail_pk;
+
+ if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0)
+ goto fail_salt;
+
+ ctx = EVP_PKEY_CTX_new(priv, NULL);
+ if (ctx == NULL)
+ goto fail_salt;
+
+ ret = EVP_PKEY_decapsulate_init(ctx, NULL);
+ if (ret != 1)
+ goto fail_ctx;
+
+ /* Get required secret length */
+ ret = EVP_PKEY_decapsulate(ctx, NULL, &secret_len, ct.data, ct.len);
+ if (ret != 1)
+ goto fail_ctx;
+
+ /* Allocate buffer for secret */
+ secret = OPENSSL_malloc(secret_len);
+ if (secret == NULL)
+ goto fail_ctx;
+
+ /* Perform decapsulation */
+ ret = EVP_PKEY_decapsulate(ctx, secret, &secret_len, ct.data, ct.len);
+ if (ret != 1)
+ goto fail_secret;
+
+ ki.secret.len = secret_len;
+ ki.secret.data = secret;
+ ki.nid = kdf;
+ ki.info.len = strlen(HKDF_INFO_ENCAP);
+ ki.info.data = (uint8_t *) HKDF_INFO_ENCAP;
+ ki.key.len = SYMMKEYSZ;
+ ki.key.data = s;
+ ki.salt.len = HKDF_SALT_LEN;
+ ki.salt.data = salt;
+
+ /* Derive symmetric key from shared secret using HKDF */
+ ret = derive_key_hkdf(&ki);
+
+ OPENSSL_free(secret);
+ EVP_PKEY_CTX_free(ctx);
+ OPENSSL_free(pk.data);
+
+ if (ret != 0)
+ return ret;
+
+ return 0;
+
+ fail_secret:
+ OPENSSL_free(secret);
+ fail_ctx:
+ EVP_PKEY_CTX_free(ctx);
+ fail_salt:
+ OPENSSL_free(pk.data);
+ fail_pk:
+ return -ECRYPT;
+}
+
+void openssl_pkp_destroy(EVP_PKEY * pkp)
+{
+ EVP_PKEY_free(pkp);
+}
+
+int __openssl_get_curve(EVP_PKEY * pub,
+ char * algo)
+{
+ int ret;
+ size_t len = KEX_ALGO_BUFSZ;
+
+ ret = EVP_PKEY_get_utf8_string_param(pub, "group", algo, len, &len);
+ return ret == 1 ? 0 : -ECRYPT;
+}
+
+int openssl_get_algo_from_pk_der(buffer_t pk,
+ char * algo)
+{
+ uint8_t * pos;
+ EVP_PKEY * pub;
+ char * type_str;
+
+ assert(pk.data != NULL);
+ assert(algo != NULL);
+
+ pos = pk.data;
+ pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len);
+ if (pub == NULL)
+ goto fail_decode;
+
+ type_str = (char *) EVP_PKEY_get0_type_name(pub);
+ if (type_str == NULL)
+ goto fail_pub;
+
+ strcpy(algo, type_str);
+
+ if ((IS_EC_GROUP(algo) || IS_DH_GROUP(algo)) &&
+ __openssl_get_curve(pub, algo) < 0)
+ goto fail_pub;
+
+ EVP_PKEY_free(pub);
+ return 0;
+
+ fail_pub:
+ EVP_PKEY_free(pub);
+ fail_decode:
+ return -ECRYPT;
+}
+
+int openssl_get_algo_from_pk_raw(buffer_t pk,
+ char * algo)
+{
+ const char * hybrid_algo;
+
+ assert(pk.data != NULL);
+ assert(algo != NULL);
+
+ hybrid_algo = __openssl_hybrid_algo_from_len(pk.len);
+ if (hybrid_algo == NULL)
+ return -ECRYPT;
+
+ strcpy(algo, hybrid_algo);
+
+ return 0;
+}
+
+int openssl_dhe_derive(EVP_PKEY * pkp,
+ buffer_t pk,
+ int kdf,
+ uint8_t * s)
+{
+ uint8_t * pos;
+ EVP_PKEY * pub;
+
+ assert(pkp != NULL);
+ assert(pk.data != NULL);
+ assert(s != NULL);
+
+ /* X.509 DER decoding for DHE */
+ pos = pk.data; /* d2i_PUBKEY increments pos, don't use key ptr! */
+ pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len);
+ if (pub == NULL)
+ goto fail_decode;
+
+ if (__openssl_dhe_derive(pkp, pub, pk, kdf, s) < 0)
+ goto fail_derive;
+
+ EVP_PKEY_free(pub);
+
+ return 0;
+ fail_derive:
+ EVP_PKEY_free(pub);
+ fail_decode:
+ return -ECRYPT;
+}
+
+int openssl_encrypt(struct ossl_crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out)
+{
+ uint8_t * ptr;
+ uint8_t * iv;
+ int in_sz;
+ int out_sz;
+ int tmp_sz;
+ int ret;
+
+ assert(ctx != NULL);
+
+ in_sz = (int) in.len;
+
+ out->data = malloc(in.len + EVP_MAX_BLOCK_LENGTH + \
+ ctx->ivsz + ctx->tagsz);
+ if (out->data == NULL)
+ goto fail_malloc;
+
+ iv = out->data;
+ ptr = out->data + ctx->ivsz;
+
+ if (random_buffer(iv, ctx->ivsz) < 0)
+ goto fail_encrypt;
+
+ /* Set IV bit 7 to current key phase (bit KEY_ROTATION_BIT of counter) */
+ if (ctx->rot.cntr & ctx->rot.mask)
+ iv[0] |= 0x80;
+ else
+ iv[0] &= 0x7F;
+
+ EVP_CIPHER_CTX_reset(ctx->evp_ctx);
+
+ ret = EVP_EncryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL,
+ ctx->keys.cur, iv);
+ if (ret != 1)
+ goto fail_encrypt;
+
+ ret = EVP_EncryptUpdate(ctx->evp_ctx, ptr, &tmp_sz, in.data, in_sz);
+ if (ret != 1)
+ goto fail_encrypt;
+
+ out_sz = tmp_sz;
+ ret = EVP_EncryptFinal_ex(ctx->evp_ctx, ptr + tmp_sz, &tmp_sz);
+ if (ret != 1)
+ goto fail_encrypt;
+
+ out_sz += tmp_sz;
+
+ /* For AEAD ciphers, get and append the authentication tag */
+ if (ctx->tagsz > 0) {
+ ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_GET_TAG,
+ ctx->tagsz, ptr + out_sz);
+ if (ret != 1)
+ goto fail_encrypt;
+ out_sz += ctx->tagsz;
+ }
+
+ assert(out_sz >= in_sz);
+
+ out->len = (size_t) out_sz + ctx->ivsz;
+
+ /* Increment packet counter and check for key rotation */
+ ctx->rot.cntr++;
+ ctx->rot.age++;
+
+ if (HAS_PHASE_BIT_TOGGLED(ctx)) {
+ if (rotate_key(ctx) != 0)
+ goto fail_encrypt;
+ }
+
+ cleanup_old_key(ctx);
+
+ return 0;
+ fail_encrypt:
+ free(out->data);
+ fail_malloc:
+ clrbuf(*out);
+ return -ECRYPT;
+}
+
+int openssl_decrypt(struct ossl_crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out)
+{
+ uint8_t * iv;
+ uint8_t * input;
+ uint8_t rx_phase;
+ int out_sz;
+ int in_sz;
+
+ assert(ctx != NULL);
+
+ in_sz = (int) in.len - ctx->ivsz;
+ if (in_sz < ctx->tagsz)
+ return -ECRYPT;
+
+ in_sz -= ctx->tagsz;
+
+ out->data = malloc(in_sz + EVP_MAX_BLOCK_LENGTH);
+ if (out->data == NULL)
+ goto fail_malloc;
+
+ iv = in.data;
+ input = in.data + ctx->ivsz;
+
+ /* Extract phase from IV bit 7 and check for key rotation */
+ rx_phase = (iv[0] & 0x80) ? 1 : 0;
+
+ if (should_rotate_key_rx(ctx, rx_phase)) {
+ if (rotate_key(ctx) != 0)
+ goto fail_decrypt;
+ }
+
+ ctx->rot.cntr++;
+ ctx->rot.age++;
+
+ if (try_decrypt(ctx, ctx->keys.cur, iv, input, in_sz, out->data,
+ &out_sz) != 0) {
+ if (ctx->keys.prv == NULL)
+ goto fail_decrypt;
+ if (try_decrypt(ctx, ctx->keys.prv, iv, input, in_sz,
+ out->data, &out_sz) != 0)
+ goto fail_decrypt;
+ }
+
+ assert(out_sz <= in_sz);
+
+ out->len = (size_t) out_sz;
+
+ return 0;
+ fail_decrypt:
+ free(out->data);
+ fail_malloc:
+ clrbuf(*out);
+ return -ECRYPT;
+}
+
+struct ossl_crypt_ctx * openssl_crypt_create_ctx(struct crypt_sk * sk)
+{
+ struct ossl_crypt_ctx * ctx;
+
+ assert(sk != NULL);
+ assert(sk->key != NULL);
+ assert(sk->rot_bit > 0 && sk->rot_bit < 32);
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto fail_malloc;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->keys.cur = OPENSSL_secure_malloc(SYMMKEYSZ);
+ if (ctx->keys.cur == NULL)
+ goto fail_key;
+
+ memcpy(ctx->keys.cur, sk->key, SYMMKEYSZ);
+
+ ctx->keys.prv = NULL;
+
+ /* Derive rotation salt from initial shared secret */
+ if (EVP_Digest(sk->key, SYMMKEYSZ, ctx->rot.salt, NULL,
+ EVP_sha256(), NULL) != 1)
+ goto fail_cipher;
+
+ ctx->cipher = EVP_get_cipherbynid(sk->nid);
+ if (ctx->cipher == NULL)
+ goto fail_cipher;
+
+ ctx->ivsz = EVP_CIPHER_iv_length(ctx->cipher);
+
+ /* Set tag size for AEAD ciphers (GCM, CCM, OCB, ChaCha20-Poly1305) */
+ if (EVP_CIPHER_flags(ctx->cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)
+ ctx->tagsz = 16; /* Standard AEAD tag length (128 bits) */
+
+ ctx->rot.cntr = 0;
+ ctx->rot.mask = (1U << sk->rot_bit);
+ ctx->rot.age = 0;
+ ctx->rot.phase = 0;
+
+ ctx->evp_ctx = EVP_CIPHER_CTX_new();
+ if (ctx->evp_ctx == NULL)
+ goto fail_cipher;
+
+ return ctx;
+
+ fail_cipher:
+ OPENSSL_secure_clear_free(ctx->keys.cur, SYMMKEYSZ);
+ fail_key:
+ free(ctx);
+ fail_malloc:
+ return NULL;
+}
+
+void openssl_crypt_destroy_ctx(struct ossl_crypt_ctx * ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ if (ctx->keys.cur != NULL)
+ OPENSSL_secure_clear_free(ctx->keys.cur, SYMMKEYSZ);
+
+ if (ctx->keys.prv != NULL)
+ OPENSSL_secure_clear_free(ctx->keys.prv, SYMMKEYSZ);
+
+ EVP_CIPHER_CTX_free(ctx->evp_ctx);
+ free(ctx);
+}
+
+int openssl_crypt_get_ivsz(struct ossl_crypt_ctx * ctx)
+{
+ assert(ctx != NULL);
+
+ return ctx->ivsz;
+}
+
+int openssl_crypt_get_tagsz(struct ossl_crypt_ctx * ctx)
+{
+ assert(ctx != NULL);
+
+ return ctx->tagsz;
+}
+
+/* AUTHENTICATION */
+
+int openssl_load_crt_file(const char * path,
+ void ** crt)
+{
+ FILE * fp;
+ X509 * xcrt;
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ goto fail_file;
+
+ xcrt = PEM_read_X509(fp, NULL, NULL, NULL);
+ if (xcrt == NULL)
+ goto fail_crt;
+
+ fclose(fp);
+
+ *crt = (void *) xcrt;
+
+ return 0;
+ fail_crt:
+ fclose(fp);
+ fail_file:
+ *crt = NULL;
+ return -1;
+}
+
+int openssl_load_crt_str(const char * str,
+ void ** crt)
+{
+ BIO * bio;
+ X509 * xcrt;
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL)
+ goto fail_bio;
+
+ if (BIO_write(bio, str, strlen(str)) < 0)
+ goto fail_crt;
+
+ xcrt = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (xcrt == NULL)
+ goto fail_crt;
+
+ BIO_free(bio);
+
+ *crt = (void *) xcrt;
+
+ return 0;
+ fail_crt:
+ BIO_free(bio);
+ fail_bio:
+ *crt = NULL;
+ return -1;
+}
+
+int openssl_load_crt_der(buffer_t buf,
+ void ** crt)
+{
+ const uint8_t * p;
+ X509 * xcrt;
+
+ assert(crt != NULL);
+
+ p = buf.data;
+
+ xcrt = d2i_X509(NULL, &p, buf.len);
+ if (xcrt == NULL)
+ goto fail_crt;
+
+ *crt = (void *) xcrt;
+
+ return 0;
+ fail_crt:
+ *crt = NULL;
+ return -1;
+}
+
+int openssl_get_pubkey_crt(void * crt,
+ void ** key)
+{
+ EVP_PKEY * pk;
+ X509 * xcrt;
+
+ assert(crt != NULL);
+ assert(key != NULL);
+
+ xcrt = (X509 *) crt;
+
+ pk = X509_get_pubkey(xcrt);
+ if (pk == NULL)
+ goto fail_key;
+
+ *key = (void *) pk;
+
+ return 0;
+ fail_key:
+ return -1;
+}
+
+void openssl_free_crt(void * crt)
+{
+ X509_free((X509 *) crt);
+}
+
+int openssl_load_privkey_file(const char * path,
+ void ** key)
+{
+ FILE * fp;
+ EVP_PKEY * pkey;
+ unsigned long err;
+ char errbuf[256];
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Failed to open %s\n", path);
+ goto fail_file;
+ }
+
+ pkey = PEM_read_PrivateKey(fp, NULL, NULL, "");
+ if (pkey == NULL) {
+ err = ERR_get_error();
+ ERR_error_string_n(err, errbuf, sizeof(errbuf));
+ fprintf(stderr,
+ "OpenSSL error loading privkey from %s: %s\n",
+ path, errbuf);
+ goto fail_key;
+ }
+
+ fclose(fp);
+
+ *key = (void *) pkey;
+
+ return 0;
+ fail_key:
+ fclose(fp);
+ fail_file:
+ *key = NULL;
+ return -1;
+}
+
+int openssl_load_privkey_str(const char * str,
+ void ** key)
+{
+ BIO * bio;
+ EVP_PKEY * pkey;
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL)
+ goto fail_bio;
+
+ if (BIO_write(bio, str, strlen(str)) < 0)
+ goto fail_key;
+
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto fail_key;
+
+ BIO_free(bio);
+
+ *key = (void *) pkey;
+
+ return 0;
+ fail_key:
+ BIO_free(bio);
+ fail_bio:
+ *key = NULL;
+ return -1;
+}
+
+int openssl_load_pubkey_file(const char * path,
+ void ** key)
+{
+ FILE * fp;
+ EVP_PKEY * pkey;
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ goto fail_file;
+
+ pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto fail_key;
+
+ fclose(fp);
+
+ *key = (void *) pkey;
+
+ return 0;
+ fail_key:
+ fclose(fp);
+ fail_file:
+ *key = NULL;
+ return -1;
+}
+
+int openssl_load_pubkey_file_to_der(const char * path,
+ buffer_t * buf)
+{
+ FILE * fp;
+ EVP_PKEY * pkey;
+ int ret;
+
+ assert(path != NULL);
+ assert(buf != NULL);
+
+ memset(buf, 0, sizeof(*buf));
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ goto fail_file;
+
+ pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto fail_key;
+
+ /* Extract public key bytes in DER format */
+ ret = get_pk_bytes_from_key(pkey, buf);
+ if (ret < 0)
+ goto fail_extract;
+
+ EVP_PKEY_free(pkey);
+
+ fclose(fp);
+
+ return 0;
+
+ fail_extract:
+ EVP_PKEY_free(pkey);
+ fail_key:
+ fclose(fp);
+ fail_file:
+ clrbuf(*buf);
+ return -1;
+}
+
+int openssl_load_pubkey_str(const char * str,
+ void ** key)
+{
+ BIO * bio;
+ EVP_PKEY * pkey;
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL)
+ goto fail_bio;
+
+ if (BIO_write(bio, str, strlen(str)) < 0)
+ goto fail_key;
+
+ pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto fail_key;
+
+ BIO_free(bio);
+
+ *key = (void *) pkey;
+
+ return 0;
+ fail_key:
+ BIO_free(bio);
+ fail_bio:
+ *key = NULL;
+ return -1;
+}
+
+int openssl_load_pubkey_raw_file(const char * path,
+ buffer_t * buf)
+{
+ FILE * fp;
+ uint8_t tmp_buf[MSGBUFSZ];
+ size_t bytes_read;
+ const char * algo;
+
+ assert(path != NULL);
+ assert(buf != NULL);
+
+ fp = fopen(path, "rb");
+ if (fp == NULL)
+ goto fail_file;
+
+ bytes_read = fread(tmp_buf, 1, MSGBUFSZ, fp);
+ if (bytes_read == 0)
+ goto fail_read;
+
+ /* Validate that this is a known hybrid KEM format */
+ algo = __openssl_hybrid_algo_from_len(bytes_read);
+ if (algo == NULL)
+ goto fail_read;
+
+ buf->data = malloc(bytes_read);
+ if (buf->data == NULL)
+ goto fail_malloc;
+
+ memcpy(buf->data, tmp_buf, bytes_read);
+ buf->len = bytes_read;
+
+ fclose(fp);
+ return 0;
+
+ fail_malloc:
+ fail_read:
+ fclose(fp);
+ fail_file:
+ clrbuf(*buf);
+ return -1;
+}
+
+/* Determine hybrid KEM algorithm from raw private key length */
+static const char * __openssl_hybrid_algo_from_sk_len(size_t len)
+{
+ switch(len) {
+ case X25519MLKEM768_SKSZ:
+ return "X25519MLKEM768";
+ case X448MLKEM1024_SKSZ:
+ return "X448MLKEM1024";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+int openssl_load_privkey_raw_file(const char * path,
+ void ** key)
+{
+ FILE * fp;
+ uint8_t tmp_buf[4096];
+ size_t bytes_read;
+ const char * algo;
+ EVP_PKEY * pkey;
+
+ assert(path != NULL);
+ assert(key != NULL);
+
+ fp = fopen(path, "rb");
+ if (fp == NULL)
+ goto fail_file;
+
+ bytes_read = fread(tmp_buf, 1, sizeof(tmp_buf), fp);
+ if (bytes_read == 0)
+ goto fail_read;
+
+ /* Determine algorithm from key size */
+ algo = __openssl_hybrid_algo_from_sk_len(bytes_read);
+ if (algo == NULL)
+ goto fail_read;
+
+ pkey = EVP_PKEY_new_raw_private_key_ex(NULL, algo, NULL,
+ tmp_buf, bytes_read);
+ /* Clear sensitive data from stack */
+ OPENSSL_cleanse(tmp_buf, bytes_read);
+
+ if (pkey == NULL)
+ goto fail_read;
+
+ fclose(fp);
+
+ *key = (void *) pkey;
+
+ return 0;
+
+ fail_read:
+ fclose(fp);
+ fail_file:
+ *key = NULL;
+ return -1;
+}
+
+int openssl_cmp_key(const EVP_PKEY * key1,
+ const EVP_PKEY * key2)
+{
+ assert(key1 != NULL);
+ assert(key2 != NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ return EVP_PKEY_eq(key1, key2) == 1 ? 0 : -1;
+#else
+ return EVP_PKEY_cmp(key1, key2) == 1 ? 0 : -1;
+#endif
+}
+
+void openssl_free_key(EVP_PKEY * key)
+{
+ EVP_PKEY_free(key);
+}
+
+int openssl_check_crt_name(void * crt,
+ const char * name)
+{
+ char * subj;
+ char * cn;
+ X509 * xcrt;
+
+ xcrt = (X509 *) crt;
+
+ subj = X509_NAME_oneline(X509_get_subject_name(xcrt), NULL, 0);
+ if (subj == NULL)
+ goto fail_subj;
+
+ cn = strstr(subj, "CN=");
+ if (cn == NULL)
+ goto fail_cn;
+
+ if (strcmp(cn + 3, name) != 0)
+ goto fail_cn;
+
+ free(subj);
+
+ return 0;
+ fail_cn:
+ free(subj);
+ fail_subj:
+ return -1;
+}
+
+int openssl_get_crt_name(void * crt,
+ char * name)
+{
+ char * subj;
+ char * cn;
+ char * end;
+ X509 * xcrt;
+
+ xcrt = (X509 *) crt;
+
+ subj = X509_NAME_oneline(X509_get_subject_name(xcrt), NULL, 0);
+ if (subj == NULL)
+ goto fail_subj;
+
+ cn = strstr(subj, "CN=");
+ if (cn == NULL)
+ goto fail_cn;
+
+ cn += 3; /* Skip "CN=" */
+
+ /* Find end of CN (comma or slash for next field) */
+ end = strpbrk(cn, ",/");
+ if (end != NULL)
+ *end = '\0';
+
+ strcpy(name, cn);
+ free(subj);
+
+ return 0;
+ fail_cn:
+ free(subj);
+ fail_subj:
+ return -1;
+}
+
+int openssl_crt_str(const void * crt,
+ char * str)
+{
+ BIO * bio;
+ X509 * xcrt;
+ char * p;
+ ssize_t len;
+
+ xcrt = (X509 *) crt;
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL)
+ goto fail_bio;
+
+ X509_print(bio, xcrt);
+
+ len = (ssize_t) BIO_get_mem_data(bio, &p);
+ if (len <= 0 || p == NULL)
+ goto fail_p;
+
+ memcpy(str, p, len);
+ str[len] = '\0';
+
+ BIO_free(bio);
+
+ return 0;
+ fail_p:
+ BIO_free(bio);
+ fail_bio:
+ return -1;
+}
+
+int openssl_crt_der(const void * crt,
+ buffer_t * buf)
+{
+ int len;
+
+ assert(crt != NULL);
+ assert(buf != NULL);
+
+ len = i2d_X509((X509 *) crt, &buf->data);
+ if (len < 0)
+ goto fail_der;
+
+ buf->len = (size_t) len;
+
+ return 0;
+
+ fail_der:
+ clrbuf(*buf);
+ return -1;
+}
+
+
+void * openssl_auth_create_store(void)
+{
+ return X509_STORE_new();
+}
+
+void openssl_auth_destroy_store(void * ctx)
+{
+ X509_STORE_free((X509_STORE *) ctx);
+}
+
+int openssl_auth_add_crt_to_store(void * store,
+ void * crt)
+{
+ int ret;
+
+ ret = X509_STORE_add_cert((X509_STORE *) store, (X509 *) crt);
+
+ return ret == 1 ? 0 : -1;
+}
+
+int openssl_verify_crt(void * store,
+ void * crt)
+{
+ X509_STORE_CTX * ctx;
+ X509_STORE * _store;
+ X509* _crt;
+ int ret;
+
+ _store = (X509_STORE *) store;
+ _crt = (X509 *) crt;
+
+ ctx = X509_STORE_CTX_new();
+ if (ctx == NULL)
+ goto fail_store_ctx;
+
+ ret = X509_STORE_CTX_init(ctx, _store, _crt, NULL);
+ if (ret != 1)
+ goto fail_ca;
+
+ ret = X509_verify_cert(ctx);
+ if (ret != 1)
+ goto fail_ca;
+
+ X509_STORE_CTX_free(ctx);
+
+ return 0;
+ fail_ca:
+ X509_STORE_CTX_free(ctx);
+ fail_store_ctx:
+ return -1;
+}
+
+static const EVP_MD * select_md(EVP_PKEY * pkey,
+ int nid)
+{
+ if (EVP_PKEY_get_id(pkey) < 0)
+ return NULL; /* Provider-based (PQC) */
+
+ if (nid == NID_undef)
+ return NULL; /* Classical requires explicit nid */
+
+ return EVP_get_digestbynid(nid);
+}
+
+int openssl_sign(EVP_PKEY * pkp,
+ int nid,
+ buffer_t msg,
+ buffer_t * sig)
+{
+ EVP_MD_CTX * mdctx;
+ const EVP_MD * md;
+ size_t required;
+
+ assert(pkp != NULL);
+ assert(sig != NULL);
+
+ mdctx = EVP_MD_CTX_new();
+ if (!mdctx)
+ goto fail_ctx;
+
+ md = select_md(pkp, nid);
+
+ if (EVP_DigestSignInit(mdctx, NULL, md, NULL, pkp) != 1)
+ goto fail_digest;
+
+ /* Get required signature buffer size */
+ if (EVP_DigestSign(mdctx, NULL, &required, msg.data, msg.len) != 1)
+ goto fail_digest;
+
+ sig->data = malloc(required);
+ if (sig->data == NULL)
+ goto fail_digest;
+
+ if (EVP_DigestSign(mdctx, sig->data, &required, msg.data, msg.len) != 1)
+ goto fail_sign;
+
+ sig->len = required;
+
+ EVP_MD_CTX_free(mdctx);
+
+ return 0;
+ fail_sign:
+ freebuf(*sig);
+ fail_digest:
+ EVP_MD_CTX_free(mdctx);
+ fail_ctx:
+ clrbuf(*sig);
+ return -1;
+}
+
+int openssl_verify_sig(EVP_PKEY * pk,
+ int nid,
+ buffer_t msg,
+ buffer_t sig)
+{
+ EVP_MD_CTX * mdctx;
+ const EVP_MD * md;
+ int ret;
+
+ assert(pk != NULL);
+
+ mdctx = EVP_MD_CTX_new();
+ if (!mdctx)
+ goto fail_ctx;
+
+ md = select_md(pk, nid);
+
+ if (EVP_DigestVerifyInit(mdctx, NULL, md, NULL, pk) != 1)
+ goto fail_digest;
+
+ ret = EVP_DigestVerify(mdctx, sig.data, sig.len, msg.data, msg.len);
+ if (ret != 1)
+ goto fail_digest;
+
+ EVP_MD_CTX_free(mdctx);
+
+ return 0;
+ fail_digest:
+ EVP_MD_CTX_free(mdctx);
+ fail_ctx:
+ clrbuf(sig);
+ return -1;
+}
+
+ssize_t openssl_md_digest(int nid,
+ buffer_t in,
+ uint8_t * out)
+{
+ const EVP_MD * md;
+ unsigned int len;
+
+ assert(in.data != NULL);
+ assert(out != NULL);
+
+ md = EVP_get_digestbynid(nid);
+ if (md == NULL)
+ return -1;
+
+ if (EVP_Digest(in.data, in.len, out, &len, md, NULL) != 1)
+ return -1;
+
+ return (ssize_t) len;
+}
+
+ssize_t openssl_md_len(int nid)
+{
+ const EVP_MD * md;
+
+ md = EVP_get_digestbynid(nid);
+ if (md == NULL)
+ return -1;
+
+ return (ssize_t) EVP_MD_get_size(md);
+}
+
+int openssl_secure_malloc_init(size_t max,
+ size_t guard)
+{
+ return CRYPTO_secure_malloc_init(max, guard) == 1 ? 0 : -1;
+}
+
+void openssl_secure_malloc_fini(void)
+{
+ CRYPTO_secure_malloc_done();
+}
+
+void * openssl_secure_malloc(size_t size)
+{
+ return OPENSSL_secure_malloc(size);
+}
+
+void openssl_secure_free(void * ptr)
+{
+ OPENSSL_secure_free(ptr);
+}
+
+void openssl_secure_clear(void * ptr,
+ size_t size)
+{
+ OPENSSL_cleanse(ptr, size);
+}
diff --git a/src/lib/crypt/openssl.h b/src/lib/crypt/openssl.h
new file mode 100644
index 00000000..a6bb5840
--- /dev/null
+++ b/src/lib/crypt/openssl.h
@@ -0,0 +1,172 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * OpenSSL based cryptographic operations
+ * Elliptic curve Diffie-Hellman key exchange
+ * AES encryption
+ # Authentication
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_CRYPT_OPENSSL_H
+#define OUROBOROS_LIB_CRYPT_OPENSSL_H
+
+struct ossl_crypt_ctx;
+
+ssize_t openssl_pkp_create(const char * algo,
+ EVP_PKEY ** pkp,
+ uint8_t * pk);
+
+void openssl_pkp_destroy(EVP_PKEY * pkp);
+
+int openssl_dhe_derive(EVP_PKEY * pkp,
+ buffer_t pk,
+ int kdf_nid,
+ uint8_t * s);
+
+ssize_t openssl_kem_encap(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+/* no X509 DER support yet for DHKEM public keys */
+ssize_t openssl_kem_encap_raw(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int openssl_kem_decap(EVP_PKEY * priv,
+ buffer_t ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int openssl_get_algo_from_pk_der(buffer_t pk,
+ char * algo);
+
+int openssl_get_algo_from_pk_raw(buffer_t pk,
+ char * algo);
+
+int openssl_encrypt(struct ossl_crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+int openssl_decrypt(struct ossl_crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+struct ossl_crypt_ctx * openssl_crypt_create_ctx(struct crypt_sk * sk);
+
+void openssl_crypt_destroy_ctx(struct ossl_crypt_ctx * ctx);
+
+int openssl_crypt_get_ivsz(struct ossl_crypt_ctx * ctx);
+
+int openssl_crypt_get_tagsz(struct ossl_crypt_ctx * ctx);
+
+/* AUTHENTICATION */
+
+int openssl_load_crt_file(const char * path,
+ void ** crt);
+
+int openssl_load_crt_str(const char * str,
+ void ** crt);
+
+int openssl_load_crt_der(buffer_t buf,
+ void ** crt);
+
+int openssl_get_pubkey_crt(void * crt,
+ void ** pk);
+
+void openssl_free_crt(void * crt);
+
+int openssl_load_privkey_file(const char * path,
+ void ** key);
+
+int openssl_load_privkey_str(const char * str,
+ void ** key);
+
+int openssl_load_pubkey_file(const char * path,
+ void ** key);
+
+int openssl_load_pubkey_str(const char * str,
+ void ** key);
+int openssl_load_pubkey_file_to_der(const char * path,
+ buffer_t * buf);
+int openssl_load_pubkey_raw_file(const char * path,
+ buffer_t * buf);
+
+int openssl_load_privkey_raw_file(const char * path,
+ void ** key);
+
+int openssl_cmp_key(const EVP_PKEY * key1,
+ const EVP_PKEY * key2);
+
+void openssl_free_key(EVP_PKEY * key);
+
+int openssl_check_crt_name(void * crt,
+ const char * name);
+
+int openssl_get_crt_name(void * crt,
+ char * name);
+
+int openssl_crt_str(const void * crt,
+ char * str);
+
+int openssl_crt_der(const void * crt,
+ buffer_t * buf);
+
+void * openssl_auth_create_store(void);
+
+void openssl_auth_destroy_store(void * store);
+
+int openssl_auth_add_crt_to_store(void * store,
+ void * crt);
+
+int openssl_verify_crt(void * store,
+ void * crt);
+
+int openssl_sign(EVP_PKEY * pkp,
+ int md_nid,
+ buffer_t msg,
+ buffer_t * sig);
+
+int openssl_verify_sig(EVP_PKEY * pk,
+ int md_nid,
+ buffer_t msg,
+ buffer_t sig);
+
+ssize_t openssl_md_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out);
+
+ssize_t openssl_md_len(int md_nid);
+
+/* Secure memory allocation */
+int openssl_secure_malloc_init(size_t max,
+ size_t guard);
+
+void openssl_secure_malloc_fini(void);
+
+void * openssl_secure_malloc(size_t size);
+
+void openssl_secure_free(void * ptr,
+ size_t size);
+
+void openssl_secure_clear(void * ptr,
+ size_t size);
+
+#endif /* OUROBOROS_LIB_CRYPT_OPENSSL_H */
diff --git a/src/lib/dev.c b/src/lib/dev.c
index 92310b9e..454dd027 100644
--- a/src/lib/dev.c
+++ b/src/lib/dev.c
@@ -27,6 +27,7 @@
#endif
#include "config.h"
+#include "ssm.h"
#include <ouroboros/bitmap.h>
#include <ouroboros/cep.h>
@@ -45,9 +46,9 @@
#include <ouroboros/pthread.h>
#include <ouroboros/random.h>
#include <ouroboros/serdes-irm.h>
-#include <ouroboros/shm_flow_set.h>
-#include <ouroboros/shm_rdrbuff.h>
-#include <ouroboros/shm_rbuff.h>
+#include <ouroboros/ssm_flow_set.h>
+#include <ouroboros/ssm_pool.h>
+#include <ouroboros/ssm_rbuff.h>
#include <ouroboros/sockets.h>
#include <ouroboros/utils.h>
#ifdef PROC_FLOW_STATS
@@ -92,14 +93,16 @@ struct flow {
struct flow_info info;
- struct shm_rbuff * rx_rb;
- struct shm_rbuff * tx_rb;
- struct shm_flow_set * set;
+ struct ssm_rbuff * rx_rb;
+ struct ssm_rbuff * tx_rb;
+ struct ssm_flow_set * set;
uint16_t oflags;
ssize_t part_idx;
- struct crypt_info crypt;
+ struct crypt_ctx * crypt;
+ int headsz; /* IV */
+ int tailsz; /* Tag + CRC */
struct timespec snd_act;
struct timespec rcv_act;
@@ -118,14 +121,14 @@ struct flow_set {
};
struct fqueue {
- struct flowevent fqueue[SHM_BUFFER_SIZE]; /* Safe copy from shm. */
+ struct flowevent fqueue[SSM_RBUFF_SIZE]; /* Safe copy from shm. */
size_t fqsize;
size_t next;
};
struct {
- struct shm_rdrbuff * rdrb;
- struct shm_flow_set * fqset;
+ struct ssm_pool * pool;
+ struct ssm_flow_set * fqset;
struct bmp * fds;
struct bmp * fqueues;
@@ -143,14 +146,14 @@ struct {
fset_t * frct_set;
pthread_rwlock_t lock;
-} ai;
+} proc;
static void flow_destroy(struct fmap * p)
{
- pthread_mutex_lock(&ai.mtx);
+ pthread_mutex_lock(&proc.mtx);
if (p->state == FLOW_DESTROY) {
- pthread_mutex_unlock(&ai.mtx);
+ pthread_mutex_unlock(&proc.mtx);
return;
}
@@ -159,12 +162,12 @@ static void flow_destroy(struct fmap * p)
else
p->state = FLOW_NULL;
- pthread_cond_signal(&ai.cond);
+ pthread_cond_signal(&proc.cond);
- pthread_cleanup_push(__cleanup_mutex_unlock, &ai.mtx);
+ pthread_cleanup_push(__cleanup_mutex_unlock, &proc.mtx);
while (p->state != FLOW_NULL)
- pthread_cond_wait(&ai.cond, &ai.mtx);
+ pthread_cond_wait(&proc.cond, &proc.mtx);
p->fd = -1;
p->state = FLOW_INIT;
@@ -175,17 +178,17 @@ static void flow_destroy(struct fmap * p)
static void flow_set_state(struct fmap * p,
enum flow_state state)
{
- pthread_mutex_lock(&ai.mtx);
+ pthread_mutex_lock(&proc.mtx);
if (p->state == FLOW_DESTROY) {
- pthread_mutex_unlock(&ai.mtx);
+ pthread_mutex_unlock(&proc.mtx);
return;
}
p->state = state;
- pthread_cond_broadcast(&ai.cond);
+ pthread_cond_broadcast(&proc.cond);
- pthread_mutex_unlock(&ai.mtx);
+ pthread_mutex_unlock(&proc.mtx);
}
static enum flow_state flow_wait_assign(int flow_id)
@@ -193,26 +196,26 @@ static enum flow_state flow_wait_assign(int flow_id)
enum flow_state state;
struct fmap * p;
- p = &ai.id_to_fd[flow_id];
+ p = &proc.id_to_fd[flow_id];
- pthread_mutex_lock(&ai.mtx);
+ pthread_mutex_lock(&proc.mtx);
if (p->state == FLOW_ALLOCATED) {
- pthread_mutex_unlock(&ai.mtx);
+ pthread_mutex_unlock(&proc.mtx);
return FLOW_ALLOCATED;
}
if (p->state == FLOW_INIT)
p->state = FLOW_ALLOC_PENDING;
- pthread_cleanup_push(__cleanup_mutex_unlock, &ai.mtx);
+ pthread_cleanup_push(__cleanup_mutex_unlock, &proc.mtx);
while (p->state == FLOW_ALLOC_PENDING)
- pthread_cond_wait(&ai.cond, &ai.mtx);
+ pthread_cond_wait(&proc.cond, &proc.mtx);
if (p->state == FLOW_DESTROY) {
p->state = FLOW_NULL;
- pthread_cond_broadcast(&ai.cond);
+ pthread_cond_broadcast(&proc.cond);
}
state = p->state;
@@ -224,13 +227,13 @@ static enum flow_state flow_wait_assign(int flow_id)
return state;
}
-static int proc_announce(const char * prog)
+static int proc_announce(const struct proc_info * proc)
{
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
int err;
- if (proc_announce__irm_req_ser(&msg, prog) < 0)
+ if (proc_announce__irm_req_ser(&msg, proc) < 0)
return -ENOMEM;
err = send_recv_msg(&msg);
@@ -244,7 +247,7 @@ static int proc_announce(const char * prog)
static void proc_exit(void)
{
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
if (proc_exit__irm_req_ser(&msg) < 0)
return;
@@ -252,6 +255,69 @@ static void proc_exit(void)
send_recv_msg(&msg);
}
+static int spb_encrypt(struct flow * flow,
+ struct ssm_pk_buff * spb)
+{
+ buffer_t in;
+ buffer_t out;
+ uint8_t * head;
+ uint8_t * tail;
+
+ if (flow->crypt == NULL)
+ return 0; /* No encryption */
+
+ in.data = ssm_pk_buff_head(spb);
+ in.len = ssm_pk_buff_len(spb);
+
+ if (crypt_encrypt(flow->crypt, in, &out) < 0)
+ goto fail_encrypt;
+
+ head = ssm_pk_buff_head_alloc(spb, flow->headsz);
+ if (head == NULL)
+ goto fail_alloc;
+
+ tail = ssm_pk_buff_tail_alloc(spb, flow->tailsz);
+ if (tail == NULL)
+ goto fail_alloc;
+
+ memcpy(head, out.data, out.len);
+
+ freebuf(out);
+
+ return 0;
+ fail_alloc:
+ freebuf(out);
+ fail_encrypt:
+ return -ECRYPT;
+}
+
+static int spb_decrypt(struct flow * flow,
+ struct ssm_pk_buff * spb)
+{
+ buffer_t in;
+ buffer_t out;
+ uint8_t * head;
+
+ if (flow->crypt == NULL)
+ return 0; /* No decryption */
+
+ in.data = ssm_pk_buff_head(spb);
+ in.len = ssm_pk_buff_len(spb);
+
+ if (crypt_decrypt(flow->crypt, in, &out) < 0)
+ return -ENOMEM;
+
+
+ head = ssm_pk_buff_head_release(spb, flow->headsz) + flow->headsz;
+ ssm_pk_buff_tail_release(spb, flow->tailsz);
+
+ memcpy(head, out.data, out.len);
+
+ freebuf(out);
+
+ return 0;
+}
+
#include "frct.c"
void * flow_tx(void * o)
@@ -272,27 +338,27 @@ void * flow_tx(void * o)
static void flow_send_keepalive(struct flow * flow,
struct timespec now)
{
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
ssize_t idx;
uint8_t * ptr;
- idx = shm_rdrbuff_alloc(ai.rdrb, 0, &ptr, &sdb);
+ idx = ssm_pool_alloc(proc.pool, 0, &ptr, &spb);
if (idx < 0)
return;
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
flow->snd_act = now;
- if (shm_rbuff_write(flow->tx_rb, idx))
- shm_rdrbuff_remove(ai.rdrb, idx);
+ if (ssm_rbuff_write(flow->tx_rb, idx))
+ ssm_pool_remove(proc.pool, idx);
else
- shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
+ ssm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
}
-/* Needs rdlock on ai. */
+/* Needs rdlock on proc. */
static void _flow_keepalive(struct flow * flow)
{
struct timespec now;
@@ -308,24 +374,24 @@ static void _flow_keepalive(struct flow * flow)
flow_id = flow->info.id;
timeo = flow->info.qs.timeout;
- acl = shm_rbuff_get_acl(flow->rx_rb);
- if (timeo == 0 || acl & (ACL_FLOWPEER | ACL_FLOWDOWN))
+ acl = ssm_rbuff_get_acl(flow->rx_rb);
+ if (timeo == 0 || acl & (ACL_FLOWPEER | ACL_FLOWDOWN))
return;
clock_gettime(PTHREAD_COND_CLOCK, &now);
- if (ts_diff_ns(&r_act, &now) > (int64_t) timeo * MILLION) {
- shm_rbuff_set_acl(flow->rx_rb, ACL_FLOWPEER);
- shm_flow_set_notify(ai.fqset, flow_id, FLOW_PEER);
+ if (ts_diff_ns(&now, &r_act) > (int64_t) timeo * MILLION) {
+ ssm_rbuff_set_acl(flow->rx_rb, ACL_FLOWPEER);
+ ssm_flow_set_notify(proc.fqset, flow_id, FLOW_PEER);
return;
}
- if (ts_diff_ns(&s_act, &now) > (int64_t) timeo * (MILLION >> 2)) {
- pthread_rwlock_unlock(&ai.lock);
+ if (ts_diff_ns(&now, &s_act) > (int64_t) timeo * (MILLION >> 2)) {
+ pthread_rwlock_unlock(&proc.lock);
flow_send_keepalive(flow, now);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
}
}
@@ -334,15 +400,15 @@ static void handle_keepalives(void)
struct list_head * p;
struct list_head * h;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- list_for_each_safe(p, h, &ai.flow_list) {
+ list_for_each_safe(p, h, &proc.flow_list) {
struct flow * flow;
flow = list_entry(p, struct flow, next);
_flow_keepalive(flow);
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
}
static void __cleanup_fqueue_destroy(void * fq)
@@ -363,7 +429,7 @@ void * flow_rx(void * o)
pthread_cleanup_push(__cleanup_fqueue_destroy, fq);
/* fevent will filter all FRCT packets for us */
- while ((ret = fevent(ai.frct_set, fq, &tic)) != 0) {
+ while ((ret = fevent(proc.frct_set, fq, &tic)) != 0) {
if (ret == -ETIMEDOUT) {
handle_keepalives();
continue;
@@ -380,67 +446,69 @@ void * flow_rx(void * o)
static void flow_clear(int fd)
{
- memset(&ai.flows[fd], 0, sizeof(ai.flows[fd]));
+ memset(&proc.flows[fd], 0, sizeof(proc.flows[fd]));
- ai.flows[fd].info.id = -1;
+ proc.flows[fd].info.id = -1;
}
static void __flow_fini(int fd)
{
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- if (ai.flows[fd].frcti != NULL) {
- ai.n_frcti--;
- if (ai.n_frcti == 0) {
- pthread_cancel(ai.tx);
- pthread_join(ai.tx, NULL);
+ if (proc.flows[fd].frcti != NULL) {
+ proc.n_frcti--;
+ if (proc.n_frcti == 0) {
+ pthread_cancel(proc.tx);
+ pthread_join(proc.tx, NULL);
}
- shm_flow_set_del(ai.fqset, 0, ai.flows[fd].info.id);
+ ssm_flow_set_del(proc.fqset, 0, proc.flows[fd].info.id);
- frcti_destroy(ai.flows[fd].frcti);
+ frcti_destroy(proc.flows[fd].frcti);
}
- if (ai.flows[fd].info.id != -1) {
- flow_destroy(&ai.id_to_fd[ai.flows[fd].info.id]);
- bmp_release(ai.fds, fd);
+ if (proc.flows[fd].info.id != -1) {
+ flow_destroy(&proc.id_to_fd[proc.flows[fd].info.id]);
+ bmp_release(proc.fds, fd);
}
- if (ai.flows[fd].rx_rb != NULL) {
- shm_rbuff_set_acl(ai.flows[fd].rx_rb, ACL_FLOWDOWN);
- shm_rbuff_close(ai.flows[fd].rx_rb);
+ if (proc.flows[fd].rx_rb != NULL) {
+ ssm_rbuff_set_acl(proc.flows[fd].rx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_close(proc.flows[fd].rx_rb);
}
- if (ai.flows[fd].tx_rb != NULL) {
- shm_rbuff_set_acl(ai.flows[fd].tx_rb, ACL_FLOWDOWN);
- shm_rbuff_close(ai.flows[fd].tx_rb);
+ if (proc.flows[fd].tx_rb != NULL) {
+ ssm_rbuff_set_acl(proc.flows[fd].tx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_close(proc.flows[fd].tx_rb);
}
- if (ai.flows[fd].set != NULL) {
- shm_flow_set_notify(ai.flows[fd].set,
- ai.flows[fd].info.id,
+ if (proc.flows[fd].set != NULL) {
+ ssm_flow_set_notify(proc.flows[fd].set,
+ proc.flows[fd].info.id,
FLOW_DEALLOC);
- shm_flow_set_close(ai.flows[fd].set);
+ ssm_flow_set_close(proc.flows[fd].set);
}
- crypt_fini(&ai.flows[fd].crypt);
+ crypt_destroy_ctx(proc.flows[fd].crypt);
- list_del(&ai.flows[fd].next);
+ list_del(&proc.flows[fd].next);
flow_clear(fd);
}
static void flow_fini(int fd)
{
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
__flow_fini(fd);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
}
+#define IS_ENCRYPTED(crypt) ((crypt)->nid != NID_undef)
+#define IS_ORDERED(flow) (flow.qs.in_order != 0)
static int flow_init(struct flow_info * info,
- buffer_t * sk)
+ struct crypt_sk * sk)
{
struct timespec now;
struct flow * flow;
@@ -449,27 +517,27 @@ static int flow_init(struct flow_info * info,
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
- fd = bmp_allocate(ai.fds);
- if (!bmp_is_id_valid(ai.fds, fd)) {
+ fd = bmp_allocate(proc.fds);
+ if (!bmp_is_id_valid(proc.fds, fd)) {
err = -EBADF;
goto fail_fds;
}
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
flow->info = *info;
- flow->rx_rb = shm_rbuff_open(info->n_pid, info->id);
+ flow->rx_rb = ssm_rbuff_open(info->n_pid, info->id);
if (flow->rx_rb == NULL)
goto fail_rx_rb;
- flow->tx_rb = shm_rbuff_open(info->n_1_pid, info->id);
+ flow->tx_rb = ssm_rbuff_open(info->n_1_pid, info->id);
if (flow->tx_rb == NULL)
goto fail_tx_rb;
- flow->set = shm_flow_set_open(info->n_1_pid);
+ flow->set = ssm_flow_set_open(info->n_1_pid);
if (flow->set == NULL)
goto fail_set;
@@ -477,59 +545,62 @@ static int flow_init(struct flow_info * info,
flow->part_idx = NO_PART;
flow->snd_act = now;
flow->rcv_act = now;
-
- flow->crypt.flags = info->qs.cypher_s; /* TODO: move cypher_s */
-
- memset(flow->crypt.key, 0, SYMMKEYSZ);
-
- if (flow->crypt.flags > 0 && sk!= NULL && sk->data != NULL)
- memcpy(flow->crypt.key, sk->data , sk->len);
-
- if (crypt_init(&flow->crypt) < 0)
- goto fail_crypt;
+ flow->crypt = NULL;
+ flow->headsz = 0;
+ flow->tailsz = 0;
+
+ if (IS_ENCRYPTED(sk)) {
+ /* Set to lower value in tests, should we make configurable? */
+ sk->rot_bit = KEY_ROTATION_BIT;
+ flow->crypt = crypt_create_ctx(sk);
+ if (flow->crypt == NULL)
+ goto fail_crypt;
+ flow->headsz = crypt_get_ivsz(flow->crypt);
+ flow->tailsz = crypt_get_tagsz(flow->crypt);
+ }
assert(flow->frcti == NULL);
- if (info->qs.in_order != 0) {
+ if (IS_ORDERED(flow->info)) {
flow->frcti = frcti_create(fd, DELT_A, DELT_R, info->mpl);
if (flow->frcti == NULL)
goto fail_frcti;
- if (shm_flow_set_add(ai.fqset, 0, info->id))
+ if (ssm_flow_set_add(proc.fqset, 0, info->id))
goto fail_flow_set_add;
- ++ai.n_frcti;
- if (ai.n_frcti == 1 &&
- pthread_create(&ai.tx, NULL, flow_tx, NULL) < 0)
+ ++proc.n_frcti;
+ if (proc.n_frcti == 1 &&
+ pthread_create(&proc.tx, NULL, flow_tx, NULL) < 0)
goto fail_tx_thread;
}
- list_add_tail(&flow->next, &ai.flow_list);
+ list_add_tail(&flow->next, &proc.flow_list);
- ai.id_to_fd[info->id].fd = fd;
+ proc.id_to_fd[info->id].fd = fd;
- flow_set_state(&ai.id_to_fd[info->id], FLOW_ALLOCATED);
+ flow_set_state(&proc.id_to_fd[info->id], FLOW_ALLOCATED);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return fd;
fail_tx_thread:
- shm_flow_set_del(ai.fqset, 0, info->id);
+ ssm_flow_set_del(proc.fqset, 0, info->id);
fail_flow_set_add:
frcti_destroy(flow->frcti);
fail_frcti:
- crypt_fini(&flow->crypt);
+ crypt_destroy_ctx(flow->crypt);
fail_crypt:
- shm_flow_set_close(flow->set);
+ ssm_flow_set_close(flow->set);
fail_set:
- shm_rbuff_close(flow->tx_rb);
+ ssm_rbuff_close(flow->tx_rb);
fail_tx_rb:
- shm_rbuff_close(flow->rx_rb);
+ ssm_rbuff_close(flow->rx_rb);
fail_rx_rb:
- bmp_release(ai.fds, fd);
+ bmp_release(proc.fds, fd);
fail_fds:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return err;
}
@@ -547,6 +618,7 @@ static void init(int argc,
char ** argv,
char ** envp)
{
+ struct proc_info info;
char * prog = argv[0];
int i;
#ifdef PROC_FLOW_STATS
@@ -564,7 +636,11 @@ static void init(int argc,
goto fail_prog;
}
- if (proc_announce(prog)) {
+ memset(&info, 0, sizeof(info));
+ info.pid = getpid();
+ strncpy(info.prog, prog, PROG_NAME_SIZE);
+
+ if (proc_announce(&info)) {
fprintf(stderr, "FATAL: Could not announce to IRMd.\n");
goto fail_prog;
}
@@ -579,26 +655,30 @@ static void init(int argc,
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
#endif
- ai.fds = bmp_create(PROG_MAX_FLOWS - PROG_RES_FDS, PROG_RES_FDS);
- if (ai.fds == NULL) {
+ proc.fds = bmp_create(PROG_MAX_FLOWS - PROG_RES_FDS, PROG_RES_FDS);
+ if (proc.fds == NULL) {
fprintf(stderr, "FATAL: Could not create fd bitmap.\n");
goto fail_fds;
}
- ai.fqueues = bmp_create(PROG_MAX_FQUEUES, 0);
- if (ai.fqueues == NULL) {
+ proc.fqueues = bmp_create(PROG_MAX_FQUEUES, 0);
+ if (proc.fqueues == NULL) {
fprintf(stderr, "FATAL: Could not create fqueue bitmap.\n");
goto fail_fqueues;
}
- ai.rdrb = shm_rdrbuff_open();
- if (ai.rdrb == NULL) {
+ if (is_ouroboros_member_uid(getuid()))
+ proc.pool = ssm_pool_open(0);
+ else
+ proc.pool = ssm_pool_open(getuid());
+
+ if (proc.pool == NULL) {
fprintf(stderr, "FATAL: Could not open packet buffer.\n");
goto fail_rdrb;
}
- ai.flows = malloc(sizeof(*ai.flows) * PROG_MAX_FLOWS);
- if (ai.flows == NULL) {
+ proc.flows = malloc(sizeof(*proc.flows) * PROG_MAX_FLOWS);
+ if (proc.flows == NULL) {
fprintf(stderr, "FATAL: Could not malloc flows.\n");
goto fail_flows;
}
@@ -606,38 +686,38 @@ static void init(int argc,
for (i = 0; i < PROG_MAX_FLOWS; ++i)
flow_clear(i);
- ai.id_to_fd = malloc(sizeof(*ai.id_to_fd) * SYS_MAX_FLOWS);
- if (ai.id_to_fd == NULL) {
+ proc.id_to_fd = malloc(sizeof(*proc.id_to_fd) * SYS_MAX_FLOWS);
+ if (proc.id_to_fd == NULL) {
fprintf(stderr, "FATAL: Could not malloc id_to_fd.\n");
goto fail_id_to_fd;
}
for (i = 0; i < SYS_MAX_FLOWS; ++i)
- ai.id_to_fd[i].state = FLOW_INIT;
+ proc.id_to_fd[i].state = FLOW_INIT;
- if (pthread_mutex_init(&ai.mtx, NULL)) {
+ if (pthread_mutex_init(&proc.mtx, NULL)) {
fprintf(stderr, "FATAL: Could not init mutex.\n");
goto fail_mtx;
}
- if (pthread_cond_init(&ai.cond, NULL) < 0) {
+ if (pthread_cond_init(&proc.cond, NULL) < 0) {
fprintf(stderr, "FATAL: Could not init condvar.\n");
goto fail_cond;
}
- if (pthread_rwlock_init(&ai.lock, NULL) < 0) {
+ if (pthread_rwlock_init(&proc.lock, NULL) < 0) {
fprintf(stderr, "FATAL: Could not initialize flow lock.\n");
goto fail_flow_lock;
}
- ai.fqset = shm_flow_set_open(getpid());
- if (ai.fqset == NULL) {
+ proc.fqset = ssm_flow_set_open(getpid());
+ if (proc.fqset == NULL) {
fprintf(stderr, "FATAL: Could not open flow set.\n");
goto fail_fqset;
}
- ai.frct_set = fset_create();
- if (ai.frct_set == NULL || ai.frct_set->idx != 0) {
+ proc.frct_set = fset_create();
+ if (proc.frct_set == NULL || proc.frct_set->idx != 0) {
fprintf(stderr, "FATAL: Could not create FRCT set.\n");
goto fail_frct_set;
}
@@ -647,6 +727,11 @@ static void init(int argc,
goto fail_timerwheel;
}
+ if (crypt_secure_malloc_init(PROC_SECMEM_MAX) < 0) {
+ fprintf(stderr, "FATAL: Could not init secure malloc.\n");
+ goto fail_timerwheel;
+ }
+
#if defined PROC_FLOW_STATS
if (strstr(argv[0], "ipcpd") == NULL) {
sprintf(procstr, "proc.%d", getpid());
@@ -656,12 +741,12 @@ static void init(int argc,
}
}
#endif
- if (pthread_create(&ai.rx, NULL, flow_rx, NULL) < 0) {
+ if (pthread_create(&proc.rx, NULL, flow_rx, NULL) < 0) {
fprintf(stderr, "FATAL: Could not start monitor thread.\n");
goto fail_monitor;
}
- list_head_init(&ai.flow_list);
+ list_head_init(&proc.flow_list);
return;
@@ -672,27 +757,27 @@ static void init(int argc,
#endif
timerwheel_fini();
fail_timerwheel:
- fset_destroy(ai.frct_set);
+ fset_destroy(proc.frct_set);
fail_frct_set:
- shm_flow_set_close(ai.fqset);
+ ssm_flow_set_close(proc.fqset);
fail_fqset:
- pthread_rwlock_destroy(&ai.lock);
+ pthread_rwlock_destroy(&proc.lock);
fail_flow_lock:
- pthread_cond_destroy(&ai.cond);
+ pthread_cond_destroy(&proc.cond);
fail_cond:
- pthread_mutex_destroy(&ai.mtx);
+ pthread_mutex_destroy(&proc.mtx);
fail_mtx:
- free(ai.id_to_fd);
+ free(proc.id_to_fd);
fail_id_to_fd:
- free(ai.flows);
+ free(proc.flows);
fail_flows:
- shm_rdrbuff_close(ai.rdrb);
+ ssm_pool_close(proc.pool);
fail_rdrb:
- bmp_destroy(ai.fqueues);
+ bmp_destroy(proc.fqueues);
fail_fqueues:
- bmp_destroy(ai.fds);
+ bmp_destroy(proc.fds);
fail_fds:
- memset(&ai, 0, sizeof(ai));
+ memset(&proc, 0, sizeof(proc));
fail_prog:
exit(EXIT_FAILURE);
}
@@ -701,51 +786,52 @@ static void fini(void)
{
int i;
- if (ai.fds == NULL)
+ if (proc.fds == NULL)
return;
- pthread_cancel(ai.rx);
- pthread_join(ai.rx, NULL);
+ pthread_cancel(proc.rx);
+ pthread_join(proc.rx, NULL);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
for (i = 0; i < PROG_MAX_FLOWS; ++i) {
- if (ai.flows[i].info.id != -1) {
+ struct flow * flow = &proc.flows[i];
+ if (flow->info.id != -1) {
ssize_t idx;
- shm_rbuff_set_acl(ai.flows[i].rx_rb, ACL_FLOWDOWN);
- while ((idx = shm_rbuff_read(ai.flows[i].rx_rb)) >= 0)
- shm_rdrbuff_remove(ai.rdrb, idx);
+ ssm_rbuff_set_acl(flow->rx_rb, ACL_FLOWDOWN);
+ while ((idx = ssm_rbuff_read(flow->rx_rb)) >= 0)
+ ssm_pool_remove(proc.pool, idx);
__flow_fini(i);
}
}
- pthread_cond_destroy(&ai.cond);
- pthread_mutex_destroy(&ai.mtx);
+ pthread_cond_destroy(&proc.cond);
+ pthread_mutex_destroy(&proc.mtx);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
#ifdef PROC_FLOW_STATS
rib_fini();
#endif
timerwheel_fini();
- fset_destroy(ai.frct_set);
+ fset_destroy(proc.frct_set);
- shm_flow_set_close(ai.fqset);
+ ssm_flow_set_close(proc.fqset);
- pthread_rwlock_destroy(&ai.lock);
+ pthread_rwlock_destroy(&proc.lock);
- free(ai.flows);
- free(ai.id_to_fd);
+ free(proc.flows);
+ free(proc.id_to_fd);
- shm_rdrbuff_close(ai.rdrb);
+ ssm_pool_close(proc.pool);
- bmp_destroy(ai.fds);
- bmp_destroy(ai.fqueues);
+ bmp_destroy(proc.fds);
+ bmp_destroy(proc.fqueues);
proc_exit();
- memset(&ai, 0, sizeof(ai));
+ memset(&proc, 0, sizeof(proc));
}
#if defined(__MACH__) && defined(__APPLE__)
@@ -762,12 +848,13 @@ __attribute__((section(FINI_SECTION))) __typeof__(fini) * __fini = fini;
int flow_accept(qosspec_t * qs,
const struct timespec * timeo)
{
- struct flow_info flow;
- uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
- buffer_t sk;
- int fd;
- int err;
+ struct flow_info flow;
+ struct crypt_sk crypt;
+ uint8_t buf[SOCK_BUF_SIZE];
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
+ uint8_t key[SYMMKEYSZ];
+ int fd;
+ int err;
#ifdef QOS_DISABLE_CRC
if (qs != NULL)
@@ -785,13 +872,15 @@ int flow_accept(qosspec_t * qs,
if (err < 0)
return err;
- err = flow__irm_result_des(&msg, &flow, &sk);
+ crypt.key = key;
+
+ err = flow__irm_result_des(&msg, &flow, &crypt);
if (err < 0)
return err;
- fd = flow_init(&flow, &sk);
+ fd = flow_init(&flow, &crypt);
- freebuf(sk);
+ crypt_secure_clear(key, SYMMKEYSZ);
if (qs != NULL)
*qs = flow.qs;
@@ -803,12 +892,13 @@ int flow_alloc(const char * dst,
qosspec_t * qs,
const struct timespec * timeo)
{
- struct flow_info flow;
- uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
- buffer_t sk; /* symmetric key */
- int fd;
- int err;
+ struct flow_info flow;
+ struct crypt_sk crypt;
+ uint8_t buf[SOCK_BUF_SIZE];
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
+ uint8_t key[SYMMKEYSZ];
+ int fd;
+ int err;
#ifdef QOS_DISABLE_CRC
if (qs != NULL)
@@ -824,16 +914,20 @@ int flow_alloc(const char * dst,
return -ENOMEM;
err = send_recv_msg(&msg);
- if (err < 0)
+ if (err < 0) {
+ printf("send_recv_msg error %d\n", err);
return err;
+ }
+
+ crypt.key = key;
- err = flow__irm_result_des(&msg, &flow, &sk);
+ err = flow__irm_result_des(&msg, &flow, &crypt);
if (err < 0)
return err;
- fd = flow_init(&flow, &sk);
+ fd = flow_init(&flow, &crypt);
- freebuf(sk);
+ crypt_secure_clear(key, SYMMKEYSZ);
if (qs != NULL)
*qs = flow.qs;
@@ -842,42 +936,37 @@ int flow_alloc(const char * dst,
}
int flow_join(const char * dst,
- qosspec_t * qs,
const struct timespec * timeo)
{
- struct flow_info flow;
- uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
- int fd;
- int err;
-
-#ifdef QOS_DISABLE_CRC
- if (qs != NULL)
- qs->ber = 1;
-#endif
- if (qs != NULL && qs->cypher_s > 0)
- return -ENOTSUP; /* TODO: Encrypted broadcast */
+ struct flow_info flow;
+ struct crypt_sk crypt;
+ uint8_t buf[SOCK_BUF_SIZE];
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
+ uint8_t key[SYMMKEYSZ];
+ int fd;
+ int err;
memset(&flow, 0, sizeof(flow));
flow.n_pid = getpid();
- flow.qs = qs == NULL ? qos_raw : *qs;
+ flow.qs = qos_np1;
- if (flow_alloc__irm_req_ser(&msg, &flow, dst, timeo))
+ if (flow_join__irm_req_ser(&msg, &flow, dst, timeo))
return -ENOMEM;
err = send_recv_msg(&msg);
if (err < 0)
return err;
- err = flow__irm_result_des(&msg, &flow, NULL);
+ crypt.key = key;
+
+ err = flow__irm_result_des(&msg, &flow, &crypt);
if (err < 0)
return err;
- fd = flow_init(&flow, NULL);
+ fd = flow_init(&flow, &crypt);
- if (qs != NULL)
- *qs = flow.qs;
+ crypt_secure_clear(key, SYMMKEYSZ);
return fd;
}
@@ -888,7 +977,7 @@ int flow_dealloc(int fd)
struct flow_info info;
uint8_t pkt[PKT_BUF_LEN];
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
struct timespec tic = TIMESPEC_INIT_NS(TICTIME);
struct timespec timeo = TIMESPEC_INIT_S(0);
struct flow * flow;
@@ -899,12 +988,12 @@ int flow_dealloc(int fd)
memset(&info, 0, sizeof(flow));
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
@@ -913,21 +1002,21 @@ int flow_dealloc(int fd)
flow->rcv_timesout = true;
flow->rcv_timeo = tic;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
flow_read(fd, buf, SOCK_BUF_SIZE);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
timeo.tv_sec = frcti_dealloc(flow->frcti);
while (timeo.tv_sec < 0) { /* keep the flow active for rtx */
ssize_t ret;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
ret = flow_read(fd, pkt, PKT_BUF_LEN);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
timeo.tv_sec = frcti_dealloc(flow->frcti);
@@ -935,9 +1024,9 @@ int flow_dealloc(int fd)
timeo.tv_sec = -timeo.tv_sec;
}
- pthread_cleanup_push(__cleanup_rwlock_unlock, &ai.lock);
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &proc.lock);
- shm_rbuff_fini(flow->tx_rb);
+ ssm_rbuff_fini(flow->tx_rb);
pthread_cleanup_pop(true);
@@ -962,28 +1051,28 @@ int ipcp_flow_dealloc(int fd)
{
struct flow_info info;
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
struct flow * flow;
int err;
if (fd < 0 || fd >= SYS_MAX_FLOWS )
return -EINVAL;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
memset(&info, 0, sizeof(flow));
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
info.id = flow->info.id;
info.n_1_pid = flow->info.n_1_pid;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
if (ipcp_flow_dealloc__irm_req_ser(&msg, &info) < 0)
return -ENOMEM;
@@ -1017,14 +1106,14 @@ int fccntl(int fd,
if (fd < 0 || fd >= SYS_MAX_FLOWS)
return -EBADF;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
va_start(l, cmd);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
va_end(l);
return -ENOTALLOC;
}
@@ -1072,16 +1161,16 @@ int fccntl(int fd,
break;
case FLOWGRXQLEN:
qlen = va_arg(l, size_t *);
- *qlen = shm_rbuff_queued(flow->rx_rb);
+ *qlen = ssm_rbuff_queued(flow->rx_rb);
break;
case FLOWGTXQLEN:
qlen = va_arg(l, size_t *);
- *qlen = shm_rbuff_queued(flow->tx_rb);
+ *qlen = ssm_rbuff_queued(flow->tx_rb);
break;
case FLOWSFLAGS:
flow->oflags = va_arg(l, uint32_t);
- rx_acl = shm_rbuff_get_acl(flow->rx_rb);
- tx_acl = shm_rbuff_get_acl(flow->rx_rb);
+ rx_acl = ssm_rbuff_get_acl(flow->rx_rb);
+ tx_acl = ssm_rbuff_get_acl(flow->rx_rb);
/*
* Making our own flow write only means making the
* the other side of the flow read only.
@@ -1094,19 +1183,19 @@ int fccntl(int fd,
if (flow->oflags & FLOWFDOWN) {
rx_acl |= ACL_FLOWDOWN;
tx_acl |= ACL_FLOWDOWN;
- shm_flow_set_notify(flow->set,
+ ssm_flow_set_notify(flow->set,
flow->info.id,
FLOW_DOWN);
} else {
rx_acl &= ~ACL_FLOWDOWN;
tx_acl &= ~ACL_FLOWDOWN;
- shm_flow_set_notify(flow->set,
+ ssm_flow_set_notify(flow->set,
flow->info.id,
FLOW_UP);
}
- shm_rbuff_set_acl(flow->rx_rb, rx_acl);
- shm_rbuff_set_acl(flow->tx_rb, tx_acl);
+ ssm_rbuff_set_acl(flow->rx_rb, rx_acl);
+ ssm_rbuff_set_acl(flow->tx_rb, tx_acl);
break;
case FLOWGFLAGS:
@@ -1130,53 +1219,56 @@ int fccntl(int fd,
*cflags = frcti_getflags(flow->frcti);
break;
default:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
va_end(l);
return -ENOTSUP;
};
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
va_end(l);
return 0;
einval:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
va_end(l);
return -EINVAL;
eperm:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
va_end(l);
return -EPERM;
}
-static int chk_crc(struct shm_du_buff * sdb)
+static int chk_crc(struct ssm_pk_buff * spb)
{
uint32_t crc;
- uint8_t * head = shm_du_buff_head(sdb);
- uint8_t * tail = shm_du_buff_tail_release(sdb, CRCLEN);
+ uint8_t * head = ssm_pk_buff_head(spb);
+ uint8_t * tail = ssm_pk_buff_tail_release(spb, CRCLEN);
mem_hash(HASH_CRC32, &crc, head, tail - head);
return !(crc == *((uint32_t *) tail));
}
-static int add_crc(struct shm_du_buff * sdb)
+static int add_crc(struct ssm_pk_buff * spb)
{
- uint8_t * head = shm_du_buff_head(sdb);
- uint8_t * tail = shm_du_buff_tail_alloc(sdb, CRCLEN);
+ uint8_t * head;
+ uint8_t * tail;
+
+ tail = ssm_pk_buff_tail_alloc(spb, CRCLEN);
if (tail == NULL)
- return -1;
+ return -ENOMEM;
+ head = ssm_pk_buff_head(spb);
mem_hash(HASH_CRC32, tail, head, tail - head);
return 0;
}
-static int flow_tx_sdb(struct flow * flow,
- struct shm_du_buff * sdb,
+static int flow_tx_spb(struct flow * flow,
+ struct ssm_pk_buff * spb,
bool block,
struct timespec * abstime)
{
@@ -1186,46 +1278,46 @@ static int flow_tx_sdb(struct flow * flow,
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
flow->snd_act = now;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- idx = shm_du_buff_get_idx(sdb);
+ idx = ssm_pk_buff_get_idx(spb);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- if (shm_du_buff_len(sdb) > 0) {
- if (frcti_snd(flow->frcti, sdb) < 0)
+ if (ssm_pk_buff_len(spb) > 0) {
+ if (frcti_snd(flow->frcti, spb) < 0)
goto enomem;
- if (crypt_encrypt(&flow->crypt, sdb) < 0)
+ if (spb_encrypt(flow, spb) < 0)
goto enomem;
- if (flow->info.qs.ber == 0 && add_crc(sdb) != 0)
+ if (flow->info.qs.ber == 0 && add_crc(spb) != 0)
goto enomem;
}
- pthread_cleanup_push(__cleanup_rwlock_unlock, &ai.lock);
+ pthread_cleanup_push(__cleanup_rwlock_unlock, &proc.lock);
if (!block)
- ret = shm_rbuff_write(flow->tx_rb, idx);
+ ret = ssm_rbuff_write(flow->tx_rb, idx);
else
- ret = shm_rbuff_write_b(flow->tx_rb, idx, abstime);
+ ret = ssm_rbuff_write_b(flow->tx_rb, idx, abstime);
if (ret < 0)
- shm_rdrbuff_remove(ai.rdrb, idx);
+ ssm_pool_remove(proc.pool, idx);
else
- shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
+ ssm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
pthread_cleanup_pop(true);
return 0;
enomem:
- pthread_rwlock_unlock(&ai.lock);
- shm_rdrbuff_remove(ai.rdrb, idx);
+ pthread_rwlock_unlock(&proc.lock);
+ ssm_pool_remove(proc.pool, idx);
return -ENOMEM;
}
@@ -1239,7 +1331,7 @@ ssize_t flow_write(int fd,
int flags;
struct timespec abs;
struct timespec * abstime = NULL;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
uint8_t * ptr;
if (buf == NULL && count != 0)
@@ -1248,14 +1340,14 @@ ssize_t flow_write(int fd,
if (fd < 0 || fd >= PROG_MAX_FLOWS)
return -EBADF;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
clock_gettime(PTHREAD_COND_CLOCK, &abs);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
@@ -1266,7 +1358,7 @@ ssize_t flow_write(int fd,
flags = flow->oflags;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
if ((flags & FLOWFACCMODE) == FLOWFRDONLY)
return -EPERM;
@@ -1274,12 +1366,12 @@ ssize_t flow_write(int fd,
if (flags & FLOWFWNOBLOCK) {
if (!frcti_is_window_open(flow->frcti))
return -EAGAIN;
- idx = shm_rdrbuff_alloc(ai.rdrb, count, &ptr, &sdb);
+ idx = ssm_pool_alloc(proc.pool, count, &ptr, &spb);
} else {
ret = frcti_window_wait(flow->frcti, abstime);
if (ret < 0)
return ret;
- idx = shm_rdrbuff_alloc_b(ai.rdrb, count, &ptr, &sdb, abstime);
+ idx = ssm_pool_alloc_b(proc.pool, count, &ptr, &spb, abstime);
}
if (idx < 0)
@@ -1288,50 +1380,51 @@ ssize_t flow_write(int fd,
if (count > 0)
memcpy(ptr, buf, count);
- ret = flow_tx_sdb(flow, sdb, !(flags & FLOWFWNOBLOCK), abstime);
+ ret = flow_tx_spb(flow, spb, !(flags & FLOWFWNOBLOCK), abstime);
return ret < 0 ? (ssize_t) ret : (ssize_t) count;
}
static bool invalid_pkt(struct flow * flow,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
- if (shm_du_buff_len(sdb) == 0)
+ if (spb == NULL || ssm_pk_buff_len(spb) == 0)
return true;
- if (flow->info.qs.ber == 0 && chk_crc(sdb) != 0)
+ if (flow->info.qs.ber == 0 && chk_crc(spb) != 0)
return true;
- if (crypt_decrypt(&flow->crypt, sdb) < 0)
+ if (spb_decrypt(flow, spb) < 0)
return true;
return false;
}
-static ssize_t flow_rx_sdb(struct flow * flow,
- struct shm_du_buff ** sdb,
+static ssize_t flow_rx_spb(struct flow * flow,
+ struct ssm_pk_buff ** spb,
bool block,
struct timespec * abstime)
{
ssize_t idx;
struct timespec now;
- idx = block ? shm_rbuff_read_b(flow->rx_rb, abstime) :
- shm_rbuff_read(flow->rx_rb);
+ idx = block ? ssm_rbuff_read_b(flow->rx_rb, abstime) :
+ ssm_rbuff_read(flow->rx_rb);
if (idx < 0)
return idx;
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
flow->rcv_act = now;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
+
+ *spb = ssm_pool_get(proc.pool, idx);
- *sdb = shm_rdrbuff_get(ai.rdrb, idx);
- if (invalid_pkt(flow, *sdb)) {
- shm_rdrbuff_remove(ai.rdrb, idx);
+ if (invalid_pkt(flow, *spb)) {
+ ssm_pool_remove(proc.pool, idx);
return -EAGAIN;
}
@@ -1345,7 +1438,7 @@ ssize_t flow_read(int fd,
ssize_t idx;
ssize_t n;
uint8_t * packet;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
struct timespec abs;
struct timespec now;
struct timespec * abstime = NULL;
@@ -1356,19 +1449,19 @@ ssize_t flow_read(int fd,
if (fd < 0 || fd >= PROG_MAX_FLOWS)
return -EBADF;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
if (flow->part_idx == DONE_PART) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
flow->part_idx = NO_PART;
return 0;
}
@@ -1384,61 +1477,61 @@ ssize_t flow_read(int fd,
idx = flow->part_idx;
if (idx < 0) {
while ((idx = frcti_queued_pdu(flow->frcti)) < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- idx = flow_rx_sdb(flow, &sdb, block, abstime);
+ idx = flow_rx_spb(flow, &spb, block, abstime);
if (idx < 0) {
if (block && idx != -EAGAIN)
return idx;
if (!block)
return idx;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
continue;
}
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- frcti_rcv(flow->frcti, sdb);
+ frcti_rcv(flow->frcti, spb);
}
}
- sdb = shm_rdrbuff_get(ai.rdrb, idx);
+ spb = ssm_pool_get(proc.pool, idx);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- packet = shm_du_buff_head(sdb);
+ packet = ssm_pk_buff_head(spb);
- n = shm_du_buff_len(sdb);
+ n = ssm_pk_buff_len(spb);
assert(n >= 0);
if (n <= (ssize_t) count) {
memcpy(buf, packet, n);
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
flow->part_idx = (partrd && n == (ssize_t) count) ?
DONE_PART : NO_PART;
flow->rcv_act = now;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return n;
} else {
if (partrd) {
memcpy(buf, packet, count);
- shm_du_buff_head_release(sdb, n);
- pthread_rwlock_wrlock(&ai.lock);
+ ssm_pk_buff_head_release(spb, n);
+ pthread_rwlock_wrlock(&proc.lock);
flow->part_idx = idx;
flow->rcv_act = now;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return count;
} else {
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return -EMSGSIZE;
}
}
@@ -1454,20 +1547,20 @@ struct flow_set * fset_create(void)
if (set == NULL)
goto fail_malloc;
- assert(ai.fqueues);
+ assert(proc.fqueues);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
- set->idx = bmp_allocate(ai.fqueues);
- if (!bmp_is_id_valid(ai.fqueues, set->idx))
+ set->idx = bmp_allocate(proc.fqueues);
+ if (!bmp_is_id_valid(proc.fqueues, set->idx))
goto fail_bmp_alloc;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return set;
fail_bmp_alloc:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
free(set);
fail_malloc:
return NULL;
@@ -1480,11 +1573,11 @@ void fset_destroy(struct flow_set * set)
fset_zero(set);
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
- bmp_release(ai.fqueues, set->idx);
+ bmp_release(proc.fqueues, set->idx);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
free(set);
}
@@ -1495,7 +1588,7 @@ struct fqueue * fqueue_create(void)
if (fq == NULL)
return NULL;
- memset(fq->fqueue, -1, SHM_BUFFER_SIZE * sizeof(*fq->fqueue));
+ memset(fq->fqueue, -1, SSM_RBUFF_SIZE * sizeof(*fq->fqueue));
fq->fqsize = 0;
fq->next = 0;
@@ -1512,7 +1605,7 @@ void fset_zero(struct flow_set * set)
if (set == NULL)
return;
- shm_flow_set_zero(ai.fqset, set->idx);
+ ssm_flow_set_zero(proc.fqset, set->idx);
}
int fset_add(struct flow_set * set,
@@ -1524,9 +1617,9 @@ int fset_add(struct flow_set * set,
if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS)
return -EINVAL;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id < 0) {
ret = -EINVAL;
@@ -1534,21 +1627,21 @@ int fset_add(struct flow_set * set,
}
if (flow->frcti != NULL)
- shm_flow_set_del(ai.fqset, 0, ai.flows[fd].info.id);
+ ssm_flow_set_del(proc.fqset, 0, flow->info.id);
- ret = shm_flow_set_add(ai.fqset, set->idx, ai.flows[fd].info.id);
+ ret = ssm_flow_set_add(proc.fqset, set->idx, flow->info.id);
if (ret < 0)
goto fail;
- if (shm_rbuff_queued(ai.flows[fd].rx_rb))
- shm_flow_set_notify(ai.fqset, ai.flows[fd].info.id, FLOW_PKT);
+ if (ssm_rbuff_queued(flow->rx_rb))
+ ssm_flow_set_notify(proc.fqset, flow->info.id, FLOW_PKT);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return ret;
fail:
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return ret;
}
@@ -1560,37 +1653,40 @@ void fset_del(struct flow_set * set,
if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS)
return;
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id >= 0)
- shm_flow_set_del(ai.fqset, set->idx, flow->info.id);
+ ssm_flow_set_del(proc.fqset, set->idx, flow->info.id);
if (flow->frcti != NULL)
- shm_flow_set_add(ai.fqset, 0, ai.flows[fd].info.id);
+ ssm_flow_set_add(proc.fqset, 0, proc.flows[fd].info.id);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
}
bool fset_has(const struct flow_set * set,
int fd)
{
- bool ret;
+ struct flow * flow;
+ bool ret;
if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS)
return false;
- pthread_rwlock_rdlock(&ai.lock);
+ flow = &proc.flows[fd];
+
+ pthread_rwlock_rdlock(&proc.lock);
- if (ai.flows[fd].info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ if (flow->info.id < 0) {
+ pthread_rwlock_unlock(&proc.lock);
return false;
}
- ret = (shm_flow_set_has(ai.fqset, set->idx, ai.flows[fd].info.id) == 1);
+ ret = (ssm_flow_set_has(proc.fqset, set->idx, flow->info.id) == 1);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return ret;
}
@@ -1598,7 +1694,7 @@ bool fset_has(const struct flow_set * set,
/* Filter fqueue events for non-data packets */
static int fqueue_filter(struct fqueue * fq)
{
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
int fd;
ssize_t idx;
struct frcti * frcti;
@@ -1607,44 +1703,44 @@ static int fqueue_filter(struct fqueue * fq)
if (fq->fqueue[fq->next].event != FLOW_PKT)
return 1;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- fd = ai.id_to_fd[fq->fqueue[fq->next].flow_id].fd;
+ fd = proc.id_to_fd[fq->fqueue[fq->next].flow_id].fd;
if (fd < 0) {
++fq->next;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
continue;
}
- frcti = ai.flows[fd].frcti;
+ frcti = proc.flows[fd].frcti;
if (frcti == NULL) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return 1;
}
if (__frcti_pdu_ready(frcti) >= 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return 1;
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- idx = flow_rx_sdb(&ai.flows[fd], &sdb, false, NULL);
+ idx = flow_rx_spb(&proc.flows[fd], &spb, false, NULL);
if (idx < 0)
return 0;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- sdb = shm_rdrbuff_get(ai.rdrb, idx);
+ spb = ssm_pool_get(proc.pool, idx);
- __frcti_rcv(frcti, sdb);
+ __frcti_rcv(frcti, spb);
if (__frcti_pdu_ready(frcti) >= 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return 1;
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
++fq->next;
}
@@ -1666,15 +1762,15 @@ int fqueue_next(struct fqueue * fq)
if (fq->next != 0 && fqueue_filter(fq) == 0)
return -EPERM;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
e = fq->fqueue + fq->next;
- fd = ai.id_to_fd[e->flow_id].fd;
+ fd = proc.id_to_fd[e->flow_id].fd;
++fq->next;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return fd;
}
@@ -1712,7 +1808,7 @@ ssize_t fevent(struct flow_set * set,
}
while (ret == 0) {
- ret = shm_flow_set_wait(ai.fqset, set->idx, fq->fqueue, t);
+ ret = ssm_flow_set_wait(proc.fqset, set->idx, fq->fqueue, t);
if (ret == -ETIMEDOUT)
return -ETIMEDOUT;
@@ -1732,7 +1828,8 @@ ssize_t fevent(struct flow_set * set,
int np1_flow_alloc(pid_t n_pid,
int flow_id)
{
- struct flow_info flow;
+ struct flow_info flow;
+ struct crypt_sk crypt = { .nid = NID_undef, .key = NULL };
memset(&flow, 0, sizeof(flow));
@@ -1742,7 +1839,7 @@ int np1_flow_alloc(pid_t n_pid,
flow.mpl = 0;
flow.n_1_pid = n_pid; /* This "flow" is upside-down! */
- return flow_init(&flow, NULL);
+ return flow_init(&flow, &crypt);
}
int np1_flow_dealloc(int flow_id,
@@ -1758,27 +1855,28 @@ int np1_flow_dealloc(int flow_id,
sleep(timeo);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- fd = ai.id_to_fd[flow_id].fd;
+ fd = proc.id_to_fd[flow_id].fd;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return fd;
}
-int np1_flow_resp(int flow_id)
+int np1_flow_resp(int flow_id,
+ int resp)
{
int fd;
- if (flow_wait_assign(flow_id) != FLOW_ALLOCATED)
+ if (resp == 0 && flow_wait_assign(flow_id) != FLOW_ALLOCATED)
return -1;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- fd = ai.id_to_fd[flow_id].fd;
+ fd = proc.id_to_fd[flow_id].fd;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return fd;
}
@@ -1786,7 +1884,7 @@ int np1_flow_resp(int flow_id)
int ipcp_create_r(const struct ipcp_info * info)
{
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
int err;
if (ipcp_create_r__irm_req_ser(&msg,info) < 0)
@@ -1805,9 +1903,11 @@ int ipcp_flow_req_arr(const buffer_t * dst,
const buffer_t * data)
{
struct flow_info flow;
- uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
- int err;
+ uint8_t buf[SOCK_BUF_SIZE];
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
+ struct crypt_sk crypt;
+ uint8_t key[SYMMKEYSZ];
+ int err;
memset(&flow, 0, sizeof(flow));
@@ -1824,16 +1924,23 @@ int ipcp_flow_req_arr(const buffer_t * dst,
if (err < 0)
return err;
- err = flow__irm_result_des(&msg, &flow, NULL);
+ crypt.key = key;
+
+ err = flow__irm_result_des(&msg, &flow, &crypt);
if (err < 0)
return err;
+ assert(crypt.nid == NID_undef); /* np1 flows are not encrypted */
+
/* inverted for np1_flow */
flow.n_1_pid = flow.n_pid;
flow.n_pid = getpid();
flow.mpl = 0;
+ flow.qs = qos_np1;
+
+ crypt.nid = NID_undef;
- return flow_init(&flow, NULL);
+ return flow_init(&flow, &crypt);
}
int ipcp_flow_alloc_reply(int fd,
@@ -1843,16 +1950,16 @@ int ipcp_flow_alloc_reply(int fd,
{
struct flow_info flow;
uint8_t buf[SOCK_BUF_SIZE];
- buffer_t msg = {buf, SOCK_BUF_SIZE};
+ buffer_t msg = {SOCK_BUF_SIZE, buf};
int err;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- flow.id = ai.flows[fd].info.id;
+ flow.id = proc.flows[fd].info.id;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
flow.mpl = mpl;
@@ -1867,169 +1974,213 @@ int ipcp_flow_alloc_reply(int fd,
}
int ipcp_flow_read(int fd,
- struct shm_du_buff ** sdb)
+ struct ssm_pk_buff ** spb)
{
struct flow * flow;
ssize_t idx = -1;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- assert(sdb);
+ assert(spb);
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
assert(flow->info.id >= 0);
while (frcti_queued_pdu(flow->frcti) < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- idx = flow_rx_sdb(flow, sdb, false, NULL);
+ idx = flow_rx_spb(flow, spb, false, NULL);
if (idx < 0)
return idx;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- frcti_rcv(flow->frcti, *sdb);
+ frcti_rcv(flow->frcti, *spb);
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return 0;
}
int ipcp_flow_write(int fd,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct flow * flow;
int ret;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- assert(sdb);
+ assert(spb);
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_wrlock(&ai.lock);
+ pthread_rwlock_wrlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
if ((flow->oflags & FLOWFACCMODE) == FLOWFRDONLY) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -EPERM;
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- ret = flow_tx_sdb(flow, sdb, true, NULL);
+ ret = flow_tx_spb(flow, spb, true, NULL);
return ret;
}
+static int pool_copy_spb(struct ssm_pool * src_pool,
+ ssize_t src_idx,
+ struct ssm_pool * dst_pool,
+ struct ssm_pk_buff ** dst_spb)
+{
+ struct ssm_pk_buff * src;
+ uint8_t * ptr;
+ size_t len;
+
+ src = ssm_pool_get(src_pool, src_idx);
+ len = ssm_pk_buff_len(src);
+
+ if (ssm_pool_alloc(dst_pool, len, &ptr, dst_spb) < 0) {
+ ssm_pool_remove(src_pool, src_idx);
+ return -ENOMEM;
+ }
+
+ memcpy(ptr, ssm_pk_buff_head(src), len);
+ ssm_pool_remove(src_pool, src_idx);
+
+ return 0;
+}
+
int np1_flow_read(int fd,
- struct shm_du_buff ** sdb)
+ struct ssm_pk_buff ** spb,
+ struct ssm_pool * pool)
{
- struct flow * flow;
- ssize_t idx = -1;
+ struct flow * flow;
+ ssize_t idx = -1;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- assert(sdb);
+ assert(spb);
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
assert(flow->info.id >= 0);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- idx = shm_rbuff_read(flow->rx_rb);;
+ idx = ssm_rbuff_read(flow->rx_rb);
if (idx < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return idx;
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- *sdb = shm_rdrbuff_get(ai.rdrb, idx);
+ if (pool == NULL) {
+ *spb = ssm_pool_get(proc.pool, idx);
+ } else {
+ /* Cross-pool copy: PUP -> GSPP */
+ if (pool_copy_spb(pool, idx, proc.pool, spb) < 0)
+ return -ENOMEM;
+ }
return 0;
}
int np1_flow_write(int fd,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb,
+ struct ssm_pool * pool)
{
- struct flow * flow;
- int ret;
- ssize_t idx;
+ struct flow * flow;
+ struct ssm_pk_buff * dst;
+ int ret;
+ ssize_t idx;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- assert(sdb);
+ assert(spb);
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -ENOTALLOC;
}
if ((flow->oflags & FLOWFACCMODE) == FLOWFRDONLY) {
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return -EPERM;
}
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
- idx = shm_du_buff_get_idx(sdb);
+ idx = ssm_pk_buff_get_idx(spb);
- ret = shm_rbuff_write_b(flow->tx_rb, idx, NULL);
- if (ret < 0)
- shm_rdrbuff_remove(ai.rdrb, idx);
- else
- shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
+ if (pool == NULL) {
+ ret = ssm_rbuff_write_b(flow->tx_rb, idx, NULL);
+ if (ret < 0)
+ ssm_pool_remove(proc.pool, idx);
+ else
+ ssm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
+ } else {
+ /* Cross-pool copy: GSPP -> PUP */
+ if (pool_copy_spb(proc.pool, idx, pool, &dst) < 0)
+ return -ENOMEM;
+ idx = ssm_pk_buff_get_idx(dst);
+ ret = ssm_rbuff_write_b(flow->tx_rb, idx, NULL);
+ if (ret < 0)
+ ssm_pool_remove(pool, idx);
+ else
+ ssm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
+ }
return ret;
}
-int ipcp_sdb_reserve(struct shm_du_buff ** sdb,
+int ipcp_spb_reserve(struct ssm_pk_buff ** spb,
size_t len)
{
- return shm_rdrbuff_alloc_b(ai.rdrb, len, NULL, sdb, NULL) < 0 ? -1 : 0;
+ return ssm_pool_alloc_b(proc.pool, len, NULL, spb, NULL) < 0 ? -1 : 0;
}
-void ipcp_sdb_release(struct shm_du_buff * sdb)
+void ipcp_spb_release(struct ssm_pk_buff * spb)
{
- shm_rdrbuff_remove(ai.rdrb, shm_du_buff_get_idx(sdb));
+ ssm_pool_remove(proc.pool, ssm_pk_buff_get_idx(spb));
}
int ipcp_flow_fini(int fd)
{
- struct shm_rbuff * rx_rb;
+ struct ssm_rbuff * rx_rb;
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- if (ai.flows[fd].info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ if (proc.flows[fd].info.id < 0) {
+ pthread_rwlock_unlock(&proc.lock);
return -1;
}
- shm_rbuff_set_acl(ai.flows[fd].rx_rb, ACL_FLOWDOWN);
- shm_rbuff_set_acl(ai.flows[fd].tx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_set_acl(proc.flows[fd].rx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_set_acl(proc.flows[fd].tx_rb, ACL_FLOWDOWN);
- shm_flow_set_notify(ai.flows[fd].set,
- ai.flows[fd].info.id,
+ ssm_flow_set_notify(proc.flows[fd].set,
+ proc.flows[fd].info.id,
FLOW_DEALLOC);
- rx_rb = ai.flows[fd].rx_rb;
+ rx_rb = proc.flows[fd].rx_rb;
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
if (rx_rb != NULL)
- shm_rbuff_fini(rx_rb);
+ ssm_rbuff_fini(rx_rb);
return 0;
}
@@ -2040,13 +2191,13 @@ int ipcp_flow_get_qoscube(int fd,
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
assert(cube);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- assert(ai.flows[fd].info.id >= 0);
+ assert(proc.flows[fd].info.id >= 0);
- *cube = qos_spec_to_cube(ai.flows[fd].info.qs);
+ *cube = qos_spec_to_cube(proc.flows[fd].info.qs);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return 0;
}
@@ -2055,56 +2206,76 @@ size_t ipcp_flow_queued(int fd)
{
size_t q;
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
- assert(ai.flows[fd].info.id >= 0);
+ assert(proc.flows[fd].info.id >= 0);
- q = shm_rbuff_queued(ai.flows[fd].tx_rb);
+ q = ssm_rbuff_queued(proc.flows[fd].tx_rb);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return q;
}
-ssize_t local_flow_read(int fd)
+int local_flow_transfer(int src_fd,
+ int dst_fd,
+ struct ssm_pool * src_pool,
+ struct ssm_pool * dst_pool)
{
- ssize_t ret;
-
- assert(fd >= 0);
-
- pthread_rwlock_rdlock(&ai.lock);
-
- ret = shm_rbuff_read(ai.flows[fd].rx_rb);
-
- pthread_rwlock_unlock(&ai.lock);
+ struct flow * src_flow;
+ struct flow * dst_flow;
+ struct ssm_pk_buff * dst_spb;
+ struct ssm_pool * sp;
+ struct ssm_pool * dp;
+ ssize_t idx;
+ int ret;
- return ret;
-}
+ assert(src_fd >= 0);
+ assert(dst_fd >= 0);
-int local_flow_write(int fd,
- size_t idx)
-{
- struct flow * flow;
- int ret;
+ src_flow = &proc.flows[src_fd];
+ dst_flow = &proc.flows[dst_fd];
- assert(fd >= 0);
+ sp = src_pool == NULL ? proc.pool : src_pool;
+ dp = dst_pool == NULL ? proc.pool : dst_pool;
- flow = &ai.flows[fd];
+ pthread_rwlock_rdlock(&proc.lock);
- pthread_rwlock_rdlock(&ai.lock);
+ idx = ssm_rbuff_read(src_flow->rx_rb);
+ if (idx < 0) {
+ pthread_rwlock_unlock(&proc.lock);
+ return idx;
+ }
- if (flow->info.id < 0) {
- pthread_rwlock_unlock(&ai.lock);
+ if (dst_flow->info.id < 0) {
+ pthread_rwlock_unlock(&proc.lock);
+ ssm_pool_remove(sp, idx);
return -ENOTALLOC;
}
- ret = shm_rbuff_write_b(flow->tx_rb, idx, NULL);
- if (ret == 0)
- shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT);
- else
- shm_rdrbuff_remove(ai.rdrb, idx);
+ pthread_rwlock_unlock(&proc.lock);
- pthread_rwlock_unlock(&ai.lock);
+ if (sp == dp) {
+ /* Same pool: zero-copy */
+ ret = ssm_rbuff_write_b(dst_flow->tx_rb, idx, NULL);
+ if (ret < 0)
+ ssm_pool_remove(sp, idx);
+ else
+ ssm_flow_set_notify(dst_flow->set,
+ dst_flow->info.id, FLOW_PKT);
+ } else {
+ /* Different pools: single copy */
+ if (pool_copy_spb(sp, idx, dp, &dst_spb) < 0)
+ return -ENOMEM;
+
+ idx = ssm_pk_buff_get_idx(dst_spb);
+ ret = ssm_rbuff_write_b(dst_flow->tx_rb, idx, NULL);
+ if (ret < 0)
+ ssm_pool_remove(dp, idx);
+ else
+ ssm_flow_set_notify(dst_flow->set,
+ dst_flow->info.id, FLOW_PKT);
+ }
return ret;
}
diff --git a/src/lib/frct.c b/src/lib/frct.c
index c6fef35c..39a82966 100644
--- a/src/lib/frct.c
+++ b/src/lib/frct.c
@@ -118,11 +118,11 @@ static int frct_rib_read(const char * path,
fd = atoi(path);
- flow = &ai.flows[fd];
+ flow = &proc.flows[fd];
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pthread_rwlock_rdlock(&ai.lock);
+ pthread_rwlock_rdlock(&proc.lock);
frcti = flow->frcti;
@@ -137,11 +137,11 @@ static int frct_rib_read(const char * path,
"Retransmit timeout RTO (ns): %20ld\n"
"Sender left window edge: %20u\n"
"Sender right window edge: %20u\n"
- "Sender inactive (ns): %20ld\n"
+ "Sender inactive (ns): %20lld\n"
"Sender current sequence number: %20u\n"
"Receiver left window edge: %20u\n"
"Receiver right window edge: %20u\n"
- "Receiver inactive (ns): %20ld\n"
+ "Receiver inactive (ns): %20lld\n"
"Receiver last ack: %20u\n"
"Number of pkt retransmissions: %20zu\n"
"Number of rtt probes: %20zu\n"
@@ -159,11 +159,11 @@ static int frct_rib_read(const char * path,
frcti->rto,
frcti->snd_cr.lwe,
frcti->snd_cr.rwe,
- ts_diff_ns(&frcti->snd_cr.act, &now),
+ ts_diff_ns(&now, &frcti->snd_cr.act),
frcti->snd_cr.seqno,
frcti->rcv_cr.lwe,
frcti->rcv_cr.rwe,
- ts_diff_ns(&frcti->rcv_cr.act, &now),
+ ts_diff_ns(&now, &frcti->rcv_cr.act),
frcti->rcv_cr.seqno,
frcti->n_rtx,
frcti->n_prb,
@@ -176,7 +176,7 @@ static int frct_rib_read(const char * path,
pthread_rwlock_unlock(&flow->frcti->lock);
- pthread_rwlock_unlock(&ai.lock);
+ pthread_rwlock_unlock(&proc.lock);
return strlen(buf);
}
@@ -237,21 +237,21 @@ static void __send_frct_pkt(int fd,
uint32_t ackno,
uint32_t rwe)
{
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
struct frct_pci * pci;
ssize_t idx;
struct flow * f;
/* Raw calls needed to bypass frcti. */
#ifdef RXM_BLOCKING
- idx = shm_rdrbuff_alloc_b(ai.rdrb, sizeof(*pci), NULL, &sdb, NULL);
+ idx = ssm_pool_alloc_b(proc.pool, sizeof(*pci), NULL, &spb, NULL);
#else
- idx = shm_rdrbuff_alloc(ai.rdrb, sizeof(*pci), NULL, &sdb);
+ idx = ssm_pool_alloc(proc.pool, sizeof(*pci), NULL, &spb);
#endif
if (idx < 0)
return;
- pci = (struct frct_pci *) shm_du_buff_head(sdb);
+ pci = (struct frct_pci *) ssm_pk_buff_head(spb);
memset(pci, 0, sizeof(*pci));
*((uint32_t *) pci) = hton32(rwe);
@@ -259,24 +259,24 @@ static void __send_frct_pkt(int fd,
pci->flags = flags;
pci->ackno = hton32(ackno);
- f = &ai.flows[fd];
+ f = &proc.flows[fd];
- if (crypt_encrypt(&f->crypt, sdb) < 0)
+ if (spb_encrypt(f, spb) < 0)
goto fail;
#ifdef RXM_BLOCKING
- if (shm_rbuff_write_b(f->tx_rb, idx, NULL))
+ if (ssm_rbuff_write_b(f->tx_rb, idx, NULL))
#else
- if (shm_rbuff_write(f->tx_rb, idx))
+ if (ssm_rbuff_write(f->tx_rb, idx))
#endif
goto fail;
- shm_flow_set_notify(f->set, f->info.id, FLOW_PKT);
+ ssm_flow_set_notify(f->set, f->info.id, FLOW_PKT);
return;
fail:
- ipcp_sdb_release(sdb);
+ ipcp_spb_release(spb);
return;
}
@@ -303,13 +303,13 @@ static void send_frct_pkt(struct frcti * frcti)
ackno = frcti->rcv_cr.lwe;
rwe = frcti->rcv_cr.rwe;
- diff = ts_diff_ns(&frcti->rcv_cr.act, &now);
+ diff = ts_diff_ns(&now, &frcti->rcv_cr.act);
if (diff > frcti->a) {
pthread_rwlock_unlock(&frcti->lock);
return;
}
- diff = ts_diff_ns(&frcti->snd_cr.act, &now);
+ diff = ts_diff_ns(&now, &frcti->snd_cr.act);
if (diff < TICTIME) {
pthread_rwlock_unlock(&frcti->lock);
return;
@@ -339,7 +339,7 @@ static struct frcti * frcti_create(int fd,
#ifdef PROC_FLOW_STATS
char frctstr[FRCT_NAME_STRLEN + 1];
#endif
- mpl *= BILLION;
+ mpl *= MILLION;
a *= BILLION;
r *= BILLION;
@@ -398,7 +398,7 @@ static struct frcti * frcti_create(int fd,
frcti->n_out = 0;
frcti->n_rqo = 0;
#endif
- if (ai.flows[fd].info.qs.loss == 0) {
+ if (proc.flows[fd].info.qs.loss == 0) {
frcti->snd_cr.cflags |= FRCTFRTX | FRCTFLINGER;
frcti->rcv_cr.cflags |= FRCTFRTX;
}
@@ -479,11 +479,11 @@ static void frcti_setflags(struct frcti * frcti,
#define frcti_queued_pdu(frcti) \
(frcti == NULL ? idx : __frcti_queued_pdu(frcti))
-#define frcti_snd(frcti, sdb) \
- (frcti == NULL ? 0 : __frcti_snd(frcti, sdb))
+#define frcti_snd(frcti, spb) \
+ (frcti == NULL ? 0 : __frcti_snd(frcti, spb))
-#define frcti_rcv(frcti, sdb) \
- (frcti == NULL ? 0 : __frcti_rcv(frcti, sdb))
+#define frcti_rcv(frcti, spb) \
+ (frcti == NULL ? 0 : __frcti_rcv(frcti, spb))
#define frcti_dealloc(frcti) \
(frcti == NULL ? 0 : __frcti_dealloc(frcti))
@@ -517,14 +517,14 @@ static bool __frcti_is_window_open(struct frcti * frcti)
frcti->t_rdvs = now;
} else {
time_t diff;
- diff = ts_diff_ns(&frcti->t_wnd, &now);
+ diff = ts_diff_ns(&now, &frcti->t_wnd);
if (diff > MAX_RDV) {
pthread_mutex_unlock(&frcti->mtx);
pthread_rwlock_unlock(&frcti->lock);
return false;
}
- diff = ts_diff_ns(&frcti->t_rdvs, &now);
+ diff = ts_diff_ns(&now, &frcti->t_rdvs);
if (diff > frcti->rdv) {
frcti->t_rdvs = now;
__send_rdv(frcti->fd);
@@ -580,13 +580,13 @@ static int __frcti_window_wait(struct frcti * frcti,
clock_gettime(PTHREAD_COND_CLOCK, &now);
- diff = ts_diff_ns(&frcti->t_wnd, &now);
+ diff = ts_diff_ns(&now, &frcti->t_wnd);
if (diff > MAX_RDV) {
pthread_mutex_unlock(&frcti->mtx);
return -ECONNRESET; /* write fails! */
}
- diff = ts_diff_ns(&frcti->t_rdvs, &now);
+ diff = ts_diff_ns(&now, &frcti->t_rdvs);
if (diff > frcti->rdv) {
frcti->t_rdvs = now;
__send_rdv(frcti->fd);
@@ -683,7 +683,7 @@ static time_t __frcti_dealloc(struct frcti * frcti)
}
static int __frcti_snd(struct frcti * frcti,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct frct_pci * pci;
struct timespec now;
@@ -693,14 +693,14 @@ static int __frcti_snd(struct frcti * frcti,
bool rtx;
assert(frcti);
- assert(shm_du_buff_len(sdb) != 0);
+ assert(ssm_pk_buff_len(spb) != 0);
snd_cr = &frcti->snd_cr;
rcv_cr = &frcti->rcv_cr;
timerwheel_move();
- pci = (struct frct_pci *) shm_du_buff_head_alloc(sdb, FRCT_PCILEN);
+ pci = (struct frct_pci *) ssm_pk_buff_head_alloc(spb, FRCT_PCILEN);
if (pci == NULL)
return -ENOMEM;
@@ -759,7 +759,7 @@ static int __frcti_snd(struct frcti * frcti,
pthread_rwlock_unlock(&frcti->lock);
if (rtx)
- timerwheel_rxm(frcti, seqno, sdb);
+ timerwheel_rxm(frcti, seqno, spb);
return 0;
}
@@ -793,7 +793,7 @@ static void rtt_estimator(struct frcti * frcti,
/* Always queues the next application packet on the RQ. */
static void __frcti_rcv(struct frcti * frcti,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
ssize_t idx;
size_t pos;
@@ -813,9 +813,9 @@ static void __frcti_rcv(struct frcti * frcti,
clock_gettime(PTHREAD_COND_CLOCK, &now);
- pci = (struct frct_pci *) shm_du_buff_head_release(sdb, FRCT_PCILEN);
+ pci = (struct frct_pci *) ssm_pk_buff_head_release(spb, FRCT_PCILEN);
- idx = shm_du_buff_get_idx(sdb);
+ idx = ssm_pk_buff_get_idx(spb);
seqno = ntoh32(pci->seqno);
pos = seqno & (RQ_SIZE - 1);
@@ -841,7 +841,7 @@ static void __frcti_rcv(struct frcti * frcti,
__send_frct_pkt(fd, FRCT_FC, 0, rwe);
- shm_rdrbuff_remove(ai.rdrb, idx);
+ ssm_pool_remove(proc.pool, idx);
return;
}
@@ -855,7 +855,7 @@ static void __frcti_rcv(struct frcti * frcti,
if (!(pci->flags & FRCT_DATA))
frcti->n_dak++;
#endif
- rtt_estimator(frcti, ts_diff_ns(&frcti->t_probe, &now));
+ rtt_estimator(frcti, ts_diff_ns(&now, &frcti->t_probe));
frcti->probe = false;
}
}
@@ -928,7 +928,7 @@ static void __frcti_rcv(struct frcti * frcti,
drop_packet:
pthread_rwlock_unlock(&frcti->lock);
- shm_rdrbuff_remove(ai.rdrb, idx);
+ ssm_pool_remove(proc.pool, idx);
send_frct_pkt(frcti);
return;
}
diff --git a/src/lib/irm.c b/src/lib/irm.c
index d25101f3..8333d0d3 100644
--- a/src/lib/irm.c
+++ b/src/lib/irm.c
@@ -523,32 +523,23 @@ int irm_unbind_process(pid_t pid,
return ret;
}
-int irm_create_name(const char * name,
- enum pol_balance pol)
+int irm_create_name(struct name_info * info)
{
irm_msg_t msg = IRM_MSG__INIT;
- name_info_msg_t ni_msg = NAME_INFO_MSG__INIT;
irm_msg_t * recv_msg;
int ret;
- if (name == NULL)
+ if (info == NULL)
return -EINVAL;
- msg.code = IRM_MSG_CODE__IRM_CREATE_NAME;
- ni_msg.name = (char *) name;
- ni_msg.pol_lb = pol;
- msg.n_names = 1;
-
- msg.names = malloc(sizeof(*msg.names));
- if (msg.names == NULL) {
- return -ENOMEM;
- }
-
- msg.names[0] = &ni_msg;
+ msg.code = IRM_MSG_CODE__IRM_CREATE_NAME;
+ msg.name_info = name_info_s_to_msg(info);
+ if (msg.name_info == NULL)
+ goto fail_info_msg;
recv_msg = send_recv_irm_msg(&msg);
- free(msg.names);
+ name_info_msg__free_unpacked(msg.name_info, NULL);
if (recv_msg == NULL)
return -EIRMD;
@@ -562,6 +553,9 @@ int irm_create_name(const char * name,
irm_msg__free_unpacked(recv_msg, NULL);
return ret;
+
+ fail_info_msg:
+ return -ENOMEM;
}
int irm_destroy_name(const char * name)
diff --git a/src/lib/notifier.c b/src/lib/notifier.c
index 45745b9a..4fccd371 100644
--- a/src/lib/notifier.c
+++ b/src/lib/notifier.c
@@ -95,18 +95,14 @@ int notifier_reg(notifier_fn_t callback,
pthread_rwlock_wrlock(&notifier.lock);
list_for_each(p, &notifier.listeners) {
- struct listener * l = list_entry(p, struct listener, next);
- if (l->callback == callback) {
- pthread_rwlock_unlock(&notifier.lock);
- return -EPERM;
- }
+ l = list_entry(p, struct listener, next);
+ if (l->callback == callback)
+ goto fail;
}
l = malloc(sizeof(*l));
- if (l == NULL) {
- pthread_rwlock_unlock(&notifier.lock);
- return -ENOMEM;
- }
+ if (l == NULL)
+ goto fail;
l->callback = callback;
l->obj = obj;
@@ -116,6 +112,10 @@ int notifier_reg(notifier_fn_t callback,
pthread_rwlock_unlock(&notifier.lock);
return 0;
+ fail:
+ pthread_rwlock_unlock(&notifier.lock);
+ return -1;
+
}
void notifier_unreg(notifier_fn_t callback)
diff --git a/src/lib/pb/enroll.proto b/src/lib/pb/enroll.proto
index 7fe612a8..60e964c6 100644
--- a/src/lib/pb/enroll.proto
+++ b/src/lib/pb/enroll.proto
@@ -24,7 +24,6 @@ syntax = "proto2";
import "ipcp_config.proto";
message enroll_req_msg {
- /* TODO authentication */
required bytes id = 1;
}
diff --git a/src/lib/pb/ipcp.proto b/src/lib/pb/ipcp.proto
index c2c7f48b..deddf6af 100644
--- a/src/lib/pb/ipcp.proto
+++ b/src/lib/pb/ipcp.proto
@@ -56,4 +56,5 @@ message ipcp_msg {
optional uint32 timeo_sec = 12;
optional sint32 mpl = 13;
optional int32 result = 14;
+ optional uint32 uid = 15; /* 0 = GSPP, >0 = PUP uid */
}
diff --git a/src/lib/pb/ipcp_config.proto b/src/lib/pb/ipcp_config.proto
index 28528b0c..1308c6d1 100644
--- a/src/lib/pb/ipcp_config.proto
+++ b/src/lib/pb/ipcp_config.proto
@@ -24,17 +24,45 @@ syntax = "proto2";
import "model.proto";
+
+message ls_config_msg {
+ required uint32 pol = 1;
+ required uint32 t_recalc = 2;
+ required uint32 t_update = 3;
+ required uint32 t_timeo = 4;
+}
+
+message routing_config_msg {
+ required uint32 pol = 1;
+ optional ls_config_msg ls = 2;
+}
+
message dt_config_msg {
- required uint32 addr_size = 1;
- required uint32 eid_size = 2;
- required uint32 max_ttl = 3;
- required uint32 routing_type = 4;
+ required uint32 addr_size = 1;
+ required uint32 eid_size = 2;
+ required uint32 max_ttl = 3;
+ required routing_config_msg routing = 4;
+}
+
+message dir_dht_config_msg {
+ required uint32 alpha = 1;
+ required uint32 k = 2;
+ required uint32 t_expire = 3;
+ required uint32 t_refresh = 4;
+ required uint32 t_replicate = 5;
+ required uint64 peer = 6;
+}
+
+message dir_config_msg {
+ required uint32 pol = 1;
+ optional dir_dht_config_msg dht = 2;
}
message uni_config_msg {
required dt_config_msg dt = 1;
- required uint32 addr_auth_type = 2;
- required uint32 cong_avoid = 3;
+ required dir_config_msg dir = 2;
+ required uint32 addr_auth_type = 3;
+ required uint32 cong_avoid = 4;
}
message eth_config_msg {
@@ -42,16 +70,24 @@ message eth_config_msg {
required uint32 ethertype = 2;
}
-message udp_config_msg {
+message udp4_config_msg {
required uint32 ip_addr = 1;
required uint32 port = 2;
required uint32 dns_addr = 3; /* set to 0 if unused */
}
+message udp6_config_msg {
+ required bytes ip_addr = 1;
+ required uint32 port = 2;
+ required bytes dns_addr = 3; /* set to NULL if unused */
+}
+
+
message ipcp_config_msg {
required layer_info_msg layer_info = 1;
required uint32 ipcp_type = 2;
optional uni_config_msg unicast = 3;
- optional udp_config_msg udp = 4;
- optional eth_config_msg eth = 5;
+ optional udp4_config_msg udp4 = 4;
+ optional udp6_config_msg udp6 = 5;
+ optional eth_config_msg eth = 6;
}
diff --git a/src/lib/pb/irm.proto b/src/lib/pb/irm.proto
index da3bd982..1845c694 100644
--- a/src/lib/pb/irm.proto
+++ b/src/lib/pb/irm.proto
@@ -75,23 +75,25 @@ message irm_msg {
optional string name = 4;
optional flow_info_msg flow_info = 5;
optional ipcp_info_msg ipcp_info = 6;
- optional string layer = 7;
- repeated string exec = 8;
- optional sint32 response = 9;
- optional string dst = 10;
- optional bytes hash = 11;
- optional sint32 flow_id = 12;
- optional qosspec_msg qosspec = 13;
- optional ipcp_config_msg conf = 14;
- optional uint32 opts = 15;
- repeated ipcp_list_msg ipcps = 16;
- repeated name_info_msg names = 17;
- optional timespec_msg timeo = 18;
+ optional name_info_msg name_info = 7;
+ optional string layer = 8;
+ repeated string exec = 9;
+ optional sint32 response = 10;
+ optional string dst = 11;
+ optional bytes hash = 12;
+ optional sint32 flow_id = 13;
+ optional qosspec_msg qosspec = 14;
+ optional ipcp_config_msg conf = 15;
+ optional uint32 opts = 16;
+ repeated ipcp_list_msg ipcps = 17;
+ repeated name_info_msg names = 18;
+ optional timespec_msg timeo = 19;
optional sint32 mpl = 20;
optional string comp = 21;
optional bytes pk = 22; /* piggyback */
- optional bytes symmkey = 23;
- optional uint32 timeo_sec = 24;
- optional uint32 timeo_nsec = 25;
- optional sint32 result = 26;
+ optional uint32 timeo_sec = 23;
+ optional uint32 timeo_nsec = 24;
+ optional sint32 result = 25;
+ optional bytes sym_key = 26; /* symmetric encryption key */
+ optional sint32 cipher_nid = 27; /* cipher NID */
}
diff --git a/src/lib/pb/model.proto b/src/lib/pb/model.proto
index f1e401f9..82700a9a 100644
--- a/src/lib/pb/model.proto
+++ b/src/lib/pb/model.proto
@@ -30,22 +30,26 @@ message qosspec_msg {
required uint32 ber = 5; /* Bit error rate, ppb. */
required uint32 in_order = 6; /* In-order delivery. */
required uint32 max_gap = 7; /* In ms. */
- required uint32 cypher_s = 8; /* Crypto strength in bits. */
- required uint32 timeout = 9; /* Timeout in ms. */
+ required uint32 timeout = 8; /* Timeout in ms. */
}
message flow_info_msg {
- required uint32 id = 1;
- required uint32 n_pid = 2;
- required uint32 n_1_pid = 3;
- required uint32 mpl = 4;
- required uint32 state = 5;
- required qosspec_msg qos = 6;
+ required uint32 id = 1;
+ required uint32 n_pid = 2;
+ required uint32 n_1_pid = 3;
+ required uint32 mpl = 4;
+ required uint32 state = 5;
+ required qosspec_msg qos = 6;
+ required uint32 uid = 7;
}
message name_info_msg {
- required string name = 1;
- required uint32 pol_lb = 2;
+ required string name = 1;
+ required uint32 pol_lb = 2;
+ required string skey = 3;
+ required string scrt = 4;
+ required string ckey = 5;
+ required string ccrt = 6;
}
message layer_info_msg {
diff --git a/src/lib/protobuf.c b/src/lib/protobuf.c
index b586168c..4617323a 100644
--- a/src/lib/protobuf.c
+++ b/src/lib/protobuf.c
@@ -23,6 +23,8 @@
#define _DEFAULT_SOURCE
#include <ouroboros/protobuf.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/proc.h>
#include <stdlib.h>
#include <string.h>
@@ -73,12 +75,13 @@ flow_info_msg_t * flow_info_s_to_msg(const struct flow_info * s)
flow_info_msg__init(msg);
- msg->id = s->id;
- msg->n_pid = s->n_pid;
- msg->n_1_pid = s->n_1_pid;
- msg->mpl = s->mpl;
- msg->state = s->state;
- msg->qos = qos_spec_s_to_msg(&s->qs);
+ msg->id = s->id;
+ msg->n_pid = s->n_pid;
+ msg->n_1_pid = s->n_1_pid;
+ msg->mpl = s->mpl;
+ msg->state = s->state;
+ msg->uid = s->uid;
+ msg->qos = qos_spec_s_to_msg(&s->qs);
if (msg->qos == NULL)
goto fail_msg;
@@ -96,16 +99,79 @@ struct flow_info flow_info_msg_to_s(const flow_info_msg_t * msg)
assert(msg != NULL);
+ memset(&s, 0, sizeof(s));
+
s.id = msg->id;
s.n_pid = msg->n_pid;
s.n_1_pid = msg->n_1_pid;
s.mpl = msg->mpl;
s.state = msg->state;
+ s.uid = msg->uid;
s.qs = qos_spec_msg_to_s(msg->qos);
return s;
}
+name_info_msg_t * name_info_s_to_msg(const struct name_info * info)
+{
+ name_info_msg_t * msg;
+
+ assert(info != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
+
+ name_info_msg__init(msg);
+
+ msg->name = strdup(info->name);
+ if (msg->name == NULL)
+ goto fail_msg;
+
+ msg->skey = strdup(info->s.key);
+ if (msg->skey == NULL)
+ goto fail_msg;
+
+ msg->scrt = strdup(info->s.crt);
+ if (msg->scrt == NULL)
+ goto fail_msg;
+
+ msg->ckey = strdup(info->c.key);
+ if (msg->skey == NULL)
+ goto fail_msg;
+
+ msg->ccrt = strdup(info->c.crt);
+ if (msg->ccrt == NULL)
+ goto fail_msg;
+
+ msg->pol_lb = info->pol_lb;
+
+ return msg;
+
+ fail_msg:
+ name_info_msg__free_unpacked(msg, NULL);
+ fail_malloc:
+ return NULL;
+}
+
+struct name_info name_info_msg_to_s(const name_info_msg_t * msg)
+{
+ struct name_info s;
+
+ assert(msg != NULL);
+ assert(strlen(msg->name) <= NAME_SIZE);
+
+ strcpy(s.name, msg->name);
+ strcpy(s.s.key, msg->skey);
+ strcpy(s.s.crt, msg->scrt);
+ strcpy(s.c.key, msg->ckey);
+ strcpy(s.c.crt, msg->ccrt);
+
+ s.pol_lb = msg->pol_lb;
+
+ return s;
+}
+
layer_info_msg_t * layer_info_s_to_msg(const struct layer_info * s)
{
layer_info_msg_t * msg;
@@ -188,6 +254,95 @@ struct ipcp_info ipcp_info_msg_to_s(const ipcp_info_msg_t * msg)
return s;
}
+ls_config_msg_t * ls_config_s_to_msg(const struct ls_config * s)
+{
+ ls_config_msg_t * msg;
+
+ assert(s != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
+
+ ls_config_msg__init(msg);
+
+ msg->pol = s->pol;
+ msg->t_recalc = s->t_recalc;
+ msg->t_update = s->t_update;
+ msg->t_timeo = s->t_timeo;
+
+ return msg;
+
+ fail_malloc:
+ return NULL;
+}
+
+struct ls_config ls_config_msg_to_s(const ls_config_msg_t * msg)
+{
+ struct ls_config s;
+
+ assert(msg != NULL);
+
+ s.pol = msg->pol;
+ s.t_recalc = msg->t_recalc;
+ s.t_update = msg->t_update;
+ s.t_timeo = msg->t_timeo;
+
+ return s;
+}
+
+routing_config_msg_t * routing_config_s_to_msg(const struct routing_config * s)
+{
+ routing_config_msg_t * msg;
+
+ assert(s != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ routing_config_msg__init(msg);
+
+ switch (s->pol) {
+ case ROUTING_LINK_STATE:
+ msg->ls = ls_config_s_to_msg(&s->ls);
+ if (msg->ls == NULL)
+ goto fail_ls;
+ break;
+ default:
+ /* No checks here */
+ break;
+ }
+
+ msg->pol = s->pol;
+
+ return msg;
+
+ fail_ls:
+ routing_config_msg__free_unpacked(msg, NULL);
+ return NULL;
+}
+
+struct routing_config routing_config_msg_to_s(const routing_config_msg_t * msg)
+{
+ struct routing_config s;
+
+ assert(msg != NULL);
+
+ switch (msg->pol) {
+ case ROUTING_LINK_STATE:
+ s.ls = ls_config_msg_to_s(msg->ls);
+ break;
+ default:
+ /* No checks here */
+ break;
+ }
+
+ s.pol = msg->pol;
+
+ return s;
+}
+
dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s)
{
dt_config_msg_t * msg;
@@ -203,9 +358,14 @@ dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s)
msg->addr_size = s->addr_size;
msg->eid_size = s->eid_size;
msg->max_ttl = s->max_ttl;
- msg->routing_type = s->routing_type;
+ msg->routing = routing_config_s_to_msg(&s->routing);
+ if (msg->routing == NULL)
+ goto fail_routing;
return msg;
+ fail_routing:
+ dt_config_msg__free_unpacked(msg, NULL);
+ return NULL;
}
struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg)
@@ -217,11 +377,102 @@ struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg)
s.addr_size = msg->addr_size;
s.eid_size = msg->eid_size;
s.max_ttl = msg->max_ttl;
- s.routing_type = msg->routing_type;
+ s.routing = routing_config_msg_to_s(msg->routing);
return s;
}
+struct dir_dht_config dir_dht_config_msg_to_s(const dir_dht_config_msg_t * msg)
+{
+ struct dir_dht_config s;
+
+ assert(msg != NULL);
+
+ s.params.alpha = msg->alpha;
+ s.params.k = msg->k;
+ s.params.t_expire = msg->t_expire;
+ s.params.t_refresh = msg->t_refresh;
+ s.params.t_replicate = msg->t_replicate;
+ s.peer = msg->peer;
+
+ return s;
+}
+
+dir_dht_config_msg_t * dir_dht_config_s_to_msg(const struct dir_dht_config * s)
+{
+ dir_dht_config_msg_t * msg;
+
+ assert(s != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ dir_dht_config_msg__init(msg);
+
+ msg->alpha = s->params.alpha;
+ msg->k = s->params.k;
+ msg->t_expire = s->params.t_expire;
+ msg->t_refresh = s->params.t_refresh;
+ msg->t_replicate = s->params.t_replicate;
+ msg->peer = s->peer;
+
+ return msg;
+}
+
+struct dir_config dir_config_msg_to_s(const dir_config_msg_t * msg)
+{
+ struct dir_config s;
+
+ assert(msg != NULL);
+
+ switch (msg->pol) {
+ case DIR_DHT:
+ s.dht = dir_dht_config_msg_to_s(msg->dht);
+ break;
+ default:
+ /* No checks here */
+ break;
+ }
+
+ s.pol = msg->pol;
+
+ return s;
+}
+
+dir_config_msg_t * dir_config_s_to_msg(const struct dir_config * s)
+{
+ dir_config_msg_t * msg;
+
+ assert(s != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ dir_config_msg__init(msg);
+
+ switch (s->pol) {
+ case DIR_DHT:
+ msg->dht = dir_dht_config_s_to_msg(&s->dht);
+ if (msg->dht == NULL)
+ goto fail_msg;
+ break;
+ default:
+ /* No checks here */
+ break;
+ }
+
+ msg->pol = s->pol;
+
+ return msg;
+
+ fail_msg:
+ dir_config_msg__free_unpacked(msg, NULL);
+ return NULL;
+}
+
+
uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s)
{
uni_config_msg_t * msg;
@@ -238,6 +489,11 @@ uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s)
if (msg->dt == NULL)
goto fail_msg;
+ msg->dir = dir_config_s_to_msg(&s->dir);
+ if (msg->dir == NULL)
+ goto fail_msg;
+
+
msg->addr_auth_type = s->addr_auth_type;
msg->cong_avoid = s->cong_avoid;
@@ -254,6 +510,7 @@ struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg)
struct uni_config s;
s.dt = dt_config_msg_to_s(msg->dt);
+ s.dir = dir_config_msg_to_s(msg->dir);
s.addr_auth_type = msg->addr_auth_type;
s.cong_avoid = msg->cong_avoid;
@@ -261,9 +518,9 @@ struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg)
return s;
}
-udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s)
+udp4_config_msg_t * udp4_config_s_to_msg(const struct udp4_config * s)
{
- udp_config_msg_t * msg;
+ udp4_config_msg_t * msg;
assert(s != NULL);
@@ -271,24 +528,77 @@ udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s)
if (msg == NULL)
return NULL;
- udp_config_msg__init(msg);
+ udp4_config_msg__init(msg);
- msg->ip_addr = s->ip_addr;
- msg->dns_addr = s->dns_addr;
+ msg->ip_addr = s->ip_addr.s_addr;
+ msg->dns_addr = s->dns_addr.s_addr;
msg->port = s->port;
return msg;
}
-struct udp_config udp_config_msg_to_s(const udp_config_msg_t * msg)
+struct udp4_config udp4_config_msg_to_s(const udp4_config_msg_t * msg)
{
- struct udp_config s;
+ struct udp4_config s;
assert(msg != NULL);
- s.ip_addr = msg->ip_addr;
- s.dns_addr = msg->dns_addr;
- s.port = msg->port;
+ s.ip_addr.s_addr = msg->ip_addr;
+ s.dns_addr.s_addr = msg->dns_addr;
+ s.port = msg->port;
+
+ return s;
+}
+
+#define IN6_LEN sizeof(struct in6_addr)
+udp6_config_msg_t * udp6_config_s_to_msg(const struct udp6_config * s)
+{
+ udp6_config_msg_t * msg;
+
+ assert(s != NULL);
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
+
+ udp6_config_msg__init(msg);
+
+ msg->ip_addr.data = malloc(IN6_LEN);
+ if (msg->ip_addr.data == NULL)
+ goto fail_msg;
+
+ msg->ip_addr.len = IN6_LEN;
+ memcpy(msg->ip_addr.data, &s->ip_addr.s6_addr, IN6_LEN);
+
+ msg->dns_addr.data = malloc(IN6_LEN);
+ if (msg->dns_addr.data == NULL)
+ goto fail_msg;
+
+ msg->dns_addr.len = IN6_LEN;
+ memcpy(msg->dns_addr.data, &s->dns_addr.s6_addr, IN6_LEN);
+
+ msg->port = s->port;
+
+ return msg;
+
+ fail_msg:
+ udp6_config_msg__free_unpacked(msg, NULL);
+ fail_malloc:
+ return NULL;
+}
+
+struct udp6_config udp6_config_msg_to_s(const udp6_config_msg_t * msg)
+{
+ struct udp6_config s;
+
+ assert(msg != NULL);
+
+ assert(msg->ip_addr.len == IN6_LEN);
+ assert(msg->dns_addr.len == IN6_LEN);
+
+ memcpy(&s.ip_addr.s6_addr, msg->ip_addr.data, IN6_LEN);
+ memcpy(&s.dns_addr.s6_addr, msg->dns_addr.data, IN6_LEN);
+ s.port = msg->port;
return s;
}
@@ -362,9 +672,14 @@ ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s)
if (msg->eth == NULL)
goto fail_msg;
break;
- case IPCP_UDP:
- msg->udp = udp_config_s_to_msg(&s->udp);
- if (msg->udp == NULL)
+ case IPCP_UDP4:
+ msg->udp4 = udp4_config_s_to_msg(&s->udp4);
+ if (msg->udp4 == NULL)
+ goto fail_msg;
+ break;
+ case IPCP_UDP6:
+ msg->udp6 = udp6_config_s_to_msg(&s->udp6);
+ if (msg->udp6 == NULL)
goto fail_msg;
break;
default:
@@ -407,8 +722,11 @@ struct ipcp_config ipcp_config_msg_to_s(const ipcp_config_msg_t * msg)
case IPCP_ETH_DIX:
s.eth = eth_config_msg_to_s(msg->eth);
break;
- case IPCP_UDP:
- s.udp = udp_config_msg_to_s(msg->udp);
+ case IPCP_UDP4:
+ s.udp4 = udp4_config_msg_to_s(msg->udp4);
+ break;
+ case IPCP_UDP6:
+ s.udp6 = udp6_config_msg_to_s(msg->udp6);
break;
case IPCP_BROADCAST:
break;
@@ -439,7 +757,6 @@ qosspec_msg_t * qos_spec_s_to_msg(const struct qos_spec * s)
msg->ber = s->ber;
msg->in_order = s->in_order;
msg->max_gap = s->max_gap;
- msg->cypher_s = s->cypher_s;
msg->timeout = s->timeout;
return msg;
@@ -458,7 +775,6 @@ struct qos_spec qos_spec_msg_to_s(const qosspec_msg_t * msg)
s.ber = msg->ber;
s.in_order = msg->in_order;
s.max_gap = msg->max_gap;
- s.cypher_s = msg->cypher_s;
s.timeout = msg->timeout;
return s;
diff --git a/src/lib/random.c b/src/lib/random.c
index 09e0b844..2dc5f02f 100644
--- a/src/lib/random.c
+++ b/src/lib/random.c
@@ -24,22 +24,12 @@
#include <ouroboros/random.h>
-#if defined(__APPLE__) /* Barf */
-#undef __OSX_AVAILABLE
-#define __OSX_AVAILABLE(arg)
-#undef __IOS_AVAILABLE
-#define __IOS_AVAILABLE(arg)
-#undef __TVOS_AVAILABLE
-#define __TVOS_AVAILABLE(arg)
-#undef __WATCHOS_AVAILABLE
-#define __WATCHOS_AVAILABLE(arg)
-#include <sys/random.h>
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <stdlib.h>
#elif defined(HAVE_SYS_RANDOM)
#include <sys/random.h>
#elif defined(HAVE_LIBGCRYPT)
#include <gcrypt.h>
-#elif defined(__FreeBSD__)
-#include <stdlib.h>
#elif defined(HAVE_OPENSSL_RNG)
#include <openssl/rand.h>
#include <limits.h>
@@ -48,13 +38,11 @@
int random_buffer(void * buf,
size_t len)
{
-#if defined(__APPLE__)
- return getentropy(buf, len);
-#elif defined(__FreeBSD__)
+#if defined(__APPLE__) || defined(__FreeBSD__)
arc4random_buf(buf, len);
return 0;
#elif defined(HAVE_SYS_RANDOM)
- return getrandom(buf, len, GRND_NONBLOCK); /* glibc 2.25 */
+ return getrandom(buf, len, GRND_NONBLOCK);
#elif defined(HAVE_LIBGCRYPT)
gcry_randomize(buf, len, GCRY_STRONG_RANDOM);
return 0;
diff --git a/src/lib/serdes-irm.c b/src/lib/serdes-irm.c
index c4ba3053..8cbe20b1 100644
--- a/src/lib/serdes-irm.c
+++ b/src/lib/serdes-irm.c
@@ -24,6 +24,7 @@
#include "config.h"
+#include <ouroboros/crypt.h>
#include <ouroboros/errno.h>
#include <ouroboros/serdes-irm.h>
#include <ouroboros/protobuf.h>
@@ -135,14 +136,11 @@ int flow_join__irm_req_ser(buffer_t * buf,
int flow__irm_result_des(buffer_t * buf,
struct flow_info * flow,
- buffer_t * sk)
+ struct crypt_sk * sk)
{
irm_msg_t * msg;
int err;
- if (sk != NULL)
- sk->data = NULL;
-
msg = irm_msg__unpack(NULL, buf->len, buf->data);
if (msg == NULL) {
err = -EIRMD;
@@ -166,18 +164,15 @@ int flow__irm_result_des(buffer_t * buf,
*flow = flow_info_msg_to_s(msg->flow_info);
- if (flow->qs.cypher_s > 0 && sk != NULL) {
- if (msg->symmkey.data == NULL || msg->symmkey.len == 0) {
- err = -ECRYPT;
- goto fail;
- }
-
- sk->len = msg->symmkey.len;
- sk->data = msg->symmkey.data;
+ if (msg->has_cipher_nid)
+ sk->nid = msg->cipher_nid;
+ else
+ sk->nid = NID_undef;
- msg->symmkey.data = NULL;
- msg->symmkey.len = 0;
- }
+ if (msg->sym_key.len == SYMMKEYSZ)
+ memcpy(sk->key, msg->sym_key.data, SYMMKEYSZ);
+ else
+ memset(sk->key, 0, SYMMKEYSZ);
irm_msg__free_unpacked(msg, NULL);
@@ -294,8 +289,8 @@ int ipcp_create_r__irm_req_ser(buffer_t * buf,
return -ENOMEM;
}
-int proc_announce__irm_req_ser(buffer_t * buf,
- const char * prog)
+int proc_announce__irm_req_ser(buffer_t * buf,
+ const struct proc_info * proc)
{
irm_msg_t * msg;
size_t len;
@@ -308,8 +303,8 @@ int proc_announce__irm_req_ser(buffer_t * buf,
msg->code = IRM_MSG_CODE__IRM_PROC_ANNOUNCE;
msg->has_pid = true;
- msg->pid = getpid();
- msg->prog = strdup(prog);
+ msg->pid = proc->pid;
+ msg->prog = strdup(proc->prog);
if (msg->prog == NULL)
goto fail_msg;
diff --git a/src/lib/shm_rbuff.c b/src/lib/shm_rbuff.c
deleted file mode 100644
index 22cff41c..00000000
--- a/src/lib/shm_rbuff.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Ring buffer implementations for incoming packets
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#define _POSIX_C_SOURCE 200809L
-
-#include "config.h"
-
-#include <ouroboros/shm_rbuff.h>
-#include <ouroboros/lockfile.h>
-#include <ouroboros/errno.h>
-#include <ouroboros/fccntl.h>
-#include <ouroboros/pthread.h>
-#include <ouroboros/time.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#define FN_MAX_CHARS 255
-
-#define SHM_RB_FILE_SIZE ((SHM_RBUFF_SIZE) * sizeof(ssize_t) \
- + 3 * sizeof(size_t) \
- + sizeof(pthread_mutex_t) \
- + 2 * sizeof (pthread_cond_t))
-
-#define shm_rbuff_used(rb) ((*rb->head + (SHM_RBUFF_SIZE) - *rb->tail) \
- & ((SHM_RBUFF_SIZE) - 1))
-#define shm_rbuff_free(rb) (shm_rbuff_used(rb) + 1 < (SHM_RBUFF_SIZE))
-#define shm_rbuff_empty(rb) (*rb->head == *rb->tail)
-#define head_el_ptr(rb) (rb->shm_base + *rb->head)
-#define tail_el_ptr(rb) (rb->shm_base + *rb->tail)
-
-struct shm_rbuff {
- ssize_t * shm_base; /* start of entry */
- size_t * head; /* start of ringbuffer head */
- size_t * tail; /* start of ringbuffer tail */
- size_t * acl; /* access control */
- pthread_mutex_t * lock; /* lock all free space in shm */
- pthread_cond_t * add; /* packet arrived */
- pthread_cond_t * del; /* packet removed */
- pid_t pid; /* pid of the owner */
- int flow_id; /* flow_id of the flow */
-};
-
-#define MM_FLAGS (PROT_READ | PROT_WRITE)
-
-static struct shm_rbuff * rbuff_create(pid_t pid,
- int flow_id,
- int flags)
-{
- struct shm_rbuff * rb;
- int fd;
- ssize_t * shm_base;
- char fn[FN_MAX_CHARS];
-
- sprintf(fn, SHM_RBUFF_PREFIX "%d.%d", pid, flow_id);
-
- rb = malloc(sizeof(*rb));
- if (rb == NULL)
- goto fail_malloc;
-
- fd = shm_open(fn, flags, 0666);
- if (fd == -1)
- goto fail_open;
-
- if ((flags & O_CREAT) && ftruncate(fd, SHM_RB_FILE_SIZE) < 0)
- goto fail_truncate;
-
- shm_base = mmap(NULL, SHM_RB_FILE_SIZE, MM_FLAGS, MAP_SHARED, fd, 0);
- if (shm_base == MAP_FAILED)
- goto fail_truncate;
-
- close(fd);
-
- rb->shm_base = shm_base;
- rb->head = (size_t *) (rb->shm_base + (SHM_RBUFF_SIZE));
- rb->tail = rb->head + 1;
- rb->acl = rb->tail + 1;
- rb->lock = (pthread_mutex_t *) (rb->acl + 1);
- rb->add = (pthread_cond_t *) (rb->lock + 1);
- rb->del = rb->add + 1;
- rb->pid = pid;
- rb->flow_id = flow_id;
-
- return rb;
-
- fail_truncate:
- close(fd);
- if (flags & O_CREAT)
- shm_unlink(fn);
- fail_open:
- free(rb);
- fail_malloc:
- return NULL;
-}
-
-static void rbuff_destroy(struct shm_rbuff * rb)
-{
- munmap(rb->shm_base, SHM_RB_FILE_SIZE);
-
- free(rb);
-}
-
-struct shm_rbuff * shm_rbuff_create(pid_t pid,
- int flow_id)
-{
- struct shm_rbuff * rb;
- pthread_mutexattr_t mattr;
- pthread_condattr_t cattr;
- mode_t mask;
-
- mask = umask(0);
-
- rb = rbuff_create(pid, flow_id, O_CREAT | O_EXCL | O_RDWR);
-
- umask(mask);
-
- if (rb == NULL)
- goto fail_rb;
-
- if (pthread_mutexattr_init(&mattr))
- goto fail_mattr;
-
- pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
-#ifdef HAVE_ROBUST_MUTEX
- pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
-#endif
- if (pthread_mutex_init(rb->lock, &mattr))
- goto fail_mutex;
-
- if (pthread_condattr_init(&cattr))
- goto fail_cattr;
-
- pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
-#ifndef __APPLE__
- pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
-#endif
- if (pthread_cond_init(rb->add, &cattr))
- goto fail_add;
-
- if (pthread_cond_init(rb->del, &cattr))
- goto fail_del;
-
- *rb->acl = ACL_RDWR;
- *rb->head = 0;
- *rb->tail = 0;
-
- rb->pid = pid;
- rb->flow_id = flow_id;
-
- pthread_mutexattr_destroy(&mattr);
- pthread_condattr_destroy(&cattr);
-
- return rb;
-
- fail_del:
- pthread_cond_destroy(rb->add);
- fail_add:
- pthread_condattr_destroy(&cattr);
- fail_cattr:
- pthread_mutex_destroy(rb->lock);
- fail_mutex:
- pthread_mutexattr_destroy(&mattr);
- fail_mattr:
- shm_rbuff_destroy(rb);
- fail_rb:
- return NULL;
-}
-
-struct shm_rbuff * shm_rbuff_open(pid_t pid,
- int flow_id)
-{
- return rbuff_create(pid, flow_id, O_RDWR);
-}
-
-void shm_rbuff_close(struct shm_rbuff * rb)
-{
- assert(rb);
-
- rbuff_destroy(rb);
-}
-
-#if (defined(SHM_RBUFF_LOCKLESS) && \
- (defined(__GNUC__) || defined (__clang__)))
-#include "shm_rbuff_ll.c"
-#else
-#include "shm_rbuff_pthr.c"
-#endif
diff --git a/src/lib/shm_rbuff_ll.c b/src/lib/shm_rbuff_ll.c
deleted file mode 100644
index 46a5314e..00000000
--- a/src/lib/shm_rbuff_ll.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Lockless ring buffer for incoming packets
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#define RB_HEAD __sync_fetch_and_add(rb->head, 0)
-#define RB_TAIL __sync_fetch_and_add(rb->tail, 0)
-
-void shm_rbuff_destroy(struct shm_rbuff * rb)
-{
- char fn[FN_MAX_CHARS];
-
- assert(rb);
-
- sprintf(fn, SHM_RBUFF_PREFIX "%d.%d", rb->pid, rb->flow_id);
-
- __sync_bool_compare_and_swap(rb->acl, *rb->acl, ACL_FLOWDOWN);
-
- pthread_cond_broadcast(rb->del);
- pthread_cond_broadcast(rb->add);
-
- shm_rbuff_close(rb);
-
- shm_unlink(fn);
-}
-
-int shm_rbuff_write(struct shm_rbuff * rb,
- size_t idx)
-{
- size_t ohead;
- size_t nhead;
- bool was_empty = false;
-
- assert(rb);
- assert(idx < SHM_BUFFER_SIZE);
-
- if (__sync_fetch_and_add(rb->acl, 0) != ACL_RDWR) {
- if (__sync_fetch_and_add(rb->acl, 0) & ACL_FLOWDOWN)
- return -EFLOWDOWN;
- else if (__sync_fetch_and_add(rb->acl, 0) & ACL_RDONLY)
- return -ENOTALLOC;
- }
-
- if (!shm_rbuff_free(rb))
- return -EAGAIN;
-
- if (shm_rbuff_empty(rb))
- was_empty = true;
-
- nhead = RB_HEAD;
-
- *(rb->shm_base + nhead) = (ssize_t) idx;
-
- do {
- ohead = nhead;
- nhead = (ohead + 1) & ((SHM_RBUFF_SIZE) - 1);
- nhead = __sync_val_compare_and_swap(rb->head, ohead, nhead);
- } while (nhead != ohead);
-
- if (was_empty)
- pthread_cond_broadcast(rb->add);
-
- return 0;
-}
-
-/* FIXME: this is a copy of the pthr implementation */
-int shm_rbuff_write_b(struct shm_rbuff * rb,
- size_t idx,
- const struct timespec * abstime)
-{
- int ret = 0;
-
- assert(rb);
- assert(idx < SHM_BUFFER_SIZE);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- if (*rb->acl != ACL_RDWR) {
- if (*rb->acl & ACL_FLOWDOWN)
- ret = -EFLOWDOWN;
- else if (*rb->acl & ACL_RDONLY)
- ret = -ENOTALLOC;
- goto err;
- }
-
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (!shm_rbuff_free(rb) && ret != -ETIMEDOUT) {
- ret = -__timedwait(rb->add, rb->lock, abstime);
-#ifdef HAVE_ROBUST_MUTEX
- if (ret == -EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- }
-
- if (shm_rbuff_empty(rb))
- pthread_cond_broadcast(rb->add);
-
- if (ret != -ETIMEDOUT) {
- *head_el_ptr(rb) = (ssize_t) idx;
- *rb->head = (*rb->head + 1) & ((SHM_RBUFF_SIZE) -1);
- }
-
- pthread_cleanup_pop(true);
-
- return ret;
- err:
- pthread_mutex_unlock(rb->lock);
- return ret;
-}
-
-ssize_t shm_rbuff_read(struct shm_rbuff * rb)
-{
- size_t otail;
- size_t ntail;
-
- assert(rb);
-
- if (shm_rbuff_empty(rb)) {
- if (_sync_fetch_and_add(rb->acl, 0) & ACL_FLOWDOWN)
- return -EFLOWDOWN;
-
- if (_sync_fetch_and_add(rb->acl, 0) & ACL_FLOWPEER)
- return -EFLOWPEER;
-
- return -EAGAIN;
- }
-
- ntail = RB_TAIL;
-
- do {
- otail = ntail;
- ntail = (otail + 1) & ((SHM_RBUFF_SIZE) - 1);
- ntail = __sync_val_compare_and_swap(rb->tail, otail, ntail);
- } while (ntail != otail);
-
- pthread_cond_broadcast(rb->del);
-
- return *(rb->shm_base + ntail);
-}
-
-ssize_t shm_rbuff_read_b(struct shm_rbuff * rb,
- const struct timespec * abstime)
-{
- ssize_t idx = -1;
-
- assert(rb);
-
- /* try a non-blocking read first */
- idx = shm_rbuff_read(rb);
- if (idx != -EAGAIN)
- return idx;
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (shm_rbuff_empty(rb) && (idx != -ETIMEDOUT)) {
- idx = -__timedwait(rb->add, rb->lock, abstime);
-#ifdef HAVE_ROBUST_MUTEX
- if (idx == -EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- }
-
- if (idx != -ETIMEDOUT) {
- /* do a nonblocking read */
- idx = shm_rbuff_read(rb);
- assert(idx >= 0);
- }
-
- pthread_cleanup_pop(true);
-
- return idx;
-}
-
-void shm_rbuff_set_acl(struct shm_rbuff * rb,
- uint32_t flags)
-{
- assert(rb);
-
- __sync_bool_compare_and_swap(rb->acl, *rb->acl, flags);
-}
-
-uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb)
-{
- assert(rb);
-
- return __sync_fetch_and_add(rb->acl, 0);
-}
-
-void shm_rbuff_fini(struct shm_rbuff * rb)
-{
- assert(rb);
-
- if (shm_rbuff_empty(rb))
- return;
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (!shm_rbuff_empty(rb))
-#ifndef HAVE_ROBUST_MUTEX
- pthread_cond_wait(rb->del, rb->lock);
-#else
- if (pthread_cond_wait(rb->del, rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- pthread_cleanup_pop(true);
-}
-
-size_t shm_rbuff_queued(struct shm_rbuff * rb)
-{
- assert(rb);
-
- return shm_rbuff_used(rb);
-}
diff --git a/src/lib/shm_rbuff_pthr.c b/src/lib/shm_rbuff_pthr.c
deleted file mode 100644
index b543fb07..00000000
--- a/src/lib/shm_rbuff_pthr.c
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Ring buffer for incoming packets
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-void shm_rbuff_destroy(struct shm_rbuff * rb)
-{
- char fn[FN_MAX_CHARS];
-
- assert(rb != NULL);
-
-#ifdef CONFIG_OUROBOROS_DEBUG
- pthread_mutex_lock(rb->lock);
-
- *rb->acl = *rb->acl & ACL_FLOWDOWN;
-
- pthread_cond_broadcast(rb->del);
- pthread_cond_broadcast(rb->add);
-
- pthread_mutex_unlock(rb->lock);
-#endif
- sprintf(fn, SHM_RBUFF_PREFIX "%d.%d", rb->pid, rb->flow_id);
-
- shm_rbuff_close(rb);
-
- shm_unlink(fn);
-}
-
-int shm_rbuff_write(struct shm_rbuff * rb,
- size_t idx)
-{
- int ret = 0;
-
- assert(rb != NULL);
- assert(idx < SHM_BUFFER_SIZE);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- if (*rb->acl != ACL_RDWR) {
- if (*rb->acl & ACL_FLOWDOWN)
- ret = -EFLOWDOWN;
- else if (*rb->acl & ACL_RDONLY)
- ret = -ENOTALLOC;
- goto err;
- }
-
- if (!shm_rbuff_free(rb)) {
- ret = -EAGAIN;
- goto err;
- }
-
- if (shm_rbuff_empty(rb))
- pthread_cond_broadcast(rb->add);
-
- *head_el_ptr(rb) = (ssize_t) idx;
- *rb->head = (*rb->head + 1) & ((SHM_RBUFF_SIZE) - 1);
-
- pthread_mutex_unlock(rb->lock);
-
- return 0;
- err:
- pthread_mutex_unlock(rb->lock);
- return ret;
-}
-
-int shm_rbuff_write_b(struct shm_rbuff * rb,
- size_t idx,
- const struct timespec * abstime)
-{
- int ret = 0;
-
- assert(rb != NULL);
- assert(idx < SHM_BUFFER_SIZE);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- if (*rb->acl != ACL_RDWR) {
- if (*rb->acl & ACL_FLOWDOWN)
- ret = -EFLOWDOWN;
- else if (*rb->acl & ACL_RDONLY)
- ret = -ENOTALLOC;
- goto err;
- }
-
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (!shm_rbuff_free(rb)
- && ret != -ETIMEDOUT
- && !(*rb->acl & ACL_FLOWDOWN)) {
- ret = -__timedwait(rb->del, rb->lock, abstime);
-#ifdef HAVE_ROBUST_MUTEX
- if (ret == -EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- }
-
- if (ret != -ETIMEDOUT) {
- if (shm_rbuff_empty(rb))
- pthread_cond_broadcast(rb->add);
- *head_el_ptr(rb) = (ssize_t) idx;
- *rb->head = (*rb->head + 1) & ((SHM_RBUFF_SIZE) - 1);
- }
-
- pthread_cleanup_pop(true);
-
- return ret;
- err:
- pthread_mutex_unlock(rb->lock);
- return ret;
-}
-
-static int check_rb_acl(struct shm_rbuff * rb)
-{
- assert(rb != NULL);
-
- if (*rb->acl & ACL_FLOWDOWN)
- return -EFLOWDOWN;
-
- if (*rb->acl & ACL_FLOWPEER)
- return -EFLOWPEER;
-
- return -EAGAIN;
-}
-
-ssize_t shm_rbuff_read(struct shm_rbuff * rb)
-{
- ssize_t ret = 0;
-
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- if (shm_rbuff_empty(rb)) {
- ret = check_rb_acl(rb);
- pthread_mutex_unlock(rb->lock);
- return ret;
- }
-
- ret = *tail_el_ptr(rb);
- *rb->tail = (*rb->tail + 1) & ((SHM_RBUFF_SIZE) - 1);
- pthread_cond_broadcast(rb->del);
-
- pthread_mutex_unlock(rb->lock);
-
- return ret;
-}
-
-ssize_t shm_rbuff_read_b(struct shm_rbuff * rb,
- const struct timespec * abstime)
-{
- ssize_t idx = -1;
-
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- if (shm_rbuff_empty(rb) && (*rb->acl & ACL_FLOWDOWN)) {
- pthread_mutex_unlock(rb->lock);
- return -EFLOWDOWN;
- }
-
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (shm_rbuff_empty(rb) &&
- idx != -ETIMEDOUT &&
- check_rb_acl(rb) == -EAGAIN) {
- idx = -__timedwait(rb->add, rb->lock, abstime);
-#ifdef HAVE_ROBUST_MUTEX
- if (idx == -EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- }
-
- if (!shm_rbuff_empty(rb)) {
- idx = *tail_el_ptr(rb);
- *rb->tail = (*rb->tail + 1) & ((SHM_RBUFF_SIZE) - 1);
- pthread_cond_broadcast(rb->del);
- } else if (idx != -ETIMEDOUT) {
- idx = check_rb_acl(rb);
- }
-
- pthread_cleanup_pop(true);
-
- assert(idx != -EAGAIN);
-
- return idx;
-}
-
-void shm_rbuff_set_acl(struct shm_rbuff * rb,
- uint32_t flags)
-{
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- *rb->acl = (size_t) flags;
-
- pthread_cond_broadcast(rb->del);
- pthread_cond_broadcast(rb->add);
-
- pthread_mutex_unlock(rb->lock);
-}
-
-uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb)
-{
- uint32_t flags;
-
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- flags = (uint32_t) *rb->acl;
-
- pthread_mutex_unlock(rb->lock);
-
- return flags;
-}
-
-void shm_rbuff_fini(struct shm_rbuff * rb)
-{
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock);
-
- while (!shm_rbuff_empty(rb))
-#ifndef HAVE_ROBUST_MUTEX
- pthread_cond_wait(rb->del, rb->lock);
-#else
- if (pthread_cond_wait(rb->del, rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
- pthread_cleanup_pop(true);
-}
-
-size_t shm_rbuff_queued(struct shm_rbuff * rb)
-{
- size_t ret;
-
- assert(rb != NULL);
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rb->lock);
-#else
- if (pthread_mutex_lock(rb->lock) == EOWNERDEAD)
- pthread_mutex_consistent(rb->lock);
-#endif
-
- ret = shm_rbuff_used(rb);
-
- pthread_mutex_unlock(rb->lock);
-
- return ret;
-}
diff --git a/src/lib/shm_rdrbuff.c b/src/lib/shm_rdrbuff.c
deleted file mode 100644
index 7ad1bd2e..00000000
--- a/src/lib/shm_rdrbuff.c
+++ /dev/null
@@ -1,610 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Random Deletion Ring Buffer for Data Units
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#define _POSIX_C_SOURCE 200809L
-
-#include "config.h"
-
-#include <ouroboros/errno.h>
-#include <ouroboros/pthread.h>
-#include <ouroboros/shm_rdrbuff.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#define SHM_BLOCKS_SIZE ((SHM_BUFFER_SIZE) * SHM_RDRB_BLOCK_SIZE)
-#define SHM_FILE_SIZE (SHM_BLOCKS_SIZE + 2 * sizeof(size_t) \
- + sizeof(pthread_mutex_t) + 2 * sizeof(pthread_cond_t) \
- + sizeof(pid_t))
-#define DU_BUFF_OVERHEAD (DU_BUFF_HEADSPACE + DU_BUFF_TAILSPACE)
-
-#define get_head_ptr(rdrb) \
- idx_to_du_buff_ptr(rdrb, *rdrb->head)
-
-#define get_tail_ptr(rdrb) \
- idx_to_du_buff_ptr(rdrb, *rdrb->tail)
-
-#define idx_to_du_buff_ptr(rdrb, idx) \
- ((struct shm_du_buff *) (rdrb->shm_base + idx * SHM_RDRB_BLOCK_SIZE))
-
-#define shm_rdrb_used(rdrb) \
- (((*rdrb->head + (SHM_BUFFER_SIZE) - *rdrb->tail) + 1) \
- & ((SHM_BUFFER_SIZE) - 1))
-
-#define shm_rdrb_free(rdrb, i) \
- (shm_rdrb_used(rdrb) + i < (SHM_BUFFER_SIZE))
-
-#define shm_rdrb_empty(rdrb) \
- (*rdrb->tail == *rdrb->head)
-
-struct shm_du_buff {
- size_t size;
-#ifdef SHM_RDRB_MULTI_BLOCK
- size_t blocks;
-#endif
- size_t du_head;
- size_t du_tail;
- size_t refs;
- size_t idx;
-};
-
-struct shm_rdrbuff {
- uint8_t * shm_base; /* start of blocks */
- size_t * head; /* start of ringbuffer head */
- size_t * tail; /* start of ringbuffer tail */
- pthread_mutex_t * lock; /* lock all free space in shm */
- pthread_cond_t * healthy; /* flag when packet is read */
- pid_t * pid; /* pid of the irmd owner */
-};
-
-static void garbage_collect(struct shm_rdrbuff * rdrb)
-{
-#ifdef SHM_RDRB_MULTI_BLOCK
- struct shm_du_buff * sdb;
- while (!shm_rdrb_empty(rdrb) &&
- (sdb = get_tail_ptr(rdrb))->refs == 0)
- *rdrb->tail = (*rdrb->tail + sdb->blocks)
- & ((SHM_BUFFER_SIZE) - 1);
-#else
- while (!shm_rdrb_empty(rdrb) && get_tail_ptr(rdrb)->refs == 0)
- *rdrb->tail = (*rdrb->tail + 1) & ((SHM_BUFFER_SIZE) - 1);
-#endif
- pthread_cond_broadcast(rdrb->healthy);
-}
-
-#ifdef HAVE_ROBUST_MUTEX
-static void sanitize(struct shm_rdrbuff * rdrb)
-{
- --get_head_ptr(rdrb)->refs;
- garbage_collect(rdrb);
- pthread_mutex_consistent(rdrb->lock);
-}
-#endif
-
-static char * rdrb_filename(void)
-{
- char * str;
-
- str = malloc(strlen(SHM_RDRB_NAME) + 1);
- if (str == NULL)
- return NULL;
-
- sprintf(str, "%s", SHM_RDRB_NAME);
-
- return str;
-}
-
-void shm_rdrbuff_close(struct shm_rdrbuff * rdrb)
-{
- assert(rdrb);
-
- munmap(rdrb->shm_base, SHM_FILE_SIZE);
- free(rdrb);
-}
-
-void shm_rdrbuff_destroy(struct shm_rdrbuff * rdrb)
-{
- char * shm_rdrb_fn;
-
- assert(rdrb);
-
- if (getpid() != *rdrb->pid && kill(*rdrb->pid, 0) == 0) {
- free(rdrb);
- return;
- }
-
- shm_rdrbuff_close(rdrb);
-
- shm_rdrb_fn = rdrb_filename();
- if (shm_rdrb_fn == NULL)
- return;
-
- shm_unlink(shm_rdrb_fn);
- free(shm_rdrb_fn);
-}
-
-#define MM_FLAGS (PROT_READ | PROT_WRITE)
-
-static struct shm_rdrbuff * rdrb_create(int flags)
-{
- struct shm_rdrbuff * rdrb;
- int fd;
- uint8_t * shm_base;
- char * shm_rdrb_fn;
-
- shm_rdrb_fn = rdrb_filename();
- if (shm_rdrb_fn == NULL)
- goto fail_fn;
-
- rdrb = malloc(sizeof *rdrb);
- if (rdrb == NULL)
- goto fail_rdrb;
-
- fd = shm_open(shm_rdrb_fn, flags, 0666);
- if (fd == -1)
- goto fail_open;
-
- if ((flags & O_CREAT) && ftruncate(fd, SHM_FILE_SIZE) < 0)
- goto fail_truncate;
-
- shm_base = mmap(NULL, SHM_FILE_SIZE, MM_FLAGS, MAP_SHARED, fd, 0);
- if (shm_base == MAP_FAILED)
- goto fail_truncate;
-
- close(fd);
-
- rdrb->shm_base = shm_base;
- rdrb->head = (size_t *) ((uint8_t *) rdrb->shm_base + SHM_BLOCKS_SIZE);
- rdrb->tail = rdrb->head + 1;
- rdrb->lock = (pthread_mutex_t *) (rdrb->tail + 1);
- rdrb->healthy = (pthread_cond_t *) (rdrb->lock + 1);
- rdrb->pid = (pid_t *) (rdrb->healthy + 1);
-
- free(shm_rdrb_fn);
-
- return rdrb;
-
- fail_truncate:
- close(fd);
- if (flags & O_CREAT)
- shm_unlink(shm_rdrb_fn);
- fail_open:
- free(rdrb);
- fail_rdrb:
- free(shm_rdrb_fn);
- fail_fn:
- return NULL;
-}
-
-struct shm_rdrbuff * shm_rdrbuff_create(void)
-{
- struct shm_rdrbuff * rdrb;
- mode_t mask;
- pthread_mutexattr_t mattr;
- pthread_condattr_t cattr;
-
- mask = umask(0);
-
- rdrb = rdrb_create(O_CREAT | O_EXCL | O_RDWR);
-
- umask(mask);
-
- if (rdrb == NULL)
- goto fail_rdrb;
-
- if (pthread_mutexattr_init(&mattr))
- goto fail_mattr;
-
- pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
-#ifdef HAVE_ROBUST_MUTEX
- pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
-#endif
- if (pthread_mutex_init(rdrb->lock, &mattr))
- goto fail_mutex;
-
- if (pthread_condattr_init(&cattr))
- goto fail_cattr;
-
- pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
-#ifndef __APPLE__
- pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
-#endif
- if (pthread_cond_init(rdrb->healthy, &cattr))
- goto fail_healthy;
-
- *rdrb->head = 0;
- *rdrb->tail = 0;
-
- *rdrb->pid = getpid();
-
- pthread_mutexattr_destroy(&mattr);
- pthread_condattr_destroy(&cattr);
-
- return rdrb;
-
- fail_healthy:
- pthread_condattr_destroy(&cattr);
- fail_cattr:
- pthread_mutex_destroy(rdrb->lock);
- fail_mutex:
- pthread_mutexattr_destroy(&mattr);
- fail_mattr:
- shm_rdrbuff_destroy(rdrb);
- fail_rdrb:
- return NULL;
-}
-
-struct shm_rdrbuff * shm_rdrbuff_open(void)
-{
- return rdrb_create(O_RDWR);
-}
-
-void shm_rdrbuff_purge(void)
-{
- char * shm_rdrb_fn;
-
- shm_rdrb_fn = rdrb_filename();
- if (shm_rdrb_fn == NULL)
- return;
-
- shm_unlink(shm_rdrb_fn);
- free(shm_rdrb_fn);
-}
-
-ssize_t shm_rdrbuff_alloc(struct shm_rdrbuff * rdrb,
- size_t len,
- uint8_t ** ptr,
- struct shm_du_buff ** psdb)
-{
- struct shm_du_buff * sdb;
- size_t size = DU_BUFF_OVERHEAD + len;
-#ifdef SHM_RDRB_MULTI_BLOCK
- size_t blocks = 0;
- size_t padblocks = 0;
-#endif
- ssize_t sz = size + sizeof(*sdb);
-
- assert(rdrb);
- assert(psdb);
-
-#ifndef SHM_RDRB_MULTI_BLOCK
- if (sz > SHM_RDRB_BLOCK_SIZE)
- return -EMSGSIZE;
-#else
- while (sz > 0) {
- sz -= SHM_RDRB_BLOCK_SIZE;
- ++blocks;
- }
-#endif
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rdrb->lock);
-#else
- if (pthread_mutex_lock(rdrb->lock) == EOWNERDEAD)
- sanitize(rdrb);
-#endif
-#ifdef SHM_RDRB_MULTI_BLOCK
- if (blocks + *rdrb->head > (SHM_BUFFER_SIZE))
- padblocks = (SHM_BUFFER_SIZE) - *rdrb->head;
-
- if (!shm_rdrb_free(rdrb, blocks + padblocks)) {
-#else
- if (!shm_rdrb_free(rdrb, 1)) {
-#endif
- pthread_mutex_unlock(rdrb->lock);
- return -EAGAIN;
- }
-
-#ifdef SHM_RDRB_MULTI_BLOCK
- if (padblocks) {
- sdb = get_head_ptr(rdrb);
- sdb->size = 0;
- sdb->blocks = padblocks;
- sdb->refs = 0;
- sdb->du_head = 0;
- sdb->du_tail = 0;
- sdb->idx = *rdrb->head;
-
- *rdrb->head = 0;
- }
-#endif
- sdb = get_head_ptr(rdrb);
- sdb->refs = 1;
- sdb->idx = *rdrb->head;
-#ifdef SHM_RDRB_MULTI_BLOCK
- sdb->blocks = blocks;
-
- *rdrb->head = (*rdrb->head + blocks) & ((SHM_BUFFER_SIZE) - 1);
-#else
- *rdrb->head = (*rdrb->head + 1) & ((SHM_BUFFER_SIZE) - 1);
-#endif
- pthread_mutex_unlock(rdrb->lock);
-
- sdb->size = size;
- sdb->du_head = DU_BUFF_HEADSPACE;
- sdb->du_tail = sdb->du_head + len;
-
- *psdb = sdb;
- if (ptr != NULL)
- *ptr = (uint8_t *) (sdb + 1) + sdb->du_head;
-
- return sdb->idx;
-}
-
-ssize_t shm_rdrbuff_alloc_b(struct shm_rdrbuff * rdrb,
- size_t len,
- uint8_t ** ptr,
- struct shm_du_buff ** psdb,
- const struct timespec * abstime)
-{
- struct shm_du_buff * sdb;
- size_t size = DU_BUFF_OVERHEAD + len;
-#ifdef SHM_RDRB_MULTI_BLOCK
- size_t blocks = 0;
- size_t padblocks = 0;
-#endif
- ssize_t sz = size + sizeof(*sdb);
- int ret = 0;
-
- assert(rdrb);
- assert(psdb);
-
-#ifndef SHM_RDRB_MULTI_BLOCK
- if (sz > SHM_RDRB_BLOCK_SIZE)
- return -EMSGSIZE;
-#else
- while (sz > 0) {
- sz -= SHM_RDRB_BLOCK_SIZE;
- ++blocks;
- }
-#endif
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rdrb->lock);
-#else
- if (pthread_mutex_lock(rdrb->lock) == EOWNERDEAD)
- sanitize(rdrb);
-#endif
- pthread_cleanup_push(__cleanup_mutex_unlock, rdrb->lock);
-
-#ifdef SHM_RDRB_MULTI_BLOCK
- if (blocks + *rdrb->head > (SHM_BUFFER_SIZE))
- padblocks = (SHM_BUFFER_SIZE) - *rdrb->head;
-
- while (!shm_rdrb_free(rdrb, blocks + padblocks) && ret != ETIMEDOUT) {
-#else
- while (!shm_rdrb_free(rdrb, 1) && ret != ETIMEDOUT) {
-#endif
- ret = __timedwait(rdrb->healthy, rdrb->lock, abstime);
-#ifdef SHM_RDRB_MULTI_BLOCK
- if (blocks + *rdrb->head > (SHM_BUFFER_SIZE))
- padblocks = (SHM_BUFFER_SIZE) - *rdrb->head;
-#endif
- }
-
- if (ret != ETIMEDOUT) {
-#ifdef SHM_RDRB_MULTI_BLOCK
- if (padblocks) {
- sdb = get_head_ptr(rdrb);
- sdb->size = 0;
- sdb->blocks = padblocks;
- sdb->refs = 0;
- sdb->du_head = 0;
- sdb->du_tail = 0;
- sdb->idx = *rdrb->head;
-
- *rdrb->head = 0;
- }
-#endif
- sdb = get_head_ptr(rdrb);
- sdb->refs = 1;
- sdb->idx = *rdrb->head;
-#ifdef SHM_RDRB_MULTI_BLOCK
- sdb->blocks = blocks;
-
- *rdrb->head = (*rdrb->head + blocks) & ((SHM_BUFFER_SIZE) - 1);
-#else
- *rdrb->head = (*rdrb->head + 1) & ((SHM_BUFFER_SIZE) - 1);
-#endif
- }
-
- pthread_cleanup_pop(true);
-
- if (ret == ETIMEDOUT)
- return -ETIMEDOUT;
-
- sdb->size = size;
- sdb->du_head = DU_BUFF_HEADSPACE;
- sdb->du_tail = sdb->du_head + len;
-
- *psdb = sdb;
- if (ptr != NULL)
- *ptr = (uint8_t *) (sdb + 1) + sdb->du_head;
-
- return sdb->idx;
-}
-
-ssize_t shm_rdrbuff_read(uint8_t ** dst,
- struct shm_rdrbuff * rdrb,
- size_t idx)
-{
- struct shm_du_buff * sdb;
-
- assert(dst);
- assert(rdrb);
- assert(idx < (SHM_BUFFER_SIZE));
-
- sdb = idx_to_du_buff_ptr(rdrb, idx);
- *dst = ((uint8_t *) (sdb + 1)) + sdb->du_head;
-
- return (ssize_t) (sdb->du_tail - sdb->du_head);
-}
-
-struct shm_du_buff * shm_rdrbuff_get(struct shm_rdrbuff * rdrb,
- size_t idx)
-{
- assert(rdrb);
- assert(idx < (SHM_BUFFER_SIZE));
-
- return idx_to_du_buff_ptr(rdrb, idx);
-}
-
-int shm_rdrbuff_remove(struct shm_rdrbuff * rdrb,
- size_t idx)
-{
- struct shm_du_buff * sdb;
-
- assert(rdrb);
- assert(idx < (SHM_BUFFER_SIZE));
-
-#ifndef HAVE_ROBUST_MUTEX
- pthread_mutex_lock(rdrb->lock);
-#else
- if (pthread_mutex_lock(rdrb->lock) == EOWNERDEAD)
- sanitize(rdrb);
-#endif
- /* assert(!shm_rdrb_empty(rdrb)); */
-
- sdb = idx_to_du_buff_ptr(rdrb, idx);
-
- if (sdb->refs == 1) { /* only stack needs it, can be removed */
- sdb->refs = 0;
- if (idx == *rdrb->tail)
- garbage_collect(rdrb);
- }
-
- pthread_mutex_unlock(rdrb->lock);
-
- return 0;
-}
-
-size_t shm_du_buff_get_idx(struct shm_du_buff * sdb)
-{
- assert(sdb);
-
- return sdb->idx;
-}
-
-uint8_t * shm_du_buff_head(struct shm_du_buff * sdb)
-{
- assert(sdb);
-
- return (uint8_t *) (sdb + 1) + sdb->du_head;
-}
-
-uint8_t * shm_du_buff_tail(struct shm_du_buff * sdb)
-{
- assert(sdb);
-
- return (uint8_t *) (sdb + 1) + sdb->du_tail;
-}
-
-size_t shm_du_buff_len(struct shm_du_buff * sdb)
-{
- assert(sdb);
-
- return sdb->du_tail - sdb->du_head;
-}
-
-uint8_t * shm_du_buff_head_alloc(struct shm_du_buff * sdb,
- size_t size)
-{
- assert(sdb);
-
- if (sdb->du_head < size)
- return NULL;
-
- sdb->du_head -= size;
-
- return (uint8_t *) (sdb + 1) + sdb->du_head;
-}
-
-uint8_t * shm_du_buff_tail_alloc(struct shm_du_buff * sdb,
- size_t size)
-{
- uint8_t * buf;
-
- assert(sdb);
-
- if (sdb->du_tail + size >= sdb->size)
- return NULL;
-
- buf = (uint8_t *) (sdb + 1) + sdb->du_tail;
-
- sdb->du_tail += size;
-
- return buf;
-}
-
-uint8_t * shm_du_buff_head_release(struct shm_du_buff * sdb,
- size_t size)
-{
- uint8_t * buf;
-
- assert(sdb);
- assert(!(size > sdb->du_tail - sdb->du_head));
-
- buf = (uint8_t *) (sdb + 1) + sdb->du_head;
-
- sdb->du_head += size;
-
- return buf;
-}
-
-uint8_t * shm_du_buff_tail_release(struct shm_du_buff * sdb,
- size_t size)
-{
- assert(sdb);
- assert(!(size > sdb->du_tail - sdb->du_head));
-
- sdb->du_tail -= size;
-
- return (uint8_t *) (sdb + 1) + sdb->du_tail;
-}
-
-void shm_du_buff_truncate(struct shm_du_buff * sdb,
- size_t len)
-{
- assert(sdb);
- assert(len <= sdb->size);
-
- sdb->du_tail = sdb->du_head + len;
-}
-
-int shm_du_buff_wait_ack(struct shm_du_buff * sdb)
-{
- __sync_add_and_fetch(&sdb->refs, 1);
-
- return 0;
-}
-
-int shm_du_buff_ack(struct shm_du_buff * sdb)
-{
- __sync_sub_and_fetch(&sdb->refs, 1);
- return 0;
-}
diff --git a/src/lib/sockets.c b/src/lib/sockets.c
index 13219db0..5dfbcb5c 100644
--- a/src/lib/sockets.c
+++ b/src/lib/sockets.c
@@ -154,12 +154,13 @@ int send_recv_msg(buffer_t * msg)
return len < 0 ? -1 : 0;
}
-char * ipcp_sock_path(pid_t pid)
+static char * __sock_path(pid_t pid,
+ const char * prefix,
+ const char * suffix)
{
char * full_name = NULL;
char * pid_string = NULL;
size_t len = 0;
- char * delim = "_";
len = n_digits(pid);
pid_string = malloc(len + 1);
@@ -168,9 +169,9 @@ char * ipcp_sock_path(pid_t pid)
sprintf(pid_string, "%d", pid);
- len += strlen(IPCP_SOCK_PATH_PREFIX);
- len += strlen(delim);
- len += strlen(SOCK_PATH_SUFFIX);
+ len += strlen(prefix);
+ len += strlen(pid_string);
+ len += strlen(suffix);
full_name = malloc(len + 1);
if (full_name == NULL) {
@@ -178,12 +179,17 @@ char * ipcp_sock_path(pid_t pid)
return NULL;
}
- strcpy(full_name, IPCP_SOCK_PATH_PREFIX);
- strcat(full_name, delim);
+ strcpy(full_name, prefix);
strcat(full_name, pid_string);
- strcat(full_name, SOCK_PATH_SUFFIX);
+ strcat(full_name, suffix);
free(pid_string);
return full_name;
}
+
+char * sock_path(pid_t pid,
+ const char * prefix)
+{
+ return __sock_path(pid, prefix, SOCK_PATH_SUFFIX);
+}
diff --git a/src/lib/shm_flow_set.c b/src/lib/ssm/flow_set.c
index 39913fd1..ab24d357 100644
--- a/src/lib/shm_flow_set.c
+++ b/src/lib/ssm/flow_set.c
@@ -23,11 +23,12 @@
#define _POSIX_C_SOURCE 200809L
#include "config.h"
+#include "ssm.h"
#include <ouroboros/errno.h>
#include <ouroboros/lockfile.h>
#include <ouroboros/pthread.h>
-#include <ouroboros/shm_flow_set.h>
+#include <ouroboros/ssm_flow_set.h>
#include <ouroboros/time.h>
#include <assert.h>
@@ -54,17 +55,17 @@
#define FN_MAX_CHARS 255
#define FS_PROT (PROT_READ | PROT_WRITE)
-#define QUEUESIZE ((SHM_BUFFER_SIZE) * sizeof(struct flowevent))
+#define QUEUESIZE ((SSM_RBUFF_SIZE) * sizeof(struct flowevent))
-#define SHM_FSET_FILE_SIZE (SYS_MAX_FLOWS * sizeof(ssize_t) \
+#define SSM_FSET_FILE_SIZE (SYS_MAX_FLOWS * sizeof(ssize_t) \
+ PROG_MAX_FQUEUES * sizeof(size_t) \
+ PROG_MAX_FQUEUES * sizeof(pthread_cond_t) \
+ PROG_MAX_FQUEUES * QUEUESIZE \
+ sizeof(pthread_mutex_t))
-#define fqueue_ptr(fs, idx) (fs->fqueues + (SHM_BUFFER_SIZE) * idx)
+#define fqueue_ptr(fs, idx) (fs->fqueues + (SSM_RBUFF_SIZE) * idx)
-struct shm_flow_set {
+struct ssm_flow_set {
ssize_t * mtable;
size_t * heads;
pthread_cond_t * conds;
@@ -74,15 +75,15 @@ struct shm_flow_set {
pid_t pid;
};
-static struct shm_flow_set * flow_set_create(pid_t pid,
+static struct ssm_flow_set * flow_set_create(pid_t pid,
int oflags)
{
- struct shm_flow_set * set;
+ struct ssm_flow_set * set;
ssize_t * shm_base;
char fn[FN_MAX_CHARS];
int fd;
- sprintf(fn, SHM_FLOW_SET_PREFIX "%d", pid);
+ sprintf(fn, SSM_FLOW_SET_PREFIX "%d", pid);
set = malloc(sizeof(*set));
if (set == NULL)
@@ -92,10 +93,10 @@ static struct shm_flow_set * flow_set_create(pid_t pid,
if (fd == -1)
goto fail_shm_open;
- if ((oflags & O_CREAT) && ftruncate(fd, SHM_FSET_FILE_SIZE) < 0)
+ if ((oflags & O_CREAT) && ftruncate(fd, SSM_FSET_FILE_SIZE) < 0)
goto fail_truncate;
- shm_base = mmap(NULL, SHM_FSET_FILE_SIZE, FS_PROT, MAP_SHARED, fd, 0);
+ shm_base = mmap(NULL, SSM_FSET_FILE_SIZE, FS_PROT, MAP_SHARED, fd, 0);
if (shm_base == MAP_FAILED)
goto fail_mmap;
@@ -106,7 +107,7 @@ static struct shm_flow_set * flow_set_create(pid_t pid,
set->conds = (pthread_cond_t *)(set->heads + PROG_MAX_FQUEUES);
set->fqueues = (struct flowevent *) (set->conds + PROG_MAX_FQUEUES);
set->lock = (pthread_mutex_t *)
- (set->fqueues + PROG_MAX_FQUEUES * (SHM_BUFFER_SIZE));
+ (set->fqueues + PROG_MAX_FQUEUES * (SSM_RBUFF_SIZE));
return set;
@@ -121,9 +122,9 @@ static struct shm_flow_set * flow_set_create(pid_t pid,
return NULL;
}
-struct shm_flow_set * shm_flow_set_create(pid_t pid)
+struct ssm_flow_set * ssm_flow_set_create(pid_t pid)
{
- struct shm_flow_set * set;
+ struct ssm_flow_set * set;
pthread_mutexattr_t mattr;
pthread_condattr_t cattr;
mode_t mask;
@@ -184,38 +185,38 @@ struct shm_flow_set * shm_flow_set_create(pid_t pid)
fail_mattr_set:
pthread_mutexattr_destroy(&mattr);
fail_mutexattr_init:
- shm_flow_set_destroy(set);
+ ssm_flow_set_destroy(set);
fail_set:
return NULL;
}
-struct shm_flow_set * shm_flow_set_open(pid_t pid)
+struct ssm_flow_set * ssm_flow_set_open(pid_t pid)
{
return flow_set_create(pid, O_RDWR);
}
-void shm_flow_set_destroy(struct shm_flow_set * set)
+void ssm_flow_set_destroy(struct ssm_flow_set * set)
{
char fn[FN_MAX_CHARS];
assert(set);
- sprintf(fn, SHM_FLOW_SET_PREFIX "%d", set->pid);
+ sprintf(fn, SSM_FLOW_SET_PREFIX "%d", set->pid);
- shm_flow_set_close(set);
+ ssm_flow_set_close(set);
shm_unlink(fn);
}
-void shm_flow_set_close(struct shm_flow_set * set)
+void ssm_flow_set_close(struct ssm_flow_set * set)
{
assert(set);
- munmap(set->mtable, SHM_FSET_FILE_SIZE);
+ munmap(set->mtable, SSM_FSET_FILE_SIZE);
free(set);
}
-void shm_flow_set_zero(struct shm_flow_set * set,
+void ssm_flow_set_zero(struct ssm_flow_set * set,
size_t idx)
{
ssize_t i = 0;
@@ -235,7 +236,7 @@ void shm_flow_set_zero(struct shm_flow_set * set,
}
-int shm_flow_set_add(struct shm_flow_set * set,
+int ssm_flow_set_add(struct ssm_flow_set * set,
size_t idx,
int flow_id)
{
@@ -257,7 +258,7 @@ int shm_flow_set_add(struct shm_flow_set * set,
return 0;
}
-void shm_flow_set_del(struct shm_flow_set * set,
+void ssm_flow_set_del(struct ssm_flow_set * set,
size_t idx,
int flow_id)
{
@@ -273,7 +274,7 @@ void shm_flow_set_del(struct shm_flow_set * set,
pthread_mutex_unlock(set->lock);
}
-int shm_flow_set_has(struct shm_flow_set * set,
+int ssm_flow_set_has(struct ssm_flow_set * set,
size_t idx,
int flow_id)
{
@@ -293,7 +294,7 @@ int shm_flow_set_has(struct shm_flow_set * set,
return ret;
}
-void shm_flow_set_notify(struct shm_flow_set * set,
+void ssm_flow_set_notify(struct ssm_flow_set * set,
int flow_id,
int event)
{
@@ -323,7 +324,7 @@ void shm_flow_set_notify(struct shm_flow_set * set,
}
-ssize_t shm_flow_set_wait(const struct shm_flow_set * set,
+ssize_t ssm_flow_set_wait(const struct ssm_flow_set * set,
size_t idx,
struct flowevent * fqueue,
const struct timespec * abstime)
diff --git a/src/lib/ssm/pool.c b/src/lib/ssm/pool.c
new file mode 100644
index 00000000..e938f644
--- /dev/null
+++ b/src/lib/ssm/pool.c
@@ -0,0 +1,953 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Secure Shared Memory Infrastructure (SSMI) Packet Buffer
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "config.h"
+
+#include <ouroboros/errno.h>
+#include <ouroboros/pthread.h>
+#include <ouroboros/ssm_pool.h>
+
+#include "ssm.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+/* Global Shared Packet Pool (GSPP) configuration */
+static const struct ssm_size_class_cfg ssm_gspp_cfg[SSM_POOL_MAX_CLASSES] = {
+ { (1 << 8), SSM_GSPP_256_BLOCKS },
+ { (1 << 9), SSM_GSPP_512_BLOCKS },
+ { (1 << 10), SSM_GSPP_1K_BLOCKS },
+ { (1 << 11), SSM_GSPP_2K_BLOCKS },
+ { (1 << 12), SSM_GSPP_4K_BLOCKS },
+ { (1 << 14), SSM_GSPP_16K_BLOCKS },
+ { (1 << 16), SSM_GSPP_64K_BLOCKS },
+ { (1 << 18), SSM_GSPP_256K_BLOCKS },
+ { (1 << 20), SSM_GSPP_1M_BLOCKS },
+};
+
+/* Per-User Pool (PUP) configuration */
+static const struct ssm_size_class_cfg ssm_pup_cfg[SSM_POOL_MAX_CLASSES] = {
+ { (1 << 8), SSM_PUP_256_BLOCKS },
+ { (1 << 9), SSM_PUP_512_BLOCKS },
+ { (1 << 10), SSM_PUP_1K_BLOCKS },
+ { (1 << 11), SSM_PUP_2K_BLOCKS },
+ { (1 << 12), SSM_PUP_4K_BLOCKS },
+ { (1 << 14), SSM_PUP_16K_BLOCKS },
+ { (1 << 16), SSM_PUP_64K_BLOCKS },
+ { (1 << 18), SSM_PUP_256K_BLOCKS },
+ { (1 << 20), SSM_PUP_1M_BLOCKS },
+};
+
+#define PTR_TO_OFFSET(pool_base, ptr) \
+ ((uintptr_t)(ptr) - (uintptr_t)(pool_base))
+
+#define OFFSET_TO_PTR(pool_base, offset) \
+ ((offset == 0) ? NULL : (void *)((uintptr_t)(pool_base) + offset))
+
+#define GET_SHARD_FOR_PID(pid) ((int)((pid) % SSM_POOL_SHARDS))
+
+#define LOAD_RELAXED(ptr) \
+ (__atomic_load_n(ptr, __ATOMIC_RELAXED))
+
+#define LOAD_ACQUIRE(ptr) \
+ (__atomic_load_n(ptr, __ATOMIC_ACQUIRE))
+
+#define STORE_RELEASE(ptr, val) \
+ (__atomic_store_n(ptr, val, __ATOMIC_RELEASE))
+
+#define LOAD(ptr) \
+ (__atomic_load_n(ptr, __ATOMIC_SEQ_CST))
+
+#define STORE(ptr, val) \
+ (__atomic_store_n(ptr, val, __ATOMIC_SEQ_CST))
+
+#define FETCH_ADD(ptr, val) \
+ (__atomic_fetch_add(ptr, val, __ATOMIC_SEQ_CST))
+
+#define FETCH_SUB(ptr, val) \
+ (__atomic_fetch_sub(ptr, val, __ATOMIC_SEQ_CST))
+
+#define SSM_FILE_SIZE (SSM_POOL_TOTAL_SIZE + sizeof(struct _ssm_pool_hdr))
+#define SSM_GSPP_FILE_SIZE (SSM_GSPP_TOTAL_SIZE + sizeof(struct _ssm_pool_hdr))
+#define SSM_PUP_FILE_SIZE (SSM_PUP_TOTAL_SIZE + sizeof(struct _ssm_pool_hdr))
+
+#define IS_GSPP(uid) ((uid) == SSM_GSPP_UID)
+#define GET_POOL_TOTAL_SIZE(uid) (IS_GSPP(uid) ? SSM_GSPP_TOTAL_SIZE \
+ : SSM_PUP_TOTAL_SIZE)
+#define GET_POOL_FILE_SIZE(uid) (IS_GSPP(uid) ? SSM_GSPP_FILE_SIZE \
+ : SSM_PUP_FILE_SIZE)
+#define GET_POOL_CFG(uid) (IS_GSPP(uid) ? ssm_gspp_cfg : ssm_pup_cfg)
+
+struct ssm_pool {
+ uint8_t * shm_base; /* start of blocks */
+ struct _ssm_pool_hdr * hdr; /* shared memory header */
+ void * pool_base; /* base of the memory pool */
+ uid_t uid; /* user owner (0 = GSPP) */
+ size_t total_size; /* total data size */
+};
+
+static __inline__
+struct ssm_pk_buff * list_remove_head(struct _ssm_list_head * head,
+ void * base)
+{
+ uint32_t off;
+ uint32_t next_off;
+ struct ssm_pk_buff * blk;
+
+ assert(head != NULL);
+ assert(base != NULL);
+
+ off = LOAD(&head->head_offset);
+ if (off == 0)
+ return NULL;
+
+ /* Validate offset is within pool bounds */
+ if (off >= SSM_POOL_TOTAL_SIZE)
+ return NULL;
+
+ blk = OFFSET_TO_PTR(base, off);
+ next_off = LOAD(&blk->next_offset);
+
+
+
+ STORE(&head->head_offset, next_off);
+ STORE(&head->count, LOAD(&head->count) - 1);
+
+ return blk;
+}
+static __inline__ void list_add_head(struct _ssm_list_head * head,
+ struct ssm_pk_buff * blk,
+ void * base)
+{
+ uint32_t off;
+ uint32_t old;
+
+ assert(head != NULL);
+ assert(blk != NULL);
+ assert(base != NULL);
+
+ off = (uint32_t) PTR_TO_OFFSET(base, blk);
+ old = LOAD(&head->head_offset);
+
+ STORE(&blk->next_offset, old);
+ STORE(&head->head_offset, off);
+ STORE(&head->count, LOAD(&head->count) + 1);
+}
+
+static __inline__ int select_size_class(struct ssm_pool * pool,
+ size_t len)
+{
+ size_t sz;
+ int i;
+
+ assert(pool != NULL);
+
+ /* Total space needed: header + headspace + data + tailspace */
+ sz = sizeof(struct ssm_pk_buff) + SSM_PK_BUFF_HEADSPACE + len
+ + SSM_PK_BUFF_TAILSPACE;
+
+ for (i = 0; i < SSM_POOL_MAX_CLASSES; i++) {
+ struct _ssm_size_class * sc;
+
+ sc = &pool->hdr->size_classes[i];
+ if (sc->object_size > 0 && sz <= sc->object_size)
+ return i;
+ }
+
+ return -1;
+}
+
+static __inline__ int find_size_class_for_offset(struct ssm_pool * pool,
+ size_t offset)
+{
+ int c;
+
+ assert(pool != NULL);
+
+ for (c = 0; c < SSM_POOL_MAX_CLASSES; c++) {
+ struct _ssm_size_class * sc = &pool->hdr->size_classes[c];
+
+ if (sc->object_size == 0)
+ continue;
+
+ if (offset >= sc->pool_start &&
+ offset < sc->pool_start + sc->pool_size)
+ return c;
+ }
+
+ return -1;
+}
+
+static void init_size_classes(struct ssm_pool * pool)
+{
+ const struct ssm_size_class_cfg * cfg;
+ struct _ssm_size_class * sc;
+ struct _ssm_shard * shard;
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+ uint8_t * region;
+ size_t offset;
+ int c; /* class iterator */
+ int s; /* shard iterator */
+ size_t i;
+
+ assert(pool != NULL);
+
+ /* Check if already initialized */
+ if (LOAD(&pool->hdr->initialized) != 0)
+ return;
+
+ cfg = GET_POOL_CFG(pool->uid);
+
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+#ifdef HAVE_ROBUST_MUTEX
+ pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
+#endif
+ pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
+
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+#ifndef __APPLE__
+ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
+#endif
+ offset = 0;
+
+ for (c = 0; c < SSM_POOL_MAX_CLASSES; c++) {
+ if (cfg[c].blocks == 0)
+ continue;
+
+ sc = &pool->hdr->size_classes[c];
+
+ sc->object_size = cfg[c].size;
+ sc->pool_start = offset;
+ sc->pool_size = cfg[c].size * cfg[c].blocks;
+ sc->object_count = cfg[c].blocks;
+
+ /* Initialize all shards */
+ for (s = 0; s < SSM_POOL_SHARDS; s++) {
+ shard = &sc->shards[s];
+
+ STORE(&shard->free_list.head_offset, 0);
+ STORE(&shard->free_list.count, 0);
+ STORE(&shard->free_count, 0);
+
+ pthread_mutex_init(&shard->mtx, &mattr);
+ pthread_cond_init(&shard->cond, &cattr);
+ }
+
+ /* Lazy distribution: put all blocks in shard 0 initially */
+ region = pool->shm_base + offset;
+
+ for (i = 0; i < sc->object_count; ++i) {
+ struct ssm_pk_buff * blk;
+
+ blk = (struct ssm_pk_buff *)
+ (region + i * sc->object_size);
+
+ STORE(&blk->refcount, 0);
+ blk->allocator_pid = 0;
+ STORE(&blk->next_offset, 0);
+
+ list_add_head(&sc->shards[0].free_list, blk,
+ pool->pool_base);
+ FETCH_ADD(&sc->shards[0].free_count, 1);
+ }
+
+ offset += sc->pool_size;
+ }
+
+ /* Mark as initialized - acts as memory barrier */
+ STORE(&pool->hdr->initialized, 1);
+
+ pthread_mutexattr_destroy(&mattr);
+ pthread_condattr_destroy(&cattr);
+}
+
+/*
+ * Reclaim all blocks allocated by a specific pid in a size class.
+ * Called with shard mutex held.
+ */
+static size_t reclaim_pid_from_sc(struct _ssm_size_class * sc,
+ struct _ssm_shard * shard,
+ void * pool_base,
+ pid_t pid)
+{
+ uint8_t * region;
+ size_t i;
+ size_t recovered = 0;
+ struct ssm_pk_buff * blk;
+
+ region = (uint8_t *) pool_base + sc->pool_start;
+
+ for (i = 0; i < sc->object_count; ++i) {
+ blk = (struct ssm_pk_buff *)(region + i * sc->object_size);
+
+ if (blk->allocator_pid == pid && LOAD(&blk->refcount) > 0) {
+ STORE(&blk->refcount, 0);
+ blk->allocator_pid = 0;
+ list_add_head(&shard->free_list, blk, pool_base);
+ FETCH_ADD(&shard->free_count, 1);
+ recovered++;
+ }
+ }
+
+ return recovered;
+}
+
+void ssm_pool_reclaim_orphans(struct ssm_pool * pool,
+ pid_t pid)
+{
+ size_t sc_idx;
+
+ if (pool == NULL || pid <= 0)
+ return;
+
+ for (sc_idx = 0; sc_idx < SSM_POOL_MAX_CLASSES; sc_idx++) {
+ struct _ssm_size_class * sc;
+ struct _ssm_shard * shard;
+
+ sc = &pool->hdr->size_classes[sc_idx];
+ if (sc->object_count == 0)
+ continue;
+
+ /* Reclaim to shard 0 for simplicity */
+ shard = &sc->shards[0];
+ robust_mutex_lock(&shard->mtx);
+ reclaim_pid_from_sc(sc, shard, pool->pool_base, pid);
+ pthread_mutex_unlock(&shard->mtx);
+ }
+}
+
+static __inline__
+struct ssm_pk_buff * try_alloc_from_shard(struct _ssm_shard * shard,
+ void * base)
+{
+ struct ssm_pk_buff * blk;
+
+ robust_mutex_lock(&shard->mtx);
+
+ if (LOAD(&shard->free_count) > 0) {
+ blk = list_remove_head(&shard->free_list, base);
+ if (blk != NULL) {
+ FETCH_SUB(&shard->free_count, 1);
+ return blk; /* Caller must unlock */
+ }
+ FETCH_SUB(&shard->free_count, 1);
+ }
+
+ pthread_mutex_unlock(&shard->mtx);
+ return NULL;
+}
+
+static __inline__ ssize_t init_block(struct ssm_pool * pool,
+ struct _ssm_size_class * sc,
+ struct _ssm_shard * shard,
+ struct ssm_pk_buff * blk,
+ size_t len,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb)
+{
+ STORE(&blk->refcount, 1);
+ blk->allocator_pid = getpid();
+ blk->size = (uint32_t) (sc->object_size -
+ sizeof(struct ssm_pk_buff));
+ blk->pk_head = SSM_PK_BUFF_HEADSPACE;
+ blk->pk_tail = blk->pk_head + (uint32_t) len;
+ blk->off = (uint32_t) PTR_TO_OFFSET(pool->pool_base, blk);
+
+ pthread_mutex_unlock(&shard->mtx);
+
+ *spb = blk;
+ if (ptr != NULL)
+ *ptr = blk->data + blk->pk_head;
+
+ return blk->off;
+}
+
+/* Non-blocking allocation from size class */
+static ssize_t alloc_from_sc(struct ssm_pool * pool,
+ int idx,
+ size_t len,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb)
+{
+ struct _ssm_size_class * sc;
+ struct ssm_pk_buff * blk;
+ int local;
+ int s;
+
+ assert(pool != NULL);
+ assert(idx >= 0 && idx < SSM_POOL_MAX_CLASSES);
+ assert(spb != NULL);
+
+ sc = &pool->hdr->size_classes[idx];
+ local = GET_SHARD_FOR_PID(getpid());
+
+ for (s = 0; s < SSM_POOL_SHARDS; s++) {
+ struct _ssm_shard * shard;
+ int idx;
+
+ idx = (local + s) % SSM_POOL_SHARDS;
+ shard = &sc->shards[idx];
+
+ blk = try_alloc_from_shard(shard, pool->pool_base);
+ if (blk != NULL)
+ return init_block(pool, sc, shard, blk, len, ptr, spb);
+ }
+
+ return -EAGAIN;
+}
+
+/* Blocking allocation from size class */
+static ssize_t alloc_from_sc_b(struct ssm_pool * pool,
+ int idx,
+ size_t len,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb,
+ const struct timespec * abstime)
+{
+ struct _ssm_size_class * sc;
+ struct _ssm_shard * shard;
+ struct ssm_pk_buff * blk = NULL;
+ int local;
+ int s;
+ int ret = 0;
+
+ assert(pool != NULL);
+ assert(idx >= 0 && idx < SSM_POOL_MAX_CLASSES);
+ assert(spb != NULL);
+
+ sc = &pool->hdr->size_classes[idx];
+ local = GET_SHARD_FOR_PID(getpid());
+
+ while (blk == NULL && ret != ETIMEDOUT) {
+ /* Try non-blocking allocation from any shard */
+ for (s = 0; s < SSM_POOL_SHARDS && blk == NULL; s++) {
+ shard = &sc->shards[(local + s) % SSM_POOL_SHARDS];
+ blk = try_alloc_from_shard(shard, pool->pool_base);
+ }
+
+ if (blk != NULL)
+ break;
+
+ /* Nothing available, wait for signal */
+ shard = &sc->shards[local];
+ robust_mutex_lock(&shard->mtx);
+ ret = robust_wait(&shard->cond, &shard->mtx, abstime);
+ pthread_mutex_unlock(&shard->mtx);
+ }
+
+ if (ret == ETIMEDOUT)
+ return -ETIMEDOUT;
+
+ return init_block(pool, sc, shard, blk, len, ptr, spb);
+}
+
+/* Generate pool filename: uid=0 for GSPP, uid>0 for PUP */
+static char * pool_filename(uid_t uid)
+{
+ char * str;
+ char * test_suffix;
+ char base[64];
+
+ if (IS_GSPP(uid))
+ snprintf(base, sizeof(base), "%s", SSM_GSPP_NAME);
+ else
+ snprintf(base, sizeof(base), SSM_PUP_NAME_FMT, (int) uid);
+
+ test_suffix = getenv("OUROBOROS_TEST_POOL_SUFFIX");
+ if (test_suffix != NULL) {
+ str = malloc(strlen(base) + strlen(test_suffix) + 1);
+ if (str == NULL)
+ return NULL;
+ sprintf(str, "%s%s", base, test_suffix);
+ } else {
+ str = strdup(base);
+ }
+
+ return str;
+}
+
+void ssm_pool_close(struct ssm_pool * pool)
+{
+ size_t file_size;
+
+ assert(pool != NULL);
+
+ file_size = GET_POOL_FILE_SIZE(pool->uid);
+
+ munmap(pool->shm_base, file_size);
+ free(pool);
+}
+
+void ssm_pool_destroy(struct ssm_pool * pool)
+{
+ char * fn;
+
+ assert(pool != NULL);
+
+ if (getpid() != pool->hdr->pid && kill(pool->hdr->pid, 0) == 0) {
+ ssm_pool_close(pool);
+ free(pool);
+ return;
+ }
+
+ fn = pool_filename(pool->uid);
+ if (fn == NULL) {
+ ssm_pool_close(pool);
+ free(pool);
+ return;
+ }
+
+ ssm_pool_close(pool);
+
+ shm_unlink(fn);
+ free(fn);
+}
+
+#define MM_FLAGS (PROT_READ | PROT_WRITE)
+
+static struct ssm_pool * __pool_create(const char * name,
+ int flags,
+ uid_t uid,
+ mode_t mode)
+{
+ struct ssm_pool * pool;
+ int fd;
+ uint8_t * shm_base;
+ size_t file_size;
+ size_t total_size;
+
+ file_size = GET_POOL_FILE_SIZE(uid);
+ total_size = GET_POOL_TOTAL_SIZE(uid);
+
+ pool = malloc(sizeof(*pool));
+ if (pool == NULL)
+ goto fail_pool;
+
+ fd = shm_open(name, flags, mode);
+ if (fd == -1)
+ goto fail_open;
+
+ if ((flags & O_CREAT) && ftruncate(fd, (off_t) file_size) < 0)
+ goto fail_truncate;
+
+ shm_base = mmap(NULL, file_size, MM_FLAGS, MAP_SHARED, fd, 0);
+ if (shm_base == MAP_FAILED)
+ goto fail_truncate;
+
+ pool->shm_base = shm_base;
+ pool->pool_base = shm_base;
+ pool->hdr = (struct _ssm_pool_hdr *) (shm_base + total_size);
+ pool->uid = uid;
+ pool->total_size = total_size;
+
+ if (flags & O_CREAT)
+ pool->hdr->mapped_addr = shm_base;
+
+ close(fd);
+
+ return pool;
+
+ fail_truncate:
+ close(fd);
+ if (flags & O_CREAT)
+ shm_unlink(name);
+ fail_open:
+ free(pool);
+ fail_pool:
+ return NULL;
+}
+
+struct ssm_pool * ssm_pool_create(uid_t uid,
+ gid_t gid)
+{
+ struct ssm_pool * pool;
+ char * fn;
+ mode_t mask;
+ mode_t mode;
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+ int fd;
+
+ fn = pool_filename(uid);
+ if (fn == NULL)
+ goto fail_fn;
+
+ mode = IS_GSPP(uid) ? 0660 : 0600;
+ mask = umask(0);
+
+ pool = __pool_create(fn, O_CREAT | O_EXCL | O_RDWR, uid, mode);
+
+ umask(mask);
+
+ if (pool == NULL)
+ goto fail_pool;
+
+ fd = shm_open(fn, O_RDWR, 0);
+ if (fd >= 0) {
+ fchown(fd, uid, gid);
+ fchmod(fd, mode);
+ close(fd);
+ }
+
+ if (pthread_mutexattr_init(&mattr))
+ goto fail_mattr;
+
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+#ifdef HAVE_ROBUST_MUTEX
+ pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
+#endif
+ if (pthread_mutex_init(&pool->hdr->mtx, &mattr))
+ goto fail_mutex;
+
+ if (pthread_condattr_init(&cattr))
+ goto fail_cattr;
+
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+#ifndef __APPLE__
+ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
+#endif
+ if (pthread_cond_init(&pool->hdr->healthy, &cattr))
+ goto fail_healthy;
+
+ pool->hdr->pid = getpid();
+ STORE(&pool->hdr->initialized, 0);
+
+ init_size_classes(pool);
+
+ pthread_mutexattr_destroy(&mattr);
+ pthread_condattr_destroy(&cattr);
+ free(fn);
+
+ return pool;
+
+ fail_healthy:
+ pthread_condattr_destroy(&cattr);
+ fail_cattr:
+ pthread_mutex_destroy(&pool->hdr->mtx);
+ fail_mutex:
+ pthread_mutexattr_destroy(&mattr);
+ fail_mattr:
+ ssm_pool_close(pool);
+ shm_unlink(fn);
+ fail_pool:
+ free(fn);
+ fail_fn:
+ return NULL;
+}
+
+struct ssm_pool * ssm_pool_open(uid_t uid)
+{
+ struct ssm_pool * pool;
+ char * fn;
+
+ fn = pool_filename(uid);
+ if (fn == NULL)
+ return NULL;
+
+ pool = __pool_create(fn, O_RDWR, uid, 0);
+ if (pool != NULL)
+ init_size_classes(pool);
+
+ free(fn);
+
+ return pool;
+}
+
+void ssm_pool_gspp_purge(void)
+{
+ char * fn;
+
+ fn = pool_filename(SSM_GSPP_UID);
+ if (fn == NULL)
+ return;
+
+ shm_unlink(fn);
+ free(fn);
+}
+
+int ssm_pool_mlock(struct ssm_pool * pool)
+{
+ size_t file_size;
+
+ assert(pool != NULL);
+
+ file_size = GET_POOL_FILE_SIZE(pool->uid);
+
+ return mlock(pool->shm_base, file_size);
+}
+
+ssize_t ssm_pool_alloc(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb)
+{
+ int idx;
+
+ assert(pool != NULL);
+ assert(spb != NULL);
+
+ idx = select_size_class(pool, count);
+ if (idx >= 0)
+ return alloc_from_sc(pool, idx, count, ptr, spb);
+
+ return -EMSGSIZE;
+}
+
+ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb,
+ const struct timespec * abstime)
+{
+ int idx;
+
+ assert(pool != NULL);
+ assert(spb != NULL);
+
+ idx = select_size_class(pool, count);
+ if (idx >= 0)
+ return alloc_from_sc_b(pool, idx, count, ptr, spb, abstime);
+
+ return -EMSGSIZE;
+}
+
+ssize_t ssm_pool_read(uint8_t ** dst,
+ struct ssm_pool * pool,
+ size_t off)
+{
+ struct ssm_pk_buff * blk;
+
+ assert(dst != NULL);
+ assert(pool != NULL);
+
+ blk = OFFSET_TO_PTR(pool->pool_base, off);
+ if (blk == NULL)
+ return -EINVAL;
+
+ *dst = blk->data + blk->pk_head;
+
+ return (ssize_t) (blk->pk_tail - blk->pk_head);
+}
+
+struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool,
+ size_t off)
+{
+ struct ssm_pk_buff * blk;
+
+ assert(pool != NULL);
+
+ if (off == 0 || off >= pool->total_size)
+ return NULL;
+
+ blk = OFFSET_TO_PTR(pool->pool_base, off);
+ if (blk == NULL)
+ return NULL;
+
+ if (LOAD(&blk->refcount) == 0)
+ return NULL;
+
+ return blk;
+}
+
+int ssm_pool_remove(struct ssm_pool * pool,
+ size_t off)
+{
+ struct ssm_pk_buff * blk;
+ struct _ssm_size_class * sc;
+ struct _ssm_shard * shard;
+ int sc_idx;
+ int shard_idx;
+ uint16_t old_ref;
+
+ assert(pool != NULL);
+
+ if (off == 0 || off >= pool->total_size)
+ return -EINVAL;
+
+ blk = OFFSET_TO_PTR(pool->pool_base, off);
+ if (blk == NULL)
+ return -EINVAL;
+
+ sc_idx = find_size_class_for_offset(pool, off);
+ if (sc_idx < 0)
+ return -EINVAL;
+
+ sc = &pool->hdr->size_classes[sc_idx];
+
+ /* Free to allocator's shard (lazy distribution in action) */
+ shard_idx = GET_SHARD_FOR_PID(blk->allocator_pid);
+ shard = &sc->shards[shard_idx];
+
+ robust_mutex_lock(&shard->mtx);
+
+ old_ref = FETCH_SUB(&blk->refcount, 1);
+ if (old_ref > 1) {
+ /* Still referenced */
+ pthread_mutex_unlock(&shard->mtx);
+ return 0;
+ }
+
+ blk->allocator_pid = 0;
+#ifdef CONFIG_OUROBOROS_DEBUG
+ if (old_ref == 0) {
+ /* Underflow - double free attempt */
+ pthread_mutex_unlock(&shard->mtx);
+ abort();
+ }
+
+ /* Poison fields to detect use-after-free */
+ blk->pk_head = 0xDEAD;
+ blk->pk_tail = 0xBEEF;
+#endif
+ list_add_head(&shard->free_list, blk, pool->pool_base);
+ FETCH_ADD(&shard->free_count, 1);
+
+ pthread_cond_signal(&shard->cond);
+
+ pthread_mutex_unlock(&shard->mtx);
+
+ return 0;
+}
+
+size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ return spb->off;
+}
+
+uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ return spb->data + spb->pk_head;
+}
+
+uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ return spb->data + spb->pk_tail;
+}
+
+size_t ssm_pk_buff_len(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ return spb->pk_tail - spb->pk_head;
+}
+
+uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb,
+ size_t size)
+{
+ assert(spb != NULL);
+
+ if (spb->pk_head < size)
+ return NULL;
+
+ spb->pk_head -= size;
+
+ return spb->data + spb->pk_head;
+}
+
+uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb,
+ size_t size)
+{
+ uint8_t * buf;
+
+ assert(spb != NULL);
+
+ if (spb->pk_tail + size >= spb->size)
+ return NULL;
+
+ buf = spb->data + spb->pk_tail;
+
+ spb->pk_tail += size;
+
+ return buf;
+}
+
+uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb,
+ size_t size)
+{
+ uint8_t * buf;
+
+ assert(spb != NULL);
+ assert(!(size > spb->pk_tail - spb->pk_head));
+
+ buf = spb->data + spb->pk_head;
+
+ spb->pk_head += size;
+
+ return buf;
+}
+
+uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb,
+ size_t size)
+{
+ assert(spb != NULL);
+ assert(!(size > spb->pk_tail - spb->pk_head));
+
+ spb->pk_tail -= size;
+
+ return spb->data + spb->pk_tail;
+}
+
+void ssm_pk_buff_truncate(struct ssm_pk_buff * spb,
+ size_t len)
+{
+ assert(spb != NULL);
+ assert(len <= spb->size);
+
+ spb->pk_tail = spb->pk_head + len;
+}
+
+int ssm_pk_buff_wait_ack(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ FETCH_ADD(&spb->refcount, 1);
+
+ return 0;
+}
+
+int ssm_pk_buff_ack(struct ssm_pk_buff * spb)
+{
+ assert(spb != NULL);
+
+ FETCH_SUB(&spb->refcount, 1);
+
+ return 0;
+}
diff --git a/src/lib/ssm/rbuff.c b/src/lib/ssm/rbuff.c
new file mode 100644
index 00000000..e18f8ba7
--- /dev/null
+++ b/src/lib/ssm/rbuff.c
@@ -0,0 +1,449 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Ring buffer implementations for incoming packets
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "config.h"
+#include "ssm.h"
+
+#include <ouroboros/ssm_rbuff.h>
+#include <ouroboros/lockfile.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/fccntl.h>
+#include <ouroboros/pthread.h>
+#include <ouroboros/time.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define FN_MAX_CHARS 255
+
+#define SSM_RBUFF_FILESIZE ((SSM_RBUFF_SIZE) * sizeof(ssize_t) \
+ + 3 * sizeof(size_t) \
+ + sizeof(pthread_mutex_t) \
+ + 2 * sizeof(pthread_cond_t))
+
+#define MODB(x) ((x) & (SSM_RBUFF_SIZE - 1))
+
+#define LOAD_RELAXED(ptr) (__atomic_load_n(ptr, __ATOMIC_RELAXED))
+#define LOAD_ACQUIRE(ptr) (__atomic_load_n(ptr, __ATOMIC_ACQUIRE))
+#define STORE_RELEASE(ptr, val) \
+ (__atomic_store_n(ptr, val, __ATOMIC_RELEASE))
+
+#define HEAD(rb) (rb->shm_base[LOAD_RELAXED(rb->head)])
+#define TAIL(rb) (rb->shm_base[LOAD_RELAXED(rb->tail)])
+#define HEAD_IDX(rb) (LOAD_ACQUIRE(rb->head))
+#define TAIL_IDX(rb) (LOAD_ACQUIRE(rb->tail))
+#define ADVANCE_HEAD(rb) \
+ (STORE_RELEASE(rb->head, MODB(LOAD_RELAXED(rb->head) + 1)))
+#define ADVANCE_TAIL(rb) \
+ (STORE_RELEASE(rb->tail, MODB(LOAD_RELAXED(rb->tail) + 1)))
+#define QUEUED(rb) (MODB(HEAD_IDX(rb) - TAIL_IDX(rb)))
+#define IS_FULL(rb) (QUEUED(rb) == (SSM_RBUFF_SIZE - 1))
+#define IS_EMPTY(rb) (HEAD_IDX(rb) == TAIL_IDX(rb))
+
+struct ssm_rbuff {
+ ssize_t * shm_base; /* start of shared memory */
+ size_t * head; /* start of ringbuffer */
+ size_t * tail;
+ size_t * acl; /* access control */
+ pthread_mutex_t * mtx; /* lock for cond vars only */
+ pthread_cond_t * add; /* signal when new data */
+ pthread_cond_t * del; /* signal when data removed */
+ pid_t pid; /* pid of the owner */
+ int flow_id; /* flow_id of the flow */
+};
+
+#define MM_FLAGS (PROT_READ | PROT_WRITE)
+
+static struct ssm_rbuff * rbuff_create(pid_t pid,
+ int flow_id,
+ int flags)
+{
+ struct ssm_rbuff * rb;
+ int fd;
+ ssize_t * shm_base;
+ char fn[FN_MAX_CHARS];
+
+ sprintf(fn, SSM_RBUFF_PREFIX "%d.%d", pid, flow_id);
+
+ rb = malloc(sizeof(*rb));
+ if (rb == NULL)
+ goto fail_malloc;
+
+ fd = shm_open(fn, flags, 0666);
+ if (fd == -1)
+ goto fail_open;
+
+ if ((flags & O_CREAT) && ftruncate(fd, SSM_RBUFF_FILESIZE) < 0)
+ goto fail_truncate;
+
+ shm_base = mmap(NULL, SSM_RBUFF_FILESIZE, MM_FLAGS, MAP_SHARED, fd, 0);
+
+ close(fd);
+
+ rb->shm_base = shm_base;
+ rb->head = (size_t *) (rb->shm_base + (SSM_RBUFF_SIZE));
+ rb->tail = (size_t *) (rb->head + 1);
+ rb->acl = (size_t *) (rb->tail + 1);
+ rb->mtx = (pthread_mutex_t *) (rb->acl + 1);
+ rb->add = (pthread_cond_t *) (rb->mtx + 1);
+ rb->del = rb->add + 1;
+ rb->pid = pid;
+ rb->flow_id = flow_id;
+
+ return rb;
+
+ fail_truncate:
+ close(fd);
+ if (flags & O_CREAT)
+ shm_unlink(fn);
+ fail_open:
+ free(rb);
+ fail_malloc:
+ return NULL;
+}
+
+static void rbuff_destroy(struct ssm_rbuff * rb)
+{
+ munmap(rb->shm_base, SSM_RBUFF_FILESIZE);
+
+ free(rb);
+}
+
+struct ssm_rbuff * ssm_rbuff_create(pid_t pid,
+ int flow_id)
+{
+ struct ssm_rbuff * rb;
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+ mode_t mask;
+
+ mask = umask(0);
+
+ rb = rbuff_create(pid, flow_id, O_CREAT | O_EXCL | O_RDWR);
+
+ umask(mask);
+
+ if (rb == NULL)
+ goto fail_rb;
+
+ if (pthread_mutexattr_init(&mattr))
+ goto fail_mattr;
+
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+#ifdef HAVE_ROBUST_MUTEX
+ pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
+#endif
+ if (pthread_mutex_init(rb->mtx, &mattr))
+ goto fail_mutex;
+
+ if (pthread_condattr_init(&cattr))
+ goto fail_cattr;
+
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+#ifndef __APPLE__
+ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK);
+#endif
+ if (pthread_cond_init(rb->add, &cattr))
+ goto fail_add;
+
+ if (pthread_cond_init(rb->del, &cattr))
+ goto fail_del;
+
+ *rb->acl = ACL_RDWR;
+ *rb->head = 0;
+ *rb->tail = 0;
+
+ rb->pid = pid;
+ rb->flow_id = flow_id;
+
+ pthread_mutexattr_destroy(&mattr);
+ pthread_condattr_destroy(&cattr);
+
+ return rb;
+
+ fail_del:
+ pthread_cond_destroy(rb->add);
+ fail_add:
+ pthread_condattr_destroy(&cattr);
+ fail_cattr:
+ pthread_mutex_destroy(rb->mtx);
+ fail_mutex:
+ pthread_mutexattr_destroy(&mattr);
+ fail_mattr:
+ ssm_rbuff_destroy(rb);
+ fail_rb:
+ return NULL;
+}
+
+void ssm_rbuff_destroy(struct ssm_rbuff * rb)
+{
+ char fn[FN_MAX_CHARS];
+
+ assert(rb != NULL);
+
+ sprintf(fn, SSM_RBUFF_PREFIX "%d.%d", rb->pid, rb->flow_id);
+
+ ssm_rbuff_close(rb);
+
+ shm_unlink(fn);
+}
+
+struct ssm_rbuff * ssm_rbuff_open(pid_t pid,
+ int flow_id)
+{
+ return rbuff_create(pid, flow_id, O_RDWR);
+}
+
+void ssm_rbuff_close(struct ssm_rbuff * rb)
+{
+ assert(rb);
+
+ rbuff_destroy(rb);
+}
+
+int ssm_rbuff_write(struct ssm_rbuff * rb,
+ size_t idx)
+{
+ size_t acl;
+ bool was_empty;
+ int ret = 0;
+
+ assert(rb != NULL);
+
+ acl = __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+ if (acl != ACL_RDWR) {
+ if (acl & ACL_FLOWDOWN) {
+ ret = -EFLOWDOWN;
+ goto fail_acl;
+ }
+ if (acl & ACL_RDONLY) {
+ ret = -ENOTALLOC;
+ goto fail_acl;
+ }
+ }
+
+ robust_mutex_lock(rb->mtx);
+
+ if (IS_FULL(rb)) {
+ ret = -EAGAIN;
+ goto fail_mutex;
+ }
+
+ was_empty = IS_EMPTY(rb);
+
+ HEAD(rb) = (ssize_t) idx;
+ ADVANCE_HEAD(rb);
+
+ if (was_empty)
+ pthread_cond_broadcast(rb->add);
+
+ pthread_mutex_unlock(rb->mtx);
+
+ return 0;
+
+ fail_mutex:
+ pthread_mutex_unlock(rb->mtx);
+ fail_acl:
+ return ret;
+}
+
+int ssm_rbuff_write_b(struct ssm_rbuff * rb,
+ size_t idx,
+ const struct timespec * abstime)
+{
+ size_t acl;
+ int ret = 0;
+ bool was_empty;
+
+ assert(rb != NULL);
+
+ acl = __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+ if (acl != ACL_RDWR) {
+ if (acl & ACL_FLOWDOWN) {
+ ret = -EFLOWDOWN;
+ goto fail_acl;
+ }
+ if (acl & ACL_RDONLY) {
+ ret = -ENOTALLOC;
+ goto fail_acl;
+ }
+ }
+
+ robust_mutex_lock(rb->mtx);
+
+ pthread_cleanup_push(__cleanup_mutex_unlock, rb->mtx);
+
+ while (IS_FULL(rb) && ret != -ETIMEDOUT) {
+ acl = __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+ if (acl & ACL_FLOWDOWN) {
+ ret = -EFLOWDOWN;
+ break;
+ }
+ ret = -robust_wait(rb->del, rb->mtx, abstime);
+ }
+
+ pthread_cleanup_pop(false);
+
+ if (ret != -ETIMEDOUT && ret != -EFLOWDOWN) {
+ was_empty = IS_EMPTY(rb);
+ HEAD(rb) = (ssize_t) idx;
+ ADVANCE_HEAD(rb);
+ if (was_empty)
+ pthread_cond_broadcast(rb->add);
+ }
+
+ pthread_mutex_unlock(rb->mtx);
+
+ fail_acl:
+ return ret;
+}
+
+static int check_rb_acl(struct ssm_rbuff * rb)
+{
+ size_t acl;
+
+ assert(rb != NULL);
+
+ acl = __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+
+ if (acl & ACL_FLOWDOWN)
+ return -EFLOWDOWN;
+
+ if (acl & ACL_FLOWPEER)
+ return -EFLOWPEER;
+
+ return -EAGAIN;
+}
+
+ssize_t ssm_rbuff_read(struct ssm_rbuff * rb)
+{
+ ssize_t ret;
+
+ assert(rb != NULL);
+
+ if (IS_EMPTY(rb))
+ return check_rb_acl(rb);
+
+ robust_mutex_lock(rb->mtx);
+
+ ret = TAIL(rb);
+ ADVANCE_TAIL(rb);
+
+ pthread_cond_broadcast(rb->del);
+
+ pthread_mutex_unlock(rb->mtx);
+
+ return ret;
+}
+
+ssize_t ssm_rbuff_read_b(struct ssm_rbuff * rb,
+ const struct timespec * abstime)
+{
+ ssize_t idx = -1;
+ size_t acl;
+
+ assert(rb != NULL);
+
+ acl = __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+ if (IS_EMPTY(rb) && (acl & ACL_FLOWDOWN))
+ return -EFLOWDOWN;
+
+ robust_mutex_lock(rb->mtx);
+
+ pthread_cleanup_push(__cleanup_mutex_unlock, rb->mtx);
+
+ while (IS_EMPTY(rb) &&
+ idx != -ETIMEDOUT &&
+ check_rb_acl(rb) == -EAGAIN) {
+ idx = -robust_wait(rb->add, rb->mtx, abstime);
+ }
+
+ pthread_cleanup_pop(false);
+
+ if (!IS_EMPTY(rb)) {
+ idx = TAIL(rb);
+ ADVANCE_TAIL(rb);
+ pthread_cond_broadcast(rb->del);
+ } else if (idx != -ETIMEDOUT) {
+ idx = check_rb_acl(rb);
+ }
+
+ pthread_mutex_unlock(rb->mtx);
+
+ assert(idx != -EAGAIN);
+
+ return idx;
+}
+
+void ssm_rbuff_set_acl(struct ssm_rbuff * rb,
+ uint32_t flags)
+{
+ assert(rb != NULL);
+
+ __atomic_store_n(rb->acl, (size_t) flags, __ATOMIC_SEQ_CST);
+}
+
+uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb)
+{
+ assert(rb != NULL);
+
+ return (uint32_t) __atomic_load_n(rb->acl, __ATOMIC_SEQ_CST);
+}
+
+void ssm_rbuff_fini(struct ssm_rbuff * rb)
+{
+ assert(rb != NULL);
+
+ robust_mutex_lock(rb->mtx);
+
+ pthread_cleanup_push(__cleanup_mutex_unlock, rb->mtx);
+
+ while (!IS_EMPTY(rb))
+ robust_wait(rb->del, rb->mtx, NULL);
+
+ pthread_cleanup_pop(true);
+}
+
+size_t ssm_rbuff_queued(struct ssm_rbuff * rb)
+{
+ assert(rb != NULL);
+
+ return QUEUED(rb);
+}
+
+int ssm_rbuff_mlock(struct ssm_rbuff * rb)
+{
+ assert(rb != NULL);
+
+ return mlock(rb->shm_base, SSM_RBUFF_FILESIZE);
+}
diff --git a/src/lib/ssm/ssm.h.in b/src/lib/ssm/ssm.h.in
new file mode 100644
index 00000000..b9246c8b
--- /dev/null
+++ b/src/lib/ssm/ssm.h.in
@@ -0,0 +1,171 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Secure Shared Memory configuration
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_SSM_H
+#define OUROBOROS_LIB_SSM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdatomic.h>
+#include <sys/types.h>
+
+/* Pool naming configuration */
+#define SSM_PREFIX "@SSM_PREFIX@"
+#define SSM_GSPP_NAME "@SSM_GSPP_NAME@"
+#define SSM_PUP_NAME_FMT "@SSM_PUP_NAME_FMT@"
+#define SSM_GSPP_UID 0
+
+/* Legacy SSM constants */
+#define SSM_RBUFF_PREFIX "@SSM_RBUFF_PREFIX@"
+#define SSM_FLOW_SET_PREFIX "@SSM_FLOW_SET_PREFIX@"
+#define SSM_POOL_NAME "@SSM_POOL_NAME@"
+#define SSM_POOL_BLOCKS @SSM_POOL_BLOCKS@
+#define SSM_RBUFF_SIZE @SSM_RBUFF_SIZE@
+
+/* Packet buffer space reservation */
+#define SSM_PK_BUFF_HEADSPACE @SSM_PK_BUFF_HEADSPACE@
+#define SSM_PK_BUFF_TAILSPACE @SSM_PK_BUFF_TAILSPACE@
+
+/* Global Shared Packet Pool (GSPP) - for privileged processes */
+#define SSM_GSPP_256_BLOCKS @SSM_GSPP_256_BLOCKS@
+#define SSM_GSPP_512_BLOCKS @SSM_GSPP_512_BLOCKS@
+#define SSM_GSPP_1K_BLOCKS @SSM_GSPP_1K_BLOCKS@
+#define SSM_GSPP_2K_BLOCKS @SSM_GSPP_2K_BLOCKS@
+#define SSM_GSPP_4K_BLOCKS @SSM_GSPP_4K_BLOCKS@
+#define SSM_GSPP_16K_BLOCKS @SSM_GSPP_16K_BLOCKS@
+#define SSM_GSPP_64K_BLOCKS @SSM_GSPP_64K_BLOCKS@
+#define SSM_GSPP_256K_BLOCKS @SSM_GSPP_256K_BLOCKS@
+#define SSM_GSPP_1M_BLOCKS @SSM_GSPP_1M_BLOCKS@
+#define SSM_GSPP_TOTAL_SIZE @SSM_GSPP_TOTAL_SIZE@
+
+/* Per-User Pool (PUP) - for unprivileged applications */
+#define SSM_PUP_256_BLOCKS @SSM_PUP_256_BLOCKS@
+#define SSM_PUP_512_BLOCKS @SSM_PUP_512_BLOCKS@
+#define SSM_PUP_1K_BLOCKS @SSM_PUP_1K_BLOCKS@
+#define SSM_PUP_2K_BLOCKS @SSM_PUP_2K_BLOCKS@
+#define SSM_PUP_4K_BLOCKS @SSM_PUP_4K_BLOCKS@
+#define SSM_PUP_16K_BLOCKS @SSM_PUP_16K_BLOCKS@
+#define SSM_PUP_64K_BLOCKS @SSM_PUP_64K_BLOCKS@
+#define SSM_PUP_256K_BLOCKS @SSM_PUP_256K_BLOCKS@
+#define SSM_PUP_1M_BLOCKS @SSM_PUP_1M_BLOCKS@
+#define SSM_PUP_TOTAL_SIZE @SSM_PUP_TOTAL_SIZE@
+
+/* Legacy pool blocks (same as GSPP for compatibility) */
+#define SSM_POOL_256_BLOCKS @SSM_POOL_256_BLOCKS@
+#define SSM_POOL_512_BLOCKS @SSM_POOL_512_BLOCKS@
+#define SSM_POOL_1K_BLOCKS @SSM_POOL_1K_BLOCKS@
+#define SSM_POOL_2K_BLOCKS @SSM_POOL_2K_BLOCKS@
+#define SSM_POOL_4K_BLOCKS @SSM_POOL_4K_BLOCKS@
+#define SSM_POOL_16K_BLOCKS @SSM_POOL_16K_BLOCKS@
+#define SSM_POOL_64K_BLOCKS @SSM_POOL_64K_BLOCKS@
+#define SSM_POOL_256K_BLOCKS @SSM_POOL_256K_BLOCKS@
+#define SSM_POOL_1M_BLOCKS @SSM_POOL_1M_BLOCKS@
+#define SSM_POOL_TOTAL_SIZE @SSM_POOL_TOTAL_SIZE@
+
+/* Size class configuration */
+#define SSM_POOL_MAX_CLASSES 9
+#define SSM_POOL_SHARDS @SSM_POOL_SHARDS@
+
+/* Internal structures - exposed for testing */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <pthread.h>
+
+#include <ouroboros/pthread.h>
+
+static __inline__ void robust_mutex_lock(pthread_mutex_t * mtx)
+{
+#ifndef HAVE_ROBUST_MUTEX
+ pthread_mutex_lock(mtx);
+#else
+ if (pthread_mutex_lock(mtx) == EOWNERDEAD)
+ pthread_mutex_consistent(mtx);
+#endif
+}
+
+static __inline__ int robust_wait(pthread_cond_t * cond,
+ pthread_mutex_t * mtx,
+ const struct timespec * abstime)
+{
+ int ret = __timedwait(cond, mtx, abstime);
+#ifdef HAVE_ROBUST_MUTEX
+ if (ret == EOWNERDEAD)
+ pthread_mutex_consistent(mtx);
+#endif
+ return ret;
+}
+
+/* Packet buffer structure used by pool, rbuff, and tests */
+struct ssm_pk_buff {
+ uint32_t next_offset; /* List linkage (pool < 4GB) */
+ uint16_t refcount; /* Reference count (app + rtx) */
+ pid_t allocator_pid; /* For orphan detection */
+ uint32_t size; /* Block size (max 1MB) */
+ uint32_t pk_head; /* Head offset into data */
+ uint32_t pk_tail; /* Tail offset into data */
+ uint32_t off; /* Block offset in pool */
+ uint8_t data[]; /* Packet data */
+};
+
+/* Size class configuration table */
+struct ssm_size_class_cfg {
+ size_t size;
+ size_t blocks;
+};
+
+struct _ssm_list_head {
+ uint32_t head_offset;
+ uint32_t count;
+};
+
+struct _ssm_shard {
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+ struct _ssm_list_head free_list;
+ size_t free_count;
+};
+
+struct _ssm_size_class {
+ struct _ssm_shard shards[SSM_POOL_SHARDS];
+ size_t object_size;
+ size_t pool_start;
+ size_t pool_size;
+ size_t object_count;
+};
+
+struct _ssm_pool_hdr {
+ pthread_mutex_t mtx;
+ pthread_cond_t healthy;
+ pid_t pid;
+ uint32_t initialized;
+ void * mapped_addr;
+ struct _ssm_size_class size_classes[SSM_POOL_MAX_CLASSES];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OUROBOROS_LIB_SSM_H */
diff --git a/src/lib/ssm/tests/CMakeLists.txt b/src/lib/ssm/tests/CMakeLists.txt
new file mode 100644
index 00000000..5cac70d1
--- /dev/null
+++ b/src/lib/ssm/tests/CMakeLists.txt
@@ -0,0 +1,22 @@
+get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+
+compute_test_prefix()
+
+create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
+ # Add new tests here
+ pool_test.c
+ pool_sharding_test.c
+ rbuff_test.c
+ flow_set_test.c
+ )
+
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests})
+
+disable_test_logging_for_target(${PARENT_DIR}_test)
+target_link_libraries(${PARENT_DIR}_test ouroboros-common)
+
+add_dependencies(build_tests ${PARENT_DIR}_test)
+
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests}
+ ENVIRONMENT "OUROBOROS_TEST_POOL_SUFFIX=.test")
diff --git a/src/lib/ssm/tests/flow_set_test.c b/src/lib/ssm/tests/flow_set_test.c
new file mode 100644
index 00000000..f9084d3c
--- /dev/null
+++ b/src/lib/ssm/tests/flow_set_test.c
@@ -0,0 +1,255 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the SSM flow set
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include "config.h"
+#include "ssm.h"
+
+#include <test/test.h>
+#include <ouroboros/ssm_flow_set.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/time.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+static int test_ssm_flow_set_create_destroy(void)
+{
+ struct ssm_flow_set * set;
+ pid_t pid;
+
+ TEST_START();
+
+ pid = getpid();
+
+ set = ssm_flow_set_create(pid);
+ if (set == NULL) {
+ printf("Failed to create flow set.\n");
+ goto fail;
+ }
+
+ ssm_flow_set_destroy(set);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_flow_set_add_del_has(void)
+{
+ struct ssm_flow_set * set;
+ pid_t pid;
+ size_t idx = 0;
+ int flow_id = 42;
+
+ TEST_START();
+
+ pid = getpid();
+
+ set = ssm_flow_set_create(pid);
+ if (set == NULL) {
+ printf("Failed to create flow set.\n");
+ goto fail;
+ }
+
+ if (ssm_flow_set_has(set, idx, flow_id)) {
+ printf("Flow should not be in set initially.\n");
+ goto fail_destroy;
+ }
+
+ if (ssm_flow_set_add(set, idx, flow_id) < 0) {
+ printf("Failed to add flow to set.\n");
+ goto fail_destroy;
+ }
+
+ if (!ssm_flow_set_has(set, idx, flow_id)) {
+ printf("Flow should be in set after add.\n");
+ goto fail_destroy;
+ }
+
+ /* Adding same flow again should fail */
+ if (ssm_flow_set_add(set, idx, flow_id) != -EPERM) {
+ printf("Should not be able to add flow twice.\n");
+ goto fail_destroy;
+ }
+
+ ssm_flow_set_del(set, idx, flow_id);
+
+ if (ssm_flow_set_has(set, idx, flow_id)) {
+ printf("Flow should not be in set after delete.\n");
+ goto fail_destroy;
+ }
+
+ ssm_flow_set_destroy(set);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+fail_destroy:
+ ssm_flow_set_destroy(set);
+fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_flow_set_zero(void)
+{
+ struct ssm_flow_set * set;
+ pid_t pid;
+ size_t idx = 0;
+ int flow_id1 = 10;
+ int flow_id2 = 20;
+
+ TEST_START();
+
+ pid = getpid();
+
+ set = ssm_flow_set_create(pid);
+ if (set == NULL) {
+ printf("Failed to create flow set.\n");
+ goto fail;
+ }
+
+ if (ssm_flow_set_add(set, idx, flow_id1) < 0) {
+ printf("Failed to add flow1 to set.\n");
+ goto fail_destroy;
+ }
+
+ if (ssm_flow_set_add(set, idx, flow_id2) < 0) {
+ printf("Failed to add flow2 to set.\n");
+ goto fail_destroy;
+ }
+
+ ssm_flow_set_zero(set, idx);
+
+ if (ssm_flow_set_has(set, idx, flow_id1)) {
+ printf("Flow1 should not be in set after zero.\n");
+ goto fail_destroy;
+ }
+
+ if (ssm_flow_set_has(set, idx, flow_id2)) {
+ printf("Flow2 should not be in set after zero.\n");
+ goto fail_destroy;
+ }
+
+ ssm_flow_set_destroy(set);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+fail_destroy:
+ ssm_flow_set_destroy(set);
+fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_flow_set_notify_wait(void)
+{
+ struct ssm_flow_set * set;
+ pid_t pid;
+ size_t idx = 0;
+ int flow_id = 100;
+ struct flowevent events[SSM_RBUFF_SIZE];
+ struct timespec timeout;
+ ssize_t ret;
+
+ TEST_START();
+
+ pid = getpid();
+
+ set = ssm_flow_set_create(pid);
+ if (set == NULL) {
+ printf("Failed to create flow set.\n");
+ goto fail;
+ }
+
+ if (ssm_flow_set_add(set, idx, flow_id) < 0) {
+ printf("Failed to add flow to set.\n");
+ goto fail_destroy;
+ }
+
+ /* Test immediate timeout when no events */
+ clock_gettime(PTHREAD_COND_CLOCK, &timeout);
+ ret = ssm_flow_set_wait(set, idx, events, &timeout);
+ if (ret != -ETIMEDOUT) {
+ printf("Wait should timeout immediately when no events.\n");
+ goto fail_destroy;
+ }
+
+ /* Notify an event */
+ ssm_flow_set_notify(set, flow_id, FLOW_PKT);
+
+ /* Should be able to read the event immediately */
+ clock_gettime(PTHREAD_COND_CLOCK, &timeout);
+ ts_add(&timeout, &timeout, &((struct timespec) {1, 0}));
+
+ ret = ssm_flow_set_wait(set, idx, events, &timeout);
+ if (ret != 1) {
+ printf("Wait should return 1 event, got %zd.\n", ret);
+ goto fail_destroy;
+ }
+
+ if (events[0].flow_id != flow_id) {
+ printf("Event flow_id mismatch: expected %d, got %d.\n",
+ flow_id, events[0].flow_id);
+ goto fail_destroy;
+ }
+
+ if (events[0].event != FLOW_PKT) {
+ printf("Event type mismatch: expected %d, got %d.\n",
+ FLOW_PKT, events[0].event);
+ goto fail_destroy;
+ }
+
+ ssm_flow_set_destroy(set);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+fail_destroy:
+ ssm_flow_set_destroy(set);
+fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int flow_set_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_ssm_flow_set_create_destroy();
+ ret |= test_ssm_flow_set_add_del_has();
+ ret |= test_ssm_flow_set_zero();
+ ret |= test_ssm_flow_set_notify_wait();
+
+ return ret;
+}
diff --git a/src/lib/ssm/tests/pool_sharding_test.c b/src/lib/ssm/tests/pool_sharding_test.c
new file mode 100644
index 00000000..46eecd8d
--- /dev/null
+++ b/src/lib/ssm/tests/pool_sharding_test.c
@@ -0,0 +1,496 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test of the SSM pool sharding with fallback
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include "config.h"
+#include "ssm.h"
+
+#include <test/test.h>
+#include <ouroboros/ssm_pool.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#define TEST_SIZE 256
+
+/* Helper to get pool header for inspection */
+static struct _ssm_pool_hdr * get_pool_hdr(struct ssm_pool * pool)
+{
+ /* ssm_pool is opaque, but we know its layout:
+ * uint8_t * shm_base
+ * struct _ssm_pool_hdr * hdr
+ * void * pool_base
+ */
+ struct _ssm_pool_hdr ** hdr_ptr =
+ (struct _ssm_pool_hdr **)((uint8_t *)pool + sizeof(void *));
+
+ return *hdr_ptr;
+}
+
+static int test_lazy_distribution(void)
+{
+ struct ssm_pool * pool;
+ struct _ssm_pool_hdr * hdr;
+ struct _ssm_size_class * sc;
+ int i;
+ int sc_idx;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL) {
+ printf("Failed to create pool.\n");
+ goto fail;
+ }
+
+ hdr = get_pool_hdr(pool);
+ if (hdr == NULL) {
+ printf("Failed to get pool header.\n");
+ goto fail_pool;
+ }
+
+ /* Find the first size class with blocks */
+ sc_idx = -1;
+ for (i = 0; i < SSM_POOL_MAX_CLASSES; i++) {
+ if (hdr->size_classes[i].object_count > 0) {
+ sc_idx = i;
+ break;
+ }
+ }
+
+ if (sc_idx < 0) {
+ printf("No size classes configured.\n");
+ for (i = 0; i < SSM_POOL_MAX_CLASSES; i++) {
+ printf(" Class %d: count=%zu\n", i,
+ hdr->size_classes[i].object_count);
+ }
+ goto fail_pool;
+ }
+
+ sc = &hdr->size_classes[sc_idx];
+
+ /* Verify all blocks start in shard 0 */
+ if (sc->shards[0].free_count == 0) {
+ printf("Shard 0 should have all blocks initially.\n");
+ goto fail_pool;
+ }
+
+ /* Verify other shards are empty */
+ for (i = 1; i < SSM_POOL_SHARDS; i++) {
+ if (sc->shards[i].free_count != 0) {
+ printf("Shard %d should be empty, has %zu.\n",
+ i, sc->shards[i].free_count);
+ goto fail_pool;
+ }
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_shard_migration(void)
+{
+ struct ssm_pool * pool;
+ struct _ssm_pool_hdr * hdr;
+ struct _ssm_size_class * sc;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ ssize_t off;
+ int shard_idx;
+ int sc_idx;
+ int i;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL) {
+ printf("Failed to create pool.\n");
+ goto fail;
+ }
+
+ hdr = get_pool_hdr(pool);
+
+ /* Find the first size class with blocks */
+ sc_idx = -1;
+ for (i = 0; i < SSM_POOL_MAX_CLASSES; i++) {
+ if (hdr->size_classes[i].object_count > 0) {
+ sc_idx = i;
+ break;
+ }
+ }
+
+ if (sc_idx < 0) {
+ printf("No size classes configured.\n");
+ goto fail;
+ }
+
+ sc = &hdr->size_classes[sc_idx];
+
+ /* Allocate from this process */
+ off = ssm_pool_alloc(pool, TEST_SIZE, &ptr, &spb);
+ if (off < 0) {
+ printf("Allocation failed: %zd.\n", off);
+ goto fail_pool;
+ }
+
+ /* Free it - should go to this process's shard */
+ shard_idx = getpid() % SSM_POOL_SHARDS;
+ if (ssm_pool_remove(pool, off) != 0) {
+ printf("Remove failed.\n");
+ goto fail_pool;
+ }
+
+ /* Verify block migrated away from shard 0 or in allocator's shard */
+ if (sc->shards[shard_idx].free_count == 0 &&
+ sc->shards[0].free_count == 0) {
+ printf("Block should have been freed to a shard.\n");
+ goto fail_pool;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_fallback_stealing(void)
+{
+ struct ssm_pool * pool;
+ struct _ssm_pool_hdr * hdr;
+ struct _ssm_size_class * sc;
+ struct ssm_pk_buff ** spbs;
+ uint8_t ** ptrs;
+ size_t total_blocks;
+ size_t total_free;
+ size_t i;
+ int sc_idx;
+ int c;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL) {
+ printf("Failed to create pool.\n");
+ goto fail;
+ }
+
+ hdr = get_pool_hdr(pool);
+
+ /* Find the first size class with blocks */
+ sc_idx = -1;
+ for (c = 0; c < SSM_POOL_MAX_CLASSES; c++) {
+ if (hdr->size_classes[c].object_count > 0) {
+ sc_idx = c;
+ break;
+ }
+ }
+
+ if (sc_idx < 0) {
+ printf("No size classes configured.\n");
+ goto fail;
+ }
+
+ sc = &hdr->size_classes[sc_idx];
+ total_blocks = sc->object_count;
+
+ spbs = malloc(total_blocks * sizeof(struct ssm_pk_buff *));
+ ptrs = malloc(total_blocks * sizeof(uint8_t *));
+ if (spbs == NULL || ptrs == NULL) {
+ printf("Failed to allocate test arrays.\n");
+ goto fail_pool;
+ }
+
+ /* Allocate half the blocks from single process */
+ for (i = 0; i < total_blocks / 2; i++) {
+ ssize_t off = ssm_pool_alloc(pool, TEST_SIZE,
+ &ptrs[i], &spbs[i]);
+ if (off < 0) {
+ printf("Allocation %zu failed: %zd.\n", i, off);
+ free(spbs);
+ free(ptrs);
+ goto fail_pool;
+ }
+ }
+
+ /* Free them all - they go to local_shard */
+ for (i = 0; i < total_blocks / 2; i++) {
+ size_t off = ssm_pk_buff_get_idx(spbs[i]);
+ if (ssm_pool_remove(pool, off) != 0) {
+ printf("Remove %zu failed.\n", i);
+ free(spbs);
+ free(ptrs);
+ goto fail_pool;
+ }
+ }
+
+ /* Freed blocks should be in shards (all blocks free again) */
+ total_free = 0;
+ for (i = 0; i < SSM_POOL_SHARDS; i++) {
+ total_free += sc->shards[i].free_count;
+ }
+
+ if (total_free != total_blocks) {
+ printf("Expected %zu free blocks total, got %zu.\n",
+ total_blocks, total_free);
+ free(spbs);
+ free(ptrs);
+ goto fail_pool;
+ }
+
+ /* Allocate again - should succeed by taking from shards */
+ for (i = 0; i < total_blocks / 2; i++) {
+ ssize_t off = ssm_pool_alloc(pool, TEST_SIZE,
+ &ptrs[i], &spbs[i]);
+ if (off < 0) {
+ printf("Fallback alloc %zu failed: %zd.\n", i, off);
+ free(spbs);
+ free(ptrs);
+ goto fail_pool;
+ }
+ }
+
+ /* Now all allocated blocks are in use again */
+ /* Cleanup - free all allocated blocks */
+ for (i = 0; i < total_blocks / 2; i++) {
+ size_t off = ssm_pk_buff_get_idx(spbs[i]);
+ ssm_pool_remove(pool, off);
+ }
+
+ free(spbs);
+ free(ptrs);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_multiprocess_sharding(void)
+{
+ struct ssm_pool * pool;
+ struct _ssm_pool_hdr * hdr;
+ struct _ssm_size_class * sc;
+ pid_t children[SSM_POOL_SHARDS];
+ int i;
+ int status;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL) {
+ printf("Failed to create pool.\n");
+ goto fail;
+ }
+
+ /* Fork processes to test different shards */
+ for (i = 0; i < SSM_POOL_SHARDS; i++) {
+ children[i] = fork();
+ if (children[i] == -1) {
+ printf("Fork %d failed.\n", i);
+ goto fail_children;
+ }
+
+ if (children[i] == 0) {
+ /* Child process */
+ struct ssm_pool * child_pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ ssize_t off;
+ int my_shard;
+
+ child_pool = ssm_pool_open(0);
+ if (child_pool == NULL)
+ exit(EXIT_FAILURE);
+
+ my_shard = getpid() % SSM_POOL_SHARDS;
+ (void) my_shard; /* Reserved for future use */
+
+ /* Each child allocates and frees a block */
+ off = ssm_pool_alloc(child_pool, TEST_SIZE,
+ &ptr, &spb);
+ if (off < 0) {
+ ssm_pool_close(child_pool);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Small delay to ensure allocation visible */
+ usleep(10000);
+
+ if (ssm_pool_remove(child_pool, off) != 0) {
+ ssm_pool_close(child_pool);
+ exit(EXIT_FAILURE);
+ }
+
+ ssm_pool_close(child_pool);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ /* Wait for all children */
+ for (i = 0; i < SSM_POOL_SHARDS; i++) {
+ if (waitpid(children[i], &status, 0) == -1) {
+ printf("Waitpid %d failed.\n", i);
+ goto fail_children;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ printf("Child %d failed.\n", i);
+ goto fail_pool;
+ }
+ }
+
+ /* Verify blocks distributed across shards */
+ hdr = get_pool_hdr(pool);
+
+ /* Find the first size class with blocks */
+ sc = NULL;
+ for (i = 0; i < SSM_POOL_MAX_CLASSES; i++) {
+ if (hdr->size_classes[i].object_count > 0) {
+ sc = &hdr->size_classes[i];
+ break;
+ }
+ }
+
+ if (sc == NULL) {
+ printf("No size classes configured.\n");
+ goto fail_pool;
+ }
+
+ /* After children allocate and free, blocks should be in shards
+ * (though exact distribution depends on PID values)
+ */
+ for (i = 0; i < SSM_POOL_SHARDS; i++) {
+ /* At least some shards should have blocks */
+ if (sc->shards[i].free_count > 0) {
+ break;
+ }
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_children:
+ /* Kill any remaining children */
+ for (i = 0; i < SSM_POOL_SHARDS; i++) {
+ if (children[i] > 0)
+ kill(children[i], SIGKILL);
+ }
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_exhaustion_with_fallback(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ ssize_t off;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL) {
+ printf("Failed to create pool.\n");
+ goto fail;
+ }
+
+ /* Allocate until exhausted across all shards */
+ while (true) {
+ off = ssm_pool_alloc(pool, TEST_SIZE, &ptr, &spb);
+ if (off < 0) {
+ if (off == -EAGAIN)
+ break;
+ printf("Unexpected error: %zd.\n", off);
+ goto fail_pool;
+ }
+ }
+
+ /* Should fail with -EAGAIN when truly exhausted */
+ off = ssm_pool_alloc(pool, TEST_SIZE, &ptr, &spb);
+ if (off != -EAGAIN) {
+ printf("Expected -EAGAIN, got %zd.\n", off);
+ goto fail_pool;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int pool_sharding_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_lazy_distribution();
+ ret |= test_shard_migration();
+ ret |= test_fallback_stealing();
+ ret |= test_multiprocess_sharding();
+ ret |= test_exhaustion_with_fallback();
+
+ return ret;
+}
diff --git a/src/lib/ssm/tests/pool_test.c b/src/lib/ssm/tests/pool_test.c
new file mode 100644
index 00000000..53f7f541
--- /dev/null
+++ b/src/lib/ssm/tests/pool_test.c
@@ -0,0 +1,1036 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the Secure Shared Memory (SSM) system
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "config.h"
+#include "ssm.h"
+
+#include <test/test.h>
+#include <ouroboros/ssm_pool.h>
+#include <ouroboros/ssm_rbuff.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <time.h>
+
+#define POOL_256 256
+#define POOL_512 512
+#define POOL_1K 1024
+#define POOL_2K 2048
+#define POOL_4K 4096
+#define POOL_16K 16384
+#define POOL_64K 65536
+#define POOL_256K 262144
+#define POOL_1M 1048576
+#define POOL_2M (2 * 1024 * 1024)
+
+static int test_ssm_pool_basic_allocation(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr;
+ struct ssm_pk_buff * spb;
+ ssize_t ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Alloc failed: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ if (spb == NULL) {
+ printf("Spb is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ptr == NULL) {
+ printf("Ptr is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ssm_pk_buff_len(spb) != POOL_256) {
+ printf("Bad length: %zu.\n", ssm_pk_buff_len(spb));
+ goto fail_alloc;
+ }
+
+ ret = ssm_pool_remove(pool, ret);
+ if (ret != 0) {
+ printf("Remove failed: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_multiple_allocations(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr1;
+ uint8_t * ptr2;
+ uint8_t * ptr3;
+ struct ssm_pk_buff * spb1;
+ struct ssm_pk_buff * spb2;
+ struct ssm_pk_buff * spb3;
+ ssize_t ret1;
+ ssize_t ret2;
+ ssize_t ret3;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret1 = ssm_pool_alloc(pool, POOL_256, &ptr1, &spb1);
+ ret2 = ssm_pool_alloc(pool, POOL_256, &ptr2, &spb2);
+ ret3 = ssm_pool_alloc(pool, POOL_256, &ptr3, &spb3);
+ if (ret1 < 0 || ret2 < 0 || ret3 < 0) {
+ printf("Allocs failed: %zd, %zd, %zd.\n", ret1, ret2, ret3);
+ goto fail_alloc;
+ }
+
+ if (spb1 == NULL) {
+ printf("Spb1 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ptr1 == NULL) {
+ printf("Ptr1 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (spb2 == NULL) {
+ printf("Spb2 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ptr2 == NULL) {
+ printf("Ptr2 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (spb3 == NULL) {
+ printf("Spb3 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ptr3 == NULL) {
+ printf("Ptr3 is NULL.\n");
+ goto fail_alloc;
+ }
+
+ if (ssm_pk_buff_len(spb1) != POOL_256) {
+ printf("Bad length spb1: %zu.\n", ssm_pk_buff_len(spb1));
+ goto fail_alloc;
+ }
+
+ if (ssm_pk_buff_len(spb2) != POOL_256) {
+ printf("Bad length spb2: %zu.\n", ssm_pk_buff_len(spb2));
+ goto fail_alloc;
+ }
+
+ if (ssm_pk_buff_len(spb3) != POOL_256) {
+ printf("Bad length spb3: %zu.\n", ssm_pk_buff_len(spb3));
+ goto fail_alloc;
+ }
+
+ if (ssm_pool_remove(pool, ret2) != 0) {
+ printf("Remove ret2 failed.\n");
+ goto fail_alloc;
+ }
+
+ if (ssm_pool_remove(pool, ret1) != 0) {
+ printf("Remove ret1 failed.\n");
+ goto fail_alloc;
+ }
+
+ if (ssm_pool_remove(pool, ret3) != 0) {
+ printf("Remove ret3 failed.\n");
+ goto fail_alloc;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_no_fallback_for_large(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr;
+ struct ssm_pk_buff * spb;
+ ssize_t ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_alloc(pool, POOL_2M, &ptr, &spb);
+ if (ret >= 0) {
+ printf("Oversized alloc succeeded: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ if (ret != -EMSGSIZE) {
+ printf("Wrong error: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_blocking_vs_nonblocking(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr;
+ struct ssm_pk_buff * spb;
+ ssize_t ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_alloc(pool, POOL_2M, &ptr, &spb);
+ if (ret != -EMSGSIZE) {
+ printf("Nonblocking oversized: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ret = ssm_pool_alloc_b(pool, POOL_2M, &ptr, &spb, NULL);
+ if (ret != -EMSGSIZE) {
+ printf("Blocking oversized: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Valid alloc failed: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ssm_pool_remove(pool, ret);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_stress_test(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr;
+ struct ssm_pk_buff * spb;
+ ssize_t * indices = NULL;
+ ssize_t ret;
+ size_t count = 0;
+ size_t i;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ indices = malloc(100 * sizeof(*indices));
+ if (indices == NULL) {
+ printf("Malloc failed.\n");
+ goto fail_alloc;
+ }
+
+ for (i = 0; i < 100; i++) {
+ size_t j;
+ size_t num;
+ size_t size;
+
+ num = (i % 100) + 1;
+
+ for (j = 0; j < num && count < 100; j++) {
+ switch (i % 4) {
+ case 0:
+ /* FALLTHRU */
+ case 1:
+ size = POOL_256;
+ break;
+ case 2:
+ /* FALLTHRU */
+ case 3:
+ size = POOL_1K;
+ break;
+ default:
+ size = POOL_256;
+ break;
+ }
+
+ ret = ssm_pool_alloc(pool, size, &ptr, &spb);
+ if (ret < 0) {
+ printf("Alloc at iter %zu: %zd.\n", i, ret);
+ goto fail_test;
+ }
+ indices[count++] = ret;
+ }
+
+ for (j = 0; j < count / 2; j++) {
+ size_t idx = j * 2;
+ if (idx < count) {
+ ret = ssm_pool_remove(pool, indices[idx]);
+ if (ret != 0) {
+ printf("Remove at iter %zu: %zd.\n",
+ i, ret);
+ goto fail_test;
+ }
+ memmove(&indices[idx], &indices[idx + 1],
+ (count - idx - 1) * sizeof(*indices));
+ count--;
+ }
+ }
+
+ if (i % 10 == 0) {
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Periodic alloc at %zu: %zd.\n", i, ret);
+ goto fail_test;
+ }
+ ssm_pool_remove(pool, ret);
+ }
+ }
+
+ for (i = 0; i < count; i++)
+ ssm_pool_remove(pool, indices[i]);
+
+ free(indices);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_test:
+ for (i = 0; i < count; i++)
+ ssm_pool_remove(pool, indices[i]);
+ free(indices);
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_open_initializes_ssm(void)
+{
+ struct ssm_pool * creator;
+ struct ssm_pool * opener;
+ uint8_t * ptr;
+ struct ssm_pk_buff * spb;
+ ssize_t ret;
+
+ TEST_START();
+
+ creator = ssm_pool_create(0, getgid());
+ if (creator == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_alloc(creator, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Creator alloc failed: %zd.\n", ret);
+ goto fail_creator;
+ }
+ ssm_pool_remove(creator, ret);
+
+ opener = ssm_pool_open(0);
+ if (opener == NULL) {
+ printf("Open failed.\n");
+ goto fail_creator;
+ }
+
+ ret = ssm_pool_alloc(opener, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Opener alloc failed: %zd.\n", ret);
+ goto fail_opener;
+ }
+
+ ssm_pool_remove(opener, ret);
+ ssm_pool_close(opener);
+ ssm_pool_destroy(creator);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_opener:
+ ssm_pool_close(opener);
+ fail_creator:
+ ssm_pool_destroy(creator);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_bounds_checking(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ ssize_t ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_alloc(pool, POOL_256, NULL, &spb);
+ if (ret < 0) {
+ printf("alloc failed: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ spb = ssm_pool_get(pool, 0);
+ if (spb != NULL) {
+ printf("Get at offset 0.\n");
+ goto fail_alloc;
+ }
+
+ spb = ssm_pool_get(pool, 100000000UL);
+ if (spb != NULL) {
+ printf("Get beyond pool.\n");
+ goto fail_alloc;
+ }
+
+ ret = ssm_pool_remove(pool, 0);
+ if (ret != -EINVAL) {
+ printf("Remove at offset 0: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ret = ssm_pool_remove(pool, 100000000UL);
+ if (ret != -EINVAL) {
+ printf("Remove beyond pool: %zd.\n", ret);
+ goto fail_alloc;
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_inter_process_communication(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_rbuff * rb;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ uint8_t * data;
+ const char * msg = "inter-process test";
+ size_t len;
+ ssize_t idx;
+ pid_t pid;
+ int status;
+
+ TEST_START();
+
+ len = strlen(msg) + 1;
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ rb = ssm_rbuff_create(getpid(), 1);
+ if (rb == NULL) {
+ printf("Rbuff create failed.\n");
+ goto fail_pool;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ printf("Fork failed.\n");
+ goto fail_rbuff;
+ }
+
+ if (pid == 0) {
+ idx = ssm_rbuff_read_b(rb, NULL);
+ if (idx < 0) {
+ printf("Child: rbuff read: %zd.\n", idx);
+ exit(1);
+ }
+
+ spb = ssm_pool_get(pool, idx);
+ if (spb == NULL) {
+ printf("Child: pool get failed.\n");
+ exit(1);
+ }
+
+ data = ssm_pk_buff_head(spb);
+ if (data == NULL) {
+ printf("Child: data is NULL.\n");
+ ssm_pool_remove(pool, idx);
+ exit(1);
+ }
+
+ if (strcmp((char *)data, msg) != 0) {
+ printf("Child: data mismatch.\n");
+ ssm_pool_remove(pool, idx);
+ exit(1);
+ }
+
+ ssm_pool_remove(pool, idx);
+ exit(0);
+ }
+
+ idx = ssm_pool_alloc(pool, len, &ptr, &spb);
+ if (idx < 0) {
+ printf("Parent: pool alloc: %zd.\n", idx);
+ goto fail_child;
+ }
+
+ memcpy(ptr, msg, len);
+
+ if (ssm_rbuff_write(rb, idx) < 0) {
+ printf("Parent: rbuff write failed.\n");
+ ssm_pool_remove(pool, idx);
+ goto fail_child;
+ }
+
+ if (waitpid(pid, &status, 0) < 0) {
+ printf("Parent: waitpid failed.\n");
+ ssm_pool_remove(pool, idx);
+ goto fail_rbuff;
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ printf("Child failed.\n");
+ ssm_pool_remove(pool, idx);
+ goto fail_rbuff;
+ }
+
+ ssm_rbuff_destroy(rb);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_child:
+ waitpid(pid, &status, 0);
+ fail_rbuff:
+ ssm_rbuff_destroy(rb);
+ fail_pool:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_read_operation(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * wptr;
+ uint8_t * rptr;
+ const char * data = "ssm_pool_read test";
+ size_t len;
+ ssize_t idx;
+ ssize_t ret;
+
+ TEST_START();
+
+ len = strlen(data) + 1;
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ idx = ssm_pool_alloc(pool, len, &wptr, &spb);
+ if (idx < 0) {
+ printf("alloc failed: %zd.\n", idx);
+ goto fail_alloc;
+ }
+
+ memcpy(wptr, data, len);
+
+ ret = ssm_pool_read(&rptr, pool, idx);
+ if (ret < 0) {
+ printf("Read failed: %zd.\n", ret);
+ goto fail_read;
+ }
+
+ if (rptr == NULL) {
+ printf("NULL pointer.\n");
+ goto fail_read;
+ }
+
+ if (strcmp((char *)rptr, data) != 0) {
+ printf("Data mismatch.\n");
+ goto fail_read;
+ }
+
+ ssm_pool_remove(pool, idx);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_read:
+ ssm_pool_remove(pool, idx);
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_mlock_operation(void)
+{
+ struct ssm_pool * pool;
+ int ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ ret = ssm_pool_mlock(pool);
+ if (ret < 0)
+ printf("Mlock failed: %d (may need privileges).\n", ret);
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pk_buff_operations(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ uint8_t * head;
+ uint8_t * tail;
+ const char * data = "packet buffer test";
+ size_t dlen;
+ size_t len;
+ ssize_t idx;
+
+ TEST_START();
+
+ dlen = strlen(data);
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ idx = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (idx < 0) {
+ printf("alloc failed: %zd.\n", idx);
+ goto fail_alloc;
+ }
+
+ head = ssm_pk_buff_head(spb);
+ if (head != ptr) {
+ printf("Head mismatch.\n");
+ goto fail_ops;
+ }
+
+ len = ssm_pk_buff_len(spb);
+ if (len != POOL_256) {
+ printf("Bad length: %zu.\n", len);
+ goto fail_ops;
+ }
+
+ tail = ssm_pk_buff_tail(spb);
+ if (tail != ptr + len) {
+ printf("Tail mismatch.\n");
+ goto fail_ops;
+ }
+
+ memcpy(head, data, dlen);
+
+ tail = ssm_pk_buff_tail_alloc(spb, 32);
+ if (tail == NULL) {
+ printf("Tail_alloc failed.\n");
+ goto fail_ops;
+ }
+
+ if (ssm_pk_buff_len(spb) != POOL_256 + 32) {
+ printf("Length after tail_alloc: %zu.\n",
+ ssm_pk_buff_len(spb));
+ goto fail_ops;
+ }
+
+ if (memcmp(head, data, dlen) != 0) {
+ printf("Data corrupted.\n");
+ goto fail_ops;
+ }
+
+ tail = ssm_pk_buff_tail_release(spb, 32);
+ if (tail == NULL) {
+ printf("Tail_release failed.\n");
+ goto fail_ops;
+ }
+
+ if (ssm_pk_buff_len(spb) != POOL_256) {
+ printf("Length after tail_release: %zu.\n",
+ ssm_pk_buff_len(spb));
+ goto fail_ops;
+ }
+
+ ssm_pool_remove(pool, idx);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_ops:
+ ssm_pool_remove(pool, idx);
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+#define OVERHEAD (offsetof(struct ssm_pk_buff, data) + \
+ SSM_PK_BUFF_HEADSPACE + SSM_PK_BUFF_TAILSPACE)
+static int test_ssm_pool_size_class_boundaries(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ size_t sizes[] = {
+ 1,
+ POOL_512 - OVERHEAD,
+ POOL_512 - OVERHEAD + 1,
+ POOL_1K - OVERHEAD,
+ POOL_1K - OVERHEAD + 1,
+ POOL_2K - OVERHEAD,
+ POOL_2K - OVERHEAD + 1,
+ POOL_4K - OVERHEAD,
+ POOL_4K - OVERHEAD + 1,
+ POOL_16K - OVERHEAD,
+ POOL_16K - OVERHEAD + 1,
+ POOL_64K - OVERHEAD,
+ POOL_64K - OVERHEAD + 1,
+ POOL_256K - OVERHEAD,
+ POOL_256K - OVERHEAD + 1,
+ POOL_1M - OVERHEAD,
+ };
+ size_t expected_classes[] = {
+ 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, 16384,
+ 16384, 65536, 65536, 262144, 262144, 1048576, 1048576
+ };
+ size_t i;
+ ssize_t idx;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
+ struct ssm_pk_buff * hdr;
+ size_t actual_class;
+
+ idx = ssm_pool_alloc(pool, sizes[i], &ptr, &spb);
+ if (idx < 0) {
+ printf("Alloc at %zu failed: %zd.\n", sizes[i], idx);
+ goto fail_alloc;
+ }
+
+ if (ssm_pk_buff_len(spb) != sizes[i]) {
+ printf("Length mismatch at %zu: %zu.\n",
+ sizes[i], ssm_pk_buff_len(spb));
+ ssm_pool_remove(pool, idx);
+ goto fail_alloc;
+ }
+
+ /* Verify correct size class was used
+ * hdr->size is the data array size (object_size - header) */
+ hdr = spb;
+ actual_class = hdr->size + offsetof(struct ssm_pk_buff, data);
+ if (actual_class != expected_classes[i]) {
+ printf("Wrong class for len=%zu: want %zu, got %zu.\n",
+ sizes[i], expected_classes[i], actual_class);
+ ssm_pool_remove(pool, idx);
+ goto fail_alloc;
+ }
+
+ memset(ptr, i & 0xFF, sizes[i]);
+
+ ssm_pool_remove(pool, idx);
+ }
+
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_exhaustion(void)
+{
+ struct ssm_pool * pool;
+ struct ssm_pk_buff * spb;
+ uint8_t * ptr;
+ ssize_t * indices;
+ size_t count = 0;
+ size_t i;
+ ssize_t ret;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ indices = malloc(2048 * sizeof(*indices));
+ if (indices == NULL) {
+ printf("Malloc failed.\n");
+ goto fail_alloc;
+ }
+
+ for (i = 0; i < 2048; i++) {
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ break;
+ printf("Alloc error: %zd.\n", ret);
+ goto fail_test;
+ }
+ indices[count++] = ret;
+ }
+
+ if (count == 0) {
+ printf("No allocs succeeded.\n");
+ goto fail_test;
+ }
+
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret >= 0) {
+ ssm_pool_remove(pool, ret);
+ } else if (ret != -EAGAIN) {
+ printf("Unexpected error: %zd.\n", ret);
+ goto fail_test;
+ }
+
+ for (i = 0; i < count; i++)
+ ssm_pool_remove(pool, indices[i]);
+
+ ret = ssm_pool_alloc(pool, POOL_256, &ptr, &spb);
+ if (ret < 0) {
+ printf("Alloc after free failed: %zd.\n", ret);
+ goto fail_test;
+ }
+ ssm_pool_remove(pool, ret);
+
+ free(indices);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_test:
+ for (i = 0; i < count; i++)
+ ssm_pool_remove(pool, indices[i]);
+ free(indices);
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_pool_reclaim_orphans(void)
+{
+ struct ssm_pool * pool;
+ uint8_t * ptr1;
+ uint8_t * ptr2;
+ uint8_t * ptr3;
+ struct ssm_pk_buff * spb1;
+ struct ssm_pk_buff * spb2;
+ struct ssm_pk_buff * spb3;
+ ssize_t ret1;
+ ssize_t ret2;
+ ssize_t ret3;
+ pid_t my_pid;
+ pid_t fake_pid = 99999;
+
+ TEST_START();
+
+ pool = ssm_pool_create(0, getgid());
+ if (pool == NULL)
+ goto fail_create;
+
+ my_pid = getpid();
+
+ /* Allocate some blocks */
+ ret1 = ssm_pool_alloc(pool, POOL_256, &ptr1, &spb1);
+ ret2 = ssm_pool_alloc(pool, POOL_512, &ptr2, &spb2);
+ ret3 = ssm_pool_alloc(pool, POOL_1K, &ptr3, &spb3);
+ if (ret1 < 0 || ret2 < 0 || ret3 < 0) {
+ printf("Allocs failed: %zd, %zd, %zd.\n", ret1, ret2, ret3);
+ goto fail_alloc;
+ }
+
+ /* Simulate blocks from another process by changing allocator_pid */
+ spb1->allocator_pid = fake_pid;
+ spb2->allocator_pid = fake_pid;
+ /* Keep spb3 with our pid */
+
+ /* Reclaim orphans from fake_pid */
+ ssm_pool_reclaim_orphans(pool, fake_pid);
+
+ /* Verify spb1 and spb2 have refcount 0 (reclaimed) */
+ if (spb1->refcount != 0) {
+ printf("spb1 refcount should be 0, got %u.\n", spb1->refcount);
+ goto fail_test;
+ }
+
+ if (spb2->refcount != 0) {
+ printf("spb2 refcount should be 0, got %u.\n", spb2->refcount);
+ goto fail_test;
+ }
+
+ /* Verify spb3 still has refcount 1 (not reclaimed) */
+ if (spb3->refcount != 1) {
+ printf("spb3 refcount should be 1, got %u.\n", spb3->refcount);
+ goto fail_test;
+ }
+
+ /* Clean up */
+ ssm_pool_remove(pool, ret3);
+
+ /* Try allocating again - should get blocks from reclaimed pool */
+ ret1 = ssm_pool_alloc(pool, POOL_256, &ptr1, &spb1);
+ if (ret1 < 0) {
+ printf("Alloc after reclaim failed: %zd.\n", ret1);
+ goto fail_test;
+ }
+
+ /* Verify new allocation has our pid */
+ if (spb1->allocator_pid != my_pid) {
+ printf("New block has wrong pid: %d vs %d.\n",
+ spb1->allocator_pid, my_pid);
+ goto fail_test;
+ }
+
+ ssm_pool_remove(pool, ret1);
+ ssm_pool_destroy(pool);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_test:
+ ssm_pool_remove(pool, ret3);
+ fail_alloc:
+ ssm_pool_destroy(pool);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int pool_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_ssm_pool_basic_allocation();
+ ret |= test_ssm_pool_multiple_allocations();
+ ret |= test_ssm_pool_no_fallback_for_large();
+ ret |= test_ssm_pool_blocking_vs_nonblocking();
+ ret |= test_ssm_pool_stress_test();
+ ret |= test_ssm_pool_open_initializes_ssm();
+ ret |= test_ssm_pool_bounds_checking();
+ ret |= test_ssm_pool_inter_process_communication();
+ ret |= test_ssm_pool_read_operation();
+ ret |= test_ssm_pool_mlock_operation();
+ ret |= test_ssm_pk_buff_operations();
+ ret |= test_ssm_pool_size_class_boundaries();
+ ret |= test_ssm_pool_exhaustion();
+ ret |= test_ssm_pool_reclaim_orphans();
+
+ return ret;
+}
diff --git a/src/lib/ssm/tests/rbuff_test.c b/src/lib/ssm/tests/rbuff_test.c
new file mode 100644
index 00000000..6e1cb5ec
--- /dev/null
+++ b/src/lib/ssm/tests/rbuff_test.c
@@ -0,0 +1,675 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the SSM notification ring buffer
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include "config.h"
+#include "ssm.h"
+
+#include <test/test.h>
+#include <ouroboros/ssm_rbuff.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/time.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+static int test_ssm_rbuff_create_destroy(void)
+{
+ struct ssm_rbuff * rb;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 1);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_write_read(void)
+{
+ struct ssm_rbuff * rb;
+ ssize_t idx;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 2);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ if (ssm_rbuff_write(rb, 42) < 0) {
+ printf("Failed to write value.\n");
+ goto fail_rb;
+ }
+
+ if (ssm_rbuff_queued(rb) != 1) {
+ printf("Queue length should be 1, got %zu.\n",
+ ssm_rbuff_queued(rb));
+ goto fail_rb;
+ }
+
+ idx = ssm_rbuff_read(rb);
+ if (idx != 42) {
+ printf("Expected 42, got %zd.\n", idx);
+ goto fail_rb;
+ }
+
+ if (ssm_rbuff_queued(rb) != 0) {
+ printf("Queue should be empty, got %zu.\n",
+ ssm_rbuff_queued(rb));
+ goto fail_rb;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_read_empty(void)
+{
+ struct ssm_rbuff * rb;
+ ssize_t ret;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 3);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ ret = ssm_rbuff_read(rb);
+ if (ret != -EAGAIN) {
+ printf("Expected -EAGAIN, got %zd.\n", ret);
+ goto fail_rb;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_fill_drain(void)
+{
+ struct ssm_rbuff * rb;
+ size_t i;
+ ssize_t ret;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 4);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) {
+ if (ssm_rbuff_queued(rb) != i) {
+ printf("Expected %zu queued, got %zu.\n",
+ i, ssm_rbuff_queued(rb));
+ goto fail_rb;
+ }
+ if (ssm_rbuff_write(rb, i) < 0) {
+ printf("Failed to write at index %zu.\n", i);
+ goto fail_rb;
+ }
+ }
+
+ if (ssm_rbuff_queued(rb) != SSM_RBUFF_SIZE - 1) {
+ printf("Expected %d queued, got %zu.\n",
+ SSM_RBUFF_SIZE - 1, ssm_rbuff_queued(rb));
+ goto fail_rb;
+ }
+
+ ret = ssm_rbuff_write(rb, 999);
+ if (ret != -EAGAIN) {
+ printf("Expected -EAGAIN on full buffer, got %zd.\n", ret);
+ goto fail_rb;
+ }
+
+ for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) {
+ ret = ssm_rbuff_read(rb);
+ if (ret != (ssize_t) i) {
+ printf("Expected %zu, got %zd.\n", i, ret);
+ goto fail_rb;
+ }
+ }
+
+ if (ssm_rbuff_queued(rb) != 0) {
+ printf("Expected empty queue, got %zu.\n",
+ ssm_rbuff_queued(rb));
+ goto fail_rb;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ while (ssm_rbuff_read(rb) >= 0)
+ ;
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_acl(void)
+{
+ struct ssm_rbuff * rb;
+ uint32_t acl;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 5);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ acl = ssm_rbuff_get_acl(rb);
+ if (acl != ACL_RDWR) {
+ printf("Expected ACL_RDWR, got %u.\n", acl);
+ goto fail_rb;
+ }
+
+ ssm_rbuff_set_acl(rb, ACL_RDONLY);
+ acl = ssm_rbuff_get_acl(rb);
+ if (acl != ACL_RDONLY) {
+ printf("Expected ACL_RDONLY, got %u.\n", acl);
+ goto fail_rb;
+ }
+
+ if (ssm_rbuff_write(rb, 1) != -ENOTALLOC) {
+ printf("Expected -ENOTALLOC on RDONLY.\n");
+ goto fail_rb;
+ }
+
+ ssm_rbuff_set_acl(rb, ACL_FLOWDOWN);
+ if (ssm_rbuff_write(rb, 1) != -EFLOWDOWN) {
+ printf("Expected -EFLOWDOWN on FLOWDOWN.\n");
+ goto fail_rb;
+ }
+
+ if (ssm_rbuff_read(rb) != -EFLOWDOWN) {
+ printf("Expected -EFLOWDOWN on read with FLOWDOWN.\n");
+ goto fail_rb;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_open_close(void)
+{
+ struct ssm_rbuff * rb1;
+ struct ssm_rbuff * rb2;
+ pid_t pid;
+
+ TEST_START();
+
+ pid = getpid();
+
+ rb1 = ssm_rbuff_create(pid, 6);
+ if (rb1 == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ if (ssm_rbuff_write(rb1, 123) < 0) {
+ printf("Failed to write value.\n");
+ goto fail_rb1;
+ }
+
+ rb2 = ssm_rbuff_open(pid, 6);
+ if (rb2 == NULL) {
+ printf("Failed to open existing rbuff.\n");
+ goto fail_rb1;
+ }
+
+ if (ssm_rbuff_queued(rb2) != 1) {
+ printf("Expected 1 queued in opened rbuff, got %zu.\n",
+ ssm_rbuff_queued(rb2));
+ goto fail_rb2;
+ }
+
+ if (ssm_rbuff_read(rb2) != 123) {
+ printf("Failed to read from opened rbuff.\n");
+ goto fail_rb2;
+ }
+
+ ssm_rbuff_close(rb2);
+ ssm_rbuff_destroy(rb1);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb2:
+ ssm_rbuff_close(rb2);
+ fail_rb1:
+ ssm_rbuff_destroy(rb1);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+struct thread_args {
+ struct ssm_rbuff * rb;
+ int iterations;
+ int delay_us;
+};
+
+static void * writer_thread(void * arg)
+{
+ struct thread_args * args = (struct thread_args *) arg;
+ struct timespec delay = {0, 0};
+ int i;
+
+ delay.tv_nsec = args->delay_us * 1000L;
+
+ for (i = 0; i < args->iterations; ++i) {
+ while (ssm_rbuff_write(args->rb, i) < 0)
+ nanosleep(&delay, NULL);
+ }
+
+ return NULL;
+}
+
+static void * reader_thread(void * arg)
+{
+ struct thread_args * args = (struct thread_args *) arg;
+ struct timespec delay = {0, 0};
+ int i;
+ ssize_t val;
+
+ delay.tv_nsec = args->delay_us * 1000L;
+
+ for (i = 0; i < args->iterations; ++i) {
+ val = ssm_rbuff_read(args->rb);
+ while (val < 0) {
+ nanosleep(&delay, NULL);
+ val = ssm_rbuff_read(args->rb);
+ }
+ if (val != i) {
+ printf("Expected %d, got %zd.\n", i, val);
+ return (void *) -1;
+ }
+ }
+
+ return NULL;
+}
+
+static void * blocking_writer_thread(void * arg)
+{
+ struct thread_args * args = (struct thread_args *) arg;
+ int i;
+
+ for (i = 0; i < args->iterations; ++i) {
+ if (ssm_rbuff_write_b(args->rb, i, NULL) < 0)
+ return (void *) -1;
+ }
+
+ return NULL;
+}
+
+static void * blocking_reader_thread(void * arg)
+{
+ struct thread_args * args = (struct thread_args *) arg;
+ int i;
+ ssize_t val;
+
+ for (i = 0; i < args->iterations; ++i) {
+ val = ssm_rbuff_read_b(args->rb, NULL);
+ if (val < 0 || val != i) {
+ printf("Expected %d, got %zd.\n", i, val);
+ return (void *) -1;
+ }
+ }
+
+ return NULL;
+}
+
+static int test_ssm_rbuff_blocking(void)
+{
+ struct ssm_rbuff * rb;
+ pthread_t wthread;
+ pthread_t rthread;
+ struct thread_args args;
+ struct timespec delay = {0, 10 * MILLION};
+ void * ret_w;
+ void * ret_r;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 8);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ args.rb = rb;
+ args.iterations = 50;
+ args.delay_us = 0;
+
+ if (pthread_create(&rthread, NULL, blocking_reader_thread, &args)) {
+ printf("Failed to create reader thread.\n");
+ goto fail_rthread;
+ }
+
+ nanosleep(&delay, NULL);
+
+ if (pthread_create(&wthread, NULL, blocking_writer_thread, &args)) {
+ printf("Failed to create writer thread.\n");
+ pthread_cancel(rthread);
+ goto fail_wthread;
+ }
+
+ pthread_join(wthread, &ret_w);
+ pthread_join(rthread, &ret_r);
+
+ if (ret_w != NULL || ret_r != NULL) {
+ printf("Thread returned error.\n");
+ goto fail_ret;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_ret:
+ fail_wthread:
+ pthread_join(rthread, NULL);
+ fail_rthread:
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_blocking_timeout(void)
+{
+ struct ssm_rbuff * rb;
+ struct timespec abs_timeout;
+ struct timespec interval = {0, 100 * MILLION};
+ struct timespec start;
+ struct timespec end;
+ ssize_t ret;
+ long elapsed_ms;
+ size_t i;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 9);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &start);
+ ts_add(&start, &interval, &abs_timeout);
+
+ ret = ssm_rbuff_read_b(rb, &abs_timeout);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &end);
+
+ if (ret != -ETIMEDOUT) {
+ printf("Expected -ETIMEDOUT, got %zd.\n", ret);
+ goto fail_rb;
+ }
+
+ elapsed_ms = (end.tv_sec - start.tv_sec) * 1000L +
+ (end.tv_nsec - start.tv_nsec) / 1000000L;
+
+ if (elapsed_ms < 90 || elapsed_ms > 200) {
+ printf("Timeout took %ld ms, expected ~100 ms.\n",
+ elapsed_ms);
+ goto fail_rb;
+ }
+
+ for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) {
+ if (ssm_rbuff_write(rb, i) < 0) {
+ printf("Failed to fill buffer.\n");
+ goto fail_rb;
+ }
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &start);
+ ts_add(&start, &interval, &abs_timeout);
+
+ ret = ssm_rbuff_write_b(rb, 999, &abs_timeout);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &end);
+
+ if (ret != -ETIMEDOUT) {
+ printf("Expected -ETIMEDOUT on full buffer, got %zd.\n",
+ ret);
+ goto fail_rb;
+ }
+
+ elapsed_ms = (end.tv_sec - start.tv_sec) * 1000L +
+ (end.tv_nsec - start.tv_nsec) / 1000000L;
+
+ if (elapsed_ms < 90 || elapsed_ms > 200) {
+ printf("Write timeout took %ld ms, expected ~100 ms.\n",
+ elapsed_ms);
+ goto fail_rb;
+ }
+
+ while (ssm_rbuff_read(rb) >= 0)
+ ;
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ while (ssm_rbuff_read(rb) >= 0)
+ ;
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_blocking_flowdown(void)
+{
+ struct ssm_rbuff * rb;
+ struct timespec abs_timeout;
+ struct timespec now;
+ struct timespec interval = {5, 0};
+ ssize_t ret;
+ size_t i;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 10);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &now);
+ ts_add(&now, &interval, &abs_timeout);
+
+ ssm_rbuff_set_acl(rb, ACL_FLOWDOWN);
+
+ ret = ssm_rbuff_read_b(rb, &abs_timeout);
+ if (ret != -EFLOWDOWN) {
+ printf("Expected -EFLOWDOWN, got %zd.\n", ret);
+ goto fail_rb;
+ }
+
+ ssm_rbuff_set_acl(rb, ACL_RDWR);
+
+ for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) {
+ if (ssm_rbuff_write(rb, i) < 0) {
+ printf("Failed to fill buffer.\n");
+ goto fail_rb;
+ }
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &now);
+ ts_add(&now, &interval, &abs_timeout);
+
+ ssm_rbuff_set_acl(rb, ACL_FLOWDOWN);
+
+ ret = ssm_rbuff_write_b(rb, 999, &abs_timeout);
+ if (ret != -EFLOWDOWN) {
+ printf("Expected -EFLOWDOWN on write, got %zd.\n", ret);
+ goto fail_rb;
+ }
+
+ ssm_rbuff_set_acl(rb, ACL_RDWR);
+ while (ssm_rbuff_read(rb) >= 0)
+ ;
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ while (ssm_rbuff_read(rb) >= 0)
+ ;
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_ssm_rbuff_threaded(void)
+{
+ struct ssm_rbuff * rb;
+ pthread_t wthread;
+ pthread_t rthread;
+ struct thread_args args;
+ void * ret_w;
+ void * ret_r;
+
+ TEST_START();
+
+ rb = ssm_rbuff_create(getpid(), 7);
+ if (rb == NULL) {
+ printf("Failed to create rbuff.\n");
+ goto fail;
+ }
+
+ args.rb = rb;
+ args.iterations = 100;
+ args.delay_us = 100;
+
+ if (pthread_create(&wthread, NULL, writer_thread, &args)) {
+ printf("Failed to create writer thread.\n");
+ goto fail_rb;
+ }
+
+ if (pthread_create(&rthread, NULL, reader_thread, &args)) {
+ printf("Failed to create reader thread.\n");
+ pthread_cancel(wthread);
+ pthread_join(wthread, NULL);
+ goto fail_rb;
+ }
+
+ pthread_join(wthread, &ret_w);
+ pthread_join(rthread, &ret_r);
+
+ if (ret_w != NULL || ret_r != NULL) {
+ printf("Thread returned error.\n");
+ goto fail_rb;
+ }
+
+ ssm_rbuff_destroy(rb);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+
+ fail_rb:
+ ssm_rbuff_destroy(rb);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int rbuff_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_ssm_rbuff_create_destroy();
+ ret |= test_ssm_rbuff_write_read();
+ ret |= test_ssm_rbuff_read_empty();
+ ret |= test_ssm_rbuff_fill_drain();
+ ret |= test_ssm_rbuff_acl();
+ ret |= test_ssm_rbuff_open_close();
+ ret |= test_ssm_rbuff_threaded();
+ ret |= test_ssm_rbuff_blocking();
+ ret |= test_ssm_rbuff_blocking_timeout();
+ ret |= test_ssm_rbuff_blocking_flowdown();
+
+ return ret;
+}
diff --git a/src/lib/tests/CMakeLists.txt b/src/lib/tests/CMakeLists.txt
index 0e114548..23d01f9b 100644
--- a/src/lib/tests/CMakeLists.txt
+++ b/src/lib/tests/CMakeLists.txt
@@ -1,28 +1,31 @@
get_filename_component(PARENT_PATH ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
get_filename_component(PARENT_DIR ${PARENT_PATH} NAME)
+compute_test_prefix()
+
create_test_sourcelist(${PARENT_DIR}_tests test_suite.c
# Add new tests here
+ auth_test.c
+ auth_test_pqc.c
bitmap_test.c
btree_test.c
crc32_test.c
+ crypt_test.c
hash_test.c
+ kex_test.c
+ kex_test_pqc.c
md5_test.c
sha3_test.c
- shm_rbuff_test.c
+ sockets_test.c
time_test.c
+ tpm_test.c
)
-add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests})
+add_executable(${PARENT_DIR}_test ${${PARENT_DIR}_tests})
+disable_test_logging_for_target(${PARENT_DIR}_test)
target_link_libraries(${PARENT_DIR}_test ouroboros-common)
-add_dependencies(check ${PARENT_DIR}_test)
-
-set(tests_to_run ${${PARENT_DIR}_tests})
-remove(tests_to_run test_suite.c)
+add_dependencies(build_tests ${PARENT_DIR}_test)
-foreach (test ${tests_to_run})
- get_filename_component(test_name ${test} NAME_WE)
- add_test(${test_name} ${C_TEST_PATH}/${PARENT_DIR}_test ${test_name})
-endforeach (test)
+ouroboros_register_tests(TARGET ${PARENT_DIR}_test TESTS ${${PARENT_DIR}_tests})
diff --git a/src/lib/tests/auth_test.c b/src/lib/tests/auth_test.c
new file mode 100644
index 00000000..b3f09277
--- /dev/null
+++ b/src/lib/tests/auth_test.c
@@ -0,0 +1,548 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the authentication functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "config.h"
+
+#include <test/test.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/random.h>
+#include <ouroboros/utils.h>
+
+#include <test/certs.h>
+
+#define TEST_MSG_SIZE 1500
+
+static int test_auth_create_destroy_ctx(void)
+{
+ struct auth_ctx * ctx;
+
+ TEST_START();
+
+ ctx = auth_create_ctx();
+ if (ctx == NULL) {
+ printf("Failed to create auth context.\n");
+ goto fail_create;
+ }
+
+ auth_destroy_ctx(ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_crt(void)
+{
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(root_ca_crt_ec, &crt) < 0) {
+ printf("Failed to load certificate string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_crypt_get_pubkey_crt(void)
+{
+ void * pk;
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(signed_server_crt_ec, &crt) < 0) {
+ printf("Failed to load server certificate from string.\n");
+ goto fail_load;
+ }
+
+ if (crypt_get_pubkey_crt(crt, &pk) < 0) {
+ printf("Failed to get public key from certificate.\n");
+ goto fail_get_pubkey;
+ }
+
+ crypt_free_key(pk);
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_get_pubkey:
+ crypt_free_crt(crt);
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_check_crt_name(void)
+{
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(signed_server_crt_ec, &crt) < 0) {
+ printf("Failed to load certificate from string.\n");
+ goto fail_load;
+ }
+
+ if (crypt_check_crt_name(crt, "test-1.unittest.o7s") < 0) {
+ printf("Failed to verify correct name.\n");
+ goto fail_check;
+ }
+
+ if (crypt_check_crt_name(crt, "bogus.name") == 0) {
+ printf("Failed to detect incorrect name.\n");
+ goto fail_check;
+ }
+
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_check:
+ crypt_free_crt(crt);
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_privkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_privkey_str(server_pkp_ec, &key) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_pubkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_pubkey_str(server_pk_ec, &key) < 0) {
+ printf("Failed to load server public key from string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_crypt_check_pubkey_crt(void)
+{
+ void * pk;
+ void * crt_pk;
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(signed_server_crt_ec, &crt) < 0) {
+ printf("Failed to load public certificate from string.\n");
+ goto fail_crt;
+ }
+
+ if (crypt_load_pubkey_str(server_pk_ec, &pk) < 0) {
+ printf("Failed to load public key from string.\n");
+ goto fail_pubkey;
+ }
+
+ if (crypt_get_pubkey_crt(crt, &crt_pk) < 0) {
+ printf("Failed to get public key from certificate.\n");
+ goto fail_get_pubkey;
+ }
+
+ if (crypt_cmp_key(pk, crt_pk) != 0) {
+ printf("Public keys do not match .\n");
+ goto fail_check;
+ }
+
+
+ crypt_free_key(crt_pk);
+ crypt_free_key(pk);
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_check:
+ crypt_free_key(crt_pk);
+ fail_get_pubkey:
+ crypt_free_key(pk);
+ fail_pubkey:
+ crypt_free_crt(crt);
+ fail_crt:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_store_add(void)
+{
+ struct auth_ctx * ctx;
+ void * _root_ca_crt;
+
+ TEST_START();
+
+ ctx = auth_create_ctx();
+ if (ctx == NULL) {
+ printf("Failed to create auth context.\n");
+ goto fail_create;
+ }
+
+ if (crypt_load_crt_str(root_ca_crt_ec, &_root_ca_crt) < 0) {
+ printf("Failed to load root crt from string.\n");
+ goto fail_load;
+ }
+
+ if (auth_add_crt_to_store(ctx, _root_ca_crt) < 0) {
+ printf("Failed to add root crt to auth store.\n");
+ goto fail_add;
+ }
+
+ crypt_free_crt(_root_ca_crt);
+ auth_destroy_ctx(ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_add:
+ crypt_free_crt(_root_ca_crt);
+ fail_load:
+ crypt_free_crt(_root_ca_crt);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_verify_crt(void)
+{
+ struct auth_ctx * auth;
+ void * _server_crt;
+ void * _signed_server_crt;
+ void * _root_ca_crt;
+ void * _im_ca_crt;
+
+ TEST_START();
+
+ auth = auth_create_ctx();
+ if (auth == NULL) {
+ printf("Failed to create auth context.\n");
+ goto fail_create_ctx;
+ }
+
+ if (crypt_load_crt_str(server_crt_ec, &_server_crt) < 0) {
+ printf("Failed to load self-signed crt from string.\n");
+ goto fail_load_server_crt;
+ }
+
+ if (crypt_load_crt_str(signed_server_crt_ec, &_signed_server_crt) < 0) {
+ printf("Failed to load signed crt from string.\n");
+ goto fail_load_signed_server_crt;
+ }
+
+ if (crypt_load_crt_str(root_ca_crt_ec, &_root_ca_crt) < 0) {
+ printf("Failed to load root crt from string.\n");
+ goto fail_load_root_ca_crt;
+ }
+
+ if (crypt_load_crt_str(im_ca_crt_ec, &_im_ca_crt) < 0) {
+ printf("Failed to load intermediate crt from string.\n");
+ goto fail_load_im_ca_crt;
+ }
+
+ if (auth_add_crt_to_store(auth, _root_ca_crt) < 0) {
+ printf("Failed to add root ca crt to auth store.\n");
+ goto fail_verify;
+ }
+
+ if (auth_add_crt_to_store(auth, _im_ca_crt) < 0) {
+ printf("Failed to add intermediate ca crt to auth store.\n");
+ goto fail_verify;
+ }
+
+ if (auth_verify_crt(auth, _signed_server_crt) < 0) {
+ printf("Failed to verify signed crt with ca crt.\n");
+ goto fail_verify;
+ }
+
+ if (auth_verify_crt(auth, _server_crt) == 0) {
+ printf("Failed to detect untrusted crt.\n");
+ goto fail_verify;
+ }
+
+ crypt_free_crt(_im_ca_crt);
+ crypt_free_crt(_root_ca_crt);
+ crypt_free_crt(_signed_server_crt);
+ crypt_free_crt(_server_crt);
+
+ auth_destroy_ctx(auth);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ crypt_free_crt(_im_ca_crt);
+ fail_load_im_ca_crt:
+ crypt_free_crt(_root_ca_crt);
+ fail_load_root_ca_crt:
+ crypt_free_crt(_signed_server_crt);
+ fail_load_signed_server_crt:
+ crypt_free_crt(_server_crt);
+ fail_load_server_crt:
+ auth_destroy_ctx(auth);
+ fail_create_ctx:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int test_auth_sign(void)
+{
+ uint8_t buf[TEST_MSG_SIZE];
+ void * pkp;
+ void * pk;
+ buffer_t msg;
+ buffer_t sig;
+
+ TEST_START();
+
+ msg.data = buf;
+ msg.len = sizeof(buf);
+
+ if (random_buffer(msg.data, msg.len) < 0) {
+ printf("Failed to generate random message.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_privkey_str(server_pkp_ec, &pkp) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_pubkey_str(server_pk_ec, &pk) < 0) {
+ printf("Failed to load public key.\n");
+ goto fail_pubkey;
+ }
+
+ if (auth_sign(pkp, 0, msg, &sig) < 0) {
+ printf("Failed to sign message.\n");
+ goto fail_sign;
+ }
+
+ if (auth_verify_sig(pk, 0, msg, sig) < 0) {
+ printf("Failed to verify signature.\n");
+ goto fail_verify;
+ }
+
+ freebuf(sig);
+
+ crypt_free_key(pk);
+ crypt_free_key(pkp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ freebuf(sig);
+ fail_sign:
+ crypt_free_key(pk);
+ fail_pubkey:
+ crypt_free_key(pkp);
+ fail_init:
+ return TEST_RC_FAIL;
+}
+
+int test_auth_bad_signature(void)
+{
+ uint8_t buf[TEST_MSG_SIZE];
+ void * pkp;
+ void * pk;
+ buffer_t msg;
+ buffer_t sig;
+ buffer_t fake_sig;
+
+ TEST_START();
+
+ msg.data = buf;
+ msg.len = sizeof(buf);
+
+ if (random_buffer(msg.data, msg.len) < 0) {
+ printf("Failed to generate random message.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_privkey_str(server_pkp_ec, &pkp) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_pubkey_str(server_pk_ec, &pk) < 0) {
+ printf("Failed to load public key.\n");
+ goto fail_pubkey;
+ }
+
+ if (auth_sign(pkp, 0, msg, &sig) < 0) {
+ printf("Failed to sign message.\n");
+ goto fail_sign;
+ }
+
+ fake_sig.data = malloc(sig.len);
+ if (fake_sig.data == NULL) {
+ printf("Failed to allocate memory for fake signature.\n");
+ goto fail_malloc;
+ }
+
+ fake_sig.len = sig.len;
+ if (random_buffer(fake_sig.data, fake_sig.len) < 0) {
+ printf("Failed to generate random fake signature.\n");
+ goto fail_malloc;
+ }
+
+ if (auth_verify_sig(pk, 0, msg, fake_sig) == 0) {
+ printf("Failed to detect bad signature.\n");
+ goto fail_verify;
+ }
+
+ freebuf(fake_sig);
+ freebuf(sig);
+
+ crypt_free_key(pk);
+ crypt_free_key(pkp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ freebuf(fake_sig);
+ fail_malloc:
+ freebuf(sig);
+ fail_sign:
+ crypt_free_key(pk);
+ fail_pubkey:
+ crypt_free_key(pkp);
+ fail_init:
+ return TEST_RC_FAIL;
+}
+
+#define SSC_BUF_SIZE 4096 /* OpenSSL version my return different lengths */
+int test_crt_str(void)
+{
+ char str[SSC_BUF_SIZE];
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(signed_server_crt_ec, &crt) < 0) {
+ printf("Failed to load certificate from string.\n");
+ goto fail_load;
+ }
+
+ if (crypt_crt_str(crt, str) < 0) {
+ printf("Failed to convert certificate to string.\n");
+ goto fail_to_str;
+ }
+
+ printf("Certificate string:\n%s\n", str);
+
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+
+ fail_to_str:
+ crypt_free_crt(crt);
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int auth_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_auth_create_destroy_ctx();
+#ifdef HAVE_OPENSSL
+ ret |= test_load_free_crt();
+ ret |= test_check_crt_name();
+ ret |= test_crypt_get_pubkey_crt();
+ ret |= test_load_free_privkey();
+ ret |= test_load_free_pubkey();
+ ret |= test_crypt_check_pubkey_crt();
+ ret |= test_store_add();
+ ret |= test_verify_crt();
+ ret |= test_auth_sign();
+ ret |= test_auth_bad_signature();
+ ret |= test_crt_str();
+#else
+ (void) test_load_free_crt;
+ (void) test_check_crt_name;
+ (void) test_crypt_get_pubkey_crt;
+ (void) test_load_free_privkey;
+ (void) test_load_free_pubkey;
+ (void) test_crypt_check_pubkey_crt;
+ (void) test_store_add;
+ (void) test_verify_crt;
+ (void) test_auth_sign;
+ (void) test_auth_bad_signature;
+ (void) test_crt_str;
+
+ ret = TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/lib/tests/auth_test_pqc.c b/src/lib/tests/auth_test_pqc.c
new file mode 100644
index 00000000..349636d2
--- /dev/null
+++ b/src/lib/tests/auth_test_pqc.c
@@ -0,0 +1,356 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test of the PQC authentication functions (ML-DSA-65)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "config.h"
+
+#include <test/test.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/random.h>
+#include <ouroboros/utils.h>
+
+#include <test/certs_pqc.h>
+
+#define TEST_MSG_SIZE 1500
+
+static int test_auth_create_destroy_ctx(void)
+{
+ struct auth_ctx * ctx;
+
+ TEST_START();
+
+ ctx = auth_create_ctx();
+ if (ctx == NULL) {
+ printf("Failed to create auth context.\n");
+ goto fail_create;
+ }
+
+ auth_destroy_ctx(ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_crt(void)
+{
+ void * crt;
+
+ TEST_START();
+
+ if (crypt_load_crt_str(root_ca_crt_ml, &crt) < 0) {
+ printf("Failed to load root crt from string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_crt(crt);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_privkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_privkey_str(server_pkp_ml, &key) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_load_free_pubkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_pubkey_str(server_pk_ml, &key) < 0) {
+ printf("Failed to load server public key from string.\n");
+ goto fail_load;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_load:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_verify_crt(void)
+{
+ struct auth_ctx * auth;
+ void * _server_crt;
+ void * _signed_server_crt;
+ void * _root_ca_crt;
+ void * _im_ca_crt;
+
+ TEST_START();
+
+ auth = auth_create_ctx();
+ if (auth == NULL) {
+ printf("Failed to create auth context.\n");
+ goto fail_create_ctx;
+ }
+
+ if (crypt_load_crt_str(server_crt_ml, &_server_crt) < 0) {
+ printf("Failed to load self-signed crt from string.\n");
+ goto fail_load_server_crt;
+ }
+
+ if (crypt_load_crt_str(signed_server_crt_ml, &_signed_server_crt) < 0) {
+ printf("Failed to load signed crt from string.\n");
+ goto fail_load_signed_server_crt;
+ }
+
+ if (crypt_load_crt_str(root_ca_crt_ml, &_root_ca_crt) < 0) {
+ printf("Failed to load root crt from string.\n");
+ goto fail_load_root_ca_crt;
+ }
+
+ if (crypt_load_crt_str(im_ca_crt_ml, &_im_ca_crt) < 0) {
+ printf("Failed to load intermediate crt from string.\n");
+ goto fail_load_im_ca_crt;
+ }
+
+ if (auth_add_crt_to_store(auth, _root_ca_crt) < 0) {
+ printf("Failed to add root ca crt to auth store.\n");
+ goto fail_verify;
+ }
+
+ if (auth_add_crt_to_store(auth, _im_ca_crt) < 0) {
+ printf("Failed to add intermediate ca crt to auth store.\n");
+ goto fail_verify;
+ }
+
+ if (auth_verify_crt(auth, _signed_server_crt) < 0) {
+ printf("Failed to verify signed crt with ca crt.\n");
+ goto fail_verify;
+ }
+
+ if (auth_verify_crt(auth, _server_crt) == 0) {
+ printf("Failed to detect untrusted crt.\n");
+ goto fail_verify;
+ }
+
+ crypt_free_crt(_im_ca_crt);
+ crypt_free_crt(_root_ca_crt);
+ crypt_free_crt(_signed_server_crt);
+ crypt_free_crt(_server_crt);
+
+ auth_destroy_ctx(auth);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ crypt_free_crt(_im_ca_crt);
+ fail_load_im_ca_crt:
+ crypt_free_crt(_root_ca_crt);
+ fail_load_root_ca_crt:
+ crypt_free_crt(_signed_server_crt);
+ fail_load_signed_server_crt:
+ crypt_free_crt(_server_crt);
+ fail_load_server_crt:
+ auth_destroy_ctx(auth);
+ fail_create_ctx:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_auth_sign(void)
+{
+ uint8_t buf[TEST_MSG_SIZE];
+ void * pkp;
+ void * pk;
+ buffer_t msg;
+ buffer_t sig;
+
+ TEST_START();
+
+ msg.data = buf;
+ msg.len = sizeof(buf);
+
+ if (random_buffer(msg.data, msg.len) < 0) {
+ printf("Failed to generate random message.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_privkey_str(server_pkp_ml, &pkp) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_pubkey_str(server_pk_ml, &pk) < 0) {
+ printf("Failed to load public key from string.\n");
+ goto fail_pubkey;
+ }
+
+ if (auth_sign(pkp, 0, msg, &sig) < 0) {
+ printf("Failed to sign message.\n");
+ goto fail_sign;
+ }
+
+ if (auth_verify_sig(pk, 0, msg, sig) < 0) {
+ printf("Failed to verify signature.\n");
+ goto fail_verify;
+ }
+
+ freebuf(sig);
+
+ crypt_free_key(pk);
+ crypt_free_key(pkp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ freebuf(sig);
+ fail_sign:
+ crypt_free_key(pk);
+ fail_pubkey:
+ crypt_free_key(pkp);
+ fail_init:
+ return TEST_RC_FAIL;
+}
+
+static int test_auth_bad_signature(void)
+{
+ uint8_t buf[TEST_MSG_SIZE];
+ void * pkp;
+ void * pk;
+ buffer_t msg;
+ buffer_t sig;
+ buffer_t fake_sig;
+
+ TEST_START();
+
+ msg.data = buf;
+ msg.len = sizeof(buf);
+
+ if (random_buffer(msg.data, msg.len) < 0) {
+ printf("Failed to generate random message.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_privkey_str(server_pkp_ml, &pkp) < 0) {
+ printf("Failed to load server key pair from string.\n");
+ goto fail_init;
+ }
+
+ if (crypt_load_pubkey_str(server_pk_ml, &pk) < 0) {
+ printf("Failed to load public key from string.\n");
+ goto fail_pubkey;
+ }
+
+ if (auth_sign(pkp, 0, msg, &sig) < 0) {
+ printf("Failed to sign message.\n");
+ goto fail_sign;
+ }
+
+ fake_sig.data = malloc(sig.len);
+ if (fake_sig.data == NULL) {
+ printf("Failed to allocate memory for fake signature.\n");
+ goto fail_malloc;
+ }
+
+ fake_sig.len = sig.len;
+ if (random_buffer(fake_sig.data, fake_sig.len) < 0) {
+ printf("Failed to generate random fake signature.\n");
+ goto fail_malloc;
+ }
+
+ if (auth_verify_sig(pk, 0, msg, fake_sig) == 0) {
+ printf("Failed to detect bad ML-DSA-65 signature.\n");
+ goto fail_verify;
+ }
+
+ freebuf(fake_sig);
+ freebuf(sig);
+
+ crypt_free_key(pk);
+ crypt_free_key(pkp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_verify:
+ freebuf(fake_sig);
+ fail_malloc:
+ freebuf(sig);
+ fail_sign:
+ crypt_free_key(pk);
+ fail_pubkey:
+ crypt_free_key(pkp);
+ fail_init:
+ return TEST_RC_FAIL;
+}
+
+int auth_test_pqc(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+#ifdef HAVE_OPENSSL_PQC
+ ret |= test_auth_create_destroy_ctx();
+ ret |= test_load_free_crt();
+ ret |= test_load_free_privkey();
+ ret |= test_load_free_pubkey();
+ ret |= test_verify_crt();
+ ret |= test_auth_sign();
+ ret |= test_auth_bad_signature();
+#else
+ (void) test_auth_create_destroy_ctx;
+ (void) test_load_free_crt;
+ (void) test_load_free_privkey;
+ (void) test_load_free_pubkey;
+ (void) test_verify_crt;
+ (void) test_auth_sign;
+ (void) test_auth_bad_signature;
+
+ ret = TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/lib/tests/crypt_test.c b/src/lib/tests/crypt_test.c
new file mode 100644
index 00000000..e250ad2a
--- /dev/null
+++ b/src/lib/tests/crypt_test.c
@@ -0,0 +1,459 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the cryptography functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "config.h"
+
+#include <test/test.h>
+#include <ouroboros/random.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/utils.h>
+
+#include <stdio.h>
+
+#define TEST_PACKET_SIZE 1500
+
+extern const uint16_t crypt_supported_nids[];
+extern const uint16_t md_supported_nids[];
+
+static int test_crypt_create_destroy(void)
+{
+ struct crypt_ctx * ctx;
+ uint8_t key[SYMMKEYSZ];
+ struct crypt_sk sk = {
+ .nid = NID_aes_256_gcm,
+ .key = key,
+ .rot_bit = KEY_ROTATION_BIT
+ };
+
+ TEST_START();
+
+ memset(key, 0, sizeof(key));
+
+ ctx = crypt_create_ctx(&sk);
+ if (ctx == NULL) {
+ printf("Failed to initialize cryptography.\n");
+ goto fail;
+ }
+
+ crypt_destroy_ctx(ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_crypt_encrypt_decrypt(int nid)
+{
+ uint8_t pkt[TEST_PACKET_SIZE];
+ struct crypt_ctx * ctx;
+ uint8_t key[SYMMKEYSZ];
+ struct crypt_sk sk = {
+ .nid = NID_aes_256_gcm,
+ .key = key,
+ .rot_bit = KEY_ROTATION_BIT
+ };
+ buffer_t in;
+ buffer_t out;
+ buffer_t out2;
+ const char * cipher;
+
+ cipher = crypt_nid_to_str(nid);
+ TEST_START("(%s)", cipher);
+
+ if (random_buffer(key, sizeof(key)) < 0) {
+ printf("Failed to generate random key.\n");
+ goto fail_init;
+ }
+
+ if (random_buffer(pkt, sizeof(pkt)) < 0) {
+ printf("Failed to generate random data.\n");
+ goto fail_init;
+ }
+
+ ctx = crypt_create_ctx(&sk);
+ if (ctx == NULL) {
+ printf("Failed to initialize cryptography.\n");
+ goto fail_init;
+ }
+
+ in.len = sizeof(pkt);
+ in.data = pkt;
+
+ if (crypt_encrypt(ctx, in, &out) < 0) {
+ printf("Encryption failed.\n");
+ goto fail_encrypt;
+ }
+
+ if (out.len < in.len) {
+ printf("Encryption returned too little data.\n");
+ goto fail_encrypt;
+ }
+
+ if (crypt_decrypt(ctx, out, &out2) < 0) {
+ printf("Decryption failed.\n");
+ goto fail_decrypt;
+ }
+
+ if (out2.len != in.len) {
+ printf("Decrypted data length does not match original.\n");
+ goto fail_chk;
+ }
+
+ if (memcmp(in.data, out2.data, in.len) != 0) {
+ printf("Decrypted data does not match original.\n");
+ goto fail_chk;
+ }
+
+ crypt_destroy_ctx(ctx);
+ freebuf(out2);
+ freebuf(out);
+
+ TEST_SUCCESS("(%s)", cipher);
+
+ return TEST_RC_SUCCESS;
+ fail_chk:
+ freebuf(out2);
+ fail_decrypt:
+ freebuf(out);
+ fail_encrypt:
+ crypt_destroy_ctx(ctx);
+ fail_init:
+ TEST_FAIL("(%s)", cipher);
+ return TEST_RC_FAIL;
+}
+
+static int test_encrypt_decrypt_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; crypt_supported_nids[i] != NID_undef; i++)
+ ret |= test_crypt_encrypt_decrypt(crypt_supported_nids[i]);
+
+ return ret;
+}
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+
+static int test_cipher_nid_values(void)
+{
+ int i;
+
+ TEST_START();
+
+ /* Loop over all supported ciphers and verify NIDs match OpenSSL's */
+ for (i = 0; crypt_supported_nids[i] != NID_undef; i++) {
+ uint16_t our_nid = crypt_supported_nids[i];
+ const char * str = crypt_nid_to_str(our_nid);
+ const EVP_CIPHER * cipher;
+ int openssl_nid;
+
+ if (str == NULL) {
+ printf("crypt_nid_to_str failed for NID %u\n", our_nid);
+ goto fail;
+ }
+
+ cipher = EVP_get_cipherbyname(str);
+ if (cipher == NULL) {
+ printf("OpenSSL doesn't recognize cipher '%s'\n", str);
+ goto fail;
+ }
+
+ openssl_nid = EVP_CIPHER_nid(cipher);
+
+ if (our_nid != openssl_nid) {
+ printf("NID mismatch for '%s': ours=%u, OpenSSL=%d\n",
+ str, our_nid, openssl_nid);
+ goto fail;
+ }
+
+ /* Test reverse conversion */
+ if (crypt_str_to_nid(str) != our_nid) {
+ printf("crypt_str_to_nid failed for '%s'\n", str);
+ goto fail;
+ }
+ }
+
+ /* Test error cases */
+ if (crypt_str_to_nid("invalid") != NID_undef) {
+ printf("crypt_str_to_nid: no NID_undef for invalid.\n");
+ goto fail;
+ }
+
+ if (crypt_nid_to_str(9999) != NULL) {
+ printf("crypt_nid_to_str should return NULL for invalid NID\n");
+ goto fail;
+ }
+
+ if (crypt_str_to_nid(NULL) != NID_undef) {
+ printf("crypt_str_to_nid should return NID_undef for NULL\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_md_nid_values(void)
+{
+ int i;
+
+ TEST_START();
+
+ for (i = 0; md_supported_nids[i] != NID_undef; i++) {
+ uint16_t our_nid = md_supported_nids[i];
+ const EVP_MD * md;
+ int openssl_nid;
+
+ md = EVP_get_digestbynid(our_nid);
+ if (md == NULL) {
+ printf("OpenSSL doesn't recognize NID %u\n", our_nid);
+ goto fail;
+ }
+
+ openssl_nid = EVP_MD_nid(md);
+ if (our_nid != openssl_nid) {
+ printf("NID mismatch: ours=%u, OpenSSL=%d\n",
+ our_nid, openssl_nid);
+ goto fail;
+ }
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+#endif
+
+static int test_key_rotation(void)
+{
+ uint8_t pkt[TEST_PACKET_SIZE];
+ struct crypt_ctx * tx_ctx;
+ struct crypt_ctx * rx_ctx;
+ uint8_t key[SYMMKEYSZ];
+ struct crypt_sk sk = {
+ .nid = NID_aes_256_gcm,
+ .key = key,
+ .rot_bit = 7
+ };
+ buffer_t in;
+ buffer_t enc;
+ buffer_t dec;
+ uint32_t i;
+ uint32_t threshold;
+
+ TEST_START();
+
+ if (random_buffer(key, sizeof(key)) < 0) {
+ printf("Failed to generate random key.\n");
+ goto fail;
+ }
+
+ if (random_buffer(pkt, sizeof(pkt)) < 0) {
+ printf("Failed to generate random data.\n");
+ goto fail;
+ }
+
+ tx_ctx = crypt_create_ctx(&sk);
+ if (tx_ctx == NULL) {
+ printf("Failed to create TX context.\n");
+ goto fail;
+ }
+
+ rx_ctx = crypt_create_ctx(&sk);
+ if (rx_ctx == NULL) {
+ printf("Failed to create RX context.\n");
+ goto fail_tx;
+ }
+
+ in.len = sizeof(pkt);
+ in.data = pkt;
+
+ threshold = (1U << sk.rot_bit);
+
+ /* Encrypt and decrypt across multiple rotations */
+ for (i = 0; i < threshold * 3; i++) {
+ if (crypt_encrypt(tx_ctx, in, &enc) < 0) {
+ printf("Encryption failed at packet %u.\n", i);
+ goto fail_rx;
+ }
+
+ if (crypt_decrypt(rx_ctx, enc, &dec) < 0) {
+ printf("Decryption failed at packet %u.\n", i);
+ freebuf(enc);
+ goto fail_rx;
+ }
+
+ if (dec.len != in.len ||
+ memcmp(in.data, dec.data, in.len) != 0) {
+ printf("Data mismatch at packet %u.\n", i);
+ freebuf(dec);
+ freebuf(enc);
+ goto fail_rx;
+ }
+
+ freebuf(dec);
+ freebuf(enc);
+ }
+
+ crypt_destroy_ctx(rx_ctx);
+ crypt_destroy_ctx(tx_ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_rx:
+ crypt_destroy_ctx(rx_ctx);
+ fail_tx:
+ crypt_destroy_ctx(tx_ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_key_phase_bit(void)
+{
+ uint8_t pkt[TEST_PACKET_SIZE];
+ struct crypt_ctx * ctx;
+ uint8_t key[SYMMKEYSZ];
+ struct crypt_sk sk = {
+ .nid = NID_aes_256_gcm,
+ .key = key,
+ .rot_bit = 7
+ };
+ buffer_t in;
+ buffer_t out;
+ uint32_t count;
+ uint32_t threshold;
+ uint8_t phase_before;
+ uint8_t phase_after;
+ int ivsz;
+
+ TEST_START();
+
+ if (random_buffer(key, sizeof(key)) < 0) {
+ printf("Failed to generate random key.\n");
+ goto fail;
+ }
+
+ if (random_buffer(pkt, sizeof(pkt)) < 0) {
+ printf("Failed to generate random data.\n");
+ goto fail;
+ }
+
+ ctx = crypt_create_ctx(&sk);
+ if (ctx == NULL) {
+ printf("Failed to initialize cryptography.\n");
+ goto fail;
+ }
+
+ ivsz = crypt_get_ivsz(ctx);
+ if (ivsz <= 0) {
+ printf("Invalid IV size.\n");
+ goto fail_ctx;
+ }
+
+ in.len = sizeof(pkt);
+ in.data = pkt;
+
+ /* Encrypt packets up to just before rotation threshold */
+ threshold = (1U << sk.rot_bit);
+
+ /* Encrypt threshold - 1 packets (indices 0 to threshold-2) */
+ for (count = 0; count < threshold - 1; count++) {
+ if (crypt_encrypt(ctx, in, &out) < 0) {
+ printf("Encryption failed at count %u.\n", count);
+ goto fail_ctx;
+ }
+ freebuf(out);
+ }
+
+ /* Packet at index threshold-1: phase should still be initial */
+ if (crypt_encrypt(ctx, in, &out) < 0) {
+ printf("Encryption failed before rotation.\n");
+ goto fail_ctx;
+ }
+ phase_before = (out.data[0] & 0x80) ? 1 : 0;
+ freebuf(out);
+
+ /* Packet at index threshold: phase should have toggled */
+ if (crypt_encrypt(ctx, in, &out) < 0) {
+ printf("Encryption failed at rotation threshold.\n");
+ goto fail_ctx;
+ }
+ phase_after = (out.data[0] & 0x80) ? 1 : 0;
+ freebuf(out);
+
+ /* Phase bit should have toggled */
+ if (phase_before == phase_after) {
+ printf("Phase bit did not toggle: before=%u, after=%u.\n",
+ phase_before, phase_after);
+ goto fail_ctx;
+ }
+
+ crypt_destroy_ctx(ctx);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_ctx:
+ crypt_destroy_ctx(ctx);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int crypt_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_crypt_create_destroy();
+ ret |= test_encrypt_decrypt_all();
+#ifdef HAVE_OPENSSL
+ ret |= test_cipher_nid_values();
+ ret |= test_md_nid_values();
+ ret |= test_key_rotation();
+ ret |= test_key_phase_bit();
+#else
+ (void) test_key_rotation;
+ (void) test_key_phase_bit;
+
+ return TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/lib/tests/hash_test.c b/src/lib/tests/hash_test.c
index 970d9185..fb428b47 100644
--- a/src/lib/tests/hash_test.c
+++ b/src/lib/tests/hash_test.c
@@ -21,7 +21,7 @@
*/
#include <ouroboros/hash.h>
-#include <ouroboros/test.h>
+#include <test/test.h>
#include <stdlib.h>
#include <stdint.h>
diff --git a/src/lib/tests/kex_test.c b/src/lib/tests/kex_test.c
new file mode 100644
index 00000000..0a588550
--- /dev/null
+++ b/src/lib/tests/kex_test.c
@@ -0,0 +1,844 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test of the key exchange functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "config.h"
+
+#include <test/test.h>
+#include <ouroboros/utils.h>
+#include <ouroboros/crypt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#endif
+
+/* Test configuration strings */
+#define KEX_CONFIG_CUSTOM \
+ "kex=X25519\n"
+
+#define KEX_CONFIG_NONE \
+ "none\n"
+
+#define KEX_CONFIG_WHITESPACE \
+ "# Comment line\n" \
+ "kex = X448" \
+ "\n" \
+ "# Another comment\n"
+
+#define KEX_CONFIG_CIPHER \
+ "kex=X25519\n" \
+ "cipher=chacha20-poly1305\n"
+
+#define KEX_CONFIG_DIGEST \
+ "kex=X25519\n" \
+ "digest=sha384\n"
+
+/* Test key material for key loading tests */
+#define X25519_PRIVKEY_PEM \
+ "-----BEGIN PRIVATE KEY-----\n" \
+ "MC4CAQAwBQYDK2VuBCIEIJDd3+/0k2IZlaH5sZ9Z2e5J8dV2U0nsXaSUm70ZaMhL\n" \
+ "-----END PRIVATE KEY-----\n"
+
+#define X25519_PUBKEY_PEM \
+ "-----BEGIN PUBLIC KEY-----\n" \
+ "MCowBQYDK2VuAyEAKYLIycSZtLFlwAX07YWWgBAYhEnRxHfgK1TVw9+mtBs=\n" \
+ "-----END PUBLIC KEY-----\n"
+
+/* Helper macro to open string constant as FILE stream */
+#define FMEMOPEN_STR(str) fmemopen((void *) (str), strlen(str), "r")
+
+extern const uint16_t kex_supported_nids[];
+
+int parse_sec_config(struct sec_config * cfg,
+ FILE * fp);
+
+static int test_kex_create_destroy(void)
+{
+ struct sec_config cfg;
+
+ TEST_START();
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.x.nid = NID_X9_62_prime256v1;
+ cfg.x.str = kex_nid_to_str(cfg.x.nid);
+ cfg.c.nid = NID_aes_256_gcm;
+ cfg.c.str = crypt_nid_to_str(cfg.c.nid);
+
+ if (cfg.x.nid == NID_undef || cfg.c.nid == NID_undef) {
+ printf("Failed to initialize kex config.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_dh_pkp_create_destroy(void)
+{
+ struct sec_config kex;
+ void * pkp;
+ uint8_t buf[MSGBUFSZ];
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, "prime256v1");
+
+ if (kex_pkp_create(&kex, &pkp, buf) < 0) {
+ printf("Failed to create DH PKP.\n");
+ goto fail;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_get_algo_from_pk(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp;
+ buffer_t pk;
+ ssize_t len;
+ uint8_t buf[MSGBUFSZ];
+ char extracted_algo[256];
+
+ TEST_START("(%s)", algo);
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp, buf);
+ if (len < 0) {
+ printf("Failed to create key pair.\n");
+ goto fail;
+ }
+
+ pk.len = (size_t) len;
+ pk.data = buf;
+
+ /* Use raw decode for hybrid KEMs, DER for others */
+ if (IS_HYBRID_KEM(algo)) {
+ if (kex_get_algo_from_pk_raw(pk, extracted_algo) < 0) {
+ printf("Failed to extract algo from pk.\n");
+ goto fail_pkp;
+ }
+ } else {
+ if (kex_get_algo_from_pk_der(pk, extracted_algo) < 0) {
+ printf("Failed to extract algo from pk.\n");
+ goto fail_pkp;
+ }
+ }
+
+ /* All algorithms should now return the specific group name */
+ if (strcmp(extracted_algo, algo) != 0) {
+ printf("Algo mismatch: expected %s, got %s.\n",
+ algo, extracted_algo);
+ goto fail_pkp;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp:
+ kex_pkp_destroy(pkp);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_get_algo_from_pk_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+ ret |= test_kex_get_algo_from_pk(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_dhe_derive(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp1;
+ void * pkp2;
+ buffer_t pk1;
+ buffer_t pk2;
+ ssize_t len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t s1[SYMMKEYSZ];
+ uint8_t s2[SYMMKEYSZ];
+
+ TEST_START("(%s)", algo);
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp1, buf1);
+ if (len < 0) {
+ printf("Failed to create first key pair for %s.\n", algo);
+ goto fail;
+ }
+
+ pk1.len = (size_t) len;
+ pk1.data = buf1;
+
+ len = kex_pkp_create(&kex, &pkp2, buf2);
+ if (len < 0) {
+ printf("Failed to create second key pair for %s.\n", algo);
+ goto fail_pkp1;
+ }
+
+ pk2.len = (size_t) len;
+ pk2.data = buf2;
+
+ if (kex_dhe_derive(&kex, pkp1, pk2, s1) < 0) {
+ printf("Failed to derive first key for %s.\n", algo);
+ goto fail_pkp2;
+ }
+
+ if (kex_dhe_derive(&kex, pkp2, pk1, s2) < 0) {
+ printf("Failed to derive second key for %s.\n", algo);
+ goto fail_pkp2;
+ }
+
+ if (memcmp(s1, s2, SYMMKEYSZ) != 0) {
+ printf("Derived keys do not match for %s.\n", algo);
+ goto fail_pkp2;
+ }
+
+ kex_pkp_destroy(pkp2);
+ kex_pkp_destroy(pkp1);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp2:
+ kex_pkp_destroy(pkp2);
+ fail_pkp1:
+ kex_pkp_destroy(pkp1);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_validate_algo(void)
+{
+ TEST_START();
+
+ if (kex_validate_algo("prime256v1") != 0) {
+ printf("prime256v1 should be valid.\n");
+ goto fail;
+ }
+
+ if (kex_validate_algo("X25519") != 0) {
+ printf("X25519 should be valid.\n");
+ goto fail;
+ }
+
+#ifdef HAVE_OPENSSL_PQC
+ if (kex_validate_algo("ML-KEM-768") != 0) {
+ printf("ML-KEM-768 should be valid.\n");
+ goto fail;
+ }
+#endif
+
+ if (kex_validate_algo("ffdhe2048") != 0) {
+ printf("ffdhe2048 should be valid.\n");
+ goto fail;
+ }
+
+ if (kex_validate_algo("invalid_algo") == 0) {
+ printf("invalid_algo should be rejected.\n");
+ goto fail;
+ }
+
+ if (kex_validate_algo("rsa2048") == 0) {
+ printf("rsa2048 should be rejected.\n");
+ goto fail;
+ }
+
+ if (kex_validate_algo(NULL) == 0) {
+ printf("NULL should be rejected.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_dhe_corrupted_pubkey(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp;
+ buffer_t pk;
+ ssize_t len;
+ uint8_t buf[MSGBUFSZ];
+ uint8_t s[SYMMKEYSZ];
+
+ TEST_START("(%s)", algo);
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp, buf);
+ if (len < 0) {
+ printf("Failed to create key pair.\n");
+ goto fail;
+ }
+
+ pk.len = (size_t) len;
+ pk.data = buf;
+
+ /* Corrupt the public key */
+ buf[0] ^= 0xFF;
+ buf[len - 1] ^= 0xFF;
+
+ if (kex_dhe_derive(&kex, pkp, pk, s) == 0) {
+ printf("Should fail with corrupted public key.\n");
+ goto fail_pkp;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp:
+ kex_pkp_destroy(pkp);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_dhe_wrong_algo(void)
+{
+ struct sec_config kex1;
+ struct sec_config kex2;
+ void * pkp1;
+ void * pkp2;
+ buffer_t pk2;
+ ssize_t len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t s[SYMMKEYSZ];
+ const char * algo1 = "X25519";
+ const char * algo2 = "X448";
+
+ TEST_START("(%s vs %s)", algo1, algo2);
+
+ memset(&kex1, 0, sizeof(kex1));
+ memset(&kex2, 0, sizeof(kex2));
+ SET_KEX_ALGO(&kex1, algo1);
+ SET_KEX_ALGO(&kex2, algo2);
+
+ if (kex_pkp_create(&kex1, &pkp1, buf1) < 0) {
+ printf("Failed to create first key pair.\n");
+ goto fail;
+ }
+
+ len = kex_pkp_create(&kex2, &pkp2, buf2);
+ if (len < 0) {
+ printf("Failed to create second key pair.\n");
+ goto fail_pkp1;
+ }
+
+ pk2.len = (size_t) len;
+ pk2.data = buf2;
+
+ /* Try to derive with mismatched algorithms */
+ if (kex_dhe_derive(&kex1, pkp1, pk2, s) == 0) {
+ printf("Should fail with mismatched algorithms.\n");
+ goto fail_pkp2;
+ }
+
+ kex_pkp_destroy(pkp2);
+ kex_pkp_destroy(pkp1);
+
+ TEST_SUCCESS("(%s vs %s)", algo1, algo2);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp2:
+ kex_pkp_destroy(pkp2);
+ fail_pkp1:
+ kex_pkp_destroy(pkp1);
+ fail:
+ TEST_FAIL("(%s vs %s)", algo1, algo2);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_load_dhe_privkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_privkey_str(X25519_PRIVKEY_PEM, &key) < 0) {
+ printf("Failed to load X25519 private key.\n");
+ goto fail;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_load_dhe_pubkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_pubkey_str(X25519_PUBKEY_PEM, &key) < 0) {
+ printf("Failed to load X25519 public key.\n");
+ goto fail;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+#ifdef HAVE_OPENSSL
+#include <openssl/obj_mac.h>
+
+static int test_kex_nid_values(void)
+{
+ int i;
+
+ TEST_START();
+
+ /* Verify all KEX algorithm NIDs match OpenSSL's */
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ uint16_t our_nid = kex_supported_nids[i];
+ const char * kex_name;
+ int openssl_nid;
+
+ kex_name = kex_nid_to_str(our_nid);
+ if (kex_name == NULL) {
+ printf("kex_nid_to_str failed for NID %u\n", our_nid);
+ goto fail;
+ }
+
+ /* Test reverse conversion */
+ if (kex_str_to_nid(kex_name) != our_nid) {
+ printf("kex_str_to_nid failed for '%s'\n", kex_name);
+ goto fail;
+ }
+
+ /* Get OpenSSL's NID for this name */
+ openssl_nid = OBJ_txt2nid(kex_name);
+ if (openssl_nid != NID_undef) {
+ /* OpenSSL recognizes this algorithm */
+ if (our_nid != openssl_nid) {
+ printf("NID mismatch for '%s': "
+ "ours=%d, OpenSSL=%d\n",
+ kex_name, our_nid, openssl_nid);
+ goto fail;
+ }
+ } else {
+ /* Verify no NID collision with different algorithm */
+ const char * ossl_name = OBJ_nid2sn(our_nid);
+ if (ossl_name != NULL &&
+ strcmp(ossl_name, kex_name) != 0) {
+ printf("NID collision for '%d': "
+ "ours=%s, OpenSSL=%s\n",
+ our_nid, kex_name, ossl_name);
+ goto fail;
+ }
+ }
+ }
+
+ /* Test error cases */
+ if (kex_str_to_nid("invalid") != NID_undef) {
+ printf("kex_str_to_nid should return NID_undef for invalid\n");
+ goto fail;
+ }
+
+ if (kex_nid_to_str(9999) != NULL) {
+ printf("kex_nid_to_str should return NULL for invalid NID\n");
+ goto fail;
+ }
+
+ if (kex_str_to_nid(NULL) != NID_undef) {
+ printf("kex_str_to_nid should return NID_undef for NULL\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+#endif
+
+static int test_kex_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ /* KEM tests are in kex_test_pqc.c */
+ if (IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_dhe_derive(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_dhe_corrupted_pubkey_all(void)
+{
+ int ret = 0;
+ int i;
+
+ /* Test corruption for all DHE algorithms */
+ /* KEM error injection tests are in kex_test_pqc.c */
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_dhe_corrupted_pubkey(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_parse_config_empty(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR("\n");
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse empty config.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (strcmp(kex.x.str, "prime256v1") != 0) {
+ printf("Empty config should use prime256v1.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_parse_config_custom(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR(KEX_CONFIG_CUSTOM);
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse custom config.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (strcmp(kex.x.str, "X25519") != 0) {
+ printf("Algorithm not set correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_parse_config_none(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR(KEX_CONFIG_NONE);
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse 'none' config.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (kex.x.nid != NID_undef) {
+ printf("'none' keyword should disable encryption.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_parse_config_whitespace(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR(KEX_CONFIG_WHITESPACE);
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse config with comments.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (strcmp(kex.x.str, "X448") != 0) {
+ printf("Algorithm with whitespace not parsed correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_parse_config_cipher(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR(KEX_CONFIG_CIPHER);
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse cipher config.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (strcmp(kex.x.str, "X25519") != 0) {
+ printf("Algorithm not set correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (kex.c.nid != NID_chacha20_poly1305) {
+ printf("Cipher not set correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_parse_config_digest(void)
+{
+ struct sec_config kex;
+ FILE * fp;
+
+ TEST_START();
+
+ memset(&kex, 0, sizeof(kex));
+
+ fp = FMEMOPEN_STR(KEX_CONFIG_DIGEST);
+ if (fp == NULL) {
+ printf("Failed to open memory stream.\n");
+ goto fail;
+ }
+
+ if (parse_sec_config(&kex, fp) < 0) {
+ printf("Failed to parse digest config.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (strcmp(kex.x.str, "X25519") != 0) {
+ printf("Algorithm not set correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ if (kex.d.nid != NID_sha384) {
+ printf("Digest not set correctly.\n");
+ fclose(fp);
+ goto fail;
+ }
+
+ fclose(fp);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int kex_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_kex_create_destroy();
+ ret |= test_kex_parse_config_empty();
+ ret |= test_kex_parse_config_none();
+#ifdef HAVE_OPENSSL
+ ret |= test_kex_parse_config_custom();
+ ret |= test_kex_parse_config_whitespace();
+ ret |= test_kex_parse_config_cipher();
+ ret |= test_kex_parse_config_digest();
+ ret |= test_kex_nid_values();
+ ret |= test_kex_dh_pkp_create_destroy();
+ ret |= test_kex_all();
+ ret |= test_kex_validate_algo();
+ ret |= test_kex_get_algo_from_pk_all();
+ ret |= test_kex_dhe_wrong_algo();
+ ret |= test_kex_dhe_corrupted_pubkey_all();
+ ret |= test_kex_load_dhe_privkey();
+ ret |= test_kex_load_dhe_pubkey();
+#else
+ (void) test_kex_parse_config_custom;
+ (void) test_kex_parse_config_whitespace;
+ (void) test_kex_parse_config_cipher;
+ (void) test_kex_parse_config_digest;
+ (void) test_kex_dh_pkp_create_destroy;
+ (void) test_kex_all;
+ (void) test_kex_validate_algo;
+ (void) test_kex_get_algo_from_pk_all;
+ (void) test_kex_dhe_wrong_algo();
+ (void) test_kex_dhe_corrupted_pubkey_all;
+ (void) test_kex_load_dhe_privkey;
+ (void) test_kex_load_dhe_pubkey;
+
+ ret = TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/lib/tests/kex_test_pqc.c b/src/lib/tests/kex_test_pqc.c
new file mode 100644
index 00000000..d4579eca
--- /dev/null
+++ b/src/lib/tests/kex_test_pqc.c
@@ -0,0 +1,549 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test of the post-quantum key exchange functions
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "config.h"
+
+#include <test/test.h>
+#include <ouroboros/utils.h>
+#include <ouroboros/crypt.h>
+#include <ouroboros/random.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#endif
+
+extern const uint16_t kex_supported_nids[];
+extern const uint16_t md_supported_nids[];
+
+static int get_random_kdf(void)
+{
+ static int idx = 0;
+ int count;
+
+ if (md_supported_nids[0] == NID_undef)
+ return NID_undef;
+
+ for (count = 0; md_supported_nids[count] != NID_undef; count++)
+ ;
+
+ return md_supported_nids[(idx++) % count];
+}
+
+/* ML-KEM-768 test key material */
+
+#define MLKEM768_PRIVKEY_PEM \
+ "-----BEGIN PRIVATE KEY-----\n" \
+ "MIIJvgIBADALBglghkgBZQMEBAIEggmqMIIJpgRA+QIIiQLQkS5fl5RluSmgXRjZ\n" \
+ "YU16W4TVt0dmnBP41rLTTRT3S8CRtkb+xmoFAcWTfEzbdr5pp3g2CBRx+APXTwSC\n" \
+ "CWBll6AecTd1Kqdyix3zNQcthDBP0XnwdTHDqkKuFzMP58Y+0gc9Bo+W0xBOK2ZK\n" \
+ "gcAmix3YLJuDS8Teep/Tdc7KIm5AaLNoI8BIMgKC/ASsW8kC+78BV4OIgqNWurS9\n" \
+ "BrTiCmiag7c+6DsVDJHJ4kfcccwUDBKiW0v+LAkk1HXBcx6usrwuFC0H3ICli2sC\n" \
+ "o5DfGL7g4kWHhobXjAZnxn298C8FGmLQK5kah4nZiJ+MuHqrirziCGTLKkY1a8vC\n" \
+ "GFgzfHIcvB4dtyi9dxZmWpSXqDf2AVNgqrD2C7WQEULQOKxm/I8Mw31Yp8TC6SAP\n" \
+ "RzM4cBAXF00W4Rce05O0am/ga5dStAhikMESyckCoEGlPFFXOmjy1HmOasI+AbGk\n" \
+ "2BKp6cfbImbjd0ePdCSFEgIQwAQHm7+4UoZR2JmNwSI1AC2P4FMRAIaD2A69i6LC\n" \
+ "kFniGcOog5m09nw5FqZmeEfNs6yyFGSX16D1YyjuooAFGlU0FFX7aKwsYM8t1gkS\n" \
+ "YSUfMxIW9yzhSW4vZHuGyxlxBMr1y51RZrW8gnvW5p/Ip5yDBJRahY6KMWT15C14\n" \
+ "C2rIe8U+d4Xi5IMI3D1JNpwFebYhKs3/ManxoU7Fwwa0GzQrgLYU5KhqO8/hopnl\n" \
+ "8mQH+BPh+TR5lqYawS7HZXFJE8JzOnCtOSgB6Hz2U7oG9ik8h0FRqVD3ak20EmZU\n" \
+ "c7gpGW8Odc51uaIBzDu4ej4dGgwo4awYaX4ugLOutHqGqRfCjIVb6XQ4m35p4KKi\n" \
+ "qBVQ211aIhavUIgNECJ7WUETilXyyHLB9x3EFJdidEfSRUxLYJNAC5XM2WFCyhnE\n" \
+ "pKmossSNq6ZOqBjPegE0J6zfNg65dR/OlIdGVDgrVTIpwYAUzBMW2nTnCa00EmPj\n" \
+ "F7tRscHI8qb/QlnRVEUN+S+A2CtVIH1c666zOoRFRI9G4bmVoa8k2x0ANB51tCns\n" \
+ "vAYqkMybIgMvWwbqoAxeW0G1O3qObGXtgs94BzhAEM3RbG/hy3GR1qUNSk/qyDKc\n" \
+ "t1qpiaao0aLVsnpb28eBIk6+q0I82reGdV31OYvUpnVxRbRPFXEFs5PNS3s/7I8a\n" \
+ "SlSLUGOh+mhrUzDPSJCzgEvOmrwrRxe3F52tS0nAt6Z5zKToASHphoISUi7lGX1F\n" \
+ "Owx62qhSqqlI98bKqh7yQRZYrHXqE0bscAHCcIaZ8RVya42JHDCoQWyxqBuLOWEl\n" \
+ "+Fz6vI5DqEnJkA7ke49EvBAOJ58lxAXQIV5remtzYGPKdyG2oamiFHiLVQDzGX/l\n" \
+ "aFNMGXRWcK4/Y3mnkJvx9QGtq6KstQN/J4a51ZeX5YwNBcoY9UcFS6kHRW5rR3UM\n" \
+ "tEZj5VN8BL9nyWM9h7hUSHQboaxO7M5qswfXB8f21xR16T40Ki4nawx/6zHGCQsc\n" \
+ "uKr5SaCV88tghqJYHBorU5iKB5KsLDSHqYYrNo/Vy8W6kMA2jGAO24d4G32DSshR\n" \
+ "sEF9W1nuAHK/5ste01G5KmX2KhdZBE37oGhM98HRQ6hU8qwuKrhdV7vZis5C8LXY\n" \
+ "7MbDyDt1NnFqWFc6lYeVa6eRcmYzeAbXahrxwiiaLIdHXD95aZ/0S6+tKBGgQzwm\n" \
+ "ZsbwdXhl+n+yqDNE6Sow2bwueqhDwZVWoMCv5SK+HAGPtcZ7UU9oWrqpiL085m7F\n" \
+ "5G49KJUEZadVtj4Z9zrkeQkida+4I7v3Y3MzsWsGJww7YhTDJpsxxmSm85bHwx98\n" \
+ "hZXSqckJTL4c2nBzgrBlukIT9Wl+qItMthVvABPzp4wGZhdgKrEIRl3yCnhhUgpL\n" \
+ "lUxYegwWDMEjZxKlSbIyl5p9lCS8w2lsBzsQ2FJiAy/MWLa56aA+wFs3C8smZ6Cf\n" \
+ "p5NWa8Rm+k898GWBxZivhF03CBOZ42du0YUZdCPoA5V1KC6bh4JyWFI49VFbQFMG\n" \
+ "gwAqc0ErAH3iMammKC9746WWagnUIG3o8LygZrusuGeTohXJhVUTJDw2s0rzNhbw\n" \
+ "5IyookkY5BWENKFKTIgdBxvYelOKwbGE8Z36FEW0ABlmx7SRCKWlNVjSEAIXmMiQ\n" \
+ "VLdQF33QVYD9RR5chja254VuJH4plo+5JwiKWz8LlCIBm7CVkifZMLofmMk3s3L4\n" \
+ "sXtE+Bhfm5Plk3RrgDdlHH+hK7gk61XGdynGjDY7aLtCKZ0SMsVskSLom1pbIR5M\n" \
+ "KLYsQ1Pse4mhfDOFCkWFLI5TShGMuIoo1k7XeIE6g8QoUlV5EXyWHHhIVaE4yWGP\n" \
+ "AVgEp0UswKFeeo3SoCAeADA3U88ymxpBJp73yDIqok5dM3SgkjfPWZDkgkAI8WHs\n" \
+ "CKKeqrSOs1kkE3JXtE7kcTHT6XHo162TmgGkqMVwOQ3EmR6FRpYxJhZvuVbjJsSx\n" \
+ "YjW3ScnR4Zivoi7q95ypco331pIlIZpqV0NydUpMyQaz1cnoPKYDh1xa6LhcqEKK\n" \
+ "8a68iXjQgzgqQBDABonVybNDtlJ5lnTTuKhak8PBFAmmhj1JdrPqoIvQRCmLaark\n" \
+ "J7/q9RLtk6kTOJ0qtLe2qqwCxJwyoMd2Q5F4+xTWZHu90ljRdcnYewarqcKzoL27\n" \
+ "tcpTOmVz88I1hYVUJEV7aB36QMhTS1dquTqJZCD0hBPWAMToEoD4OFvKWmbFmzaW\n" \
+ "xrMc4ECYeDAAKYs2YqoXSLfAixBmZjb6UDB61l2GA58pFJW0ZwN8S5tApA2NRi+7\n" \
+ "oC/zgMgBGHft6E0+OUVb8It89pY1t7ybq5+fkBvEixDId3f1pK3gqcaYqG/YhoMJ\n" \
+ "MJWkqYxCNGmdZ8gFo46V6K+4xZUblQWKypN6+RYO4kDh0koppWGEULjgBoCH+V8E\n" \
+ "7GcoE8SRdQY1BIMoRVWb8Ur8ZYIVU8lqgaZPlWM3oRCiWk0kRxexFF0i5WlILIK9\n" \
+ "GT8saX+bmRd9KSy3JrpPhQn59CpJBRxz8WKdJ3wwtqE/2TbxQhLooEWHYVrZEG5E\n" \
+ "SkIoOkUAJUR+CzLLFDMdUE8w3CasE4ys+hco7AA5TAms24A1FXcxMgNb6VHA0bi5\n" \
+ "c8rPCZvjubLXR4A0/A2Ualo4cy3UAr9k0rbZOJnjqk8eExkeaxbyh42cJpU75i4O\n" \
+ "NLYsRZJkg9bkCpPgZKb707sPZO72CX3h/lQdXVgGkZ7Tqd1qzM+JOhSWvrYiBLa+\n" \
+ "5IKSmFwT+5sw1InEesXwRN09000U90vAkbZG/sZqBQHFk3xM23a+aad4NggUcfgD\n" \
+ "108=\n" \
+ "-----END PRIVATE KEY-----\n"
+
+#define MLKEM768_PUBKEY_PEM \
+ "-----BEGIN PUBLIC KEY-----\n" \
+ "MIIEsjALBglghkgBZQMEBAIDggShAMPIO3U2cWpYVzqVh5Vrp5FyZjN4BtdqGvHC\n" \
+ "KJosh0dcP3lpn/RLr60oEaBDPCZmxvB1eGX6f7KoM0TpKjDZvC56qEPBlVagwK/l\n" \
+ "Ir4cAY+1xntRT2hauqmIvTzmbsXkbj0olQRlp1W2Phn3OuR5CSJ1r7gju/djczOx\n" \
+ "awYnDDtiFMMmmzHGZKbzlsfDH3yFldKpyQlMvhzacHOCsGW6QhP1aX6oi0y2FW8A\n" \
+ "E/OnjAZmF2AqsQhGXfIKeGFSCkuVTFh6DBYMwSNnEqVJsjKXmn2UJLzDaWwHOxDY\n" \
+ "UmIDL8xYtrnpoD7AWzcLyyZnoJ+nk1ZrxGb6Tz3wZYHFmK+EXTcIE5njZ27RhRl0\n" \
+ "I+gDlXUoLpuHgnJYUjj1UVtAUwaDACpzQSsAfeIxqaYoL3vjpZZqCdQgbejwvKBm\n" \
+ "u6y4Z5OiFcmFVRMkPDazSvM2FvDkjKiiSRjkFYQ0oUpMiB0HG9h6U4rBsYTxnfoU\n" \
+ "RbQAGWbHtJEIpaU1WNIQAheYyJBUt1AXfdBVgP1FHlyGNrbnhW4kfimWj7knCIpb\n" \
+ "PwuUIgGbsJWSJ9kwuh+YyTezcvixe0T4GF+bk+WTdGuAN2Ucf6EruCTrVcZ3KcaM\n" \
+ "Njtou0IpnRIyxWyRIuibWlshHkwotixDU+x7iaF8M4UKRYUsjlNKEYy4iijWTtd4\n" \
+ "gTqDxChSVXkRfJYceEhVoTjJYY8BWASnRSzAoV56jdKgIB4AMDdTzzKbGkEmnvfI\n" \
+ "MiqiTl0zdKCSN89ZkOSCQAjxYewIop6qtI6zWSQTcle0TuRxMdPpcejXrZOaAaSo\n" \
+ "xXA5DcSZHoVGljEmFm+5VuMmxLFiNbdJydHhmK+iLur3nKlyjffWkiUhmmpXQ3J1\n" \
+ "SkzJBrPVyeg8pgOHXFrouFyoQorxrryJeNCDOCpAEMAGidXJs0O2UnmWdNO4qFqT\n" \
+ "w8EUCaaGPUl2s+qgi9BEKYtpquQnv+r1Eu2TqRM4nSq0t7aqrALEnDKgx3ZDkXj7\n" \
+ "FNZke73SWNF1ydh7BqupwrOgvbu1ylM6ZXPzwjWFhVQkRXtoHfpAyFNLV2q5Oolk\n" \
+ "IPSEE9YAxOgSgPg4W8paZsWbNpbGsxzgQJh4MAApizZiqhdIt8CLEGZmNvpQMHrW\n" \
+ "XYYDnykUlbRnA3xLm0CkDY1GL7ugL/OAyAEYd+3oTT45RVvwi3z2ljW3vJurn5+Q\n" \
+ "G8SLEMh3d/WkreCpxpiob9iGgwkwlaSpjEI0aZ1nyAWjjpXor7jFlRuVBYrKk3r5\n" \
+ "Fg7iQOHSSimlYYRQuOAGgIf5XwTsZygTxJF1BjUEgyhFVZvxSvxlghVTyWqBpk+V\n" \
+ "YzehEKJaTSRHF7EUXSLlaUgsgr0ZPyxpf5uZF30pLLcmuk+FCfn0KkkFHHPxYp0n\n" \
+ "fDC2oT/ZNvFCEuigRYdhWtkQbkRKQig6RQAlRH4LMssUMx1QTzDcJqwTjKz6Fyjs\n" \
+ "ADlMCazbgDUVdzEyA1vpUcDRuLlzys8Jm+O5stdHgDT8DZRqWjhzLdQCv2TSttk4\n" \
+ "meOqTx4TGR5rFvKHjZwmlTvmLg40tixFkmSD1uQKk+BkpvvTuw9k7vYJfeH+VB1d\n" \
+ "WAaRntOp\n" \
+ "-----END PUBLIC KEY-----\n"
+
+/* Helper macro to open string constant as FILE stream */
+#define FMEMOPEN_STR(str) fmemopen((void *) (str), strlen(str), "r")
+
+static int test_kex_load_kem_privkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_privkey_str(MLKEM768_PRIVKEY_PEM, &key) < 0) {
+ printf("Failed to load ML-KEM-768 private key.\n");
+ goto fail;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_load_kem_pubkey(void)
+{
+ void * key;
+
+ TEST_START();
+
+ if (crypt_load_pubkey_str(MLKEM768_PUBKEY_PEM, &key) < 0) {
+ printf("Failed to load ML-KEM-768 public key.\n");
+ goto fail;
+ }
+
+ crypt_free_key(key);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_kem(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp;
+ buffer_t pk;
+ buffer_t ct;
+ ssize_t len;
+ ssize_t ct_len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t s1[SYMMKEYSZ];
+ uint8_t s2[SYMMKEYSZ];
+ int kdf;
+
+ TEST_START("(%s)", algo);
+
+ kdf = get_random_kdf();
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp, buf1);
+ if (len < 0) {
+ printf("Failed to create key pair for %s.\n", algo);
+ goto fail;
+ }
+
+ pk.len = (size_t) len;
+ pk.data = buf1;
+
+ if (IS_HYBRID_KEM(algo))
+ ct_len = kex_kem_encap_raw(pk, buf2, kdf, s1);
+ else
+ ct_len = kex_kem_encap(pk, buf2, kdf, s1);
+
+ if (ct_len < 0) {
+ printf("Failed to encapsulate for %s.\n", algo);
+ goto fail_pkp;
+ }
+
+ ct.len = (size_t) ct_len;
+ ct.data = buf2;
+
+ if (kex_kem_decap(pkp, ct, kdf, s2) < 0) {
+ printf("Failed to decapsulate for %s.\n", algo);
+ goto fail_pkp;
+ }
+
+ if (memcmp(s1, s2, SYMMKEYSZ) != 0) {
+ printf("Shared secrets don't match for %s.\n", algo);
+ goto fail_pkp;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp:
+ kex_pkp_destroy(pkp);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_kem_corrupted_ciphertext(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp;
+ buffer_t pk;
+ buffer_t ct;
+ ssize_t len;
+ ssize_t ct_len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t s1[SYMMKEYSZ];
+ uint8_t s2[SYMMKEYSZ];
+ int kdf;
+
+ TEST_START("(%s)", algo);
+
+ kdf = get_random_kdf();
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp, buf1);
+ if (len < 0) {
+ printf("Failed to create key pair.\n");
+ goto fail;
+ }
+
+ pk.len = (size_t) len;
+ pk.data = buf1;
+
+ if (IS_HYBRID_KEM(algo))
+ ct_len = kex_kem_encap_raw(pk, buf2, kdf, s1);
+ else
+ ct_len = kex_kem_encap(pk, buf2, kdf, s1);
+
+ if (ct_len < 0) {
+ printf("Failed to encapsulate.\n");
+ goto fail_pkp;
+ }
+
+ ct.len = (size_t) ct_len;
+ ct.data = buf2;
+
+ /* Corrupt the ciphertext */
+ buf2[0] ^= 0xFF;
+ buf2[ct_len - 1] ^= 0xFF;
+
+ /* ML-KEM uses implicit rejection */
+ if (kex_kem_decap(pkp, ct, kdf, s2) < 0) {
+ printf("Decapsulation failed unexpectedly.\n");
+ goto fail_pkp;
+ }
+
+ /* The shared secrets should NOT match with corrupted CT */
+ if (memcmp(s1, s2, SYMMKEYSZ) == 0) {
+ printf("Corrupted ciphertext produced same secret.\n");
+ goto fail_pkp;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp:
+ kex_pkp_destroy(pkp);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_kem_wrong_keypair(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp1;
+ void * pkp2;
+ buffer_t pk1;
+ buffer_t ct;
+ ssize_t len;
+ ssize_t ct_len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t buf3[MSGBUFSZ];
+ uint8_t s1[SYMMKEYSZ];
+ uint8_t s2[SYMMKEYSZ];
+
+ TEST_START("(%s)", algo);
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp1, buf1);
+ if (len < 0) {
+ printf("Failed to create first key pair.\n");
+ goto fail;
+ }
+
+ pk1.len = (size_t) len;
+ pk1.data = buf1;
+
+ if (kex_pkp_create(&kex, &pkp2, buf2) < 0) {
+ printf("Failed to create second key pair.\n");
+ goto fail_pkp1;
+ }
+
+ if (IS_HYBRID_KEM(algo))
+ ct_len = kex_kem_encap_raw(pk1, buf3, NID_sha256, s1);
+ else
+ ct_len = kex_kem_encap(pk1, buf3, NID_sha256, s1);
+
+ if (ct_len < 0) {
+ printf("Failed to encapsulate.\n");
+ goto fail_pkp2;
+ }
+
+ ct.len = (size_t) ct_len;
+ ct.data = buf3;
+
+ if (kex_kem_decap(pkp2, ct, NID_sha256, s2) == 0) {
+ if (memcmp(s1, s2, SYMMKEYSZ) == 0) {
+ printf("Wrong keypair produced same secret.\n");
+ goto fail_pkp2;
+ }
+ }
+
+ kex_pkp_destroy(pkp2);
+ kex_pkp_destroy(pkp1);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp2:
+ kex_pkp_destroy(pkp2);
+ fail_pkp1:
+ kex_pkp_destroy(pkp1);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_kem_truncated_ciphertext(const char * algo)
+{
+ struct sec_config kex;
+ void * pkp;
+ buffer_t pk;
+ buffer_t ct;
+ ssize_t len;
+ ssize_t ct_len;
+ uint8_t buf1[MSGBUFSZ];
+ uint8_t buf2[MSGBUFSZ];
+ uint8_t s1[SYMMKEYSZ];
+ uint8_t s2[SYMMKEYSZ];
+
+ TEST_START("(%s)", algo);
+
+ memset(&kex, 0, sizeof(kex));
+ SET_KEX_ALGO(&kex, algo);
+
+ len = kex_pkp_create(&kex, &pkp, buf1);
+ if (len < 0) {
+ printf("Failed to create key pair.\n");
+ goto fail;
+ }
+
+ pk.len = (size_t) len;
+ pk.data = buf1;
+
+ if (IS_HYBRID_KEM(algo))
+ ct_len = kex_kem_encap_raw(pk, buf2, NID_sha256, s1);
+ else
+ ct_len = kex_kem_encap(pk, buf2, NID_sha256, s1);
+
+ if (ct_len < 0) {
+ printf("Failed to encapsulate.\n");
+ goto fail_pkp;
+ }
+
+ /* Truncate the ciphertext */
+ ct.len = (size_t) ct_len / 2;
+ ct.data = buf2;
+
+ if (kex_kem_decap(pkp, ct, NID_sha256, s2) == 0) {
+ printf("Should fail with truncated ciphertext.\n");
+ goto fail_pkp;
+ }
+
+ kex_pkp_destroy(pkp);
+
+ TEST_SUCCESS("(%s)", algo);
+
+ return TEST_RC_SUCCESS;
+ fail_pkp:
+ kex_pkp_destroy(pkp);
+ fail:
+ TEST_FAIL("(%s)", algo);
+ return TEST_RC_FAIL;
+}
+
+static int test_kex_kem_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (!IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_kem(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_kem_corrupted_ciphertext_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (!IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_kem_corrupted_ciphertext(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_kem_wrong_keypair_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (!IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_kem_wrong_keypair(algo);
+ }
+
+ return ret;
+}
+
+static int test_kex_kem_truncated_ciphertext_all(void)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; kex_supported_nids[i] != NID_undef; i++) {
+ const char * algo = kex_nid_to_str(kex_supported_nids[i]);
+
+ if (!IS_KEM_ALGORITHM(algo))
+ continue;
+
+ ret |= test_kex_kem_truncated_ciphertext(algo);
+ }
+
+ return ret;
+}
+
+int kex_test_pqc(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+#ifdef HAVE_OPENSSL_PQC
+ ret |= test_kex_load_kem_privkey();
+ ret |= test_kex_load_kem_pubkey();
+ ret |= test_kex_kem_all();
+ ret |= test_kex_kem_corrupted_ciphertext_all();
+ ret |= test_kex_kem_wrong_keypair_all();
+ ret |= test_kex_kem_truncated_ciphertext_all();
+#else
+ (void) test_kex_load_kem_privkey;
+ (void) test_kex_load_kem_pubkey;
+ (void) test_kex_kem_all;
+ (void) test_kex_kem_corrupted_ciphertext_all;
+ (void) test_kex_kem_wrong_keypair_all;
+ (void) test_kex_kem_truncated_ciphertext_all;
+
+ ret = TEST_RC_SKIP;
+#endif
+ return ret;
+}
diff --git a/src/lib/tests/shm_rbuff_test.c b/src/lib/tests/shm_rbuff_test.c
deleted file mode 100644
index e36c3229..00000000
--- a/src/lib/tests/shm_rbuff_test.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2024
- *
- * Test of the shm_rbuff
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#define _POSIX_C_SOURCE 200112L
-
-#include "config.h"
-
-#include <ouroboros/shm_rbuff.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-int shm_rbuff_test(int argc,
- char ** argv)
-{
- struct shm_rbuff * rb;
- size_t i;
-
- (void) argc;
- (void) argv;
-
- printf("Test: create rbuff...");
-
- rb = shm_rbuff_create(getpid(), 1);
- if (rb == NULL)
- goto err;
-
- printf("success.\n\n");
- printf("Test: write a value...");
-
- if (shm_rbuff_write(rb, 1) < 0)
- goto error;
-
- printf("success.\n\n");
- printf("Test: check queue length is 1...");
-
- if (shm_rbuff_queued(rb) != 1)
- goto error;
-
- printf("success.\n\n");
- printf("Test: read a value...");
-
- if (shm_rbuff_read(rb) != 1)
- goto error;
-
- printf("success.\n\n");
- printf("Test: check queue is empty...");
-
- if (shm_rbuff_read(rb) != -EAGAIN)
- goto error;
-
- printf("success.\n\n");
- printf("Test: fill the queue...");
-
- for (i = 0; i < SHM_RBUFF_SIZE - 1; ++i) {
- if (shm_rbuff_queued(rb) != i)
- goto error;
- if (shm_rbuff_write(rb, 1) < 0)
- goto error;
- }
-
- printf("success.\n\n");
- printf("Test: check queue is full...");
-
- if (shm_rbuff_queued(rb) != SHM_RBUFF_SIZE - 1)
- goto error;
-
- printf("success [%zd entries].\n\n", shm_rbuff_queued(rb));
-
- printf("Test: check queue is full by writing value...");
- if (!(shm_rbuff_write(rb, 1) < 0))
- goto error;
-
- printf("success [%zd entries].\n\n", shm_rbuff_queued(rb));
-
- /* empty the rbuff */
- while (shm_rbuff_read(rb) >= 0)
- ;
-
- shm_rbuff_destroy(rb);
-
- return 0;
-
- error:
- /* empty the rbuff */
- while (shm_rbuff_read(rb) >= 0)
- ;
-
- shm_rbuff_destroy(rb);
- err:
- printf("failed.\n\n");
- return -1;
-}
diff --git a/src/lib/tests/sockets_test.c b/src/lib/tests/sockets_test.c
new file mode 100644
index 00000000..ce9051b6
--- /dev/null
+++ b/src/lib/tests/sockets_test.c
@@ -0,0 +1,102 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Tests for socket.c
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/sockets.h>
+#include <test/test.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TEST_PID 1234
+#define TEST_PID_STR "1234"
+#define TEST_SERVER_PATH "/tmp/test.sock"
+#define TEST_SERVER_PREFIX "/tmp/ouroboros/test."
+#define TEST_SOCK_PATH_PREFIX "var/run/ouroboros/test."
+
+static int test_sock_path(void)
+{
+ char * path;
+ char * exp = TEST_SOCK_PATH_PREFIX TEST_PID_STR SOCK_PATH_SUFFIX;
+
+ TEST_START();
+
+ path = sock_path(TEST_PID, TEST_SOCK_PATH_PREFIX);
+ if (path == NULL) {
+ printf("Path is NULL.\n");
+ goto fail_path;
+ }
+
+ if (strcmp(path, exp) != 0) {
+ printf("Expected path '%s', got '%s'.\n", exp, path);
+ goto fail_cmp;
+ }
+
+ free(path);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+ fail_cmp:
+ free(path);
+ fail_path:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_server_socket_open(void)
+{
+ int sockfd;
+
+ TEST_START();
+
+ sockfd = server_socket_open(TEST_SERVER_PATH);
+ if (sockfd < 0) {
+ printf("Failed to open server socket.\n");
+ goto fail_sock;
+ }
+
+ close(sockfd);
+
+ unlink(TEST_SERVER_PATH);
+
+ TEST_SUCCESS();
+ return TEST_RC_SUCCESS;
+ fail_sock:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int sockets_test(void)
+{
+ int ret = 0;
+
+ ret |= test_sock_path();
+ ret |= test_server_socket_open();
+
+ return ret;
+}
diff --git a/src/lib/tests/time_test.c b/src/lib/tests/time_test.c
index 65f896bb..4685310b 100644
--- a/src/lib/tests/time_test.c
+++ b/src/lib/tests/time_test.c
@@ -22,143 +22,508 @@
#define _POSIX_C_SOURCE 200809L
+#include <test/test.h>
#include <ouroboros/time.h>
#include <stdio.h>
-static void ts_print(struct timespec * s)
+static int ts_check(struct timespec * s,
+ time_t sec,
+ time_t nsec)
{
- printf("timespec is %zd:%ld.\n", (ssize_t) s->tv_sec, s->tv_nsec);
+ return s->tv_sec == sec && s->tv_nsec == nsec;
}
-static void tv_print(struct timeval * v)
+static int tv_check(struct timeval * v,
+ time_t sec,
+ time_t usec)
{
- printf("timeval is %zd:%zu.\n", (ssize_t) v->tv_sec, (size_t) v->tv_usec);
+ return v->tv_sec == sec && v->tv_usec == usec;
}
-static void ts_init(struct timespec * s,
- time_t sec,
- time_t nsec)
+
+static int test_time_ts_init(void)
{
- s->tv_sec = sec;
- s->tv_nsec = nsec;
+ struct timespec s = TIMESPEC_INIT_S (100);
+ struct timespec ms = TIMESPEC_INIT_MS(100);
+ struct timespec us = TIMESPEC_INIT_US(100);
+ struct timespec ns = TIMESPEC_INIT_NS(100);
+
+ TEST_START();
+
+ if (!ts_check(&s, 100, 0)) {
+ printf("timespec_init_s failed.\n");
+ goto fail;
+ }
+
+ if (!ts_check(&ms, 0, 100 * MILLION)) {
+ printf("timespec_init_ms failed.\n");
+ goto fail;
+ }
+
+ if (!ts_check(&us, 0, 100* 1000L)) {
+ printf("timespec_init_us failed.\n");
+ goto fail;
+ }
+
+ if (!ts_check(&ns, 0, 100)) {
+ printf("timespec_init_ns failed.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
-static void tv_init(struct timeval * v,
- time_t sec,
- time_t usec)
+static int test_time_tv_init(void)
{
- v->tv_sec = sec;
- v->tv_usec = usec;
+ struct timeval s = TIMEVAL_INIT_S (100);
+ struct timeval ms = TIMEVAL_INIT_MS(100);
+ struct timeval us = TIMEVAL_INIT_US(100);
+
+ TEST_START();
+
+ if (!tv_check(&s, 100, 0)) {
+ printf("timeval_init_s failed.\n");
+ goto fail;
+ }
+
+ if (!tv_check(&ms, 0, 100 * 1000L)) {
+ printf("timeval_init_ms failed.\n");
+ goto fail;
+ }
+
+ if (!tv_check(&us, 0, 100)) {
+ printf("timeval_init_us failed.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
-static int ts_check(struct timespec * s,
- time_t sec,
- time_t nsec)
+static int test_ts_diff(void)
{
- return s->tv_sec == sec && s->tv_nsec == nsec;
+ struct timespec s0 = TIMESPEC_INIT_S (100);
+ struct timespec s1 = TIMESPEC_INIT_S (200);
+ struct timespec ms0 = TIMESPEC_INIT_MS(100);
+ struct timespec ms1 = TIMESPEC_INIT_MS(200);
+ struct timespec us0 = TIMESPEC_INIT_US(100);
+ struct timespec us1 = TIMESPEC_INIT_US(200);
+ struct timespec ns0 = TIMESPEC_INIT_NS(100);
+ struct timespec ns1 = TIMESPEC_INIT_NS(200);
+ struct timespec res;
+
+ TEST_START();
+
+ ts_diff(&s0, &s1, &res);
+ if (!ts_check(&res, -100, 0)) {
+ printf("timespec_diff failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ ts_diff(&s1, &s0, &res);
+ if (!ts_check(&res, 100, 0)) {
+ printf("timespec_diff failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ ts_diff(&ms0, &ms1, &res);
+ if (!ts_check(&res, -1, 900 * MILLION)) {
+ printf("timespec_diff failed at ms0 - ms1.\n");
+ goto fail;
+ }
+
+ ts_diff(&ms1, &ms0, &res);
+ if (!ts_check(&res, 0, 100 * MILLION)) {
+ printf("timespec_diff failed at ms1 - ms0.\n");
+ goto fail;
+ }
+
+ ts_diff(&us0, &us1, &res);
+ if (!ts_check(&res, -1, 999900 * 1000L)) {
+ printf("timespec_diff failed at us0 - us1.\n");
+ goto fail;
+ }
+
+ ts_diff(&us1, &us0, &res);
+ if (!ts_check(&res, 0, 100 * 1000L)) {
+ printf("timespec_diff failed at us1 - us0.\n");
+ goto fail;
+ }
+
+ ts_diff(&ns0, &ns1, &res);
+ if (!ts_check(&res, -1, 999999900)) {
+ printf("timespec_diff failed at ns0 - ns1.\n");
+ goto fail;
+ }
+
+ ts_diff(&ns1, &ns0, &res);
+ if (!ts_check(&res, 0, 100)) {
+ printf("timespec_diff failed at ns1 - ns0.\n");
+ goto fail;
+ }
+
+ ts_diff(&s0, &ms0, &res);
+ if (!ts_check(&res, 99, 900 * MILLION)) {
+ printf("timespec_diff failed at s0 - ms0.\n");
+ goto fail;
+ }
+
+ ts_diff(&s0, &us0, &res);
+ if (!ts_check(&res, 99, 999900 * 1000L)) {
+ printf("timespec_diff failed at s0 - us0.\n");
+ goto fail;
+ }
+
+ ts_diff(&s0, &ns0, &res);
+ if (!ts_check(&res, 99, 999999900)) {
+ printf("timespec_diff failed at s0 - ns0.\n");
+ goto fail;
+ }
+
+ ts_diff(&ms0, &us0, &res);
+ if (!ts_check(&res, 0, 99900 * 1000L)) {
+ printf("timespec_diff failed at ms0 - us0.\n");
+ goto fail;
+ }
+
+ ts_diff(&ms0, &ns0, &res);
+ if (!ts_check(&res, 0, 99999900)) {
+ printf("timespec_diff failed at ms0 - ns0.\n");
+ goto fail;
+ }
+
+ ts_diff(&us0, &ns0, &res);
+ if (!ts_check(&res, 0, 99900)) {
+ printf("timespec_diff failed at us0 - ns0.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
-static int tv_check(struct timeval * v,
- time_t sec,
- time_t usec)
+static int test_tv_diff(void)
{
- return v->tv_sec == sec && v->tv_usec == usec;
+ struct timeval s0 = TIMEVAL_INIT_S (100);
+ struct timeval s1 = TIMEVAL_INIT_S (200);
+ struct timeval ms0 = TIMEVAL_INIT_MS(100);
+ struct timeval ms1 = TIMEVAL_INIT_MS(200);
+ struct timeval us0 = TIMEVAL_INIT_US(100);
+ struct timeval us1 = TIMEVAL_INIT_US(200);
+ struct timeval res;
+
+ TEST_START();
+
+ tv_diff(&s0, &s1, &res);
+ if (!tv_check(&res, -100, 0)) {
+ printf("timeval_diff failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ tv_diff(&s1, &s0, &res);
+ if (!tv_check(&res, 100, 0)) {
+ printf("timeval_diff failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ tv_diff(&ms0, &ms1, &res);
+ if (!tv_check(&res, -1, 900 * 1000L)) {
+ printf("timeval_diff failed at ms0 - ms1.\n");
+ goto fail;
+ }
+
+ tv_diff(&ms1, &ms0, &res);
+ if (!tv_check(&res, 0, 100 * 1000L)) {
+ printf("timeval_diff failed at ms1 - ms0.\n");
+ goto fail;
+ }
+
+ tv_diff(&us0, &us1, &res);
+ if (!tv_check(&res, -1, 999900)) {
+ printf("timeval_diff failed at us0 - us1.\n");
+ goto fail;
+ }
+
+ tv_diff(&us1, &us0, &res);
+ if (!tv_check(&res, 0, 100)) {
+ printf("timeval_diff failed at us1 - us0.\n");
+ goto fail;
+ }
+
+ tv_diff(&s0, &ms0, &res);
+ if (!tv_check(&res, 99, 900 * 1000L)) {
+ printf("timeval_diff failed at s0 - ms0.\n");
+ goto fail;
+ }
+
+ tv_diff(&s0, &us0, &res);
+ if (!tv_check(&res, 99, 999900)) {
+ printf("timeval_diff failed at s0 - us0.\n");
+ goto fail;
+ }
+
+ tv_diff(&ms0, &us0, &res);
+ if (!tv_check(&res, 0, 99900)) {
+ printf("timeval_diff failed at ms0 - us0.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
}
-int time_test(int argc,
- char ** argv)
+static int test_ts_diff_time(void)
{
- struct timespec s0;
- struct timespec s1;
- struct timespec s2;
+ struct timespec s0 = TIMESPEC_INIT_S (100);
+ struct timespec s1 = TIMESPEC_INIT_S (200);
+ struct timespec ms0 = TIMESPEC_INIT_MS(100);
+ struct timespec ms1 = TIMESPEC_INIT_MS(200);
+ struct timespec us0 = TIMESPEC_INIT_US(100);
+ struct timespec us1 = TIMESPEC_INIT_US(200);
+ struct timespec ns0 = TIMESPEC_INIT_NS(100);
+ struct timespec ns1 = TIMESPEC_INIT_NS(200);
- struct timeval v0;
- struct timeval v1;
- struct timeval v2;
+ TEST_START();
- (void) argc;
- (void) argv;
+ if (ts_diff_ms(&s0, &s1) != -100 * 1000L) {
+ printf("timespec_diff_ms failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ if (ts_diff_ms(&s1, &s0) != 100 * 1000L) {
+ printf("timespec_diff_ms failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ if (ts_diff_us(&s0, &s1) != -100 * MILLION) {
+ printf("timespec_diff_us failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ if (ts_diff_us(&s1, &s0) != 100 * MILLION) {
+ printf("timespec_diff_us failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ if (ts_diff_ns(&s0, &s1) != -100 * BILLION) {
+ printf("timespec_diff_ns failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ if (ts_diff_ns(&s1, &s0) != 100 * BILLION) {
+ printf("timespec_diff_ns failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ if (ts_diff_ms(&ms0, &ms1) != -100) {
+ printf("timespec_diff_ms failed at ms0 - ms1.\n");
+ goto fail;
+ }
+
+ if (ts_diff_ms(&ms1, &ms0) != 100) {
+ printf("timespec_diff_ms failed at ms1 - ms0.\n");
+ goto fail;
+ }
- ts_init(&s0, 0, 0);
- ts_init(&s1, 5, 0);
+ if (ts_diff_us(&ms0, &ms1) != -100 * 1000L) {
+ printf("timespec_diff_us failed at ms0 - ms1.\n");
+ goto fail;
+ }
- ts_add(&s0, &s1, &s2);
- if (!ts_check(&s2, 5, 0)) {
- printf("ts_add failed.\n");
- ts_print(&s2);
- return -1;
+ if (ts_diff_us(&ms1, &ms0) != 100 * 1000L) {
+ printf("timespec_diff_us failed at ms1 - ms0.\n");
+ goto fail;
}
- tv_init(&v0, 0, 0);
- tv_init(&v1, 5, 0);
+ if (ts_diff_ns(&ms0, &ms1) != -100 * MILLION) {
+ printf("timespec_diff_ns failed at ms0 - ms1.\n");
+ goto fail;
+ }
- tv_add(&v0, &v1, &v2);
- if (!tv_check(&v2, 5, 0)) {
- printf("tv_add failed.\n");
- tv_print(&v2);
- return -1;
+ if (ts_diff_ns(&ms1, &ms0) != 100 * MILLION) {
+ printf("timespec_diff_ns failed at ms1 - ms0.\n");
+ goto fail;
}
- ts_init(&s0, 0, 500 * MILLION);
- ts_init(&s1, 0, 600 * MILLION);
+ if (ts_diff_ms(&us0, &us1) != 0) {
+ printf("timespec_diff_ms failed at us0 - us1.\n");
+ goto fail;
+ }
- ts_add(&s0, &s1, &s2);
- if (!ts_check(&s2, 1, 100 * MILLION)) {
- printf("ts_add with nano overflow failed.\n");
- ts_print(&s2);
- return -1;
+ if (ts_diff_ms(&us1, &us0) != 0) {
+ printf("timespec_diff_ms failed at us1 - us0.\n");
+ goto fail;
}
- tv_init(&v0, 0, 500 * 1000);
- tv_init(&v1, 0, 600 * 1000);
+ if (ts_diff_us(&us0, &us1) != -100) {
+ printf("timespec_diff_us failed at us0 - us1.\n");
+ goto fail;
+ }
- tv_add(&v0, &v1, &v2);
- if (!tv_check(&v2, 1, 100 * 1000)) {
- printf("tv_add with nano overflow failed.\n");
- tv_print(&v2);
- return -1;
+ if (ts_diff_us(&us1, &us0) != 100) {
+ printf("timespec_diff_us failed at us1 - us0.\n");
+ goto fail;
}
- ts_init(&s0, 0, 0);
- ts_init(&s1, 5, 0);
+ if (ts_diff_ns(&us0, &us1) != -100 * 1000L) {
+ printf("timespec_diff_ns failed at us0 - us1.\n");
+ goto fail;
+ }
- ts_diff(&s0, &s1, &s2);
- if (!ts_check(&s2, -5, 0)) {
- printf("ts_diff failed.\n");
- ts_print(&s2);
- return -1;
+ if (ts_diff_ns(&us1, &us0) != 100 * 1000L) {
+ printf("timespec_diff_ns failed at us1 - us0.\n");
+ goto fail;
}
- tv_init(&v0, 0, 0);
- tv_init(&v1, 5, 0);
+ if (ts_diff_ms(&ns0, &ns1) != 0) {
+ printf("timespec_diff_ms failed at ns0 - ns1.\n");
+ goto fail;
+ }
- tv_diff(&v0, &v1, &v2);
- if (!tv_check(&v2, -5, 0)) {
- printf("tv_diff failed.\n");
- tv_print(&v2);
- return -1;
+ if (ts_diff_ms(&ns1, &ns0) != 0) {
+ printf("timespec_diff_ms failed at ns1 - ns0.\n");
+ goto fail;
}
- ts_init(&s0, 0, 500 * MILLION);
- ts_init(&s1, 0, 600 * MILLION);
+ if (ts_diff_us(&ns0, &ns1) != 0) {
+ printf("timespec_diff_us failed at ns0 - ns1.\n");
+ goto fail;
+ }
- ts_diff(&s0, &s1, &s2);
- if (!ts_check(&s2, -1, 900 * MILLION)) {
- printf("ts_diff with nano underflow failed.\n");
- ts_print(&s2);
- return -1;
+ if (ts_diff_us(&ns1, &ns0) != 0) {
+ printf("timespec_diff_us failed at ns1 - ns0.\n");
+ goto fail;
}
- tv_init(&v0, 0, 500 * 1000);
- tv_init(&v1, 0, 600 * 1000);
+ if (ts_diff_ns(&ns0, &ns1) != -100) {
+ printf("timespec_diff_ns failed at ns0 - ns1.\n");
+ goto fail;
+ }
- tv_diff(&v0, &v1, &v2);
- if (!tv_check(&v2, -1, 900 * 1000)) {
- printf("tv_diff with nano underflow failed.\n");
- tv_print(&v2);
- return -1;
+ if (ts_diff_ns(&ns1, &ns0) != 100) {
+ printf("timespec_diff_ns failed at ns1 - ns0.\n");
+ goto fail;
}
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_tv_diff_time(void)
+{
+ struct timeval s0 = TIMEVAL_INIT_S (100);
+ struct timeval s1 = TIMEVAL_INIT_S (200);
+ struct timeval ms0 = TIMEVAL_INIT_MS(100);
+ struct timeval ms1 = TIMEVAL_INIT_MS(200);
+ struct timeval us0 = TIMEVAL_INIT_US(100);
+ struct timeval us1 = TIMEVAL_INIT_US(200);
+
+ TEST_START();
+
+ if (tv_diff_ms(&s0, &s1) != -100 * 1000L) {
+ printf("timeval_diff_ms failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_ms(&s1, &s0) != 100 * 1000L) {
+ printf("timeval_diff_ms failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&s0, &s1) != -100 * MILLION) {
+ printf("timeval_diff_us failed at s0 - s1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&s1, &s0) != 100 * MILLION) {
+ printf("timeval_diff_us failed at s1 - s0.\n");
+ goto fail;
+ }
+
+ if (tv_diff_ms(&ms0, &ms1) != -100) {
+ printf("timeval_diff_ms failed at ms0 - ms1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_ms(&ms1, &ms0) != 100) {
+ printf("timeval_diff_ms failed at ms1 - ms0.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&ms0, &ms1) != -100 * 1000L) {
+ printf("timeval_diff_us failed at ms0 - ms1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&ms1, &ms0) != 100 * 1000L) {
+ printf("timeval_diff_us failed at ms1 - ms0.\n");
+ goto fail;
+ }
+
+ if (tv_diff_ms(&us0, &us1) != 0) {
+ printf("timeval_diff_ms failed at us0 - us1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_ms(&us1, &us0) != 0) {
+ printf("timeval_diff_ms failed at us1 - us0.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&us0, &us1) != -100) {
+ printf("timeval_diff_us failed at us0 - us1.\n");
+ goto fail;
+ }
+
+ if (tv_diff_us(&us1, &us0) != 100) {
+ printf("timeval_diff_us failed at us1 - us0.\n");
+ goto fail;
+ }
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int time_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_time_ts_init();
+ ret |= test_time_tv_init();
+ ret |= test_ts_diff();
+ ret |= test_tv_diff();
+ ret |= test_ts_diff_time();
+ ret |= test_tv_diff_time();
+
+ return ret;
}
diff --git a/src/lib/tests/tpm_test.c b/src/lib/tests/tpm_test.c
new file mode 100644
index 00000000..41bce964
--- /dev/null
+++ b/src/lib/tests/tpm_test.c
@@ -0,0 +1,104 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Tests for the threadpool manager
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+
+#include "tpm.c"
+
+#include <test/test.h>
+
+static void * test_func(void * o)
+{
+ (void) o;
+
+ while(1)
+ sleep(1);
+
+ return NULL;
+}
+
+static int test_tpm_create_destroy(void)
+{
+ struct tpm *tpm;
+
+ TEST_START();
+
+ tpm = tpm_create(2, 2, &test_func, NULL);
+ if (tpm == NULL) {
+ printf("Failed to initialize TPM.\n");
+ goto fail;
+ }
+
+ tpm_destroy(tpm);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_tpm_start_stop(void * (* fn)(void *),
+ void * o)
+{
+ struct tpm *tpm;
+
+ TEST_START();
+
+ tpm = tpm_create(2, 2, fn, o);
+ if (tpm == NULL) {
+ printf("Failed to initialize TPM.\n");
+ goto fail_create;
+ }
+
+ if (tpm_start(tpm) < 0) {
+ printf("Failed to start TPM.\n");
+ goto fail_start;
+ }
+
+ tpm_stop(tpm);
+
+ tpm_destroy(tpm);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_start:
+ tpm_destroy(tpm);
+ fail_create:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int tpm_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_tpm_create_destroy();
+ ret |= test_tpm_start_stop(&test_func, NULL);
+
+ return ret;
+}
diff --git a/src/lib/timerwheel.c b/src/lib/timerwheel.c
index 96f4ac47..ed235047 100644
--- a/src/lib/timerwheel.c
+++ b/src/lib/timerwheel.c
@@ -31,7 +31,7 @@ struct rxm {
struct list_head next;
uint32_t seqno;
#ifndef RXM_BUFFER_ON_HEAP
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
#endif
struct frct_pci * pkt;
size_t len;
@@ -81,8 +81,8 @@ static void timerwheel_fini(void)
#ifdef RXM_BUFFER_ON_HEAP
free(rxm->pkt);
#else
- shm_du_buff_ack(rxm->sdb);
- ipcp_sdb_release(rxm->sdb);
+ ssm_pk_buff_ack(rxm->spb);
+ ipcp_spb_release(rxm->spb);
#endif
free(rxm);
}
@@ -160,7 +160,7 @@ static void timerwheel_move(void)
size_t slot;
size_t rslot;
ssize_t idx;
- struct shm_du_buff * sdb;
+ struct ssm_pk_buff * spb;
struct frct_pci * pci;
struct flow * f;
uint32_t snd_lwe;
@@ -173,9 +173,9 @@ static void timerwheel_move(void)
snd_cr = &r->frcti->snd_cr;
rcv_cr = &r->frcti->rcv_cr;
- f = &ai.flows[r->fd];
+ f = &proc.flows[r->fd];
#ifndef RXM_BUFFER_ON_HEAP
- shm_du_buff_ack(r->sdb);
+ ssm_pk_buff_ack(r->spb);
#endif
if (f->frcti == NULL
|| f->info.id != r->flow_id)
@@ -224,45 +224,45 @@ static void timerwheel_move(void)
rslot = (rslot + slot + 1) & (RXMQ_SLOTS - 1);
#ifdef RXM_BLOCKING
- if (ipcp_sdb_reserve(&sdb, r->len) < 0)
+ if (ipcp_spb_reserve(&spb, r->len) < 0)
#else
- if (shm_rdrbuff_alloc(ai.rdrb, r->len, NULL,
- &sdb) < 0)
+ if (ssm_pool_alloc(proc.pool, r->len, NULL,
+ &spb) < 0)
#endif
goto reschedule; /* rdrbuff full */
- pci = (struct frct_pci *) shm_du_buff_head(sdb);
+ pci = (struct frct_pci *) ssm_pk_buff_head(spb);
memcpy(pci, r->pkt, r->len);
#ifndef RXM_BUFFER_ON_HEAP
- ipcp_sdb_release(r->sdb);
- r->sdb = sdb;
+ ipcp_spb_release(r->spb);
+ r->spb = spb;
r->pkt = pci;
- shm_du_buff_wait_ack(sdb);
+ ssm_pk_buff_wait_ack(spb);
#endif
- idx = shm_du_buff_get_idx(sdb);
+ idx = ssm_pk_buff_get_idx(spb);
/* Retransmit the copy. */
pci->ackno = hton32(rcv_lwe);
#ifdef RXM_BLOCKING
- if (shm_rbuff_write_b(f->tx_rb, idx, NULL) < 0)
+ if (ssm_rbuff_write_b(f->tx_rb, idx, NULL) < 0)
#else
- if (shm_rbuff_write(f->tx_rb, idx) < 0)
+ if (ssm_rbuff_write(f->tx_rb, idx) < 0)
#endif
goto flow_down;
- shm_flow_set_notify(f->set, f->info.id,
+ ssm_flow_set_notify(f->set, f->info.id,
FLOW_PKT);
reschedule:
list_add(&r->next, &rw.rxms[lvl][rslot]);
continue;
flow_down:
- shm_rbuff_set_acl(f->tx_rb, ACL_FLOWDOWN);
- shm_rbuff_set_acl(f->rx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_set_acl(f->tx_rb, ACL_FLOWDOWN);
+ ssm_rbuff_set_acl(f->rx_rb, ACL_FLOWDOWN);
cleanup:
#ifdef RXM_BUFFER_ON_HEAP
free(r->pkt);
#else
- ipcp_sdb_release(r->sdb);
+ ipcp_spb_release(r->spb);
#endif
free(r);
}
@@ -288,7 +288,7 @@ static void timerwheel_move(void)
list_del(&a->next);
- f = &ai.flows[a->fd];
+ f = &proc.flows[a->fd];
rw.map[j & (ACKQ_SLOTS - 1)][a->fd] = false;
@@ -306,7 +306,7 @@ static void timerwheel_move(void)
static int timerwheel_rxm(struct frcti * frcti,
uint32_t seqno,
- struct shm_du_buff * sdb)
+ struct ssm_pk_buff * spb)
{
struct timespec now;
struct rxm * r;
@@ -323,17 +323,17 @@ static int timerwheel_rxm(struct frcti * frcti,
r->t0 = ts_to_ns(now);
r->seqno = seqno;
r->frcti = frcti;
- r->len = shm_du_buff_len(sdb);
+ r->len = ssm_pk_buff_len(spb);
#ifdef RXM_BUFFER_ON_HEAP
r->pkt = malloc(r->len);
if (r->pkt == NULL) {
free(r);
return -ENOMEM;
}
- memcpy(r->pkt, shm_du_buff_head(sdb), r->len);
+ memcpy(r->pkt, ssm_pk_buff_head(spb), r->len);
#else
- r->sdb = sdb;
- r->pkt = (struct frct_pci *) shm_du_buff_head(sdb);
+ r->spb = spb;
+ r->pkt = (struct frct_pci *) ssm_pk_buff_head(spb);
#endif
pthread_rwlock_rdlock(&r->frcti->lock);
@@ -341,7 +341,7 @@ static int timerwheel_rxm(struct frcti * frcti,
slot = r->t0 >> RXMQ_RES;
r->fd = frcti->fd;
- r->flow_id = ai.flows[r->fd].info.id;
+ r->flow_id = proc.flows[r->fd].info.id;
pthread_rwlock_unlock(&r->frcti->lock);
@@ -365,7 +365,7 @@ static int timerwheel_rxm(struct frcti * frcti,
list_add_tail(&r->next, &rw.rxms[lvl][slot]);
#ifndef RXM_BUFFER_ON_HEAP
- shm_du_buff_wait_ack(sdb);
+ ssm_pk_buff_wait_ack(spb);
#endif
pthread_mutex_unlock(&rw.lock);
@@ -394,7 +394,7 @@ static int timerwheel_delayed_ack(int fd,
a->fd = fd;
a->frcti = frcti;
- a->flow_id = ai.flows[fd].info.id;
+ a->flow_id = proc.flows[fd].info.id;
pthread_mutex_lock(&rw.lock);
diff --git a/src/lib/tpm.c b/src/lib/tpm.c
index 0ef1fda8..ad5c1068 100644
--- a/src/lib/tpm.c
+++ b/src/lib/tpm.c
@@ -26,21 +26,32 @@
#include <ouroboros/errno.h>
#include <ouroboros/list.h>
+#include <ouroboros/pthread.h>
#include <ouroboros/time.h>
#include <ouroboros/tpm.h>
+#ifdef CONFIG_OUROBOROS_DEBUG
+#define OUROBOROS_PREFIX "tpm"
+#include <ouroboros/logs.h>
+#endif
+
#include <assert.h>
-#include <pthread.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-#define TPM_TIMEOUT 1000
+#define TPM_TIMEOUT 1000
struct pthr_el {
struct list_head next;
bool kill;
bool busy;
-
+ bool wait;
+#ifdef CONFIG_OUROBOROS_DEBUG
+ struct timespec start;
+ struct timespec last;
+#endif
pthread_t thr;
};
@@ -63,16 +74,45 @@ struct tpm {
enum tpm_state state;
pthread_cond_t cond;
- pthread_mutex_t lock;
+ pthread_mutex_t mtx;
pthread_t mgr;
};
+#ifdef CONFIG_OUROBOROS_DEBUG
+#define BETWEEN(a, x, y) ((a) > (x) && (a) <= (y))
+static void tpm_debug_thread(struct pthr_el * e)
+{
+ struct timespec now;
+ time_t diff;
+ time_t intv;
+
+ if (e->wait || !e->busy)
+ return;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+
+ diff = ts_diff_ms(&now, &e->start) / 1000;
+ intv = ts_diff_ms(&now, &e->last) / 1000;
+
+ (void) diff; /* Never read if both build options off (0) */
+ (void) intv; /* Never read if report option off (0) */
+
+ if (BETWEEN(TPM_DEBUG_REPORT_INTERVAL, 0, intv)) {
+ log_dbg("Thread %d:%lx running for %ld s.\n",
+ getpid(), (unsigned long) e->thr, diff);
+ e->last = now;
+ }
+
+ if (BETWEEN(TPM_DEBUG_ABORT_TIMEOUT, 0, diff))
+ assert(false); /* TODO: Grab a coffee and fire up GDB */
+}
+#endif
+
static void tpm_join(struct tpm * tpm)
{
struct list_head * p;
struct list_head * h;
-
list_for_each_safe(p, h, &tpm->pool) {
struct pthr_el * e = list_entry(p, struct pthr_el, next);
if (tpm->state != TPM_RUNNING) {
@@ -86,15 +126,18 @@ static void tpm_join(struct tpm * tpm)
list_for_each_safe(p, h, &tpm->pool) {
struct pthr_el * e = list_entry(p, struct pthr_el, next);
+#ifdef CONFIG_OUROBOROS_DEBUG
+ tpm_debug_thread(e);
+#endif
if (e->kill) {
pthread_t thr = e->thr;
list_del(&e->next);
free(e);
- pthread_mutex_unlock(&tpm->lock);
+ pthread_mutex_unlock(&tpm->mtx);
pthread_join(thr, NULL);
- pthread_mutex_lock(&tpm->lock);
+ pthread_mutex_lock(&tpm->mtx);
}
}
}
@@ -114,56 +157,59 @@ static void tpm_kill(struct tpm * tpm)
}
}
-static void * tpmgr(void * o)
+static int __tpm(struct tpm * tpm)
{
struct timespec dl;
struct timespec to = TIMESPEC_INIT_MS(TPM_TIMEOUT);
- struct tpm * tpm = (struct tpm *) o;
-
- while (true) {
- clock_gettime(PTHREAD_COND_CLOCK, &dl);
- ts_add(&dl, &to, &dl);
- pthread_mutex_lock(&tpm->lock);
+ clock_gettime(PTHREAD_COND_CLOCK, &dl);
+ ts_add(&dl, &to, &dl);
- if (tpm->state != TPM_RUNNING) {
- tpm_join(tpm);
- pthread_mutex_unlock(&tpm->lock);
- break;
- }
+ pthread_mutex_lock(&tpm->mtx);
+ if (tpm->state != TPM_RUNNING) {
tpm_join(tpm);
+ pthread_mutex_unlock(&tpm->mtx);
+ return -1;
+ }
- if (tpm->cur - tpm->wrk < tpm->min) {
- size_t i;
- for (i = 0; i < tpm->inc; ++i) {
- struct pthr_el * e = malloc(sizeof(*e));
- if (e == NULL)
- break;
+ tpm_join(tpm);
- e->kill = false;
- e->busy = false;
+ if (tpm->cur - tpm->wrk < tpm->min) {
+ size_t i;
+ for (i = 0; i < tpm->inc; ++i) {
+ struct pthr_el * e = malloc(sizeof(*e));
+ if (e == NULL)
+ break;
- if (pthread_create(&e->thr, NULL,
- tpm->func, tpm->o)) {
- free(e);
- break;
- }
+ memset(e, 0, sizeof(*e));
- list_add(&e->next, &tpm->pool);
+ if (pthread_create(&e->thr, NULL, tpm->func, tpm->o)) {
+ free(e);
+ break;
}
- tpm->cur += i;
+ list_add(&e->next, &tpm->pool);
}
- if (pthread_cond_timedwait(&tpm->cond, &tpm->lock, &dl)
- == ETIMEDOUT)
- if (tpm->cur - tpm->wrk > tpm->min)
- tpm_kill(tpm);
-
- pthread_mutex_unlock(&tpm->lock);
+ tpm->cur += i;
}
+ pthread_cleanup_push(__cleanup_mutex_unlock, &tpm->mtx);
+
+ if (pthread_cond_timedwait(&tpm->cond, &tpm->mtx, &dl) == ETIMEDOUT)
+ if (tpm->cur - tpm->wrk > tpm->min)
+ tpm_kill(tpm);
+
+ pthread_cleanup_pop(true);
+
+ return 0;
+}
+
+static void * tpmgr(void * o)
+{
+ while (__tpm((struct tpm *) o) == 0);
+
return (void *) 0;
}
@@ -175,11 +221,14 @@ struct tpm * tpm_create(size_t min,
struct tpm * tpm;
pthread_condattr_t cattr;
+ assert(func != NULL);
+ assert(inc > 0);
+
tpm = malloc(sizeof(*tpm));
if (tpm == NULL)
goto fail_malloc;
- if (pthread_mutex_init(&tpm->lock, NULL))
+ if (pthread_mutex_init(&tpm->mtx, NULL))
goto fail_lock;
if (pthread_condattr_init(&cattr))
@@ -208,7 +257,7 @@ struct tpm * tpm_create(size_t min,
fail_cond:
pthread_condattr_destroy(&cattr);
fail_cattr:
- pthread_mutex_destroy(&tpm->lock);
+ pthread_mutex_destroy(&tpm->mtx);
fail_lock:
free(tpm);
fail_malloc:
@@ -217,34 +266,39 @@ struct tpm * tpm_create(size_t min,
int tpm_start(struct tpm * tpm)
{
- pthread_mutex_lock(&tpm->lock);
+ pthread_mutex_lock(&tpm->mtx);
if (pthread_create(&tpm->mgr, NULL, tpmgr, tpm)) {
- pthread_mutex_unlock(&tpm->lock);
+ pthread_mutex_unlock(&tpm->mtx);
return -1;
}
tpm->state = TPM_RUNNING;
- pthread_mutex_unlock(&tpm->lock);
+ pthread_mutex_unlock(&tpm->mtx);
return 0;
}
void tpm_stop(struct tpm * tpm)
{
- pthread_mutex_lock(&tpm->lock);
+ pthread_mutex_lock(&tpm->mtx);
+
+ if (tpm->state != TPM_RUNNING) {
+ pthread_mutex_unlock(&tpm->mtx);
+ return;
+ }
tpm->state = TPM_NULL;
- pthread_mutex_unlock(&tpm->lock);
+ pthread_mutex_unlock(&tpm->mtx);
pthread_join(tpm->mgr, NULL);
}
void tpm_destroy(struct tpm * tpm)
{
- pthread_mutex_destroy(&tpm->lock);
+ pthread_mutex_destroy(&tpm->mtx);
pthread_cond_destroy(&tpm->cond);
free(tpm);
@@ -260,40 +314,61 @@ static struct pthr_el * tpm_pthr_el(struct tpm * tpm,
e = list_entry(p, struct pthr_el, next);
if (e->thr == thr)
return e;
-
}
return NULL;
}
-void tpm_inc(struct tpm * tpm)
+void tpm_begin_work(struct tpm * tpm)
{
struct pthr_el * e;
- pthread_mutex_lock(&tpm->lock);
+#ifdef CONFIG_OUROBOROS_DEBUG
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+#endif
+
+ pthread_mutex_lock(&tpm->mtx);
e = tpm_pthr_el(tpm, pthread_self());
if (e != NULL) {
- e->busy = false;
- --tpm->wrk;
+ e->busy = true;
+ ++tpm->wrk;
+#ifdef CONFIG_OUROBOROS_DEBUG
+ e->start = now;
+ e->last = now;
+#endif
}
- pthread_mutex_unlock(&tpm->lock);
+ pthread_cond_signal(&tpm->cond);
+
+ pthread_mutex_unlock(&tpm->mtx);
}
-void tpm_dec(struct tpm * tpm)
+void tpm_wait_work(struct tpm * tpm)
{
struct pthr_el * e;
- pthread_mutex_lock(&tpm->lock);
+ pthread_mutex_lock(&tpm->mtx);
+
+ e = tpm_pthr_el(tpm, pthread_self());
+ if (e != NULL)
+ e->wait = true;
+
+ pthread_mutex_unlock(&tpm->mtx);
+}
+
+void tpm_end_work(struct tpm * tpm)
+{
+ struct pthr_el * e;
+
+ pthread_mutex_lock(&tpm->mtx);
e = tpm_pthr_el(tpm, pthread_self());
if (e != NULL) {
- e->busy = true;
- ++tpm->wrk;
+ e->busy = false;
+ --tpm->wrk;
}
- pthread_cond_signal(&tpm->cond);
-
- pthread_mutex_unlock(&tpm->lock);
+ pthread_mutex_unlock(&tpm->mtx);
}
diff --git a/src/lib/utils.c b/src/lib/utils.c
index fdbcd9d9..cfddec62 100644
--- a/src/lib/utils.c
+++ b/src/lib/utils.c
@@ -20,23 +20,38 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#define _POSIX_C_SOURCE 200809L
+#define _DEFAULT_SOURCE
+
+#include "config.h"
#include <ouroboros/utils.h>
+#include <ctype.h>
+#include <grp.h>
+#include <pwd.h>
#include <stdlib.h>
#include <string.h>
+int bufcmp(const buffer_t * a,
+ const buffer_t * b)
+{
+ if (a->len != b->len)
+ return a->len < b->len ? -1 : 1;
+
+ return memcmp(a->data, b->data, a->len);
+}
+
+
int n_digits(unsigned i)
{
- int n = 1;
+ int n = 1;
- while (i > 9) {
- ++n;
- i /= 10;
- }
+ while (i > 9) {
+ ++n;
+ i /= 10;
+ }
- return n;
+ return n;
}
char * path_strip(const char * src)
@@ -57,6 +72,24 @@ char * path_strip(const char * src)
return dst;
}
+char * trim_whitespace(char * str)
+{
+ char * end;
+
+ while (isspace((unsigned char) *str))
+ str++;
+
+ if (*str == '\0')
+ return str;
+
+ /* Trim trailing space */
+ end = str + strlen(str) - 1;
+ while (end > str && isspace((unsigned char)*end))
+ *end-- = '\0';
+
+ return str;
+}
+
size_t argvlen(const char ** argv)
{
size_t argc = 0;
@@ -109,5 +142,63 @@ char ** argvdup(char ** argv)
}
argv_dup[argc] = NULL;
+
return argv_dup;
}
+
+bool is_ouroboros_member_uid(uid_t uid)
+{
+ struct group * grp;
+ struct passwd * pw;
+ gid_t gid;
+ gid_t * groups = NULL;
+ int ngroups;
+ int i;
+
+ /* Root is always privileged */
+ if (uid == 0)
+ return true;
+
+ grp = getgrnam("ouroboros");
+ if (grp == NULL)
+ return false;
+
+ gid = grp->gr_gid;
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return false;
+
+ if (pw->pw_gid == gid)
+ return true;
+
+ ngroups = 0;
+ getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups);
+ if (ngroups <= 0)
+ return false;
+
+ groups = malloc(ngroups * sizeof(*groups));
+ if (groups == NULL)
+ return false;
+
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) < 0) {
+ free(groups);
+ return false;
+ }
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] == gid) {
+ free(groups);
+ return true;
+ }
+ }
+
+ free(groups);
+
+ return false;
+}
+
+bool is_ouroboros_member(void)
+{
+ return is_ouroboros_member_uid(getuid());
+}
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 7c40d9ae..3cec8172 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -1,9 +1,71 @@
-add_subdirectory(irm)
-add_subdirectory(ocbr)
-add_subdirectory(oecho)
-add_subdirectory(obc)
-add_subdirectory(oping)
-add_subdirectory(operf)
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- add_subdirectory(ovpn)
-endif ()
+# Tools build configuration
+
+set(TOOLS_INCLUDE_DIRS
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/include
+)
+
+set(IRM_SOURCES
+ irm/irm.c
+ irm/irm_bind_program.c
+ irm/irm_bind_process.c
+ irm/irm_bind_ipcp.c
+ irm/irm_ipcp_create.c
+ irm/irm_ipcp_destroy.c
+ irm/irm_ipcp_bootstrap.c
+ irm/irm_ipcp_enroll.c
+ irm/irm_ipcp_list.c
+ irm/irm_ipcp_connect.c
+ irm/irm_ipcp_disconnect.c
+ irm/irm_unbind_program.c
+ irm/irm_unbind_process.c
+ irm/irm_unbind_ipcp.c
+ irm/irm_unbind.c
+ irm/irm_bind.c
+ irm/irm_ipcp.c
+ irm/irm_name.c
+ irm/irm_name_create.c
+ irm/irm_name_destroy.c
+ irm/irm_name_reg.c
+ irm/irm_name_unreg.c
+ irm/irm_name_list.c
+ irm/irm_utils.c
+)
+
+add_executable(irm ${IRM_SOURCES})
+target_include_directories(irm PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(irm PRIVATE ouroboros-irm)
+install(TARGETS irm RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+
+add_executable(oping oping/oping.c)
+target_include_directories(oping PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(oping PRIVATE ${LIBM_LIBRARIES} ouroboros-dev)
+install(TARGETS oping RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(oecho oecho/oecho.c)
+target_include_directories(oecho PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(oecho PRIVATE ouroboros-dev)
+install(TARGETS oecho RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ocbr ocbr/ocbr.c)
+target_include_directories(ocbr PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(ocbr PRIVATE ouroboros-dev)
+install(TARGETS ocbr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(obc obc/obc.c)
+target_include_directories(obc PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(obc PRIVATE ouroboros-dev)
+install(TARGETS obc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(operf operf/operf.c)
+target_include_directories(operf PRIVATE ${TOOLS_INCLUDE_DIRS})
+target_link_libraries(operf PRIVATE ouroboros-dev)
+install(TARGETS operf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ add_executable(ovpn ovpn/ovpn.c)
+ target_include_directories(ovpn PRIVATE ${TOOLS_INCLUDE_DIRS})
+ target_link_libraries(ovpn PRIVATE ouroboros-dev)
+ install(TARGETS ovpn RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
diff --git a/src/tools/irm/CMakeLists.txt b/src/tools/irm/CMakeLists.txt
deleted file mode 100644
index e5e5c466..00000000
--- a/src/tools/irm/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(SOURCE_FILES
- # Add source files here
- irm.c
- irm_bind_program.c
- irm_bind_process.c
- irm_bind_ipcp.c
- irm_ipcp_create.c
- irm_ipcp_destroy.c
- irm_ipcp_bootstrap.c
- irm_ipcp_enroll.c
- irm_ipcp_list.c
- irm_ipcp_connect.c
- irm_ipcp_disconnect.c
- irm_unbind_program.c
- irm_unbind_process.c
- irm_unbind_ipcp.c
- irm_unbind.c
- irm_bind.c
- irm_ipcp.c
- irm_name.c
- irm_name_create.c
- irm_name_destroy.c
- irm_name_reg.c
- irm_name_unreg.c
- irm_name_list.c
- irm_utils.c
- )
-
-add_executable(irm ${SOURCE_FILES})
-
-target_link_libraries(irm LINK_PUBLIC ouroboros-irm)
-
-install(TARGETS irm RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/tools/irm/irm_ipcp_bootstrap.c b/src/tools/irm/irm_ipcp_bootstrap.c
index b8e5c54d..3fabc3cc 100644
--- a/src/tools/irm/irm_ipcp_bootstrap.c
+++ b/src/tools/irm/irm_ipcp_bootstrap.c
@@ -46,90 +46,131 @@
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
+#include <netinet/in.h>
#ifdef __FreeBSD__
#include <sys/socket.h>
#endif
-#define UNICAST "unicast"
-#define BROADCAST "broadcast"
-#define UDP "udp"
-#define ETH_LLC "eth-llc"
-#define ETH_DIX "eth-dix"
-#define LOCAL "local"
-
-#define MD5 "MD5"
-#define SHA3_224 "SHA3_224"
-#define SHA3_256 "SHA3_256"
-#define SHA3_384 "SHA3_384"
-#define SHA3_512 "SHA3_512"
-
-#define DEFAULT_ADDR_SIZE 4
-#define DEFAULT_EID_SIZE 8
-#define DEFAULT_DDNS 0
-#define DEFAULT_TTL 60
-#define DEFAULT_ADDR_AUTH ADDR_AUTH_FLAT_RANDOM
-#define DEFAULT_ROUTING ROUTING_LINK_STATE
-#define DEFAULT_CONG_AVOID CA_MB_ECN
-#define DEFAULT_HASH_ALGO DIR_HASH_SHA3_256
-#define DEFAULT_ETHERTYPE 0xA000
-#define DEFAULT_UDP_PORT 0x0D6B /* 3435 */
-
-#define FLAT_RANDOM_ADDR_AUTH "flat"
-#define LINK_STATE_ROUTING "link_state"
-#define LINK_STATE_LFA_ROUTING "lfa"
-#define LINK_STATE_ECM_ROUTING "ecmp"
-#define NONE_CA "none"
-#define MB_ECN_CA "mb-ecn"
+#define UNICAST "unicast"
+#define BROADCAST "broadcast"
+#define IP_UDP4 "udp4"
+#define IP_UDP6 "udp6"
+#define ETH_LLC "eth-llc"
+#define ETH_DIX "eth-dix"
+#define LOCAL "local"
+
+#define MD5 "MD5"
+#define SHA3_224 "SHA3_224"
+#define SHA3_256 "SHA3_256"
+#define SHA3_384 "SHA3_384"
+#define SHA3_512 "SHA3_512"
+
+#define FLAT_RANDOM "flat"
+#define DHT_DIR "DHT"
+#define LINK_STATE "link_state"
+#define LINK_STATE_LFA "lfa"
+#define LINK_STATE_ECM "ecmp"
+#define NONE_CA "none"
+#define MB_ECN_CA "mb-ecn"
+
+#define DT(x) default_dt_config.x
+#define DHT(x) default_dht_config.params.x
+#define UNI(x) default_uni_config.x
+#define DIX(x) eth_dix_default_conf.eth.x
+#define LLC(x) eth_llc_default_conf.eth.x
+#define UD4(x) udp4_default_conf.udp4.x
+#define UD6(x) udp6_default_conf.udp6.x
+
+static char * usage_str = \
+ "Usage: irm ipcp bootstrap\n"
+ " name <ipcp name>\n"
+ " layer <layer name>\n"
+ " [type [TYPE]]\n"
+ "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " "
+ IP_UDP4 " " IP_UDP6 " " ETH_LLC " " ETH_DIX "},\n\n"
+ "if TYPE == " UNICAST "\n"
+ " [addr_auth <ADDRESS_POLICY> (default: %s)]\n"
+ " [directory <DIRECTORY_POLICY> (default: %s)]\n"
+ " [hash [ALGORITHM] (default: %s)]\n"
+ " [routing <ROUTING_POLICY> (default: %s)]\n"
+ " [congestion <CONG_POLICY> (default: %s)]\n"
+ " [autobind]\n\n"
+ "where ADDRESS_POLICY in {" FLAT_RANDOM "}\n"
+ " DIRECTORY_POLICY in {" DHT_DIR "}\n"
+ " ALGORITHM in {" SHA3_224 " " SHA3_256 " "
+ SHA3_384 " " SHA3_512 "}\n"
+ " ROUTING_POLICY in {" LINK_STATE " "
+ LINK_STATE_LFA " " LINK_STATE_ECM "}\n"
+ " CONG_POLICY in {" NONE_CA " " MB_ECN_CA "}\n"
+ " [Data Transfer Constants]\n"
+ " [addr <address size> (default: %d)]\n"
+ " [eid <eid size> (default: %d)]\n"
+ " [ttl <max time-to-live>, default: %d)]\n\n"
+ "if DIRECTORY_POLICY == " DHT_DIR "\n"
+ " [dht_alpha <search factor> (default: %u)]\n"
+ " [dht_k <replication factor> (default: %u)]\n"
+ " [dht_t_expire <expiration (s)> (default: %u)]\n"
+ " [dht_t_refresh <contact refresh (s)> (default: %u)]\n"
+ " [dht_t_replicate <replication (s)> (default: %u)]\n\n"
+ "if ROUTING_POLICY == " LINK_STATE "\n"
+ " [ls_t_recalc <pff recalc interval (s)> (default: %ld)]\n"
+ " [ls_t_update <LSA update interval (s)> (default: %ld)]\n"
+ " [ls_t_timeo <link timeout (s)> (default: %ld)]\n\n"
+ "if TYPE == " IP_UDP4 "\n"
+ " ip <IP address in dotted notation>\n"
+ " [port <UDP port> (default: %d)]\n"
+ " [dns <DDNS IPv4 address in dotted notation>"
+ " (default: none)]\n\n"
+ "if TYPE == " IP_UDP6 "\n"
+ " ip <IPv6 address>\n"
+ " [port <UDP port> (default: %d)]\n"
+ " [dns <DDNS IPv6 address>"
+ " (default: none)]\n\n"
+
+ "if TYPE == " ETH_LLC "\n"
+ " dev <interface name>\n"
+ " [hash [ALGORITHM] (default: %s)]\n"
+ "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
+ SHA3_384 " " SHA3_512 "}\n\n"
+ "if TYPE == " ETH_DIX "\n"
+ " dev <interface name>\n"
+ " [ethertype <ethertype> (default: 0x%4X)]\n"
+ " [hash [ALGORITHM] (default: %s)]\n"
+ "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
+ SHA3_384 " " SHA3_512 "}\n\n"
+ "if TYPE == " LOCAL "\n"
+ " [hash [ALGORITHM] (default: %s)]\n"
+ "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
+ SHA3_384 " " SHA3_512 "}\n\n"
+ "if TYPE == " BROADCAST "\n"
+ " [autobind]\n\n";
static void usage(void)
{
/* FIXME: Add ipcp_config stuff. */
- printf("Usage: irm ipcp bootstrap\n"
- " name <ipcp name>\n"
- " layer <layer name>\n"
- " [type [TYPE]]\n"
- "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " "
- UDP " " ETH_LLC " " ETH_DIX "},\n\n"
- "if TYPE == " UNICAST "\n"
- " [addr <address size> (default: %d)]\n"
- " [eid <eid size> (default: %d)]\n"
- " [ttl (max time-to-live value, default: %d)]\n"
- " [addr_auth <ADDRESS_POLICY> (default: %s)]\n"
- " [routing <ROUTING_POLICY> (default: %s)]\n"
- " [congestion <CONG_POLICY> (default: %s)]\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- " [autobind]\n"
- "where ADDRESS_POLICY in {" FLAT_RANDOM_ADDR_AUTH "}\n"
- " ROUTING_POLICY in {" LINK_STATE_ROUTING " "
- LINK_STATE_LFA_ROUTING " " LINK_STATE_ECM_ROUTING "}\n"
- " CONG_POLICY in {" NONE_CA " " MB_ECN_CA "}\n"
- " ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
- "if TYPE == " UDP "\n"
- " ip <IP address in dotted notation>\n"
- " [port <UDP port> (default: %d)]\n"
- " [dns <DDNS IP address in dotted notation>"
- " (default: none)]\n\n"
- "if TYPE == " ETH_LLC "\n"
- " dev <interface name>\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
- "if TYPE == " ETH_DIX "\n"
- " dev <interface name>\n"
- " [ethertype <ethertype> (default: 0x%4X)]\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
- "if TYPE == " LOCAL "\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
- "if TYPE == " BROADCAST "\n"
- " [autobind]\n\n",
- DEFAULT_ADDR_SIZE, DEFAULT_EID_SIZE, DEFAULT_TTL,
- FLAT_RANDOM_ADDR_AUTH, LINK_STATE_ROUTING, MB_ECN_CA,
- SHA3_256, DEFAULT_UDP_PORT, SHA3_256, 0xA000, SHA3_256,
+ printf(usage_str,
+ /* unicast */
+ FLAT_RANDOM, DHT_DIR, SHA3_256, LINK_STATE, MB_ECN_CA,
+ /* dt */
+ DT(addr_size), DT(eid_size), DT(max_ttl),
+ /* dht */
+ DHT(alpha), DHT(k), DHT(t_expire),
+ DHT(t_refresh), DHT(t_replicate),
+ /* ls */
+ default_ls_config.t_recalc, default_ls_config.t_update,
+ default_ls_config.t_timeo,
+ /* udp4 */
+ UD4(port),
+ /* udp6 */
+ UD6(port),
+ /* eth_llc */
+ SHA3_256,
+ /* eth_dix */
+ DIX(ethertype),
+ SHA3_256,
+ /* local */
+ SHA3_256,
+ /* broadcast */
SHA3_256);
}
@@ -139,26 +180,32 @@ int do_bootstrap_ipcp(int argc,
char * ipcp = NULL;
pid_t pid = -1;
struct ipcp_config conf;
- uint8_t addr_size = DEFAULT_ADDR_SIZE;
- uint8_t eid_size = DEFAULT_EID_SIZE;
- uint8_t max_ttl = DEFAULT_TTL;
- enum pol_addr_auth addr_auth_type = DEFAULT_ADDR_AUTH;
- enum pol_routing routing_type = DEFAULT_ROUTING;
- enum pol_dir_hash hash_algo = DEFAULT_HASH_ALGO;
- enum pol_cong_avoid cong_avoid = DEFAULT_CONG_AVOID;
- uint32_t ip_addr = 0;
- uint32_t dns_addr = DEFAULT_DDNS;
+ struct dir_config dir_config = default_dir_config;
+ uint8_t addr_size = DT(addr_size);
+ uint8_t eid_size = DT(eid_size);
+ uint8_t max_ttl = DT(max_ttl);
+ struct routing_config routing = default_routing_config;
+ enum pol_addr_auth addr_auth_type = UNI(addr_auth_type);
+ enum pol_cong_avoid cong_avoid = UNI(cong_avoid);
+ enum pol_dir_hash hash_algo = DIR_HASH_SHA3_256;
+ char * ipstr = NULL;
+ char * dnsstr = NULL;
+ struct in_addr ip4_addr = {.s_addr = INADDR_ANY};
+ struct in_addr dns4_addr = UD4(dns_addr);
+ int port4 = UD4(port);
+ struct in6_addr ip6_addr = IN6ADDR_ANY_INIT;
+ struct in6_addr dns6_addr = UD6(dns_addr);
+ int port6 = UD6(port);
char * ipcp_type = NULL;
enum ipcp_type type = IPCP_INVALID;
char * layer = NULL;
char * dev = NULL;
- uint16_t ethertype = DEFAULT_ETHERTYPE;
+ uint16_t ethertype = DIX(ethertype);
struct ipcp_list_info * ipcps;
ssize_t len = 0;
int i = 0;
bool autobind = false;
int cargs;
- int port = DEFAULT_UDP_PORT;
while (argc > 0) {
cargs = 2;
@@ -180,11 +227,9 @@ int do_bootstrap_ipcp(int argc,
else
goto unknown_param;
} else if (matches(*argv, "ip") == 0) {
- if (inet_pton (AF_INET, *(argv + 1), &ip_addr) != 1)
- goto unknown_param;
+ ipstr = *(argv + 1);
} else if (matches(*argv, "dns") == 0) {
- if (inet_pton(AF_INET, *(argv + 1), &dns_addr) != 1)
- goto unknown_param;
+ dnsstr = *(argv + 1);
} else if (matches(*argv, "device") == 0) {
dev = *(argv + 1);
} else if (matches(*argv, "ethertype") == 0) {
@@ -206,26 +251,50 @@ int do_bootstrap_ipcp(int argc,
} else if (matches(*argv, "ttl") == 0) {
max_ttl = atoi(*(argv + 1));
} else if (matches(*argv, "port") == 0) {
- port = atoi(*(argv + 1));
+ port4 = atoi(*(argv + 1));
+ port6 = port4;
} else if (matches(*argv, "autobind") == 0) {
autobind = true;
cargs = 1;
} else if (matches(*argv, "addr_auth") == 0) {
- if (strcmp(FLAT_RANDOM_ADDR_AUTH, *(argv + 1)) == 0)
+ if (strcmp(FLAT_RANDOM, *(argv + 1)) == 0)
addr_auth_type = ADDR_AUTH_FLAT_RANDOM;
else
goto unknown_param;
- } else if (matches(*argv, "routing") == 0) {
- if (strcmp(LINK_STATE_ROUTING, *(argv + 1)) == 0)
- routing_type = ROUTING_LINK_STATE;
- else if (strcmp(LINK_STATE_LFA_ROUTING,
- *(argv + 1)) == 0)
- routing_type = ROUTING_LINK_STATE_LFA;
- else if (strcmp(LINK_STATE_ECM_ROUTING,
- *(argv + 1)) == 0)
- routing_type = ROUTING_LINK_STATE_ECMP;
+ } else if (matches(*argv, "directory") == 0) {
+ if (strcmp(DHT_DIR, *(argv + 1)) == 0)
+ dir_config.pol = DIR_DHT;
else
goto unknown_param;
+ } else if (matches(*argv, "dht_alpha") == 0) {
+ dir_config.dht.params.alpha = atoi(*(argv + 1));
+ } else if (matches(*argv, "dht_k") == 0) {
+ dir_config.dht.params.k = atoi(*(argv + 1));
+ } else if (matches(*argv, "dht_t_expire") == 0) {
+ dir_config.dht.params.t_expire = atoi(*(argv + 1));
+ } else if (matches(*argv, "dht_t_refresh") == 0) {
+ dir_config.dht.params.t_refresh = atoi(*(argv + 1));
+ } else if (matches(*argv, "dht_t_replicate") == 0) {
+ dir_config.dht.params.t_replicate = atoi(*(argv + 1));
+ } else if (matches(*argv, "routing") == 0) {
+ if (strcmp(LINK_STATE, *(argv + 1)) == 0) {
+ routing.pol = ROUTING_LINK_STATE;
+ routing.ls.pol = LS_SIMPLE;
+ } else if (strcmp(LINK_STATE_LFA, *(argv + 1)) == 0) {
+ routing.pol = ROUTING_LINK_STATE;
+ routing.ls.pol = LS_LFA;
+ } else if (strcmp(LINK_STATE_ECM, *(argv + 1)) == 0) {
+ routing.pol = ROUTING_LINK_STATE;
+ routing.ls.pol = LS_ECMP;
+ } else {
+ goto unknown_param;
+ }
+ } else if (matches(*argv, "ls_t_timeo") == 0) {
+ routing.ls.t_timeo = atoi(*(argv + 1));
+ } else if (matches(*argv, "ls_t_update") == 0) {
+ routing.ls.t_update = atoi(*(argv + 1));
+ } else if (matches(*argv, "ls_t_recalc") == 0) {
+ routing.ls.t_recalc = atoi(*(argv + 1));
} else if (matches(*argv, "congestion") == 0) {
if (strcmp(NONE_CA, *(argv + 1)) == 0)
cong_avoid = CA_NONE;
@@ -257,21 +326,59 @@ int do_bootstrap_ipcp(int argc,
}
if (ipcp_type != NULL) {
- if (strcmp(ipcp_type, UNICAST) == 0)
+ if (matches(ipcp_type, UNICAST) == 0)
type = IPCP_UNICAST;
- else if (strcmp(ipcp_type, BROADCAST) == 0)
+ else if (matches(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (strcmp(ipcp_type, UDP) == 0)
- type = IPCP_UDP;
- else if (strcmp(ipcp_type, ETH_LLC) == 0)
- type = IPCP_ETH_LLC;
- else if (strcmp(ipcp_type, ETH_DIX) == 0)
+ else if (matches(ipcp_type, IP_UDP4) == 0)
+ type = IPCP_UDP4;
+ else if (matches(ipcp_type, IP_UDP6) == 0)
+ type = IPCP_UDP6;
+ else if (matches(ipcp_type, ETH_DIX) == 0)
type = IPCP_ETH_DIX;
- else if (strcmp(ipcp_type, LOCAL) == 0)
+ else if (matches(ipcp_type, ETH_LLC) == 0)
+ type = IPCP_ETH_LLC;
+ else if (matches(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
else goto fail_usage;
}
+ if (type == IPCP_UDP4) {
+ if (inet_pton (AF_INET, ipstr, &ip4_addr) != 1) {
+ printf("Invalid IPv4 address: \"%s\".\n", ipstr);
+ goto fail_usage;
+ }
+
+ if (ip4_addr.s_addr == INADDR_ANY) {
+ printf("Cannot use IPv4 address: \"%s\".\n", ipstr);
+ goto fail_usage;
+ }
+
+ if (dnsstr != NULL &&
+ inet_pton(AF_INET, dnsstr, &dns4_addr) != 1) {
+ printf("Invalid DNS IPv4 address: \"%s\".\n", dnsstr);
+ goto fail_usage;
+ }
+ }
+
+ if (type == IPCP_UDP6) {
+ if (inet_pton(AF_INET6, ipstr, &ip6_addr) != 1) {
+ printf("Invalid IPv6 address: \"%s\".\n", ipstr);
+ goto fail_usage;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6_addr)) {
+ printf("Cannot use IPv6 address: \"%s\".\n", ipstr);
+ goto fail_usage;
+ }
+
+ if (dnsstr != NULL &&
+ inet_pton(AF_INET6, dnsstr, &dns6_addr) != 1) {
+ printf("Invalid DNS IPv6 address: \"%s\".\n", dnsstr);
+ goto fail_usage;
+ }
+ }
+
if (pid == -1) {
if (ipcp_type == NULL) {
printf("No IPCPs matching %s found.\n\n", ipcp);
@@ -315,16 +422,20 @@ int do_bootstrap_ipcp(int argc,
conf.unicast.dt.addr_size = addr_size;
conf.unicast.dt.eid_size = eid_size;
conf.unicast.dt.max_ttl = max_ttl;
- conf.unicast.dt.routing_type = routing_type;
+ conf.unicast.dt.routing = routing;
conf.unicast.addr_auth_type = addr_auth_type;
conf.unicast.cong_avoid = cong_avoid;
+ conf.unicast.dir = dir_config;
break;
- case IPCP_UDP:
- if (ip_addr == 0)
- goto fail_usage;
- conf.udp.ip_addr = ip_addr;
- conf.udp.dns_addr = dns_addr;
- conf.udp.port = port;
+ case IPCP_UDP4:
+ conf.udp4.ip_addr = ip4_addr;
+ conf.udp4.dns_addr = dns4_addr;
+ conf.udp4.port = port4;
+ break;
+ case IPCP_UDP6:
+ conf.udp6.ip_addr = ip6_addr;
+ conf.udp6.dns_addr = dns6_addr;
+ conf.udp6.port = port6;
break;
case IPCP_ETH_DIX:
conf.eth.ethertype = ethertype;
diff --git a/src/tools/irm/irm_ipcp_create.c b/src/tools/irm/irm_ipcp_create.c
index 35d33782..e2a5c488 100644
--- a/src/tools/irm/irm_ipcp_create.c
+++ b/src/tools/irm/irm_ipcp_create.c
@@ -46,7 +46,8 @@
#define UNICAST "unicast"
#define BROADCAST "broadcast"
-#define UDP "udp"
+#define UDP4 "udp4"
+#define UDP6 "udp6"
#define ETH_LLC "eth-llc"
#define ETH_DIX "eth-dix"
#define LOCAL "local"
@@ -57,7 +58,7 @@ static void usage(void)
" name <ipcp name>\n"
" type [TYPE]\n\n"
"where TYPE in {" UNICAST " " BROADCAST " " LOCAL " "
- UDP " " ETH_LLC " " ETH_DIX "}\n");
+ UDP4 " " UDP6 " " ETH_LLC " " ETH_DIX "}\n");
}
int do_create_ipcp(int argc,
@@ -92,8 +93,10 @@ int do_create_ipcp(int argc,
type = IPCP_UNICAST;
else if (strcmp(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (strcmp(ipcp_type, UDP) == 0)
- type = IPCP_UDP;
+ else if (strcmp(ipcp_type, UDP4) == 0)
+ type = IPCP_UDP4;
+ else if (strcmp(ipcp_type, UDP6) == 0)
+ type = IPCP_UDP6;
else if (strcmp(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
else if (strcmp(ipcp_type, ETH_LLC) == 0)
diff --git a/src/tools/irm/irm_ipcp_list.c b/src/tools/irm/irm_ipcp_list.c
index dfa3099f..54985eb4 100644
--- a/src/tools/irm/irm_ipcp_list.c
+++ b/src/tools/irm/irm_ipcp_list.c
@@ -48,7 +48,8 @@
#define UNICAST "unicast"
#define BROADCAST "broadcast"
-#define UDP "udp"
+#define UDP4 "udp4"
+#define UDP6 "udp6"
#define ETH_LLC "eth-llc"
#define ETH_DIX "eth-dix"
#define LOCAL "local"
@@ -60,7 +61,7 @@ static void usage(void)
" [layer <layer_name>]\n\n"
" [type [TYPE]]\n\n"
"where TYPE = {" UNICAST " " LOCAL " "
- UDP " " ETH_LLC " " ETH_DIX "}\n");
+ UDP4 " " UDP6 " " ETH_LLC " " ETH_DIX "}\n");
}
static char * str_type(enum ipcp_type type)
@@ -74,8 +75,10 @@ static char * str_type(enum ipcp_type type)
return ETH_LLC;
case IPCP_ETH_DIX:
return ETH_DIX;
- case IPCP_UDP:
- return UDP;
+ case IPCP_UDP4:
+ return UDP4;
+ case IPCP_UDP6:
+ return UDP6;
case IPCP_LOCAL:
return LOCAL;
default:
@@ -113,8 +116,10 @@ int do_list_ipcp(int argc,
type = IPCP_UNICAST;
else if (strcmp(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (strcmp(ipcp_type, UDP) == 0)
- type = IPCP_UDP;
+ else if (strcmp(ipcp_type, UDP4) == 0)
+ type = IPCP_UDP4;
+ else if (strcmp(ipcp_type, UDP6) == 0)
+ type = IPCP_UDP6;
else if (strcmp(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
else if (strcmp(ipcp_type, ETH_LLC) == 0)
diff --git a/src/tools/irm/irm_name_create.c b/src/tools/irm/irm_name_create.c
index a0079cad..b132d355 100644
--- a/src/tools/irm/irm_name_create.c
+++ b/src/tools/irm/irm_name_create.c
@@ -36,31 +36,82 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 500
+
+#include <ouroboros/errno.h>
#include <ouroboros/irm.h>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include "irm_ops.h"
#include "irm_utils.h"
#define RR "round-robin"
#define SPILL "spillover"
+#define SENC "<security_dir>/server/<name>/enc.conf"
+#define SCRT "<security_dir>/server/<name>/crt.pem"
+#define SKEY "<security_dir>/server/<name>/key.pem"
+#define CENC "<security_dir>/client/<name>/enc.conf"
+#define CCRT "<security_dir>/client/<name>/crt.pem"
+#define CKEY "<security_dir>/client/<name>/key.pem"
static void usage(void)
{
printf("Usage: irm name create\n"
- " <name>\n"
- " lb [LB_POLICY], default: %s\n\n"
- "where LB_POLICY in {" RR " " SPILL "}\n", RR);
+ " <name>. max %d chars.\n"
+ " [lb LB_POLICY], default: %s\n"
+ " [sencpath <path>, default: " SENC "]\n"
+ " [scrtpath <path>, default: " SCRT "]\n"
+ " [skeypath <path>, default: " SKEY "]\n"
+ " [cencpath <path>, default: " CENC "]\n"
+ " [ccrtpath <path>, default: " CCRT "]\n"
+ " [ckeypath <path>, default: " CKEY "]\n"
+ "\n"
+ "where LB_POLICY in {" RR " " SPILL "}\n",
+ NAME_SIZE, RR);
+}
+
+static int cp_chk_path(char * buf,
+ const char * path)
+{
+ char * rp = realpath(path, NULL);
+ if (rp == NULL) {
+ printf("Failed to check path %s: %s\n.",
+ path, strerror(errno));
+ goto fail_rp;
+ }
+
+ if (strlen(rp) > NAME_PATH_SIZE) {
+ printf("File path too long: %s.\n", rp);
+ goto fail_len;
+ }
+
+ strcpy(buf, rp);
+ free(rp);
+
+ return 0;
+
+ fail_len:
+ free(rp);
+ fail_rp:
+ return -1;
}
int do_create_name(int argc,
char ** argv)
{
- char * name = NULL;
- char * lb_pol = RR;
- enum pol_balance pol_lb = LB_RR;
+ struct name_info info = {};
+ char * name = NULL;
+ char * sencpath = NULL;
+ char * scrtpath = NULL;
+ char * skeypath = NULL;
+ char * cencpath = NULL;
+ char * ccrtpath = NULL;
+ char * ckeypath = NULL;
+ char * lb_pol = RR;
name = *(argv++);
--argc;
@@ -68,6 +119,18 @@ int do_create_name(int argc,
while (argc > 0) {
if (matches(*argv, "lb") == 0) {
lb_pol = *(argv + 1);
+ } else if (matches(*argv, "sencpath") == 0) {
+ sencpath = *(argv + 1);
+ } else if (matches(*argv, "scrtpath") == 0) {
+ scrtpath = *(argv + 1);
+ } else if (matches(*argv, "skeypath") == 0) {
+ skeypath = *(argv + 1);
+ } else if (matches(*argv, "cencpath") == 0) {
+ cencpath = *(argv + 1);
+ } else if (matches(*argv, "ccrtpath") == 0) {
+ ccrtpath = *(argv + 1);
+ } else if (matches(*argv, "ckeypath") == 0) {
+ ckeypath = *(argv + 1);
} else {
printf("\"%s\" is unknown, try \"irm "
"name create\".\n", *argv);
@@ -78,19 +141,45 @@ int do_create_name(int argc,
argv += 2;
}
- if (name == NULL) {
- usage();
- return -1;
+ if (name == NULL)
+ goto fail;
+
+ if (strlen(name) > NAME_SIZE) {
+ printf("Name too long.\n");
+ goto fail;
}
+ strcpy(info.name, name);
+
+ if (sencpath != NULL && cp_chk_path(info.s.enc, sencpath) < 0)
+ goto fail;
+
+ if (scrtpath != NULL && cp_chk_path(info.s.crt, scrtpath) < 0)
+ goto fail;
+
+ if (skeypath != NULL && cp_chk_path(info.s.key, skeypath) < 0)
+ goto fail;
+
+ if (cencpath != NULL && cp_chk_path(info.c.enc, cencpath) < 0)
+ goto fail;
+
+ if (ccrtpath != NULL && cp_chk_path(info.c.crt, ccrtpath) < 0)
+ goto fail;
+
+ if (ckeypath != NULL && cp_chk_path(info.c.key, ckeypath) < 0)
+ goto fail;
+
if (strcmp(lb_pol, RR) == 0)
- pol_lb = LB_RR;
+ info.pol_lb = LB_RR;
else if (strcmp(lb_pol, SPILL) == 0)
- pol_lb = LB_SPILL;
+ info.pol_lb = LB_SPILL;
else {
usage();
return -1;
}
- return irm_create_name(name, pol_lb);
+ return irm_create_name(&info);
+ fail:
+ usage();
+ return -1;
}
diff --git a/src/tools/irm/irm_name_reg.c b/src/tools/irm/irm_name_reg.c
index 061ed8be..7689119a 100644
--- a/src/tools/irm/irm_name_reg.c
+++ b/src/tools/irm/irm_name_reg.c
@@ -107,14 +107,23 @@ int do_reg_name(int argc,
return -1;
}
+ if (strlen(name) > NAME_SIZE) {
+ printf("Name too long.\n");
+ usage();
+ return -1;
+ }
+
ipcps_len = irm_list_ipcps(&ipcps);
- if (ipcps_len < 0)
- return ipcps_len;
+ if (ipcps_len <= 0) {
+ printf("Failed to list IPCPs.\n");
+ return -1;
+ }
names_len = irm_list_names(&names);
if (names_len < 0) {
+ printf("Failed to list names.\n");
free(ipcps);
- return names_len;
+ return -1;
}
for (i = 0; i < names_len; ++i) {
@@ -124,11 +133,19 @@ int do_reg_name(int argc,
}
}
- if (name_create && irm_create_name(name, LB_SPILL)) {
- printf("Error creating name.");
- free(ipcps);
- free(name);
- return -1;
+ if (name_create) {
+ struct name_info info = {
+ .pol_lb = LB_SPILL
+ };
+
+ strcpy(info.name, name);
+
+ if (irm_create_name(&info) < 0) {
+ printf("Error creating name.");
+ free(ipcps);
+ free(names);
+ return -1;
+ }
}
for (i = 0; i < ipcps_len; ++i) {
@@ -136,6 +153,8 @@ int do_reg_name(int argc,
for (j = 0; j < layers_len; j++) {
if (wildcard_match(layers[j], ipcps[i].layer) == 0) {
if (irm_reg_name(name, ipcps[i].pid)) {
+ printf("Failed to register with %s",
+ ipcps[i].layer);
free(ipcps);
free(names);
return -1;
@@ -145,6 +164,8 @@ int do_reg_name(int argc,
for (j = 0; j < ipcp_len; j++) {
if (wildcard_match(ipcp[j], ipcps[i].name) == 0) {
if (irm_reg_name(name, ipcps[i].pid)) {
+ printf("Failed to register with %s",
+ ipcps[i].name);
free(ipcps);
free(names);
return -1;
diff --git a/src/tools/obc/CMakeLists.txt b/src/tools/obc/CMakeLists.txt
deleted file mode 100644
index db5e999b..00000000
--- a/src/tools/obc/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(SOURCE_FILES
- # Add source files here
- obc.c
- )
-
-add_executable(obc ${SOURCE_FILES})
-
-target_link_libraries(obc LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS obc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/obc/obc.c b/src/tools/obc/obc.c
index 462cbea9..778eb8a8 100644
--- a/src/tools/obc/obc.c
+++ b/src/tools/obc/obc.c
@@ -63,7 +63,7 @@ static int reader_main(const char * dst)
printf("Starting a reader.\n");
- fd = flow_join(dst, NULL, NULL);
+ fd = flow_join(dst, NULL);
if (fd < 0) {
printf("Failed to join broadcast.\n");
return -1;
@@ -91,7 +91,7 @@ static int writer_main(const char * dst,
int fd = 0;
size_t len = strlen(message) + 1;
- fd = flow_join(dst, NULL, NULL);
+ fd = flow_join(dst, NULL);
if (fd < 0) {
printf("Failed to join broadcast.\n");
return -1;
diff --git a/src/tools/ocbr/CMakeLists.txt b/src/tools/ocbr/CMakeLists.txt
deleted file mode 100644
index f7ba66cd..00000000
--- a/src/tools/ocbr/CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-set(SOURCE_FILES
- # Add source files here
- ocbr.c
- )
-
-add_executable(ocbr ${SOURCE_FILES})
-
-target_link_libraries(ocbr LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS ocbr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/ocbr/ocbr_client.c b/src/tools/ocbr/ocbr_client.c
index ba7b41f4..eada6e60 100644
--- a/src/tools/ocbr/ocbr_client.c
+++ b/src/tools/ocbr/ocbr_client.c
@@ -129,7 +129,7 @@ int client_main(char * server,
++seqnr;
- if (ts_diff_us(&start, &end) / MILLION >= duration)
+ if (ts_diff_us(&end, &start) / MILLION >= duration)
stop = true;
}
} else { /* flood */
@@ -142,7 +142,7 @@ int client_main(char * server,
++seqnr;
- if (ts_diff_us(&start, &end) / MILLION
+ if (ts_diff_us(&end, &start) / MILLION
>= (long) duration)
stop = true;
}
@@ -151,7 +151,7 @@ int client_main(char * server,
clock_gettime(CLOCK_REALTIME, &end);
- ms = ts_diff_ms(&start, &end);
+ ms = ts_diff_ms(&end, &start);
printf("sent statistics: "
"%9ld packets, %12ld bytes in %9d ms, %4.4f Mb/s\n",
diff --git a/src/tools/ocbr/ocbr_server.c b/src/tools/ocbr/ocbr_server.c
index a4bbadd4..34c4fa94 100644
--- a/src/tools/ocbr/ocbr_server.c
+++ b/src/tools/ocbr/ocbr_server.c
@@ -114,14 +114,14 @@ static void handle_flow(int fd)
bytes_read += count;
}
- if (ts_diff_us(&alive, &now)
+ if (ts_diff_us(&now, &alive)
> server_settings.timeout * MILLION) {
printf("Test on flow %d timed out\n", fd);
stop = true;
}
- if (stop || ts_diff_ms(&now, &iv_end) < 0) {
- long us = ts_diff_us(&iv_start, &now);
+ if (stop || ts_diff_ms(&now, &iv_end) > 0) {
+ long us = ts_diff_us(&now, &iv_start);
printf("Flow %4d: %9ld packets (%12ld bytes) in %9ld ms"
" => %9.4f pps, %9.4f Mbps\n",
fd,
diff --git a/src/tools/oecho/CMakeLists.txt b/src/tools/oecho/CMakeLists.txt
deleted file mode 100644
index 50a66138..00000000
--- a/src/tools/oecho/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(SOURCE_FILES
- # Add source files here
- oecho.c
- )
-
-add_executable(oecho ${SOURCE_FILES})
-
-target_link_libraries(oecho LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS oecho RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/operf/CMakeLists.txt b/src/tools/operf/CMakeLists.txt
deleted file mode 100644
index b6faf04e..00000000
--- a/src/tools/operf/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-find_library(LIBM_LIBRARIES m)
-if(NOT LIBM_LIBRARIES)
- message(FATAL_ERROR "libm not found")
-endif()
-
-set(SOURCE_FILES
- # Add source files here
- operf.c
- )
-
-add_executable(operf ${SOURCE_FILES})
-
-target_link_libraries(operf LINK_PUBLIC ${LIBM_LIBRARIES} ouroboros-dev)
-
-install(TARGETS operf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/operf/operf.c b/src/tools/operf/operf.c
index 10896bd5..3263d3bf 100644
--- a/src/tools/operf/operf.c
+++ b/src/tools/operf/operf.c
@@ -54,7 +54,6 @@
#include <stdlib.h>
#include <sys/time.h>
#include <arpa/inet.h>
-#include <math.h>
#include <errno.h>
#include <float.h>
diff --git a/src/tools/operf/operf_client.c b/src/tools/operf/operf_client.c
index 63f98299..7060ce5b 100644
--- a/src/tools/operf/operf_client.c
+++ b/src/tools/operf/operf_client.c
@@ -133,7 +133,7 @@ void * writer(void * o)
clock_gettime(CLOCK_REALTIME, &start);
clock_gettime(CLOCK_REALTIME, &now);
- while (!stop && ts_diff_ms(&start, &now) < client.duration) {
+ while (!stop && ts_diff_ms(&now, &start) > client.duration) {
if (!client.flood) {
clock_gettime(CLOCK_REALTIME, &now);
ts_add(&now, &intv, &end);
@@ -230,10 +230,10 @@ int client_main(void)
printf("%ld received, ", client.rcvd);
printf("%ld%% packet loss, ", client.sent == 0 ? 0 :
100 - ((100 * client.rcvd) / client.sent));
- printf("time: %.3f ms, ", ts_diff_us(&tic, &toc) / 1000.0);
+ printf("time: %.3f ms, ", ts_diff_us(&toc, &tic) / 1000.0);
printf("bandwidth: %.3lf Mb/s.\n",
(client.rcvd * client.size * 8)
- / (double) ts_diff_us(&tic, &toc));
+ / (double) ts_diff_us(&toc, &tic));
}
flow_dealloc(fd);
diff --git a/src/tools/operf/operf_server.c b/src/tools/operf/operf_server.c
index d11f3486..a611f79c 100644
--- a/src/tools/operf/operf_server.c
+++ b/src/tools/operf/operf_server.c
@@ -66,7 +66,7 @@ void * cleaner_thread(void * o)
pthread_mutex_lock(&server.lock);
for (i = 0; i < OPERF_MAX_FLOWS; ++i)
if (fset_has(server.flows, i) &&
- ts_diff_ms(&server.times[i], &now)
+ ts_diff_ms(&now, &server.times[i])
> server.timeout) {
printf("Flow %d timed out.\n", i);
fset_del(server.flows, i);
diff --git a/src/tools/oping/CMakeLists.txt b/src/tools/oping/CMakeLists.txt
deleted file mode 100644
index 31a4f961..00000000
--- a/src/tools/oping/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-find_library(LIBM_LIBRARIES m)
-if(NOT LIBM_LIBRARIES)
- message(FATAL_ERROR "libm not found")
-endif()
-
-mark_as_advanced(LIBM_LIBRARIES)
-
-set(SOURCE_FILES
- # Add source files here
- oping.c
- )
-
-add_executable(oping ${SOURCE_FILES})
-
-target_link_libraries(oping LINK_PUBLIC ${LIBM_LIBRARIES} ouroboros-dev)
-
-install(TARGETS oping RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/oping/oping.c b/src/tools/oping/oping.c
index ed3529e5..47b86c68 100644
--- a/src/tools/oping/oping.c
+++ b/src/tools/oping/oping.c
@@ -75,12 +75,14 @@
"\n" \
" -c, --count Number of packets\n" \
" -d, --duration Duration of the test (default 1s)\n" \
+" -f, --flood Send back-to-back without waiting\n" \
" -i, --interval Interval (default 1000ms)\n" \
" -n, --server-name Name of the oping server\n" \
-" -q, --qos QoS (raw, raw_crypt, best, video, voice, data)\n" \
+" -q, --qos QoS (raw, best, video, voice, data)\n" \
" -s, --size Payload size (B, default 64)\n" \
" -Q, --quiet Only print final statistics\n" \
" -D, --timeofday Print time of day before each line\n" \
+" --poll Server uses polling (lower latency)\n" \
"\n" \
" --help Display this help text and exit\n" \
@@ -90,6 +92,7 @@ struct {
uint32_t count;
int size;
bool timestamp;
+ bool flood;
qosspec_t qs;
/* stats */
@@ -114,6 +117,7 @@ struct {
pthread_mutex_t lock;
bool quiet;
+ bool poll;
pthread_t cleaner_pt;
pthread_t accept_pt;
@@ -172,9 +176,11 @@ int main(int argc,
client.size = 64;
client.count = INT_MAX;
client.timestamp = false;
+ client.flood = false;
client.qs = qos_raw;
client.quiet = false;
server.quiet = false;
+ server.poll = false;
while (argc > 0) {
if ((strcmp(*argv, "-i") == 0 ||
@@ -212,6 +218,9 @@ int main(int argc,
} else if (strcmp(*argv, "-l") == 0 ||
strcmp(*argv, "--listen") == 0) {
serv = true;
+ } else if (strcmp(*argv, "-f") == 0 ||
+ strcmp(*argv, "--flood") == 0) {
+ client.flood = true;
} else if (strcmp(*argv, "-D") == 0 ||
strcmp(*argv, "--timeofday") == 0) {
client.timestamp = true;
@@ -219,6 +228,8 @@ int main(int argc,
strcmp(*argv, "--quiet") == 0) {
client.quiet = true;
server.quiet = true;
+ } else if (strcmp(*argv, "--poll") == 0) {
+ server.poll = true;
} else {
goto fail;
}
@@ -244,8 +255,6 @@ int main(int argc,
client.qs = qos_voice;
else if (strcmp(qos, "data") == 0)
client.qs = qos_data;
- else if (strcmp(qos, "raw_crypt") == 0)
- client.qs = qos_raw_crypt;
else
printf("Unknown QoS cube, defaulting to raw.\n");
}
diff --git a/src/tools/oping/oping_client.c b/src/tools/oping/oping_client.c
index 7b03c83d..1513eac8 100644
--- a/src/tools/oping/oping_client.c
+++ b/src/tools/oping/oping_client.c
@@ -53,6 +53,20 @@ void shutdown_client(int signo, siginfo_t * info, void * c)
}
}
+static void update_rtt_stats(double ms)
+{
+ double d;
+
+ if (ms < client.rtt_min)
+ client.rtt_min = ms;
+ if (ms > client.rtt_max)
+ client.rtt_max = ms;
+
+ d = (ms - client.rtt_avg);
+ client.rtt_avg += d / client.rcvd;
+ client.rtt_m2 += d * (ms - client.rtt_avg);
+}
+
void * reader(void * o)
{
struct timespec timeout = {client.interval / 1000 + 2, 0};
@@ -64,7 +78,6 @@ void * reader(void * o)
int fd = *((int *) o);
int msg_len = 0;
double ms = 0;
- double d = 0;
uint32_t exp_id = 0;
fccntl(fd, FLOWSRCVTIMEO, &timeout);
@@ -100,7 +113,7 @@ void * reader(void * o)
sent.tv_sec = msg->tv_sec;
sent.tv_nsec = msg->tv_nsec;
- ms = ts_diff_us(&sent, &now) / 1000.0;
+ ms = ts_diff_us(&now, &sent) / 1000.0;
if (id < exp_id)
++client.ooo;
@@ -122,14 +135,7 @@ void * reader(void * o)
id < exp_id ? " [out-of-order]" : "");
}
- if (ms < client.rtt_min)
- client.rtt_min = ms;
- if (ms > client.rtt_max)
- client.rtt_max = ms;
-
- d = (ms - client.rtt_avg);
- client.rtt_avg += d / client.rcvd;
- client.rtt_m2 += d * (ms - client.rtt_avg);
+ update_rtt_stats(ms);
if (id >= exp_id)
exp_id = id + 1;
@@ -204,13 +210,103 @@ static void client_fini(void)
return;
}
+static void print_stats(struct timespec * tic,
+ struct timespec * toc)
+{
+ printf("\n");
+ printf("--- %s ping statistics ---\n", client.s_apn);
+ printf("%d packets transmitted, ", client.sent);
+ printf("%d received, ", client.rcvd);
+ printf("%zd out-of-order, ", client.ooo);
+ printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
+ ceil(100 - (100 * (client.rcvd / (float) client.sent))));
+ printf("time: %.3f ms\n", ts_diff_us(toc, tic) / 1000.0);
+
+ if (client.rcvd > 0) {
+ printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
+ client.rtt_min,
+ client.rtt_avg,
+ client.rtt_max);
+ if (client.rcvd > 1)
+ printf("%.3f ms\n",
+ sqrt(client.rtt_m2 / (client.rcvd - 1)));
+ else
+ printf("NaN ms\n");
+ }
+}
+
+static int flood_ping(int fd)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ struct timespec sent;
+ struct timespec rcvd;
+ double ms;
+
+ memset(buf, 0, client.size);
+
+ if (!client.quiet)
+ printf("Pinging %s with %d bytes of data (%u packets):\n\n",
+ client.s_apn, client.size, client.count);
+
+ while (!stop && client.sent < client.count) {
+ clock_gettime(CLOCK_MONOTONIC, &sent);
+
+ msg->type = htonl(ECHO_REQUEST);
+ msg->id = htonl(client.sent);
+ msg->tv_sec = sent.tv_sec;
+ msg->tv_nsec = sent.tv_nsec;
+
+ if (flow_write(fd, buf, client.size) < 0) {
+ printf("Failed to send packet.\n");
+ break;
+ }
+
+ ++client.sent;
+
+ if (flow_read(fd, buf, OPING_BUF_SIZE) < 0) {
+ printf("Failed to read packet.\n");
+ break;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &rcvd);
+
+ if (ntohl(msg->type) != ECHO_REPLY)
+ continue;
+
+ ++client.rcvd;
+
+ sent.tv_sec = msg->tv_sec;
+ sent.tv_nsec = msg->tv_nsec;
+ ms = ts_diff_us(&rcvd, &sent) / 1000.0;
+
+ update_rtt_stats(ms);
+
+ if (!client.quiet)
+ printf("%d bytes from %s: seq=%d time=%.3f ms\n",
+ client.size, client.s_apn,
+ ntohl(msg->id), ms);
+ }
+
+ return 0;
+}
+
+static int threaded_ping(int fd)
+{
+ pthread_create(&client.reader_pt, NULL, reader, &fd);
+ pthread_create(&client.writer_pt, NULL, writer, &fd);
+
+ pthread_join(client.writer_pt, NULL);
+ pthread_join(client.reader_pt, NULL);
+
+ return 0;
+}
+
static int client_main(void)
{
struct sigaction sig_act;
-
struct timespec tic;
struct timespec toc;
-
int fd;
memset(&sig_act, 0, sizeof sig_act);
@@ -241,37 +337,16 @@ static int client_main(void)
clock_gettime(CLOCK_REALTIME, &tic);
- pthread_create(&client.reader_pt, NULL, reader, &fd);
- pthread_create(&client.writer_pt, NULL, writer, &fd);
-
- pthread_join(client.writer_pt, NULL);
- pthread_join(client.reader_pt, NULL);
+ if (client.flood)
+ flood_ping(fd);
+ else
+ threaded_ping(fd);
clock_gettime(CLOCK_REALTIME, &toc);
- printf("\n");
- printf("--- %s ping statistics ---\n", client.s_apn);
- printf("%d packets transmitted, ", client.sent);
- printf("%d received, ", client.rcvd);
- printf("%zd out-of-order, ", client.ooo);
- printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
- ceil(100 - (100 * (client.rcvd / (float) client.sent))));
- printf("time: %.3f ms\n", ts_diff_us(&tic, &toc) / 1000.0);
-
- if (client.rcvd > 0) {
- printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
- client.rtt_min,
- client.rtt_avg,
- client.rtt_max);
- if (client.rcvd > 1)
- printf("%.3f ms\n",
- sqrt(client.rtt_m2 / (client.rcvd - 1)));
- else
- printf("NaN ms\n");
- }
+ print_stats(&tic, &toc);
flow_dealloc(fd);
-
client_fini();
return 0;
diff --git a/src/tools/oping/oping_server.c b/src/tools/oping/oping_server.c
index 6f76869c..13ab8f47 100644
--- a/src/tools/oping/oping_server.c
+++ b/src/tools/oping/oping_server.c
@@ -36,6 +36,8 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+ #include <ouroboros/hash.h>
+
void shutdown_server(int signo, siginfo_t * info, void * c)
{
(void) info;
@@ -67,7 +69,7 @@ void * cleaner_thread(void * o)
time_t diff;
pthread_mutex_lock(&server.lock);
- diff = ts_diff_ms(&server.times[i], &now);
+ diff = ts_diff_ms(&now, &server.times[i]);
pthread_mutex_unlock(&server.lock);
if (diff > deadline_ms) {
@@ -87,12 +89,15 @@ void * server_thread(void *o)
struct oping_msg * msg = (struct oping_msg *) buf;
struct timespec now = {0, 0};
struct timespec timeout = {0, 100 * MILLION};
+ struct timespec poll_timeout = {0, 0};
int fd;
(void) o;
while (true) {
- if (fevent(server.flows, server.fq, &timeout) == -ETIMEDOUT)
+ if (fevent(server.flows, server.fq,
+ server.poll ? &poll_timeout : &timeout)
+ == -ETIMEDOUT)
continue;
while ((fd = fqueue_next(server.fq)) >= 0) {
@@ -100,14 +105,15 @@ void * server_thread(void *o)
if (msg_len < 0)
continue;
+ if (!server.quiet)
+ printf("Received %d bytes on fd %d.\n",
+ msg_len, fd);
+
if (ntohl(msg->type) != ECHO_REQUEST) {
printf("Invalid message on fd %d.\n", fd);
continue;
}
- if (!server.quiet)
- printf("Received %d bytes on fd %d.\n", msg_len, fd);
-
clock_gettime(CLOCK_REALTIME, &now);
pthread_mutex_lock(&server.lock);
@@ -137,8 +143,8 @@ void * accept_thread(void * o)
while (true) {
fd = flow_accept(&qs, NULL);
if (fd < 0) {
- printf("Failed to accept flow.\n");
- break;
+ printf("Failed to accept flow: %d \n", fd);
+ continue;
}
printf("New flow %d.\n", fd);
diff --git a/src/tools/ovpn/CMakeLists.txt b/src/tools/ovpn/CMakeLists.txt
deleted file mode 100644
index f3a2cac8..00000000
--- a/src/tools/ovpn/CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-set(SOURCE_FILES
- # Add source files here
- ovpn.c
- )
-
-add_executable(ovpn ${SOURCE_FILES})
-
-target_link_libraries(ovpn LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS ovpn RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/ovpn/ovpn.c b/src/tools/ovpn/ovpn.c
index 5333ff40..b25e3ea2 100644
--- a/src/tools/ovpn/ovpn.c
+++ b/src/tools/ovpn/ovpn.c
@@ -68,7 +68,6 @@ static void usage(void)
"server to connect to\n"
" -i, --ip IP address to give to TUN device\n"
" -m, --mask Subnet mask to give to TUN device\n"
- " -C, --crypt AES encryption (default: off)\n"
"\n"
" --help Display this help text and exit\n");
}
@@ -194,7 +193,6 @@ int main(int argc,
{{"ip", required_argument, NULL, 'i'},
{"mask", required_argument, NULL, 'm'},
{"name", required_argument, NULL, 'n'},
- {"crypt", no_argument, NULL, 'C'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
@@ -231,9 +229,6 @@ int main(int argc,
case 'n':
name = optarg;
break;
- case 'C':
- qs = qos_raw_crypt;
- break;
case 'h':
usage();
exit(EXIT_SUCCESS);
diff --git a/src/tools/time_utils.h b/src/tools/time_utils.h
index c17282dc..a4117f44 100644
--- a/src/tools/time_utils.h
+++ b/src/tools/time_utils.h
@@ -53,17 +53,17 @@
#include <sys/time.h>
/* functions for timespecs */
-#define ts_diff_ns(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+#define ts_diff_ns(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec))
-#define ts_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define ts_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / 1000L)
-#define ts_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+#define ts_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / MILLION)
/* functions for timevals are the same */
-#define tv_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define tv_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ ((tx)->tv_usec - (t0)->tv_usec) / 1000L)
-#define tv_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+#define tv_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_usec - (t0)->tv_usec) / MILLION)
/* functions for timespecs */