diff --git a/.gitmodules b/.gitmodules index 2477762b8..60d5d9621 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,9 +17,6 @@ path = external/pybind11 url = https://github.com/pybind/pybind11 branch = stable -[submodule "external/clang-format-hooks"] - path = external/clang-format-hooks - url = https://github.com/barisione/clang-format-hooks/ [submodule "external/sqlite_orm"] path = external/sqlite_orm url = https://github.com/fnc12/sqlite_orm @@ -35,3 +32,7 @@ [submodule "external/ngtcp2"] path = external/ngtcp2 url = https://github.com/ngtcp2/ngtcp2.git + branch = v0.1.0 +[submodule "external/oxen-encoding"] + path = external/oxen-encoding + url = https://github.com/oxen-io/oxen-encoding.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d0244044..bf5e6cabd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Has to be set before `project()`, and ignored on non-macos: set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)") +option(BUILD_DAEMON "build lokinet daemon and associated utils" ON) + + set(LANGS C CXX) -if(APPLE) +if(APPLE AND BUILD_DAEMON) set(LANGS ${LANGS} OBJC Swift) endif() @@ -22,7 +25,7 @@ endif() project(lokinet - VERSION 0.9.8 + VERSION 0.9.9 DESCRIPTION "lokinet - IP packet onion router" LANGUAGES ${LANGS}) @@ -32,16 +35,15 @@ if(APPLE) set(LOKINET_APPLE_BUILD 0) endif() -set(RELEASE_MOTTO "A Series of Tubes" CACHE STRING "Release motto") +set(RELEASE_MOTTO "Gluten Free Edition" CACHE STRING "Release motto") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") -add_definitions(-DLLARP_VERSION_MAJOR=${lokinet_VERSION_MAJOR}) -add_definitions(-DLLARP_VERSION_MINOR=${lokinet_VERSION_MINOR}) -add_definitions(-DLLARP_VERSION_PATCH=${lokinet_VERSION_PATCH}) -if(RELEASE_MOTTO AND CMAKE_BUILD_TYPE MATCHES "[Rr][Ee][Ll][Ee][Aa][Ss][Ee]") - add_definitions(-DLLARP_RELEASE_MOTTO="${RELEASE_MOTTO}") +set(DEFAULT_WITH_BOOTSTRAP ON) +if(APPLE) + set(DEFAULT_WITH_BOOTSTRAP OFF) endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # Core options option(USE_AVX2 "enable avx2 code" OFF) @@ -57,12 +59,16 @@ option(WITH_COVERAGE "generate coverage data" OFF) option(USE_SHELLHOOKS "enable shell hooks on compile time (dangerous)" OFF) option(WARNINGS_AS_ERRORS "treat all warnings as errors. turn off for development, on for release" OFF) option(TRACY_ROOT "include tracy profiler source" OFF) -option(WITH_TESTS "build unit tests" ON) +option(WITH_TESTS "build unit tests" OFF) option(WITH_HIVE "build simulation stubs" OFF) option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF) +option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP}) include(cmake/enable_lto.cmake) +option(CROSS_PLATFORM "cross compiler platform" "Linux") +option(CROSS_PREFIX "toolchain cross compiler prefix" "") + option(BUILD_STATIC_DEPS "Download, build, and statically link against core dependencies" OFF) option(STATIC_LINK "link statically against dependencies" ${BUILD_STATIC_DEPS}) if(BUILD_STATIC_DEPS AND NOT STATIC_LINK) @@ -95,7 +101,6 @@ include(cmake/target_link_libraries_system.cmake) include(cmake/add_import_library.cmake) include(cmake/add_log_tag.cmake) include(cmake/libatomic.cmake) -include(cmake/link_dep_libs.cmake) if (STATIC_LINK) set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) @@ -170,19 +175,36 @@ if(NOT TARGET sodium) export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake) endif() +option(FORCE_OXENC_SUBMODULE "force using oxen-encoding submodule" OFF) +if(NOT FORCE_OXENC_SUBMODULE) + pkg_check_modules(OXENC liboxenc>=1.0.3 IMPORTED_TARGET) +endif() + +if(OXENC_FOUND) + if(NOT TARGET PkgConfig::OXENC AND CMAKE_VERSION VERSION_LESS "3.21") + # Work around cmake bug 22180 (PkgConfig::OXENC not set if no flags needed): + add_library(_empty_oxenc INTERFACE) + add_library(oxenc::oxenc ALIAS _empty_oxenc) + else() + add_library(oxenc::oxenc ALIAS PkgConfig::OXENC) + endif() + message(STATUS "Found system liboxenc ${OXENC_VERSION}") +else() + message(STATUS "using oxen-encoding submodule") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-encoding) + add_library(oxenc::oxenc ALIAS oxenc) +endif() + option(FORCE_OXENMQ_SUBMODULE "force using oxenmq submodule" OFF) if(NOT FORCE_OXENMQ_SUBMODULE) - pkg_check_modules(OXENMQ liboxenmq>=1.2.4) + pkg_check_modules(OXENMQ liboxenmq>=1.2.12 IMPORTED_TARGET) endif() if(OXENMQ_FOUND) - add_library(oxenmq INTERFACE) - link_dep_libs(oxenmq INTERFACE "${OXENMQ_LIBRARY_DIRS}" ${OXENMQ_LIBRARIES}) - target_include_directories(oxenmq INTERFACE ${OXENMQ_INCLUDE_DIRS}) - add_library(oxenmq::oxenmq ALIAS oxenmq) + add_library(oxenmq::oxenmq ALIAS PkgConfig::OXENMQ) message(STATUS "Found system liboxenmq ${OXENMQ_VERSION}") else() message(STATUS "using oxenmq submodule") - add_subdirectory(${CMAKE_SOURCE_DIR}/external/oxen-mq) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-mq) endif() @@ -327,8 +349,9 @@ endif() add_subdirectory(crypto) add_subdirectory(llarp) -add_subdirectory(daemon) - +if(BUILD_DAEMON) + add_subdirectory(daemon) +endif() if(WITH_HIVE) add_subdirectory(pybind) diff --git a/LICENSE.txt b/LICENSE similarity index 98% rename from LICENSE.txt rename to LICENSE index 49211c38d..e72bfddab 100644 --- a/LICENSE.txt +++ b/LICENSE @@ -1,16 +1,3 @@ -LokiNET is the reference implementation of LLARP (Low Latency Anonymous -Routing Protocol). - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Copyright (c) 2018-2020 The Loki Project -Copyright (c) 2018-2020 Jeff Becker -Windows NT port and portions Copyright (c) 2018-2020 Rick V. - - GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -684,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. \ No newline at end of file diff --git a/SOURCES/default-dns.patch b/SOURCES/default-dns.patch index b646e8a54..32b7cc014 100644 --- a/SOURCES/default-dns.patch +++ b/SOURCES/default-dns.patch @@ -1,8 +1,17 @@ +From cdad3e7f093c4b0c69f73580e4fbacc24067db96 Mon Sep 17 00:00:00 2001 +From: necro-nemsis +Date: Sun, 19 Jun 2022 06:47:01 -0400 +Subject: [PATCH] Change default port to 953 + +--- + llarp/config/config.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp -index 78d152602..8b07b0cec 100644 +index 6bff9611..9c89bce6 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp -@@ -703,7 +703,10 @@ namespace llarp +@@ -760,7 +760,10 @@ namespace llarp // can bind to other 127.* IPs to avoid conflicting with something else that may be listening on // 127.0.0.1:53. #ifdef __linux__ @@ -14,3 +23,5 @@ index 78d152602..8b07b0cec 100644 #else constexpr Default DefaultDNSBind{"127.0.0.1:53"}; #endif +-- +2.30.2 diff --git a/SOURCES/version-as-rpm-version.patch b/SOURCES/version-as-rpm-version.patch deleted file mode 100644 index 868783b3b..000000000 --- a/SOURCES/version-as-rpm-version.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: Jason Rhinelander -Date: Fri, 13 Dec 2019 17:23:41 -0400 -Subject: Pass debian version as GIT_VERSION - ---- - cmake/Version.cmake | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/cmake/Version.cmake b/cmake/Version.cmake -index 45037a0..d9fdaef 100644 ---- a/cmake/Version.cmake -+++ b/cmake/Version.cmake -@@ -1,4 +1,8 @@ - -+if(GIT_VERSION) -+ set(VERSIONTAG "${GIT_VERSION}") -+ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") -+else() - find_package(Git QUIET) - set(GIT_INDEX_FILE "${PROJECT_SOURCE_DIR}/.git/index") - if(EXISTS ${GIT_INDEX_FILE} AND ( GIT_FOUND OR Git_FOUND) ) -@@ -18,5 +22,6 @@ else() - set(VERSIONTAG "nogit") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") - endif() -+endif() - - add_custom_target(genversion DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") diff --git a/SPECS/lokinet.spec b/SPECS/lokinet.spec index 65cd62b15..e9a0c0a97 100644 --- a/SPECS/lokinet.spec +++ b/SPECS/lokinet.spec @@ -1,5 +1,5 @@ Name: lokinet -Version: 0.9.8 +Version: 0.9.9 Release: 1%{?dist} Summary: Lokinet anonymous, decentralized overlay network @@ -13,7 +13,6 @@ BuildRequires: cmake BuildRequires: gcc-c++ BuildRequires: pkgconfig BuildRequires: libuv-devel -BuildRequires: oxenmq-devel BuildRequires: unbound-devel BuildRequires: libsodium-devel BuildRequires: systemd-devel @@ -22,11 +21,9 @@ BuildRequires: libcurl-devel BuildRequires: jemalloc-devel BuildRequires: libsqlite3x-devel -# Puts the rpm version instead of the git tag in the version string: -Patch1: version-as-rpm-version.patch # Changes the default dns listener to 127.0.0.1:953 because Fedora's systemd-resolved doesn't like # talking to 127.3.2.1:53 for unknown reasons. -Patch2: default-dns.patch +Patch1: default-dns.patch Requires: lokinet-bin = %{version}-%{release} %{?systemd_requires} @@ -84,7 +81,7 @@ export CFLAGS="%{optflags} -march=armv6 -mtune=cortex-a53 -mfloat-abi=hard -mfpu %endif %undefine __cmake_in_source_build -%cmake -DNATIVE_BUILD=OFF -DUSE_AVX2=OFF -DWITH_TESTS=OFF %{cmake_extra_args} -DCMAKE_BUILD_TYPE=Release -DGIT_VERSION="%{release}" -DWITH_SETCAP=OFF -DSUBMODULE_CHECK=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_LIBLOKINET=OFF +%cmake -DFORCE_OXENC_SUBMODULE=ON -DFORCE_OXENMQ_SUBMODULE=ON -DNATIVE_BUILD=OFF -DUSE_AVX2=OFF -DWITH_TESTS=OFF %{cmake_extra_args} -DCMAKE_BUILD_TYPE=Release -DLOKINET_VERSIONTAG=%{release} -DWITH_SETCAP=OFF -DSUBMODULE_CHECK=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_LIBLOKINET=OFF %cmake_build %install @@ -100,7 +97,7 @@ install -Dm644 SOURCES/bootstrap.signed $RPM_BUILD_ROOT%{_sharedstatedir}/lokine %files -%license LICENSE.txt +%license LICENSE %doc readme.* %{_datadir}/polkit-1/rules.d/50-lokinet.rules %{_unitdir}/lokinet.service @@ -156,6 +153,12 @@ fi %systemd_postun lokinet.service %changelog +* Wed Jun 29 2022 Technical Tumbleweed - 0.9.9-1 +- bump version +- cmake flags for no system library search +- update port patch +- remove version patch + * Wed Nov 17 2021 Technical Tumbleweed - 0.9.8-1 - bump version diff --git a/cmake/GenVersion.cmake b/cmake/GenVersion.cmake index 21aeab9df..9cda8b8ae 100644 --- a/cmake/GenVersion.cmake +++ b/cmake/GenVersion.cmake @@ -57,4 +57,4 @@ else() endif() endif() -configure_file("${SRC}" "${DEST}") +configure_file("${SRC}" "${DEST}" @ONLY) diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index 87ba00a81..3ddbe9757 100644 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -5,32 +5,32 @@ set(LOCAL_MIRROR "" CACHE STRING "local mirror path/URL for lib downloads") -set(OPENSSL_VERSION 1.1.1l CACHE STRING "openssl version") +set(OPENSSL_VERSION 1.1.1o CACHE STRING "openssl version") set(OPENSSL_MIRROR ${LOCAL_MIRROR} https://www.openssl.org/source CACHE STRING "openssl download mirror(s)") set(OPENSSL_SOURCE openssl-${OPENSSL_VERSION}.tar.gz) -set(OPENSSL_HASH SHA256=0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1 +set(OPENSSL_HASH SHA256=9384a2b0570dd80358841464677115df785edb941c71211f75076d72fe6b438f CACHE STRING "openssl source hash") -set(EXPAT_VERSION 2.3.0 CACHE STRING "expat version") +set(EXPAT_VERSION 2.4.8 CACHE STRING "expat version") string(REPLACE "." "_" EXPAT_TAG "R_${EXPAT_VERSION}") set(EXPAT_MIRROR ${LOCAL_MIRROR} https://github.com/libexpat/libexpat/releases/download/${EXPAT_TAG} CACHE STRING "expat download mirror(s)") set(EXPAT_SOURCE expat-${EXPAT_VERSION}.tar.xz) -set(EXPAT_HASH SHA512=dde8a9a094b18d795a0e86ca4aa68488b352dc67019e0d669e8b910ed149628de4c2a49bc3a5b832f624319336a01f9e4debe03433a43e1c420f36356d886820 +set(EXPAT_HASH SHA256=f79b8f904b749e3e0d20afeadecf8249c55b2e32d4ebb089ae378df479dcaf25 CACHE STRING "expat source hash") -set(UNBOUND_VERSION 1.13.2 CACHE STRING "unbound version") +set(UNBOUND_VERSION 1.15.0 CACHE STRING "unbound version") set(UNBOUND_MIRROR ${LOCAL_MIRROR} https://nlnetlabs.nl/downloads/unbound CACHE STRING "unbound download mirror(s)") set(UNBOUND_SOURCE unbound-${UNBOUND_VERSION}.tar.gz) -set(UNBOUND_HASH SHA256=0a13b547f3b92a026b5ebd0423f54c991e5718037fd9f72445817f6a040e1a83 +set(UNBOUND_HASH SHA256=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f CACHE STRING "unbound source hash") -set(SQLITE3_VERSION 3350500 CACHE STRING "sqlite3 version") -set(SQLITE3_MIRROR ${LOCAL_MIRROR} https://www.sqlite.org/2021 +set(SQLITE3_VERSION 3380500 CACHE STRING "sqlite3 version") +set(SQLITE3_MIRROR ${LOCAL_MIRROR} https://www.sqlite.org/2022 CACHE STRING "sqlite3 download mirror(s)") set(SQLITE3_SOURCE sqlite-autoconf-${SQLITE3_VERSION}.tar.gz) -set(SQLITE3_HASH SHA512=039af796f79fc4517be0bd5ba37886264d49da309e234ae6fccdb488ef0109ed2b917fc3e6c1fc7224dff4f736824c653aaf8f0a37550c5ebc14d035cb8ac737 - CACHE STRING "sqlite3 source hash") +set(SQLITE3_HASH SHA3_256=ab649fea76f49a6ec7f907f001d87b8bd76dec0679c783e3992284c5a882a98c + CACHE STRING "sqlite3 source hash") set(SODIUM_VERSION 1.0.18 CACHE STRING "libsodium version") set(SODIUM_MIRROR ${LOCAL_MIRROR} @@ -48,29 +48,27 @@ set(ZMQ_SOURCE zeromq-${ZMQ_VERSION}.tar.gz) set(ZMQ_HASH SHA512=e198ef9f82d392754caadd547537666d4fba0afd7d027749b3adae450516bcf284d241d4616cad3cb4ad9af8c10373d456de92dc6d115b037941659f141e7c0e CACHE STRING "libzmq source hash") -set(LIBUV_VERSION 1.41.0 CACHE STRING "libuv version") +set(LIBUV_VERSION 1.44.1 CACHE STRING "libuv version") set(LIBUV_MIRROR ${LOCAL_MIRROR} https://dist.libuv.org/dist/v${LIBUV_VERSION} CACHE STRING "libuv mirror(s)") set(LIBUV_SOURCE libuv-v${LIBUV_VERSION}.tar.gz) -set(LIBUV_HASH SHA512=33613fa28e8136507300eba374351774849b6b39aab4e53c997a918d3bc1d1094c6123e0e509535095b14dc5daa885eadb1a67bed46622ad3cc79d62dc817e84 +set(LIBUV_HASH SHA512=b4f8944e2c79e3a6a31ded6cccbe4c0eeada50db6bc8a448d7015642795012a4b80ffeef7ca455bb093c59a8950d0e1430566c3c2fa87b73f82699098162d834 CACHE STRING "libuv source hash") -set(ZLIB_VERSION 1.2.11 CACHE STRING "zlib version") +set(ZLIB_VERSION 1.2.12 CACHE STRING "zlib version") set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net CACHE STRING "zlib mirror(s)") set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.gz) -set(ZLIB_HASH SHA512=73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae - CACHE STRING "zlib source hash") +set(ZLIB_HASH SHA256=91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9 + CACHE STRING "zlib source hash") -set(CURL_VERSION 7.76.1 CACHE STRING "curl version") +set(CURL_VERSION 7.83.1 CACHE STRING "curl version") set(CURL_MIRROR ${LOCAL_MIRROR} https://curl.haxx.se/download https://curl.askapache.com CACHE STRING "curl mirror(s)") set(CURL_SOURCE curl-${CURL_VERSION}.tar.xz) -set(CURL_HASH SHA256=64bb5288c39f0840c07d077e30d9052e1cbb9fa6c2dc52523824cc859e679145 +set(CURL_HASH SHA256=2cb9c2356e7263a1272fd1435ef7cdebf2cd21400ec287b068396deb705c22c4 CACHE STRING "curl source hash") - - include(ExternalProject) set(DEPS_DESTDIR ${CMAKE_BINARY_DIR}/static-deps) @@ -236,6 +234,7 @@ add_static_target(zlib zlib_external libz.a) set(openssl_system_env "") +set(openssl_configure_command ./config) if(CMAKE_CROSSCOMPILING) if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32) set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib) @@ -244,13 +243,31 @@ if(CMAKE_CROSSCOMPILING) elseif(ANDROID) set(openssl_system_env SYSTEM=Linux MACHINE=${android_machine} LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar}) set(openssl_extra_opts no-asm) + elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64) + set(openssl_system_env SYSTEM=Linux MACHINE=mips64) + set(openssl_configure_command ./Configure linux64-mips64) + elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu) + set(openssl_system_env SYSTEM=Linux MACHINE=mips) + elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu) + set(openssl_system_env SYSTEM=Linux MACHINE=mipsel) + elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu) + # cross compile arm64 + set(openssl_system_env SYSTEM=Linux MACHINE=aarch64) + elseif(ARCH_TRIPLET MATCHES arm-linux) + # cross compile armhf + set(openssl_system_env SYSTEM=Linux MACHINE=armv4) + elseif(ARCH_TRIPLET MATCHES powerpc64le) + # cross compile ppc64le + set(openssl_system_env SYSTEM=Linux MACHINE=ppc64le) endif() elseif(CMAKE_C_FLAGS MATCHES "-march=armv7") # Help openssl figure out that we're building from armv7 even if on armv8 hardware: set(openssl_system_env SYSTEM=Linux MACHINE=armv7) endif() + + build_external(openssl - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ./config + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ${openssl_configure_command} --prefix=${DEPS_DESTDIR} ${openssl_extra_opts} no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost no-heartbeats no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl2 no-ssl3 no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic "CFLAGS=${deps_CFLAGS}" @@ -277,7 +294,6 @@ build_external(expat ) add_static_target(expat expat_external libexpat.a) - build_external(unbound DEPENDS openssl_external expat_external CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared @@ -335,6 +351,10 @@ set_target_properties(libzmq PROPERTIES INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}" INTERFACE_COMPILE_DEFINITIONS "ZMQ_STATIC") +if(NOT WITH_BOOTSTRAP) + return() +endif() + set(curl_extra) if(WIN32) set(curl_ssl_opts --without-ssl --with-schannel) @@ -386,7 +406,7 @@ foreach(curl_arch ${curl_arches}) --enable-crypto-auth --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --enable-http-auth --enable-doh --disable-mime --enable-dateparse --disable-netrc --without-libidn2 --disable-progress-meter --without-brotli --with-zlib=${DEPS_DESTDIR} ${curl_ssl_opts} - --without-libmetalink --without-librtmp --disable-versioned-symbols --enable-hidden-symbols + --without-librtmp --disable-versioned-symbols --enable-hidden-symbols --without-zsh-functions-dir --without-fish-functions-dir --without-nghttp3 --without-zstd "CC=${deps_cc}" "CFLAGS=${deps_noarch_CFLAGS}${cflags_extra}" ${curl_extra} diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 45037a081..a017995eb 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,22 +1,41 @@ +# We do this via a custom command that re-invokes a cmake script because we need the DEPENDS on .git/index so that we will re-run it (to regenerate the commit tag in the version) whenever the current commit changes. If we used a configure_file directly here, it would only re-run when something else causes cmake to re-run. -find_package(Git QUIET) +set(VERSIONTAG "${GIT_VERSION}") set(GIT_INDEX_FILE "${PROJECT_SOURCE_DIR}/.git/index") -if(EXISTS ${GIT_INDEX_FILE} AND ( GIT_FOUND OR Git_FOUND) ) +find_package(Git) +if(EXISTS "${GIT_INDEX_FILE}" AND ( GIT_FOUND OR Git_FOUND) ) message(STATUS "Found Git: ${GIT_EXECUTABLE}") + set(genversion_args "-DGIT=${GIT_EXECUTABLE}") + foreach(v lokinet_VERSION lokinet_VERSION_MAJOR lokinet_VERSION_MINOR lokinet_VERSION_PATCH RELEASE_MOTTO) + list(APPEND genversion_args "-D${v}=${${v}}") + endforeach() add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" COMMAND "${CMAKE_COMMAND}" - "-D" "GIT=${GIT_EXECUTABLE}" + ${genversion_args} "-D" "SRC=${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "-D" "DEST=${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" "-P" "${CMAKE_CURRENT_LIST_DIR}/GenVersion.cmake" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${GIT_INDEX_FILE}") else() - message(STATUS "Git was not found! Setting version to to nogit") - set(VERSIONTAG "nogit") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" @ONLY) endif() -add_custom_target(genversion DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") + +if(WIN32) + foreach(exe IN ITEMS lokinet lokinet-vpn lokinet-bootstrap) + set(lokinet_EXE_NAME "${exe}.exe") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/win32/version.rc.in" "${CMAKE_BINARY_DIR}/${exe}.rc" @ONLY) + set_property(SOURCE "${CMAKE_BINARY_DIR}/${exe}.rc" PROPERTY GENERATED 1) + endforeach() +endif() + +add_custom_target(genversion_cpp DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") +if(WIN32) + add_custom_target(genversion_rc DEPENDS "${CMAKE_BINARY_DIR}/lokinet.rc" "${CMAKE_BINARY_DIR}/lokinet-vpn.rc" "${CMAKE_BINARY_DIR}/lokinet-bootstrap.rc") +else() + add_custom_target(genversion_rc) +endif() +add_custom_target(genversion DEPENDS genversion_cpp genversion_rc) diff --git a/cmake/installer.cmake b/cmake/installer.cmake index b8c04e563..bdd4958fc 100644 --- a/cmake/installer.cmake +++ b/cmake/installer.cmake @@ -1,7 +1,7 @@ set(CPACK_PACKAGE_VENDOR "lokinet.org") set(CPACK_PACKAGE_HOMEPAGE_URL "https://lokinet.org/") set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/contrib/readme-installer.txt") -set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.txt") +set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") if(WIN32) include(cmake/win32_installer_deps.cmake) diff --git a/cmake/link_dep_libs.cmake b/cmake/link_dep_libs.cmake deleted file mode 100644 index 14e4b4f16..000000000 --- a/cmake/link_dep_libs.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Properly links a target to a list of library names by finding the given libraries. Takes: -# - a target -# - a linktype (e.g. INTERFACE, PUBLIC, PRIVATE) -# - a library search path (or "" for defaults) -# - any number of library names -function(link_dep_libs target linktype libdirs) - foreach(lib ${ARGN}) - find_library(link_lib-${lib} NAMES ${lib} PATHS ${libdirs}) - if(link_lib-${lib}) - target_link_libraries(${target} ${linktype} ${link_lib-${lib}}) - endif() - endforeach() -endfunction() diff --git a/cmake/ngtcp2_lib.cmake b/cmake/ngtcp2_lib.cmake index c6d06a42a..a14160b15 100644 --- a/cmake/ngtcp2_lib.cmake +++ b/cmake/ngtcp2_lib.cmake @@ -45,8 +45,9 @@ function(add_ngtcp2_lib) configure_file(ngtcp2/cmakeconfig.h.in ngtcp2/config.h) include_directories("${CMAKE_CURRENT_BINARY_DIR}/ngtcp2") # for config.h - + set(ENABLE_STATIC_LIB ON FORCE BOOL) + set(ENABLE_SHARED_LIB OFF FORCE BOOL) add_subdirectory(ngtcp2/lib EXCLUDE_FROM_ALL) - target_compile_definitions(ngtcp2 PRIVATE -DHAVE_CONFIG_H -D_GNU_SOURCE) + target_compile_definitions(ngtcp2_static PRIVATE -DHAVE_CONFIG_H -D_GNU_SOURCE) endfunction() diff --git a/cmake/win32.cmake b/cmake/win32.cmake index 2a4af72a0..73cda29b3 100644 --- a/cmake/win32.cmake +++ b/cmake/win32.cmake @@ -15,8 +15,9 @@ if(NOT MSVC_VERSION) # to .r[o]data section one after the other! add_compile_options(-fno-ident -Wa,-mbig-obj) link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv) - # zmq requires windows xp or higher - add_definitions(-DWINVER=0x0501 -D_WIN32_WINNT=0x0501) + # the minimum windows version, set to 6 rn because supporting older windows is hell + set(_winver 0x0600) + add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver}) endif() if(EMBEDDED_CFG) diff --git a/cmake/win32_installer_deps.cmake b/cmake/win32_installer_deps.cmake index 95ea1aedf..825eda90f 100644 --- a/cmake/win32_installer_deps.cmake +++ b/cmake/win32_installer_deps.cmake @@ -1,6 +1,6 @@ if(NOT GUI_ZIP_URL) - set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/loki-network-control-panel/lokinet-gui-windows-32bit-v0.3.8.zip") - set(GUI_ZIP_HASH_OPTS EXPECTED_HASH SHA256=60c2b738cf997e5684f307e5222498fd09143d495a932924105a49bf59ded8bb) + set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip") + set(GUI_ZIP_HASH_OPTS EXPECTED_HASH SHA256=316f10489f5907bfa9c74b21f8ef2fdd7b7c7e6a0f5bcedaed2ee5f4004eab52) endif() set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe") @@ -27,14 +27,26 @@ set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico") set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin") set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) -set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ifFileExists $INSTDIR\\\\bin\\\\tuntap-install.exe 0 +2\\nExecWait '$INSTDIR\\\\bin\\\\tuntap-install.exe /S'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe --install'\\nExecWait 'sc failure lokinet reset= 60 actions= restart/1000'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe -g C:\\\\ProgramData\\\\lokinet\\\\lokinet.ini'\\nCopyFiles '$INSTDIR\\\\share\\\\bootstrap.signed' C:\\\\ProgramData\\\\lokinet\\\\bootstrap.signed\\n") -set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait 'net stop lokinet'\\nExecWait 'taskkill /f /t /im lokinet-gui.exe'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe --remove'\\nRMDir /r /REBOOTOK C:\\\\ProgramData\\\\lokinet") -set(CPACK_NSIS_CREATE_ICONS_EXTRA - "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Lokinet.lnk' '$INSTDIR\\\\share\\\\gui\\\\lokinet-gui.exe'" -) -set(CPACK_NSIS_DELETE_ICONS_EXTRA - "Delete '$SMPROGRAMS\\\\$START_MENU\\\\Lokinet.lnk'" -) + + +function(read_nsis_file filename outvar) + file(STRINGS "${filename}" _outvar) + list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\") + list(JOIN _outvar "\\n" out) + set(${outvar} ${out} PARENT_SCOPE) +endfunction() + +read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_preinstall.nsis" _extra_preinstall) +read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_install.nsis" _extra_install) +read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_uninstall.nsis" _extra_uninstall) +read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_create_icons.nsis" _extra_create_icons) +read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_delete_icons) + +set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}") +set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}") +set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") +set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}") +set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}") get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") diff --git a/contrib/android-configure.sh b/contrib/android-configure.sh new file mode 100755 index 000000000..2130b07cf --- /dev/null +++ b/contrib/android-configure.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e +set +x + +default_abis="armeabi-v7a arm64-v8a x86_64" +build_abis=${ABIS:-$default_abis} + +test x$NDK = x && echo "NDK env var not set" +test x$NDK = x && exit 1 + +echo "building abis: $build_abis" + +root="$(readlink -f $(dirname $0)/../)" +build=$root/build-android +mkdir -p $build +cd $build + +for abi in $build_abis; do + mkdir -p build-$abi + cd build-$abi + cmake \ + -G 'Unix Makefiles' \ + -DANDROID=ON \ + -DANDROID_ABI=$abi \ + -DANDROID_ARM_MODE=arm \ + -DANDROID_PLATFORM=android-23 \ + -DANDROID_STL=c++_static \ + -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ + -DBUILD_STATIC_DEPS=ON \ + -DBUILD_PACKAGE=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_LIBLOKINET=OFF \ + -DWITH_TESTS=OFF \ + -DNATIVE_BUILD=OFF \ + -DSTATIC_LINK=ON \ + -DWITH_SYSTEMD=OFF \ + -DFORCE_OXENMQ_SUBMODULE=ON \ + -DFORCE_OXENC_SUBMODULE=ON \ + -DSUBMODULE_CHECK=OFF \ + -DWITH_LTO=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + $@ $root + cd - +done +rm -f $build/Makefile +echo "# generated makefile" >> $build/Makefile +echo "all: $build_abis" >> $build/Makefile +for abi in $build_abis; do + echo -ne "$abi:\n\t" >> $build/Makefile + echo -ne '$(MAKE) -C ' >> $build/Makefile + echo "build-$abi lokinet-android" >> $build/Makefile + echo -ne "\tmkdir -p out/$abi && cp build-$abi/jni/liblokinet-android.so out/$abi/liblokinet-android.so\n\n" >> $build/Makefile +done diff --git a/contrib/android.sh b/contrib/android.sh index 9dfd64df2..a5d720206 100755 --- a/contrib/android.sh +++ b/contrib/android.sh @@ -2,50 +2,9 @@ set -e set +x -default_abis="armeabi-v7a arm64-v8a x86 x86_64" -build_abis=${ABIS:-$default_abis} - test x$NDK = x && echo "NDK env var not set" test x$NDK = x && exit 1 - -echo "building abis: $build_abis" - root="$(readlink -f $(dirname $0)/../)" -out=$root/lokinet-jni-$(git describe || echo unknown) -mkdir -p $out -mkdir -p $root/build-android -cd $root/build-android - -for abi in $build_abis; do - mkdir -p build-$abi $out/$abi - cd build-$abi - cmake \ - -G 'Unix Makefiles' \ - -DANDROID=ON \ - -DANDROID_ABI=$abi \ - -DANDROID_ARM_MODE=arm \ - -DANDROID_PLATFORM=android-23 \ - -DANDROID_STL=c++_static \ - -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ - -DBUILD_STATIC_DEPS=ON \ - -DBUILD_PACKAGE=ON \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_TESTING=OFF \ - -DBUILD_LIBLOKINET=OFF \ - -DWITH_TESTS=OFF \ - -DNATIVE_BUILD=OFF \ - -DSTATIC_LINK=ON \ - -DWITH_SYSTEMD=OFF \ - -DFORCE_OXENMQ_SUBMODULE=ON \ - -DSUBMODULE_CHECK=OFF \ - -DWITH_LTO=OFF \ - -DCMAKE_BUILD_TYPE=Release \ - $@ $root - make lokinet-android -j${JOBS:-$(nproc)} - cp jni/liblokinet-android.so $out/$abi/liblokinet-android.so - cd - -done - - -echo -echo "build artifacts outputted to $out" +cd "$root" +./contrib/android-configure.sh $@ +make -C build-android -j ${JOBS:-$(nproc)} diff --git a/contrib/bootserv/.gitignore b/contrib/bootserv/.gitignore deleted file mode 100644 index 0c119ef3e..000000000 --- a/contrib/bootserv/.gitignore +++ /dev/null @@ -1 +0,0 @@ -lokinet-bootserv \ No newline at end of file diff --git a/contrib/bootserv/config/lokinet-bootserv-nginx.conf b/contrib/bootserv/config/lokinet-bootserv-nginx.conf deleted file mode 100644 index 4fe24ee47..000000000 --- a/contrib/bootserv/config/lokinet-bootserv-nginx.conf +++ /dev/null @@ -1,29 +0,0 @@ -# replace your.server.tld with your server's fqdn - -server { - listen 80; - server_name your.server.tld; - location / { - return 302 https://your.server.tld$request_uri; - } - location /.well-known/acme-challenge { - root /var/www/letsencrypt; - } -} - -server { - listen 443 ssl; - server_name your.server.tld; - ssl_certificate /etc/letsencrypt/live/your.server.tld/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/your.server.tld/privkey.pem; - - location / { - root /var/www/lokinet-bootserv; - } - - location /bootstrap.signed { - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME /usr/local/bin/lokinet-bootserv; - fastcgi_pass unix://tmp/cgi.sock; - } -} \ No newline at end of file diff --git a/contrib/bootserv/config/lokinet-bootserv.ini b/contrib/bootserv/config/lokinet-bootserv.ini deleted file mode 100644 index 1c1eda45b..000000000 --- a/contrib/bootserv/config/lokinet-bootserv.ini +++ /dev/null @@ -1,4 +0,0 @@ - -# set me to where the nodedb is for lokinet -#[nodedb] -#dir=/path/to/nodedb/ diff --git a/contrib/bootserv/makefile b/contrib/bootserv/makefile deleted file mode 100644 index 081016c40..000000000 --- a/contrib/bootserv/makefile +++ /dev/null @@ -1,20 +0,0 @@ - -SRC = $(sort $(wildcard src/*.cpp)) -OBJS = $(SRC:.cpp=.cpp.o) - -CGI_EXE = lokinet-bootserv - -CXX = clang++ - -all: build - -build: $(CGI_EXE) - -%.cpp.o: %.cpp - $(CXX) -g -std=c++17 -c -Wall -Werror -Wpedantic $^ -o $^.o - -$(CGI_EXE): $(OBJS) - $(CXX) -o $(CGI_EXE) $^ - -clean: - rm -f $(CGI_EXE) $(OBJS) diff --git a/contrib/bootserv/readme.md b/contrib/bootserv/readme.md deleted file mode 100644 index 572ba50f8..000000000 --- a/contrib/bootserv/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -# lokinet-bootserv - -cgi executable for serving a random RC for bootstrap from a nodedb - -## configuring - -copy the example config (privileged) - - # cp configs/lokinet-bootserv.ini /usr/local/etc/lokinet-bootserv.ini - -edit config to have proper values, -specifically make sure the `[nodedb]` section has a `dir` value that points to a static copy of a healthy nodedb - -## building - -to build: - - $ make - -## installing (priviledged) - -install cgi binary: - - # cp lokinet-bootserv /usr/local/bin/lokinet-bootserv - -set up with nginx cgi: - - # cp configs/lokinet-bootserv-nginx.conf /etc/nginx/sites-available/lokinet-bootserv.conf - # ln -s /etc/nginx/sites-available/lokinet-bootserv.conf /etc/nginx/sites-enabled/ - -## maintainence - -add the following to crontab - - 0 0 * * * /usr/local/bin/lokinet-bootserv --cron diff --git a/contrib/bootserv/src/cgi.cpp b/contrib/bootserv/src/cgi.cpp deleted file mode 100644 index 46482ab7c..000000000 --- a/contrib/bootserv/src/cgi.cpp +++ /dev/null @@ -1,171 +0,0 @@ -#include "lokinet-cgi.hpp" -#include -#include -#include -#include - -namespace lokinet -{ - namespace bootserv - { - CGIHandler::CGIHandler(std::ostream& o) : Handler(o) - { - } - - CGIHandler::~CGIHandler() - { - } - - int - CGIHandler::Exec(const Config& conf) - { - const char* e = getenv("REQUEST_METHOD"); - if(e == nullptr) - return ReportError("$REQUEST_METHOD not set"); - std::string_view method(e); - - if(method != "GET") - { - out << "Content-Type: text/plain" << std::endl; - out << "Status: 405 Method Not Allowed" << std::endl << std::endl; - return 0; - } - - std::string fname; - if(!conf.VisitSection( - "nodedb", [&](const Config::Section_t& sect) -> bool { - auto itr = sect.find("dir"); - if(itr == sect.end()) - return false; - fname = PickRandomFileInDir( - std::string(itr->second.data(), itr->second.size())); - return true; - })) - - return ReportError("bad values in nodedb section of config"); - if(fname.empty()) - { - // no files in nodedb - out << "Content-Type: text/plain" << std::endl; - out << "Status: 404 Not Found" << std::endl << std::endl; - return 0; - } - return ServeFile(fname.c_str(), "application/octect-stream"); - } - - std::string - CGIHandler::PickRandomFileInDir(std::string dirname) const - { - // collect files - std::list< std::string > files; - { - DIR* d = opendir(dirname.c_str()); - if(d == nullptr) - { - return ""; - }; - std::list< std::string > subdirs; - dirent* ent = nullptr; - while((ent = readdir(d))) - { - std::string_view f = ent->d_name; - if(f != "." && f != "..") - { - std::stringstream ss; - ss << dirname; - ss << '/'; - ss << f; - subdirs.emplace_back(ss.str()); - } - } - closedir(d); - for(const auto& subdir : subdirs) - { - d = opendir(subdir.c_str()); - if(d) - { - while((ent = readdir(d))) - { - std::string_view f; - f = ent->d_name; - if(f != "." && f != ".." - && f.find_last_of(".signed") != std::string_view::npos) - { - std::stringstream ss; - ss << subdir << "/" << f; - files.emplace_back(ss.str()); - } - } - closedir(d); - } - } - } - uint32_t randint; - { - std::basic_ifstream< uint32_t > randf("/dev/urandom"); - if(!randf.is_open()) - return ""; - randf.read(&randint, 1); - } - auto itr = files.begin(); - if(files.size() > 1) - std::advance(itr, randint % files.size()); - return *itr; - } - - int - CGIHandler::ServeFile(const char* fname, const char* contentType) const - { - std::ifstream f(fname); - if(f.is_open()) - { - f.seekg(0, std::ios::end); - auto sz = f.tellg(); - f.seekg(0, std::ios::beg); - if(sz) - { - out << "Content-Type: " << contentType << std::endl; - out << "Status: 200 OK" << std::endl; - out << "Content-Length: " << std::to_string(sz) << std::endl - << std::endl; - char buf[512] = {0}; - size_t r = 0; - while((r = f.readsome(buf, sizeof(buf))) > 0) - out.write(buf, r); - out << std::flush; - } - else - { - out << "Content-Type: text/plain" << std::endl; - out << "Status: 500 Internal Server Error" << std::endl << std::endl; - out << "could not serve '" << fname << "' as it is an empty file" - << std::endl; - } - } - else - { - out << "Content-Type: text/plain" << std::endl; - out << "Status: 404 Not Found" << std::endl << std::endl; - out << "could not serve '" << fname - << "' as it does not exist on the filesystem" << std::endl; - } - return 0; - } - - int - CGIHandler::ReportError(const char* err) - { - out << "Content-Type: text/plain" << std::endl; - out << "Status: 500 Internal Server Error" << std::endl << std::endl; - out << err << std::endl; - return 0; - } - - Handler_ptr - NewCGIHandler(std::ostream& out) - { - return std::make_unique< CGIHandler >(out); - } - - } // namespace bootserv -} // namespace lokinet diff --git a/contrib/bootserv/src/handler.hpp b/contrib/bootserv/src/handler.hpp deleted file mode 100644 index 7327e900c..000000000 --- a/contrib/bootserv/src/handler.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef LOKINET_BOOTSERV_HANDLER_HPP -#define LOKINET_BOOTSERV_HANDLER_HPP -#include -#include "lokinet-config.hpp" - -namespace lokinet -{ - namespace bootserv - { - struct Handler - { - Handler(std::ostream& o) : out(o){}; - - virtual ~Handler(){}; - - /// handle command - /// return exit code - virtual int - Exec(const Config& conf) = 0; - - /// report an error to system however that is done - /// return exit code - virtual int - ReportError(const char* err) = 0; - - protected: - std::ostream& out; - }; - - using Handler_ptr = std::unique_ptr< Handler >; - - /// create cgi handler - Handler_ptr - NewCGIHandler(std::ostream& out); - - /// create cron handler - Handler_ptr - NewCronHandler(std::ostream& out); - - } // namespace bootserv -} // namespace lokinet - -#endif diff --git a/contrib/bootserv/src/lokinet-cgi.hpp b/contrib/bootserv/src/lokinet-cgi.hpp deleted file mode 100644 index 032bf3d3d..000000000 --- a/contrib/bootserv/src/lokinet-cgi.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BOOTSERV_LOKINET_CRON_HPP -#define BOOTSERV_LOKINET_CRON_HPP - -#include "handler.hpp" - -namespace lokinet -{ - namespace bootserv - { - struct CGIHandler final : public Handler - { - CGIHandler(std::ostream& o); - ~CGIHandler(); - - int - Exec(const Config& conf) override; - - int - ReportError(const char* err) override; - - int - ServeFile(const char* fname, const char* mime) const; - - std::string - PickRandomFileInDir(std::string dirname) const; - }; - - } // namespace bootserv -} // namespace lokinet - -#endif diff --git a/contrib/bootserv/src/lokinet-config.cpp b/contrib/bootserv/src/lokinet-config.cpp deleted file mode 100644 index 155fb66f7..000000000 --- a/contrib/bootserv/src/lokinet-config.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "lokinet-config.hpp" -#include -#include -#include - -namespace lokinet -{ - namespace bootserv - { - const char* Config::DefaultPath = "/usr/local/etc/lokinet-bootserv.ini"; - - bool - Config::LoadFile(const char* fname) - { - { - std::ifstream f(fname); - if(!f.is_open()) - return false; - f.seekg(0, std::ios::end); - m_Data.resize(f.tellg()); - f.seekg(0, std::ios::beg); - if(m_Data.size() == 0) - return false; - f.read(m_Data.data(), m_Data.size()); - } - return Parse(); - } - - void - Config::Clear() - { - m_Config.clear(); - m_Data.clear(); - } - - bool - Config::Parse() - { - std::list< String_t > lines; - { - auto itr = m_Data.begin(); - // split into lines - while(itr != m_Data.end()) - { - auto beg = itr; - while(itr != m_Data.end() && *itr != '\n' && *itr != '\r') - ++itr; - lines.emplace_back(std::addressof(*beg), (itr - beg)); - if(itr == m_Data.end()) - break; - ++itr; - } - } - - String_t sectName; - - for(const auto& line : lines) - { - String_t realLine; - auto comment = line.find_first_of(';'); - if(comment == String_t::npos) - comment = line.find_first_of('#'); - if(comment == String_t::npos) - realLine = line; - else - realLine = line.substr(0, comment); - // blank or commented line? - if(realLine.size() == 0) - continue; - // find delimiters - auto sectOpenPos = realLine.find_first_of('['); - auto sectClosPos = realLine.find_first_of(']'); - auto kvDelim = realLine.find_first_of('='); - if(sectOpenPos != String_t::npos && sectClosPos != String_t::npos - && kvDelim == String_t::npos) - { - // section header - - // clamp whitespaces - ++sectOpenPos; - while(std::isspace(realLine[sectOpenPos]) - && sectOpenPos != sectClosPos) - ++sectOpenPos; - --sectClosPos; - while(std::isspace(realLine[sectClosPos]) - && sectClosPos != sectOpenPos) - --sectClosPos; - // set section name - sectName = realLine.substr(sectOpenPos, sectClosPos); - } - else if(kvDelim != String_t::npos) - { - // key value pair - String_t::size_type k_start = 0; - String_t::size_type k_end = kvDelim; - String_t::size_type v_start = kvDelim + 1; - String_t::size_type v_end = realLine.size() - 1; - // clamp whitespaces - while(std::isspace(realLine[k_start]) && k_start != kvDelim) - ++k_start; - while(std::isspace(realLine[k_end]) && k_end != k_start) - --k_end; - while(std::isspace(realLine[v_start]) && v_start != v_end) - ++v_start; - while(std::isspace(realLine[v_end])) - --v_end; - - // sect.k = v - String_t k = realLine.substr(k_start, k_end); - String_t v = realLine.substr(v_start, v_end); - Section_t& sect = m_Config[sectName]; - sect[k] = v; - } - else // malformed? - return false; - } - return true; - } - - bool - Config::VisitSection( - const char* name, - std::function< bool(const Section_t& sect) > visit) const - { - auto itr = m_Config.find(name); - if(itr == m_Config.end()) - return false; - return visit(itr->second); - } - - } // namespace bootserv -} // namespace lokinet diff --git a/contrib/bootserv/src/lokinet-config.hpp b/contrib/bootserv/src/lokinet-config.hpp deleted file mode 100644 index caad9a2bc..000000000 --- a/contrib/bootserv/src/lokinet-config.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef LOKINET_BOOTSERV_CONFIG_HPP -#define LOKINET_BOOTSERV_CONFIG_HPP -#include -#include -#include -#include -#include - -namespace lokinet -{ - namespace bootserv - { - struct Config - { - using String_t = std::string_view; - using Section_t = std::unordered_map< String_t, String_t >; - using Config_impl_t = std::unordered_map< String_t, Section_t >; - - static const char* DefaultPath; - - /// clear config - void - Clear(); - - /// load config file for bootserv - /// return true on success - /// return false on error - bool - LoadFile(const char* fname); - - /// visit a section in config read only by name - /// return false if no section or value propagated from visitor - bool - VisitSection(const char* name, - std::function< bool(const Section_t&) > visit) const; - - private: - bool - Parse(); - - std::vector< char > m_Data; - Config_impl_t m_Config; - }; - } // namespace bootserv -} // namespace lokinet - -#endif diff --git a/contrib/bootserv/src/lokinet-cron.cpp b/contrib/bootserv/src/lokinet-cron.cpp deleted file mode 100644 index dfca782a2..000000000 --- a/contrib/bootserv/src/lokinet-cron.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "lokinet-cron.hpp" - -namespace lokinet -{ - namespace bootserv - { - CronHandler::CronHandler(std::ostream& o) : Handler(o) - { - } - - CronHandler::~CronHandler() - { - } - - int - CronHandler::Exec(const Config& conf) - { - // this runs the cron tasks - // TODO: implement me - return 0; - } - - int - CronHandler::ReportError(const char* err) - { - out << "error: " << err << std::endl; - return 1; - } - - Handler_ptr - NewCronHandler(std::ostream& out) - { - return std::make_unique< CronHandler >(out); - } - - } // namespace bootserv -} // namespace lokinet diff --git a/contrib/bootserv/src/lokinet-cron.hpp b/contrib/bootserv/src/lokinet-cron.hpp deleted file mode 100644 index 8bd9ba3c6..000000000 --- a/contrib/bootserv/src/lokinet-cron.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef BOOTSERV_LOKINET_CRON_HPP -#define BOOTSERV_LOKINET_CRON_HPP - -#include "handler.hpp" - -namespace lokinet -{ - namespace bootserv - { - struct CronHandler final : public Handler - { - CronHandler(std::ostream& o); - ~CronHandler(); - - int - Exec(const Config& conf) override; - - int - ReportError(const char* err) override; - }; - - } // namespace bootserv -} // namespace lokinet - -#endif diff --git a/contrib/bootserv/src/main.cpp b/contrib/bootserv/src/main.cpp deleted file mode 100644 index ad51d0f0b..000000000 --- a/contrib/bootserv/src/main.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "handler.hpp" -#include "lokinet-config.hpp" - -#include -#include -#include - -static int -printhelp(const char* exe) -{ - std::cout << "usage: " << exe << " [--cron] [--conf /path/to/alt/config.ini]" - << std::endl; - return 1; -} - -int -main(int argc, char* argv[]) -{ - bool RunCron = false; - - const char* confFile = lokinet::bootserv::Config::DefaultPath; - lokinet::bootserv::Config config; - - lokinet::bootserv::Handler_ptr handler; - - option longopts[] = {{"cron", no_argument, 0, 'C'}, - {"help", no_argument, 0, 'h'}, - {"conf", required_argument, 0, 'c'}, - {0, 0, 0, 0}}; - - int c = 0; - int index = 0; - while((c = getopt_long(argc, argv, "hCc:", longopts, &index)) != -1) - { - switch(c) - { - case 'h': - return printhelp(argv[0]); - case 'C': - RunCron = true; - break; - case 'c': - confFile = optarg; - break; - } - } - if(RunCron) - handler = lokinet::bootserv::NewCronHandler(std::cout); - else - handler = lokinet::bootserv::NewCGIHandler(std::cout); - - if(!config.LoadFile(confFile)) - { - std::stringstream ss; - ss << "failed to load " << confFile; - return handler->ReportError(ss.str().c_str()); - } - else - return handler->Exec(config); -} diff --git a/contrib/ci/docker/rebuild-docker-images.py b/contrib/ci/docker/rebuild-docker-images.py index 916fa2ee2..11f40eb39 100755 --- a/contrib/ci/docker/rebuild-docker-images.py +++ b/contrib/ci/docker/rebuild-docker-images.py @@ -82,7 +82,7 @@ def run_or_report(*args, myline): log.write(e.output.encode()) global failure failure = True - print_line(myline, "\033[31;1mError! See {} for details", log.name) + print_line(myline, "\033[31;1mError! See {} for details".format(log.name)) raise e diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index f2b445ddb..9449ff6dd 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -43,6 +43,10 @@ elif [ -e lokinet.apk ] ; then # android af ngl archive="$base.apk" cp -av lokinet.apk "$archive" +elif [ -e build-docs ]; then + archive="$base.tar.xz" + cp -av build-docs/docs/mkdocs.yml build-docs/docs/markdown "$base" + tar cJvf "$archive" "$base" else cp -av daemon/lokinet daemon/lokinet-vpn "$base" cp -av ../contrib/bootstrap/mainnet.signed "$base/bootstrap.signed" diff --git a/contrib/cross.sh b/contrib/cross.sh new file mode 100755 index 000000000..0b7d66328 --- /dev/null +++ b/contrib/cross.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# +# helper script for me for when i cross compile +# t. jeff +# +set -e + +die() { + echo $@ + exit 1 +} + +platform=${PLATFORM:-Linux} +root="$(readlink -e $(dirname $0)/../)" +cd $root +mkdir -p build-cross + +cmake_opts="-DBUILD_STATIC_DEPS=ON \ + -DSTATIC_LINK=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_LIBLOKINET=OFF \ + -DWITH_TESTS=OFF \ + -DNATIVE_BUILD=OFF \ + -DSTATIC_LINK=ON \ + -DWITH_SYSTEMD=OFF \ + -DFORCE_OXENMQ_SUBMODULE=ON \ + -DSUBMODULE_CHECK=OFF \ + -DWITH_LTO=OFF \ + -DWITH_BOOTSTRAP=OFF \ + -DCMAKE_BUILD_TYPE=RelWithDeb" + +targets=() + +while [ "$#" -gt 0 ]; do + if [ "$1" = "--" ]; then + shift + cmake_opts=$@ + break + fi + targets+=("$1") + shift +done +test ${#targets[@]} = 0 && die no targets provided + +archs="${targets[@]}" +echo "all: $archs" > build-cross/Makefile +for arch in $archs ; do + mkdir -p $root/build-cross/build-$arch + cd $root/build-cross/build-$arch + cmake \ + -G 'Unix Makefiles' \ + -DCROSS_PLATFORM=$platform \ + -DCROSS_PREFIX=$arch \ + -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \ + -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \ + -DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \ + $cmake_opts \ + $root + cd $root/build-cross + echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile + +done +cd $root +make -j${JOBS:-$(nproc)} -C build-cross diff --git a/contrib/cross/armhf.toolchain.cmake b/contrib/cross/armhf.toolchain.cmake deleted file mode 100644 index a61a270bb..000000000 --- a/contrib/cross/armhf.toolchain.cmake +++ /dev/null @@ -1,12 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(TOOLCHAIN_PREFIX arm-linux-gnueabihf) -set(TOOLCHAIN_SUFFIX -8) - -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc${TOOLCHAIN_SUFFIX}) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++${TOOLCHAIN_SUFFIX}) diff --git a/contrib/cross/aarch64.toolchain.cmake b/contrib/cross/cross.toolchain.cmake similarity index 73% rename from contrib/cross/aarch64.toolchain.cmake rename to contrib/cross/cross.toolchain.cmake index 03357daf1..2a7cde131 100644 --- a/contrib/cross/aarch64.toolchain.cmake +++ b/contrib/cross/cross.toolchain.cmake @@ -1,7 +1,5 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(TOOLCHAIN_PREFIX aarch64-linux-gnu) -#set(TOOLCHAIN_SUFFIX) - +set(CMAKE_SYSTEM_NAME ${CROSS_PLATFORM}) +set(TOOLCHAIN_PREFIX ${CROSS_PREFIX}) set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) @@ -10,3 +8,4 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc${TOOLCHAIN_SUFFIX}) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++${TOOLCHAIN_SUFFIX}) +set(ARCH_TRIPLET ${TOOLCHAIN_PREFIX}) diff --git a/contrib/cross/ppc64le.toolchain.cmake b/contrib/cross/ppc64le.toolchain.cmake deleted file mode 100644 index c045b4939..000000000 --- a/contrib/cross/ppc64le.toolchain.cmake +++ /dev/null @@ -1,12 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(TOOLCHAIN_PREFIX powerpc64le-linux-gnu) -set(TOOLCHAIN_SUFFIX -8) - -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc${TOOLCHAIN_SUFFIX}) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++${TOOLCHAIN_SUFFIX}) diff --git a/contrib/dtrace/lokinet.xml b/contrib/dtrace/lokinet.xml deleted file mode 100644 index 82f46a6c9..000000000 --- a/contrib/dtrace/lokinet.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/contrib/dtrace/profiler.d b/contrib/dtrace/profiler.d deleted file mode 100644 index ce45a1ea8..000000000 --- a/contrib/dtrace/profiler.d +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/sbin/dtrace -s - -syscall:::entry -/pid == $target/ -{ - @calls[ustack(10), probefunc] = count(); -} - -profile:::tick-1sec -{ - /** print */ - printa(@calls); - /** clear */ - clear(@calls); - trunc(@calls, 15); -} - - diff --git a/contrib/freebsd/openrc/lokinet.rc b/contrib/freebsd/openrc/lokinet.rc deleted file mode 100644 index 67d8b336d..000000000 --- a/contrib/freebsd/openrc/lokinet.rc +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -. /etc/rc.subr - -name=lokinet -rcvar=lokinet_enable - -command="/usr/local/bin/${name}" -command_args="/usr/local/etc/${name}/daemon.ini > /dev/null 2>&1" - -pidfile="/usr/local/etc/${name}/lokinet.pid" - -required_files="/usr/local/etc/${name}/daemon.ini" - -sig_reload="HUP" - -start_precmd="${command} -g /usr/local/etc/${name}/daemon.ini" - -load_rc_config $name -run_rc_command "$1" \ No newline at end of file diff --git a/contrib/liblokinet/CMakeLists.txt b/contrib/liblokinet/CMakeLists.txt new file mode 100644 index 000000000..6985f741b --- /dev/null +++ b/contrib/liblokinet/CMakeLists.txt @@ -0,0 +1,10 @@ + +cmake_minimum_required(VERSION 3.10) + +project(udptest LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +add_executable(udptest udptest.cpp) +include_directories(../../include) +target_link_libraries(udptest PUBLIC lokinet) + diff --git a/contrib/liblokinet/readme.md b/contrib/liblokinet/readme.md new file mode 100644 index 000000000..e0f63d135 --- /dev/null +++ b/contrib/liblokinet/readme.md @@ -0,0 +1,13 @@ +# liblokinet examples + +building: + + $ mkdir -p build + $ cd build + $ cp /path/to/liblokinet.so . + $ cmake .. -DCMAKE_EXE_LINKER_FLAGS='-L.' + $ make + +running: + + $ ./udptest /path/to/bootstrap.signed diff --git a/contrib/liblokinet/udptest.cpp b/contrib/liblokinet/udptest.cpp new file mode 100644 index 000000000..9db9c399e --- /dev/null +++ b/contrib/liblokinet/udptest.cpp @@ -0,0 +1,239 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool _run{true}; + +using Lokinet_ptr = std::shared_ptr; + +[[nodiscard]] auto +MakeLokinet(const std::vector& bootstrap) +{ + auto ctx = std::shared_ptr(lokinet_context_new(), lokinet_context_free); + if (auto err = lokinet_add_bootstrap_rc(bootstrap.data(), bootstrap.size(), ctx.get())) + throw std::runtime_error{strerror(err)}; + if (lokinet_context_start(ctx.get())) + throw std::runtime_error{"could not start context"}; + return ctx; +} + +void +WaitForReady(const Lokinet_ptr& ctx) +{ + while (_run and lokinet_wait_for_ready(1000, ctx.get())) + { + std::cout << "waiting for context..." << std::endl; + } +} + +class Flow +{ + lokinet_udp_flowinfo const _info; + lokinet_context* const _ctx; + + public: + explicit Flow(const lokinet_udp_flowinfo* info, lokinet_context* ctx) : _info{*info}, _ctx{ctx} + {} + + lokinet_context* + Context() const + { + return _ctx; + } + + std::string + String() const + { + std::stringstream ss; + ss << std::string{_info.remote_host} << ":" << std::to_string(_info.remote_port) + << " on socket " << _info.socket_id; + return ss.str(); + } +}; + +struct ConnectJob +{ + lokinet_udp_flowinfo remote; + lokinet_context* ctx; +}; + +void +CreateOutboundFlow(void* user, void** flowdata, int* timeout) +{ + auto* job = static_cast(user); + Flow* flow = new Flow{&job->remote, job->ctx}; + *flowdata = flow; + *timeout = 30; + std::cout << "made outbound flow: " << flow->String() << std::endl; + ; +} + +int +ProcessNewInboundFlow(void* user, const lokinet_udp_flowinfo* remote, void** flowdata, int* timeout) +{ + auto* ctx = static_cast(user); + Flow* flow = new Flow{remote, ctx}; + std::cout << "new udp flow: " << flow->String() << std::endl; + *flowdata = flow; + *timeout = 30; + + return 0; +} + +void +DeleteFlow(const lokinet_udp_flowinfo* remote, void* flowdata) +{ + auto* flow = static_cast(flowdata); + std::cout << "udp flow from " << flow->String() << " timed out" << std::endl; + delete flow; +} + +void +HandleUDPPacket(const lokinet_udp_flowinfo* remote, const char* pkt, size_t len, void* flowdata) +{ + auto* flow = static_cast(flowdata); + std::cout << "we got " << len << " bytes of udp from " << flow->String() << std::endl; +} + +void +BounceUDPPacket(const lokinet_udp_flowinfo* remote, const char* pkt, size_t len, void* flowdata) +{ + auto* flow = static_cast(flowdata); + std::cout << "bounce " << len << " bytes of udp from " << flow->String() << std::endl; + if (auto err = lokinet_udp_flow_send(remote, pkt, len, flow->Context())) + { + std::cout << "bounce failed: " << strerror(err) << std::endl; + } +} + +Lokinet_ptr sender, recip; + +void +signal_handler(int) +{ + _run = false; +} + +int +main(int argc, char* argv[]) +{ + if (argc == 1) + { + std::cout << "usage: " << argv[0] << " bootstrap.signed" << std::endl; + return 1; + } + + /* + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + */ + + std::vector bootstrap; + + // load bootstrap.signed + { + std::ifstream inf{argv[1], std::ifstream::ate | std::ifstream::binary}; + size_t len = inf.tellg(); + inf.seekg(0); + bootstrap.resize(len); + inf.read(bootstrap.data(), bootstrap.size()); + } + + if (auto* loglevel = getenv("LOKINET_LOG")) + lokinet_log_level(loglevel); + else + lokinet_log_level("none"); + + std::cout << "starting up" << std::endl; + + recip = MakeLokinet(bootstrap); + WaitForReady(recip); + + lokinet_udp_bind_result recipBindResult{}; + + const auto port = 10000; + + if (auto err = lokinet_udp_bind( + port, + ProcessNewInboundFlow, + BounceUDPPacket, + DeleteFlow, + recip.get(), + &recipBindResult, + recip.get())) + { + std::cout << "failed to bind recip udp socket " << strerror(err) << std::endl; + return 0; + } + + std::cout << "bound recip udp" << std::endl; + + sender = MakeLokinet(bootstrap); + WaitForReady(sender); + + std::string recipaddr{lokinet_address(recip.get())}; + + std::cout << "recip ready at " << recipaddr << std::endl; + + lokinet_udp_bind_result senderBindResult{}; + + if (auto err = lokinet_udp_bind( + port, + ProcessNewInboundFlow, + HandleUDPPacket, + DeleteFlow, + sender.get(), + &senderBindResult, + sender.get())) + { + std::cout << "failed to bind sender udp socket " << strerror(err) << std::endl; + return 0; + } + + ConnectJob connect{}; + connect.remote.socket_id = senderBindResult.socket_id; + connect.remote.remote_port = port; + std::copy_n(recipaddr.c_str(), recipaddr.size(), connect.remote.remote_host); + connect.ctx = sender.get(); + + std::cout << "bound sender udp" << std::endl; + + do + { + std::cout << "try establish to " << connect.remote.remote_host << std::endl; + if (auto err = + lokinet_udp_establish(CreateOutboundFlow, &connect, &connect.remote, sender.get())) + { + std::cout << "failed to establish to recip: " << strerror(err) << std::endl; + usleep(100000); + } + else + break; + } while (true); + std::cout << "sender established" << std::endl; + + const std::string buf{"liblokinet"}; + + const std::string senderAddr{lokinet_address(sender.get())}; + + do + { + std::cout << senderAddr << " send to remote: " << buf << std::endl; + if (auto err = lokinet_udp_flow_send(&connect.remote, buf.data(), buf.size(), sender.get())) + { + std::cout << "send failed: " << strerror(err) << std::endl; + } + usleep(100000); + } while (_run); + return 0; +} diff --git a/contrib/mac.sh b/contrib/mac.sh index 4719b40d2..0ddcbe3ff 100755 --- a/contrib/mac.sh +++ b/contrib/mac.sh @@ -9,7 +9,7 @@ set -e set +x -if ! [ -f LICENSE.txt ] || ! [ -d llarp ]; then +if ! [ -f LICENSE ] || ! [ -d llarp ]; then echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" fi diff --git a/contrib/munin/lokinet-munin.py b/contrib/munin/lokinet-munin.py deleted file mode 100644 index f9c067319..000000000 --- a/contrib/munin/lokinet-munin.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# -# requires python3-requests -# -import requests -import json -import os -import sys - -from collections import defaultdict as Dict - -from requests.exceptions import RequestException - - -def jsonrpc(method, **args): - return requests.post('http://127.0.0.1:1190/', data=json.dumps( - {'method': method, 'params': args, 'id': 'munin'}), headers={'content-type': 'application/json'}).json() - - -def exit_sessions_main(): - if len(sys.argv) == 2 and sys.argv[1] == 'config': - print("graph_title lokinet exit sessions") - print("graph_vlabel sessions") - print("graph_category network") - print("graph_info This graph shows the number of exit sessions on a lokinet exit") - print("_exit_sessions.info Number of exit sessions") - print("_exit_sessions.label sessions") - else: - count = 0 - try: - j = jsonrpc("llarp.admin.exit.list") - count = len(j['result']) - except RequestException: - pass - print("_exit_sessions.value {}".format(count)) - - -def peers_main(): - if len(sys.argv) == 2 and sys.argv[1] == 'config': - print("graph_title lokinet peers") - print("graph_vlabel peers") - print("graph_category network") - print("graph_info This graph shows the number of node to node sessions of this lokinet router") - print("_peers_outbound.info Number of outbound lokinet peers") - print("_peers_inbound.info Number of inbound lokinet peers") - print("_peers_outbound.label outbound peers") - print("_peers_inbound.label inbound peers") - print("_peers_clients.info Number of lokinet client peers") - print("_peers_clients.label lokinet client peers") - else: - inbound = Dict(int) - outbound = Dict(int) - clients = Dict(int) - try: - j = jsonrpc("llarp.admin.link.neighboors") - for peer in j['result']: - if peer["svcnode"]: - if peer["outbound"]: - outbound[peer['ident']] += 1 - else: - inbound[peer['ident']] += 1 - else: - clients[peer['ident']] += 1 - except RequestException: - pass - - print("_peers_outbound.value {}".format(len(outbound))) - print("_peers_inbound.value {}".format(len(inbound))) - print("_peers_clients.value {}".format(len(clients))) - -if __name__ == '__main__': - exe = os.path.basename(sys.argv[0]).lower() - if exe == 'lokinet_peers': - peers_main() - elif exe == 'lokinet_exit': - exit_sessions_main() - else: - print( - 'please symlink this as `lokinet_peers` or `lokinet_exit` in munin plugins dir') diff --git a/contrib/py/pylokinet/pylokinet/__init__.py b/contrib/py/pylokinet/pylokinet/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/py/pylokinet/pylokinet/__main__.py b/contrib/py/pylokinet/pylokinet/__main__.py deleted file mode 100644 index 710fd7900..000000000 --- a/contrib/py/pylokinet/pylokinet/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python3 - -from pylokinet.instance import main -main() \ No newline at end of file diff --git a/contrib/py/pylokinet/pylokinet/bencode.py b/contrib/py/pylokinet/pylokinet/bencode.py deleted file mode 100644 index 2f6963209..000000000 --- a/contrib/py/pylokinet/pylokinet/bencode.py +++ /dev/null @@ -1,111 +0,0 @@ -# -# super freaking dead simple wicked awesome bencode library -# -from io import BytesIO - -class BCodec: - encoding = 'utf-8' - def __init__(self, fd): - self._fd = fd - - def _write_bytestring(self, bs): - self._fd.write('{}:'.format(len(bs)).encode('ascii')) - self._fd.write(bs) - - def _write_list(self, l): - self._fd.write(b'l') - for item in l: - self.encode(item) - self._fd.write(b'e') - - def _write_dict(self, d): - self._fd.write(b'd') - keys = list(d.keys()) - keys.sort() - for k in keys: - if isinstance(k, str): - self._write_bytestring(k.encode(self.encoding)) - elif isinstance(k, bytes): - self._write_bytestring(k) - else: - self._write_bytestring('{}'.format(k).encode(self.encoding)) - self.encode(d[k]) - self._fd.write(b'e') - - def _write_int(self, i): - self._fd.write('i{}e'.format(i).encode(self.encoding)) - - def encode(self, obj): - if isinstance(obj, dict): - self._write_dict(obj) - elif isinstance(obj, list): - self._write_list(obj) - elif isinstance(obj, int): - self._write_int(obj) - elif isinstance(obj, str): - self._write_bytestring(obj.encode(self.encoding)) - elif isinstance(obj, bytes): - self._write_bytestring(obj) - elif hasattr(obj, bencode): - obj.bencode(self._fd) - else: - raise ValueError("invalid object type") - - def _readuntil(self, delim): - b = bytes() - while True: - ch = self._fd.read(1) - if ch == delim: - return b - b += ch - - def _decode_list(self): - l = list() - while True: - b = self._fd.read(1) - if b == b'e': - return l - l.append(self._decode(b)) - - def _decode_dict(self): - d = dict() - while True: - ch = self._fd.read(1) - if ch == b'e': - return d - k = self._decode_bytestring(ch) - d[k] = self.decode() - - def _decode_int(self): - return int(self._readuntil(b'e'), 10) - - def _decode_bytestring(self, ch): - ch += self._readuntil(b':') - l = int(ch, base=10) - return self._fd.read(l) - - def _decode(self, ch): - if ch == b'd': - return self._decode_dict() - elif ch == b'l': - return self._decode_list() - elif ch == b'i': - return self._decode_int() - elif ch in [b'0',b'1',b'2',b'3',b'4',b'5',b'6',b'7',b'8',b'9']: - return self._decode_bytestring(ch) - else: - raise ValueError(ch) - - def decode(self): - return self._decode(self._fd.read(1)) - - -def bencode(obj): - buf = BytesIO() - b = BCodec(buf) - b.encode(obj) - return buf.getvalue() - -def bdecode(bytestring): - buf = BytesIO(bytestring) - return BCodec(buf).decode() \ No newline at end of file diff --git a/contrib/py/pylokinet/pylokinet/bootserv.py b/contrib/py/pylokinet/pylokinet/bootserv.py deleted file mode 100644 index a196c507b..000000000 --- a/contrib/py/pylokinet/pylokinet/bootserv.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env python3 -# -# python wsgi application for managing many lokinet instances -# - -__doc__ = """lokinet bootserv wsgi app -also handles webhooks for CI -run me with via gunicorn pylokinet.bootserv:app -""" - -import os - -from pylokinet import rc -import json - -import random -import time -from datetime import datetime -from email.utils import parsedate, format_datetime -from dateutil.parser import parse as date_parse -import requests - - -root = './lokinet' - -def _compare_dates(left, right): - """ - return true if left timestamp is bigger than right - """ - return date_parse(left) > date_parse(right) - -class TokenHolder: - - _dir = root - _token = None - - def __init__(self, f="token"): - if not os.path.exists(self._dir): - os.mkdir(self._dir, 0o700) - f = os.path.join(self._dir, f) - if os.path.exists(f): - with open(f) as fd: - self._token = fd.read().replace("\n", "") - - def verify(self, token): - """ - return true if token matches - """ - if self._token is None: - return False - return self._token == token - -class BinHolder: - """ - serves a binary file in a dir - """ - _dir = os.path.join(root, 'bin') - - def __init__(self, f): - if not os.path.exists(self._dir): - os.mkdir(self._dir, 0o700) - self._fpath = os.path.join(self._dir, f) - - def put(self, r): - """ - put a new file into the place that is held - """ - with open(self._fpath, "wb") as f: - for chunk in r.iter_content(chunk_size=1024): - f.write(chunk) - - - def is_new(self, date): - """ - return true if last modified timestamp is fresher than current - """ - t = date_parse('{}'.format(date)) - if not t: - return False - if os.path.exists(self._fpath): - st = os.stat(self._fpath) - return st.st_mtime < t.timestamp() - return True - - - def serve(self, last_modified, respond): - """ - serve file with caching - """ - t = parsedate(last_modified) - if t: - t = time.mktime(t) - if t is None: - t = 0 - if not os.path.exists(self._fpath): - respond("404 Not Found", []) - return [] - st = os.stat(self._fpath) - if st.st_mtime < t: - respond("304 Not Modified", [("Last-Modified", format_datetime(st.st_mtime)) ]) - return [] - with open(self._fpath, "rb") as f: - data = f.read() - respond("200 OK", [("Content-Type", "application/octect-stream"), - ("Last-Modified", format_datetime(datetime.fromtimestamp(int(st.st_mtime)))),("Content-Length", "{}".format(st.st_size))]) - return [data] - - -class RCHolder: - - _dir = os.path.join(root, 'nodedb') - - _rc_files = list() - - def __init__(self): - if os.path.exists(self._dir): - for root, _, files in os.walk(self._dir): - for f in files: - self._add_rc(os.path.join(root, f)) - else: - os.mkdir(self._dir, 0o700) - - def prune(self): - """ - remove invalid entries - """ - delfiles = [] - for p in self._rc_files: - with open(p, 'rb') as f: - if not rc.validate(f.read()): - delfiles.append(p) - for f in delfiles: - os.remove(f) - - def validate_then_put(self, body): - if not rc.validate(body): - return False - k = rc.get_pubkey(body) - print(k) - if k is None: - return False - with open(os.path.join(self._dir, k), "wb") as f: - f.write(body) - return True - - def _add_rc(self, fpath): - self._rc_files.append(fpath) - - def serve_random(self): - with open(random.choice(self._rc_files), 'rb') as f: - return f.read() - - def empty(self): - return len(self._rc_files) == 0 - - -def handle_rc_upload(body, respond): - holder = RCHolder() - if holder.validate_then_put(body): - respond("200 OK", [("Content-Type", "text/plain")]) - return ["rc accepted".encode('ascii')] - else: - respond("400 Bad Request", [("Content-Type", "text/plain")]) - return ["bad rc".encode('ascii')] - - -def serve_random_rc(): - holder = RCHolder() - if holder.empty(): - return None - else: - return holder.serve_random() - -def response(status, msg, respond): - respond(status, [("Content-Type", "text/plain"), ("Content-Length", "{}".format(len(msg)))]) - return [msg.encode("utf-8")] - -def handle_serve_lokinet(modified_since, respond): - l = BinHolder('lokinet.zip') - return l.serve(modified_since, respond) - - -def fetch_lokinet(j, ref="staging", name="build:linux"): - holder = BinHolder("lokinet.zip") - if 'builds' not in j: - return False - selected = None - attrs = dict() - if 'object_attributes' in j: - attrs = j['object_attributes'] - if 'ref' not in attrs or attrs["ref"] != ref: - return True - - for build in j['builds']: - if 'name' not in build or build['name'] != name: - continue - if 'status' not in build or build['status'] != 'success': - continue - if 'finished_at' not in build or build['finished_at'] is None: - continue - if holder.is_new(build['finished_at']): - if selected is None or _compare_dates(build["finished_at"], selected["finished_at"]): - selected = build - if selected and 'id' in selected: - url = 'https://gitlab.com/lokiproject/loki-network/-/jobs/{}/artifacts/download'.format(selected['id']) - r = requests.get(url) - if r.status_code == 200: - holder.put(r) - return True - - #if 'artifacts_file' not in selected: - # return False - #f = selected["artifacts_file"] - #return True - -def handle_webhook(j, token, event, respond): - """ - handle CI webhook - """ - t = TokenHolder() - if not t.verify(token): - respond("403 Forbidden", []) - return [] - event = event.lower() - if event == 'pipeline hook': - if fetch_lokinet(j): - respond("200 OK", []) - return [] - else: - respond("500 Internal Server Error", []) - return [] - else: - respond("404 Not Found", []) - return [] - - -def app(environ, start_response): - request_body_size = int(environ.get("CONTENT_LENGTH", 0)) - method = environ.get("REQUEST_METHOD") - if method.upper() == "PUT" and request_body_size > 0: - rcbody = environ.get("wsgi.input").read(request_body_size) - return handle_rc_upload(rcbody, start_response) - elif method.upper() == "POST": - if environ.get("PATH_INFO") == "/": - j = json.loads(environ.get("wsgi.input").read(request_body_size)) - token = environ.get("HTTP_X_GITLAB_TOKEN") - return handle_webhook(j, token, environ.get("HTTP_X_GITLAB_EVENT"), start_response) - else: - return response("404 Not Found", 'bad url', start_response) - elif method.upper() == "GET": - if environ.get("PATH_INFO") == "/bootstrap.signed": - resp = serve_random_rc() - if resp is not None: - start_response('200 OK', [("Content-Type", "application/octet-stream")]) - return [resp] - else: - return response('404 Not Found', 'no RCs', start_response) - elif environ.get("PATH_INFO") == "/ping": - return response('200 OK', 'pong', start_response) - elif environ.get("PATH_INFO") == "/lokinet.zip": - return handle_serve_lokinet(environ.get("HTTP_IF_MODIFIED_SINCE"),start_response) - elif environ.get("PATH_INFO") == "/": - return response("200 OK", "lokinet bootserv", start_response) - else: - return response('404 Not Found', 'Not found', start_response) - else: - return response('405 Method Not Allowed', 'method not allowed', start_response) - - -def main(): - """ - run as cron job - """ - h = RCHolder() - h.prune() - -if __name__ == '__main__': - main() diff --git a/contrib/py/pylokinet/pylokinet/instance.py b/contrib/py/pylokinet/pylokinet/instance.py deleted file mode 100644 index 4639ffec8..000000000 --- a/contrib/py/pylokinet/pylokinet/instance.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/env python3 -# -# lokinet runtime wrapper -# - -from ctypes import * -import configparser -import signal -import time -import threading -import os -import sys -import requests - -from pylokinet import rc - -lib_file = os.path.join(os.path.realpath('.'), 'liblokinet-shared.so') - - -def log(msg): - sys.stderr.write("lokinet: {}\n".format(msg)) - sys.stderr.flush() - - -class LokiNET(threading.Thread): - - lib = None - ctx = 0 - failed = False - up = False - - asRouter = True - - def configure(self, lib, conf, ip=None, port=None, ifname=None, seedfile=None, lokid_host=None, lokid_port=None): - log("configure lib={} conf={}".format(lib, conf)) - if not os.path.exists(os.path.dirname(conf)): - os.mkdir(os.path.dirname(conf)) - try: - self.lib = CDLL(lib) - except OSError as ex: - log("failed to load library: {}".format(ex)) - return False - if self.lib.llarp_ensure_config(conf.encode('utf-8'), os.path.dirname(conf).encode('utf-8'), True, self.asRouter): - config = configparser.ConfigParser() - config.read(conf) - log('overwrite ip="{}" port="{}" ifname="{}" seedfile="{}" lokid=("{}", "{}")'.format( - ip, port, ifname, seedfile, lokid_host, lokid_port)) - if seedfile and lokid_host and lokid_port: - if not os.path.exists(seedfile): - log('cannot access service node seed at "{}"'.format(seedfile)) - return False - config['lokid'] = { - 'service-node-seed': seedfile, - 'enabled': "true", - 'jsonrpc': "{}:{}".format(lokid_host, lokid_port) - } - if ip: - config['router']['public-address'] = '{}'.format(ip) - if port: - config['router']['public-port'] = '{}'.format(port) - if ifname and port: - config['bind'] = { - ifname: '{}'.format(port) - } - with open(conf, "w") as f: - config.write(f) - self.ctx = self.lib.llarp_main_init(conf.encode('utf-8')) - else: - return False - return self.lib.llarp_main_setup(self.ctx, False) == 0 - - def inform_fail(self): - """ - inform lokinet crashed - """ - self.failed = True - self._inform() - - def inform_up(self): - self.up = True - self._inform() - - def _inform(self): - """ - inform waiter - """ - - def wait_for_up(self, timeout): - """ - wait for lokinet to go up for :timeout: seconds - :return True if we are up and running otherwise False: - """ - # return self._up.wait(timeout) - - def signal(self, sig): - if self.ctx and self.lib: - self.lib.llarp_main_signal(self.ctx, int(sig)) - - def run(self): - # self._up.acquire() - self.up = True - code = self.lib.llarp_main_run(self.ctx) - log("llarp_main_run exited with status {}".format(code)) - if code: - self.inform_fail() - self.up = False - # self._up.release() - - def close(self): - if self.lib and self.ctx: - self.lib.llarp_main_free(self.ctx) - - -def getconf(name, fallback=None): - return name in os.environ and os.environ[name] or fallback - - -def run_main(args): - seedfile = getconf("LOKI_SEED_FILE") - if seedfile is None: - print("LOKI_SEED_FILE was not set") - return - - lokid_host = getconf("LOKI_RPC_HOST", "127.0.0.1") - lokid_port = getconf("LOKI_RPC_PORT", "22023") - - root = getconf("LOKINET_ROOT") - if root is None: - print("LOKINET_ROOT was not set") - return - - rc_callback = getconf("LOKINET_SUBMIT_URL") - if rc_callback is None: - print("LOKINET_SUBMIT_URL was not set") - return - - bootstrap = getconf("LOKINET_BOOTSTRAP_URL") - if bootstrap is None: - print("LOKINET_BOOTSTRAP_URL was not set") - - lib = getconf("LOKINET_LIB", lib_file) - if not os.path.exists(lib): - lib = "liblokinet-shared.so" - timeout = int(getconf("LOKINET_TIMEOUT", "5")) - ping_interval = int(getconf("LOKINET_PING_INTERVAL", "60")) - ping_callback = getconf("LOKINET_PING_URL") - ip = getconf("LOKINET_IP") - port = getconf("LOKINET_PORT") - ifname = getconf("LOKINET_IFNAME") - if ping_callback is None: - print("LOKINET_PING_URL was not set") - return - conf = os.path.join(root, "daemon.ini") - log("going up") - loki = LokiNET() - log("bootstrapping...") - try: - r = requests.get(bootstrap) - if r.status_code == 404: - log("bootstrap gave no RCs, we are probably the seed node") - elif r.status_code != 200: - raise Exception("http {}".format(r.status_code)) - else: - data = r.content - if rc.validate(data): - log("valid RC obtained") - with open(os.path.join(root, "bootstrap.signed"), "wb") as f: - f.write(data) - else: - raise Exception("invalid RC") - except Exception as ex: - log("failed to bootstrap: {}".format(ex)) - loki.close() - return - if loki.configure(lib, conf, ip, port, ifname, seedfile, lokid_host, lokid_port): - log("configured") - - loki.start() - try: - log("waiting for spawn") - while timeout > 0: - time.sleep(1) - if loki.failed: - log("failed") - break - log("waiting {}".format(timeout)) - timeout -= 1 - if loki.up: - log("submitting rc") - try: - with open(os.path.join(root, 'self.signed'), 'rb') as f: - r = requests.put(rc_callback, data=f.read(), headers={ - "content-type": "application/octect-stream"}) - log('submit rc reply: HTTP {}'.format(r.status_code)) - except Exception as ex: - log("failed to submit rc: {}".format(ex)) - loki.signal(signal.SIGINT) - time.sleep(2) - else: - while loki.up: - time.sleep(ping_interval) - try: - r = requests.get(ping_callback) - log("ping reply: HTTP {}".format(r.status_code)) - except Exception as ex: - log("failed to submit ping: {}".format(ex)) - else: - log("failed to go up") - loki.signal(signal.SIGINT) - except KeyboardInterrupt: - loki.signal(signal.SIGINT) - time.sleep(2) - finally: - loki.close() - else: - loki.close() - - -def main(): - run_main(sys.argv[1:]) - - -if __name__ == "__main__": - main() diff --git a/contrib/py/pylokinet/pylokinet/rc.py b/contrib/py/pylokinet/pylokinet/rc.py deleted file mode 100644 index 8a8a9347c..000000000 --- a/contrib/py/pylokinet/pylokinet/rc.py +++ /dev/null @@ -1,31 +0,0 @@ -from pylokinet import bencode -import pysodium -import binascii -import time - -def _expired(ts, lifetime=84600000): - """ - return True if a timestamp is considered expired - lifetime is default 23.5 hours - """ - return (int(time.time()) * 1000) - ts >= lifetime - -def validate(data): - rc = bencode.bdecode(data) - if b'z' not in rc or b'k' not in rc: - return False - sig = rc[b'z'] - rc[b'z'] = b'\x00' * 64 - buf = bencode.bencode(rc) - try: - k = rc[b'k'] - pysodium.crypto_sign_verify_detached(sig, buf, k) - except: - return False - else: - return not _expired(rc[b't']) - -def get_pubkey(data): - rc = bencode.bdecode(data) - if b'k' in rc: - return binascii.hexlify(rc[b'k']).decode('ascii') \ No newline at end of file diff --git a/contrib/py/pylokinet/readme.md b/contrib/py/pylokinet/readme.md deleted file mode 100644 index 45b56103e..000000000 --- a/contrib/py/pylokinet/readme.md +++ /dev/null @@ -1,27 +0,0 @@ -# pylokinet - -lokinet with python 3 - - # python3 setup.py install - -## bootserv - -bootserv is a bootstrap server for accepting and serving RCs - - $ gunicorn -b 0.0.0.0:8000 pylokinet.bootserv:app - -## pylokinet instance - -obtain `liblokinet-shared.so` from a lokinet build - -run (root): - - # export LOKINET_ROOT=/tmp/lokinet-instance/ - # export LOKINET_LIB=/path/to/liblokinet-shared.so - # export LOKINET_BOOTSTRAP_URL=http://bootserv.ip.address.here:8000/bootstrap.signed - # export LOKINET_PING_URL=http://bootserv.ip.address.here:8000/ping - # export LOKINET_SUBMIT_URL=http://bootserv.ip.address.here:8000/ - # export LOKINET_IP=public.ip.goes.here - # export LOKINET_PORT=1090 - # export LOKINET_IFNAME=eth0 - # python3 -m pylokinet \ No newline at end of file diff --git a/contrib/py/pylokinet/setup.py b/contrib/py/pylokinet/setup.py deleted file mode 100644 index f20b3a8f4..000000000 --- a/contrib/py/pylokinet/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup, find_packages - - - -setup( - name="pylokinet", - version="0.0.1", - license="ZLIB", - author="jeff", - author_email="jeff@i2p.rocks", - description="lokinet python bindings", - url="https://github.com/loki-project/loki-network", - install_requires=["pysodium", "requests", "python-dateutil"], - packages=find_packages()) \ No newline at end of file diff --git a/contrib/py/vanity/.gitignore b/contrib/py/vanity/.gitignore deleted file mode 100644 index 8e5737fb0..000000000 --- a/contrib/py/vanity/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -__pycache__ -*.private \ No newline at end of file diff --git a/contrib/py/vanity/bencode.py b/contrib/py/vanity/bencode.py deleted file mode 100644 index 5c131fc9b..000000000 --- a/contrib/py/vanity/bencode.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# super freaking dead simple wicked awesome bencode library -# -from io import BytesIO - -class BCodec: - encoding = 'utf-8' - def __init__(self, fd): - self._fd = fd - - def _write_bytestring(self, bs): - self._fd.write('{}:'.format(len(bs)).encode('ascii')) - self._fd.write(bs) - - def _write_list(self, l): - self._fd.write(b'l') - for item in l: - self.encode(item) - self._fd.write(b'e') - - def _write_dict(self, d): - self._fd.write(b'd') - keys = list(d.keys()) - keys.sort() - for k in keys: - if isinstance(k, str): - self._write_bytestring(k.encode(self.encoding)) - elif isinstance(k, bytes): - self._write_bytestring(k) - else: - self._write_bytestring('{}'.format(k).encode(self.encoding)) - self.encode(d[k]) - self._fd.write(b'e') - - def _write_int(self, i): - self._fd.write('i{}e'.format(i).encode(self.encoding)) - - def encode(self, obj): - if isinstance(obj, dict): - self._write_dict(obj) - elif isinstance(obj, list): - self._write_list(obj) - elif isinstance(obj, int): - self._write_int(obj) - elif isinstance(obj, str): - self._write_bytestring(obj.encode(self.encoding)) - elif isinstance(obj, bytes): - self._write_bytestring(obj) - elif hasattr(obj, bencode): - obj.bencode(self._fd) - else: - raise ValueError("invalid object type") - - def _readuntil(self, delim): - b = bytes() - while True: - ch = self._fd.read(1) - if ch == delim: - return b - b += ch - - def _decode_list(self): - l = list() - while True: - b = self._fd.read(1) - if b == b'e': - return l - l.append(self._decode(b)) - - def _decode_dict(self): - d = dict() - while True: - ch = self._fd.read(1) - if ch == b'e': - return d - k = self._decode_bytestring(ch) - d[k] = self.decode() - - def _decode_int(self): - return int(self._readuntil(b'e'), 10) - - def _decode_bytestring(self, ch): - ch += self._readuntil(b':') - l = int(ch, base=10) - return self._fd.read(l) - - def _decode(self, ch): - if ch == b'd': - return self._decode_dict() - elif ch == b'l': - return self._decode_list() - elif ch == b'i': - return self._decode_int() - elif ch in [b'0',b'1',b'2',b'3',b'4',b'5',b'6',b'7',b'8',b'9']: - return self._decode_bytestring(ch) - else: - raise ValueError(ch) - - def decode(self): - return self._decode(self._fd.read(1)) - - -def bencode(obj): - buf = BytesIO() - b = BCodec(buf) - b.encode(obj) - return buf.bytes() - -def bdecode(bytestring): - buf = BytesIO() - buf.write(bytestring) - return BCodec(buf).decode() \ No newline at end of file diff --git a/contrib/py/vanity/lokinet-vanity.py b/contrib/py/vanity/lokinet-vanity.py deleted file mode 100644 index 5fc1119dd..000000000 --- a/contrib/py/vanity/lokinet-vanity.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python3 -import bencode -import sys -import libnacl -import struct -from io import BytesIO -import time -from multiprocessing import Process, Array, Value - - -def print_help(): - print('usage: {} keyfile.private prefix numthreads'.format(sys.argv[0])) - return 1 - - -_zalpha = ['y', 'b', 'n', 'd', 'r', 'f', 'g', '8', - 'e', 'j', 'k', 'm', 'c', 'p', 'q', 'x', - 'o', 't', '1', 'u', 'w', 'i', 's', 'z', - 'a', '3', '4', '5', 'h', '7', '6', '9'] - - -def zb32_encode(buf): - s = str() - bits = 0 - l = len(buf) - idx = 0 - tmp = buf[idx] - while bits > 0 or idx < l: - if bits < 5: - if idx < l: - tmp <<= 8 - tmp |= buf[idx] & 0xff - idx += 1 - bits += 8 - else: - tmp <<= 5 - bits - bits = 5 - bits -= 5 - s += _zalpha[(tmp >> bits) & 0x1f] - return s - - -def _gen_si(keys): - e = keys[b'e'][32:] - s = keys[b's'][32:] - v = keys[b'v'] - return {'e': e, 's': s, 'v': v} - - -class AddrGen: - - def __init__(self, threads, keys, prefix): - self._inc = threads - self._keys = keys - self._c = Value('i') - self.sync = Array('i', 3) - self._procs = [] - self.prefix = prefix - - def runit(self): - for ch in self.prefix: - if ch not in _zalpha: - print("invalid prefix, {} not a valid character".format(ch)) - return None, None - print("find ^{}.loki".format(self.prefix)) - i = self._inc - while i > 0: - p = Process(target=self._gen_addr_tick, args=(self.prefix, abs( - libnacl.randombytes_random()), abs(libnacl.randombytes_random()), _gen_si(self._keys))) - p.start() - self._procs.append(p) - i -= 1 - return self._runner() - - def _gen_addr_tick(self, prefix, lo, hi, si): - print(prefix) - fd = BytesIO() - addr = '' - enc = bencode.BCodec(fd) - while self.sync[2] == 0: - si['x'] = struct.pack('>QQ', lo, hi) - fd.seek(0, 0) - enc.encode(si) - pub = bytes(fd.getbuffer()) - addr = zb32_encode(libnacl.crypto_generichash(pub)) - if addr.startswith(prefix): - self.sync[2] = 1 - self.sync[0] = hi - self.sync[1] = lo - return - hi += self._inc - if hi == 0: - lo += 1 - self._c.value += 1 - - def _print_stats(self): - print('{} H/s'.format(self._c.value)) - self._c.value = 0 - - def _joinall(self): - for p in self._procs: - p.join() - - def _runner(self): - while self.sync[2] == 0: - time.sleep(1) - self._print_stats() - self._joinall() - fd = BytesIO() - enc = bencode.BCodec(fd) - hi = self.sync[0] - lo = self.sync[1] - si = _gen_si(self._keys) - si['x'] = struct.pack('>QQ', lo, hi) - enc.encode(si) - pub = bytes(fd.getbuffer()) - addr = zb32_encode(libnacl.crypto_generichash(pub)) - return si['x'], addr - - -def main(args): - if len(args) != 3: - return print_help() - keys = None - with open(args[0], 'rb') as fd: - dec = bencode.BCodec(fd) - keys = dec.decode() - runner = AddrGen(int(args[2]), keys, args[1]) - keys[b'x'], addr = runner.runit() - if addr: - print("found {}.loki".format(addr)) - with open(args[0], 'wb') as fd: - enc = bencode.BCodec(fd) - enc.encode(keys) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/contrib/py/vanity/readme.md b/contrib/py/vanity/readme.md deleted file mode 100644 index f8161176d..000000000 --- a/contrib/py/vanity/readme.md +++ /dev/null @@ -1,10 +0,0 @@ -# lokinet vanity address generator - -installing deps: - - sudo apt install libsodium-dev - pip3 install --user -r requirements.txt - -to generate a nonce with a prefix `^7oki` using 8 cpu threads: - - python3 lokinet-vanity.py keyfile.private 7oki 8 diff --git a/contrib/py/vanity/requirements.txt b/contrib/py/vanity/requirements.txt deleted file mode 100644 index 15d991e7b..000000000 --- a/contrib/py/vanity/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -libnacl \ No newline at end of file diff --git a/contrib/shadow/genconf.py b/contrib/shadow/genconf.py deleted file mode 100644 index 0a4c0794a..000000000 --- a/contrib/shadow/genconf.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 - -import configparser -import sys -import os - -from xml.etree import ElementTree as etree - - -def getSetting(s, name, fallback): return name in s and s[name] or fallback - - -shadowRoot = getSetting(os.environ, "SHADOW_ROOT", - os.path.join(os.environ['HOME'], '.shadow')) - -libpath = 'libshadow-plugin-lokinet.so' - - -def nodeconf(conf, baseDir, name, ifname=None, port=None): - conf['netdb'] = {'dir': 'tmp-nodes'} - conf['router'] = {} - conf['router']['contact-file'] = os.path.join( - baseDir, '{}.signed'.format(name)) - conf['router']['ident-privkey'] = os.path.join( - baseDir, '{}-ident.key'.format(name)) - conf['router']['transport-privkey'] = os.path.join( - baseDir, '{}-transport.key'.format(name)) - if ifname and port: - conf['bind'] = {ifname: port} - conf['connect'] = {} - - -def addPeer(conf, baseDir, peer): - conf['connect'][peer] = os.path.join(baseDir, '{}.signed'.format(peer)) - - -def createNode(pluginName, root, peer, life=600): - node = etree.SubElement(root, 'node') - node.attrib['id'] = peer['name'] - node.attrib['interfacebuffer'] = '{}'.format(1024 * 1024 * 100) - app = etree.SubElement(node, 'process') - app.attrib['plugin'] = pluginName - app.attrib['time'] = '{}'.format(life) - app.attrib['arguments'] = peer['configfile'] - - -def makeBase(settings, name, id): - return { - 'id': id, - 'name': name, - 'contact-file': os.path.join(getSetting(settings, 'baseDir', 'tmp'), '{}.signed'.format(name)), - 'configfile': os.path.join(getSetting(settings, 'baseDir', 'tmp'), '{}.ini'.format(name)), - 'config': configparser.ConfigParser() - } - - -def makeClient(settings, name, id): - peer = makeBase(settings, name, id) - basedir = getSetting(settings, 'baseDir', 'tmp') - nodeconf(peer['config'], basedir, name) - peer['config']['network'] = { - 'type': 'null', - 'tag': 'test', - 'prefetch-tag': 'test' - } - return peer - - -def makeSVCNode(settings, name, id, port): - peer = makeBase(settings, name, id) - nodeconf(peer['config'], getSetting( - settings, 'baseDir', 'tmp'), name, 'eth0', port) - peer['config']['network'] = { - 'type': 'null' - } - return peer - - -def genconf(settings, outf): - root = etree.Element('shadow') - root.attrib["environment"] = 'LLARP_SHADOW=1' - topology = etree.SubElement(root, 'topology') - topology.attrib['path'] = getSetting(settings, 'topology', os.path.join( - shadowRoot, 'share', 'topology.graphml.xml')) - - pluginName = getSetting(settings, 'name', 'lokinet-shared') - - kill = etree.SubElement(root, 'kill') - kill.attrib['time'] = getSetting(settings, 'runFor', '600') - - baseDir = getSetting(settings, 'baseDir', - os.path.join('/tmp', 'lokinet-shadow')) - - if not os.path.exists(baseDir): - os.mkdir(baseDir) - - plugin = etree.SubElement(root, "plugin") - plugin.attrib['id'] = pluginName - plugin.attrib['path'] = libpath - basePort = getSetting(settings, 'svc-base-port', 19000) - svcNodeCount = getSetting(settings, 'service-nodes', 80) - peers = list() - for nodeid in range(svcNodeCount): - peers.append(makeSVCNode( - settings, 'svc-node-{}'.format(nodeid), str(nodeid), basePort + 1)) - basePort += 1 - - # make all service nodes know each other - for peer in peers: - for nodeid in range(svcNodeCount): - if str(nodeid) != peer['id']: - addPeer(peer['config'], baseDir, 'svc-node-{}'.format(nodeid)) - - # add client nodes - for nodeid in range(getSetting(settings, 'client-nodes', 200)): - peer = makeClient( - settings, 'client-node-{}'.format(nodeid), str(nodeid)) - peers.append(peer) - for p in range(getSetting(settings, 'client-connect-to', 10)): - addPeer(peer['config'], baseDir, - 'svc-node-{}'.format((p + nodeid) % svcNodeCount)) - - # generate xml and settings files - for peer in peers: - createNode(pluginName, root, peer) - - with open(peer['configfile'], 'w') as f: - peer['config'].write(f) - - # render - outf.write(etree.tostring(root).decode('utf-8')) - - -if __name__ == '__main__': - settings = { - 'baseDir': os.path.join("/tmp", "lokinet-shadow"), - 'topology': os.path.join(shadowRoot, 'share', 'topology.graphml.xml'), - 'runFor': '{}'.format(60 * 10 * 10) - } - with open(sys.argv[1], 'w') as f: - genconf(settings, f) diff --git a/contrib/testnet/.gitignore b/contrib/testnet/.gitignore deleted file mode 100644 index 6f53c74ca..000000000 --- a/contrib/testnet/.gitignore +++ /dev/null @@ -1 +0,0 @@ -v/ \ No newline at end of file diff --git a/contrib/testnet/genconf.py b/contrib/testnet/genconf.py deleted file mode 100755 index 49fa5c876..000000000 --- a/contrib/testnet/genconf.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python -# -# this script generate supervisord configs for running a test network on loopback -# - - -from argparse import ArgumentParser as AP -from configparser import ConfigParser as CP - -import os - - -def svcNodeName(id): return 'svc-node-%03d' % id - - -def clientNodeName(id): return 'client-node-%03d' % id - - -def main(): - ap = AP() - ap.add_argument('--valgrind', type=bool, default=False) - ap.add_argument('--dir', type=str, default='testnet_tmp') - ap.add_argument('--svc', type=int, default=20, - help='number of service nodes') - ap.add_argument('--baseport', type=int, default=19000) - ap.add_argument('--clients', type=int, default=200, - help='number of client nodes') - ap.add_argument('--bin', type=str, required=True) - ap.add_argument('--out', type=str, required=True) - ap.add_argument('--connect', type=int, default=10) - ap.add_argument('--ip', type=str, default=None) - ap.add_argument('--ifname', type=str, default='lo') - ap.add_argument('--netid', type=str, default=None) - ap.add_argument('--loglevel', type=str, default='debug') - args = ap.parse_args() - - if args.valgrind: - exe = 'valgrind {}'.format(args.bin) - else: - exe = '{} -v'.format(args.bin) - basedir = os.path.abspath(args.dir) - - for nodeid in range(args.svc): - config = CP() - config['router'] = { - 'data-dir': '.', - 'net-threads': '1', - 'worker-threads': '4', - 'nickname': svcNodeName(nodeid), - 'min-connections': "{}".format(args.connect) - } - if args.netid: - config['router']['netid'] = args.netid - - if args.ip: - config['router']['public-ip'] = args.ip - config['router']['public-port'] = str(args.baseport + nodeid) - - config['bind'] = { - args.ifname: str(args.baseport + nodeid) - } - config["logging"] = { - "level": args.loglevel - } - config['netdb'] = { - 'dir': 'netdb' - } - config['network'] = { - 'type' : 'null', - 'save-profiles': 'false' - } - config['api'] = { - 'enabled': 'false' - } - config['lokid'] = { - 'enabled': 'false', - } - config["logging"] = { - "level": args.loglevel - } - d = os.path.join(args.dir, svcNodeName(nodeid)) - if not os.path.exists(d): - os.mkdir(d) - fp = os.path.join(d, 'daemon.ini') - with open(fp, 'w') as f: - config.write(f) - for n in [0]: - if nodeid is not 0: - f.write("[bootstrap]\nadd-node={}\n".format(os.path.join(basedir,svcNodeName(n), 'self.signed'))) - else: - f.write("[bootstrap]\nseed-node=true\n") - - for nodeid in range(args.clients): - config = CP() - - config['router'] = { - 'data-dir': '.', - 'net-threads': '1', - 'worker-threads': '2', - 'nickname': clientNodeName(nodeid) - } - if args.netid: - config['router']['netid'] = args.netid - - config["logging"] = { - "level": args.loglevel - } - - config['netdb'] = { - 'dir': 'netdb' - } - config['api'] = { - 'enabled': 'false' - } - config['network'] = { - 'type' : 'null' - } - d = os.path.join(args.dir, clientNodeName(nodeid)) - if not os.path.exists(d): - os.mkdir(d) - fp = os.path.join(d, 'client.ini') - with open(fp, 'w') as f: - config.write(f) - for n in [0]: - otherID = (n + nodeid) % args.svc - f.write("[bootstrap]\nadd-node={}\n".format(os.path.join(basedir,svcNodeName(otherID), 'self.signed'))) - - with open(args.out, 'w') as f: - basedir = os.path.join(args.dir, 'svc-node-%(process_num)03d') - f.write('''[program:svc-node] -directory = {} -command = {} -r {}/daemon.ini -autorestart=true -redirect_stderr=true -#stdout_logfile=/dev/fd/1 -stdout_logfile={}/svc-node-%(process_num)03d-log.txt -stdout_logfile_maxbytes=0 -process_name = svc-node-%(process_num)03d -numprocs = {} -'''.format(basedir, exe, basedir, args.dir, args.svc)) - basedir = os.path.join(args.dir, 'client-node-%(process_num)03d') - f.write('''[program:Client-node] -directory = {} -command = bash -c "sleep 5 && {} {}/client.ini" -autorestart=true -redirect_stderr=true -#stdout_logfile=/dev/fd/1 -stdout_logfile={}/client-node-%(process_num)03d-log.txt -stdout_logfile_maxbytes=0 -process_name = client-node-%(process_num)03d -numprocs = {} -'''.format(basedir, exe, basedir, args.dir, args.clients)) - f.write('[supervisord]\ndirectory=.\n') - - -if __name__ == '__main__': - main() diff --git a/contrib/testnet/readme.md b/contrib/testnet/readme.md deleted file mode 100644 index cb95b3334..000000000 --- a/contrib/testnet/readme.md +++ /dev/null @@ -1,23 +0,0 @@ -loopback testnet scripts - -requirements: - -* bash -* python3 -* supervisord - - -setup: - -make a testnet compatable lokinet build: - - $ cmake -DWITH_TESTNET=ON -B build-testnet -S . - $ make -C build-testnet lokinet - -usage: - -from root of repo run: - - $ ./contrib/testnet/testnet.sh build-testnet/daemon/lokinet 20 200 - -this will spin up 20 service nodes and 200 clients diff --git a/contrib/testnet/requirements.txt b/contrib/testnet/requirements.txt deleted file mode 100644 index 010599389..000000000 --- a/contrib/testnet/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flask -pynacl diff --git a/contrib/testnet/testnet.sh b/contrib/testnet/testnet.sh deleted file mode 100755 index 86d55e832..000000000 --- a/contrib/testnet/testnet.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -for arg in "$1" "$2" "$3" ; do - test x = "x$arg" && echo "usage: $0 path/to/lokinet num_svc num_clients" && exit 1; -done - -script_root=$(dirname $(readlink -e $0)) -testnet_dir=/tmp/lokinet-testnet - -mkdir -p $testnet_dir - -set -x - -$script_root/genconf.py --bin $1 --netid=testnet --out=$testnet_dir/testnet.ini --svc $2 --dir=$testnet_dir --clients $3 || exit 1 -supervisord -n -c $testnet_dir/testnet.ini diff --git a/contrib/windows.sh b/contrib/windows.sh index 5d6bf399c..7520936e9 100755 --- a/contrib/windows.sh +++ b/contrib/windows.sh @@ -17,12 +17,13 @@ cmake \ -DBUILD_PACKAGE=ON \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_TESTING=OFF \ - -DBUILD_LIBLOKINET=ON \ + -DBUILD_LIBLOKINET=OFF \ -DWITH_TESTS=OFF \ -DNATIVE_BUILD=OFF \ -DSTATIC_LINK=ON \ -DWITH_SYSTEMD=OFF \ -DFORCE_OXENMQ_SUBMODULE=ON \ + -DFORCE_OXENC_SUBMODULE=ON \ -DSUBMODULE_CHECK=OFF \ -DWITH_LTO=OFF \ -DCMAKE_BUILD_TYPE=Release \ diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index bc7be3a1f..233c436ce 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -1,8 +1,3 @@ -set(DEFAULT_WITH_BOOTSTRAP ON) -if(APPLE) - set(DEFAULT_WITH_BOOTSTRAP OFF) -endif() -option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP}) add_executable(lokinet-vpn lokinet-vpn.cpp) if(APPLE) @@ -11,11 +6,10 @@ if(APPLE) else() add_executable(lokinet lokinet.cpp) enable_lto(lokinet lokinet-vpn) - - if(WITH_BOOTSTRAP) - add_executable(lokinet-bootstrap lokinet-bootstrap.cpp) - enable_lto(lokinet-bootstrap) - endif() +endif() +if(WITH_BOOTSTRAP) + add_executable(lokinet-bootstrap lokinet-bootstrap.cpp) + enable_lto(lokinet-bootstrap) endif() @@ -55,7 +49,7 @@ endif() foreach(exe ${exetargets}) if(WIN32 AND NOT MSVC_VERSION) - target_sources(${exe} PRIVATE ../llarp/win32/version.rc) + target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 2c1a70794..ebddf84e1 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -401,19 +401,17 @@ lokinet_main(int argc, char* argv[]) "and IP based onion routing network"); // clang-format off options.add_options() - ("v,verbose", "Verbose", cxxopts::value()) #ifdef _WIN32 ("install", "install win32 daemon to SCM", cxxopts::value()) ("remove", "remove win32 daemon from SCM", cxxopts::value()) #endif - ("h,help", "help", cxxopts::value())("version", "version", cxxopts::value()) - ("g,generate", "generate client config", cxxopts::value()) - ("r,router", "run as router instead of client", cxxopts::value()) - ("f,force", "overwrite", cxxopts::value()) + ("h,help", "print this help message", cxxopts::value()) + ("version", "print version string", cxxopts::value()) + ("g,generate", "generate default configuration and exit", cxxopts::value()) + ("r,router", "run in routing mode instead of client only mode", cxxopts::value()) + ("f,force", "force writing config even if it already exists", cxxopts::value()) ("c,colour", "colour output", cxxopts::value()->default_value("true")) - ("b,background", "background mode (start, but do not connect to the network)", - cxxopts::value()) - ("config", "path to configuration file", cxxopts::value()) + ("config", "path to lokinet.ini configuration file", cxxopts::value()) ; // clang-format on @@ -426,12 +424,6 @@ lokinet_main(int argc, char* argv[]) { auto result = options.parse(argc, argv); - if (result.count("verbose") > 0) - { - SetLogLevel(llarp::eLogDebug); - llarp::LogDebug("debug logging activated"); - } - if (!result["colour"].as()) { llarp::LogContext::Instance().logStream = @@ -467,11 +459,6 @@ lokinet_main(int argc, char* argv[]) genconfigOnly = true; } - if (result.count("background") > 0) - { - opts.background = true; - } - if (result.count("router") > 0) { opts.isSNode = true; diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index c7b8a4fd5..4daf79b6c 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -3,37 +3,82 @@ if (NOT DOXYGEN) message(STATUS "Documentation generation disabled (doxygen not found)") return() endif() -find_program(SPHINX_BUILD sphinx-build) -if (NOT SPHINX_BUILD) - message(STATUS "Documentation generation disabled (sphinx-build not found)") + +find_program(MKDOCS mkdocs) +if (NOT MKDOCS) + message(STATUS "Documentation generation disabled (mkdocs not found)") return() - endif() - +endif() + set(lokinet_doc_sources "${DOCS_SRC}") string(REPLACE ";" " " lokinet_doc_sources_spaced "${lokinet_doc_sources}") +add_custom_target(clean_xml COMMAND ${CMAKE_COMMAND} -E rm -rf doxyxml) +add_custom_target(clean_markdown COMMAND ${CMAKE_COMMAND} -E rm -rf markdown) + add_custom_command( OUTPUT doxyxml/index.xml COMMAND ${DOXYGEN} Doxyfile DEPENDS + clean_xml ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${lokinet_doc_sources} ) +# find doxybook2 +find_program(DOXYBOOK2 doxybook2) +if(NOT DOXYBOOK2) + if(NOT DOXYBOOK2_ZIP_URL) + set(DOXYBOOK2_VERSION v1.4.0 CACHE STRING "doxybook2 version") + set(DOXYBOOK2_ZIP_URL "https://github.com/matusnovak/doxybook2/releases/download/${DOXYBOOK2_VERSION}/doxybook2-linux-amd64-${DOXYBOOK2_VERSION}.zip") + set(DOXYBOOK2_ZIP_HASH_OPTS EXPECTED_HASH SHA256=bab9356f5daa550cbf21d8d9b554ea59c8be039716a2caf6e96dee52713fccb0) + endif() + + file(DOWNLOAD + ${DOXYBOOK2_ZIP_URL} + ${CMAKE_CURRENT_BINARY_DIR}/doxybook2.zip + ${DOXYBOOK2_ZIP_HASH_OPTS}) + + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${CMAKE_CURRENT_BINARY_DIR}/doxybook2.zip + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set(DOXYBOOK2 ${CMAKE_CURRENT_BINARY_DIR}/bin/doxybook2) + set(doxybook_localbin ${DOXYBOOK2}) +endif() + add_custom_command( - OUTPUT html/index.html - COMMAND ${SPHINX_BUILD} -j auto - -Dbreathe_projects.lokinet=${CMAKE_CURRENT_BINARY_DIR}/doxyxml - -Dversion=${lokinet_VERSION} -Drelease=${lokinet_VERSION} - -Aversion=${lokinet_VERSION} -Aversions=${lokinet_VERSION_MAJOR},${lokinet_VERSION_MINOR},${lokinet_VERSION_PATCH} - -b html - ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/html + OUTPUT gen + COMMAND ${DOXYBOOK2} --input ${CMAKE_CURRENT_BINARY_DIR}/doxyxml --output ${CMAKE_CURRENT_BINARY_DIR}/gen --config config.json DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/index.rst - ${CMAKE_CURRENT_BINARY_DIR}/conf.py - ${CMAKE_CURRENT_BINARY_DIR}/doxyxml/index.xml + ${doxybook_localbin} + ${CMAKE_CURRENT_BINARY_DIR}/gen/index.md + ${CMAKE_CURRENT_BINARY_DIR}/config.json + ${CMAKE_CURRENT_BINARY_DIR}/doxyxml/index.xml) + +add_custom_target(clean_html COMMAND ${CMAKE_COMMAND} -E rm -rf html) + +add_custom_command( + OUTPUT markdown + COMMAND find ${CMAKE_CURRENT_BINARY_DIR}/gen/ -type f -name '*.md' -exec ${CMAKE_CURRENT_SOURCE_DIR}/fix-markdown.sh {} "\;" && ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/gen ${CMAKE_CURRENT_BINARY_DIR}/markdown + DEPENDS gen ) -add_custom_target(doc DEPENDS html/index.html) -configure_file(conf.py.in conf.py @ONLY) + +add_custom_command( + OUTPUT html + COMMAND ${MKDOCS} build + DEPENDS + clean_html + ${CMAKE_CURRENT_BINARY_DIR}/markdown) + +add_custom_target(doc DEPENDS markdown) + configure_file(Doxyfile.in Doxyfile @ONLY) -configure_file(index.rst index.rst COPYONLY) +configure_file(index.md.in index.md @ONLY) + +configure_file(config.json config.json COPYONLY) +configure_file(mkdocs.yml mkdocs.yml COPYONLY) + +# we seperate this step out so we force clean_markdown to run before markdown target +add_custom_command( + OUTPUT gen/index.md + COMMAND ${CMAKE_COMMAND} -E copy index.md gen/index.md + DEPENDS clean_markdown) diff --git a/docs/README b/docs/README deleted file mode 100644 index f01a01d3b..000000000 --- a/docs/README +++ /dev/null @@ -1,5 +0,0 @@ -Protocol Specifications Directory - -All documents in this directory are licened CC0 and placed into the public domain. - -Please note that the reference implementation LokiNET is licensed under ZLIB license diff --git a/docs/api_v0.txt b/docs/api_v0.txt deleted file mode 100644 index 0aaa9fec5..000000000 --- a/docs/api_v0.txt +++ /dev/null @@ -1,263 +0,0 @@ - -LLARP Traffic Routing Protocol (LTRP) - -LRTP is a protocol that instructs how to route hidden service traffic on LLARP -based networks. - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in RFC 2119 [RFC2119]. - -Overview: - -LRTP is a message oriented data delivery and receival protocol for hidden -service traffic. All structures are BitTorrent Encoded dictionaries sent -over TCP. - -all structures are bencoded when sent over the networks. -In this document they are provided in JSON for ease of display. - -message format: - -<2 bytes length (N)> - - - -Nouns (data structures): - -Path: information about a path that we have built - -{ - H: [router_id_32_bytes, router_id_32_bytes, router_id_32_bytes, router_id_32_bytes], - R: "<16 bytes local rxid>", - T: "<16 bytes local txid>" -} - -Introduction: a hidden service introduction - -{ - E: expiration_ms_since_epoch_uint64, - L: advertised_latency_ms_uint64, - P: "<16 bytes pathid>", - R: "<32 bytes RouterID>" -} - -ServiceInfo: public key info for hidden service address - -{ - A: "<32 bytes .loki address>", - E: "<32 bytes public encryption key>", - S: "<32 bytes public signing key>" -} - -IntroSet: information about an introduction set from the network - -{ - E: expires_at_timestamp_ms_since_epoch_uint64, - I: [Intro0, Intro1, ... IntroN], - S: ServiceInfo -} - -Converstation: information about a loki network converstation - -{ - L: "<32 bytes loki address provided if a loki address>", - S: "<32 bytes snode address provided if a snode address>", - T: "<16 bytes convo tag>" -} - -SessionInfo: information about our current session - -{ - I: [inbound,convos,here], - O: [outbound,covos,here], - P: [Path0, Path1, .... PathN], - S: Current IntroSet, -} - -Verbs (methods): - -session requset (C->S) - -the first message sent by the client - -{ - A: "session", - B: "<8 bytes random>", - T: milliseconds_since_epoch_client_now_uint64, - Y: 0, - Z: "<32 bytes keyed hash>" -} - -session accept (S->C) - -sent in reply to a session message to indicate session accept and give -a session cookie to the client. - -{ - A: "session-reply", - B: "<8 bytes random from session request>", - C: "<16 bytes session cookie>", - T: milliseconds_since_epoch_server_now_uint64, - Y: 0, - Z: "<32 bytes keyed hash>" -} - -session reject (S->C) - -sent in reply to a session message to indicate session rejection - -{ - A: "session-reject", - B: "<8 bytes random from session request>", - R: "", - T: milliseconds_since_epoch_server_now_uint64, - Y: 0, - Z: "<32 bytes keyed hash>" -} - -spawn a hidden service (C->S) - -only one hidden service can be made per session - -{ - A: "spawn", - C: "<16 bytes session cookie>", - O: config_options_dict, - Y: 1, - Z: "<32 bytes keyed hash>" -} - -inform that we have spawned a new hidden service endpoint (S->C) - -{ - A: "spawn-reply", - C: "<16 bytes session cookie>", - S: ServiceInfo, - Y: 1, - Z: "<32 bytes keyed hash>" -} - -inform that we have not spaned a new hidden service endpint (S->C) - -after sending this message the server closes the connection - -{ - A: "spawn-reject", - C: "<16 bytes session cookie>", - E: "", - Y: 1, - Z: "<32 bytes keyed hash>" -} - -create a new convseration on a loki/snode address (C->S) - -{ - A: "start-convo", - B: "<8 bytes random>", - C: "<16 bytes session cookie>", - R: "human readable remote address .snode/.loki", - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - -sent in reply to a make-convo message to indicate rejection (S->C) - -{ - A: "start-convo-reject", - B: "<8 bytes random from start-convo message>", - C: "<16 bytes session cookie>", - S: status_bitmask_uint, - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - -sent in reply to a make-convo message to indicate that we have accepted this -new conversation and gives the convo tag it uses. - -{ - A: "start-convo-accept", - B: "<8 bytes random from start-convo message>", - C: "<16 bytes session cookie>", - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash> -} - -infrom the status of a converstation on a loki address (S->C) - -for an outbund conversation it is sent every time the status bitmask changes. -for inbound convos it is sent immediately when a new inbound conversation is made. - -S bit 0 (LSB): we found the introset/endpoint for (set by outbound) -S bit 1: we found the router to align on (set by outbound) -S bit 2: we have a path right now (set by outbound) -S bit 3: we have made the converstation (set by both) -S bit 4: we are an inbound converstation (set by inbound) - -{ - A: "convo-status", - C: "<16 bytes session cookie>", - R: "human readable address .snode/.loki", - S: bitmask_status_uint64, - T: "<16 bytes convotag>", - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - -send or recieve authenticated data to or from the network (bidi) - -protocol numbers are - -1 for ipv4 -2 for ipv6 - -{ - A: "data", - C: "<16 bytes session cookie>", - T: "<16 bytes convotag>", - W: protocol_number_uint, - X: "", - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - -get session information (C->S) - -{ - A: "info", - C: "<16 bytes session cookie>", - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - - -session information update (S->C) - -sent in reply to a get session information message - -{ - A: "info-reply", - C: "<16 bytes session cookie>", - I: hiddenserviceinfo, - Y: sequence_num_uint64, - Z: "<32 bytes keyed hash>" -} - -Protocol Flow: - -all messages have an A, C, Y and Z value - -A is the function name being called - -C is the session cookie indicating the current session - -Y is the 64 bit message sequence number as an integer - -Z is the keyed hash computed by MDS(BE(msg), K) where K is HS(api_password) -with the msg.Z being set to 32 bytes of \x00 - -both client and server MUST know a variable length string api_password used to -authenticate access to the api subsystem. - -the Y value is incremented by 1 for each direction every time the sender sends -a message in that direction. diff --git a/docs/code_structure.org b/docs/code_structure.org deleted file mode 100644 index 51952c731..000000000 --- a/docs/code_structure.org +++ /dev/null @@ -1,117 +0,0 @@ - -* lokinet components - -** basic data structures -*** AlignedBuffer -*** RouterContact -**** self signed router descriptor -*** Crypto key types - - -** network / threading / sync utilities -*** threadpool -*** logic (single thread threadpool) - -** configuration -*** ini parser -*** llarp::Configuration - -** cryptography -*** llarp::Crypto interface -**** libsodium / sntrup implementation -*** llarp::CryptoManager -**** crypto implementation signleton manager - -** nodedb -*** llarp_nodedb -**** holds many RouterContacts loaded from disk in memory -**** uses a filesystem skiplist for on disk storage -**** stores in a std::unordered_map addressable via public identity key - -** event loop -*** llarp_event_loop -**** udp socket read/write -**** network interface packet read/write -**** stream connection (outbound stream) -**** stream acceptor (inbound stream) - -** link layer message transport: -*** ILinkSession -**** the interface for an entity that is single session with relay -**** responsible for delivery recieval of link layer messages in full -*** ILinkLayer -**** bound to an address / interface -**** has a direction, inbound / outbound -**** distinctly identified by the union of interface and direction -**** Holds many ILinkSessions bound on the distinctly idenitfied direction/interface - - -** link layer messages -*** link layer message parser -**** parses buffers as bencoded dicts -*** link layer message handler -**** handles each type of link layer message - - -** IHopHandler -*** llarp::PathHopConfig -**** txid, rxid, shared secret at hop -*** llarp::path::Path -**** a built path or a path being built -**** owns a std::vector for each hop's info -*** TransitHop -**** a single hop on a built path -**** has txid, rxid, shared secret, hash of shared secret - - -** pathset -*** path::Builder -**** builds and maintains a set of paths for a common use - - -** routing layer message router -*** routing::IMessageHandler -**** interface for routing layer message processing -**** transit hops implement this if they are an endpoint -**** path::Path implement this always - - -** dht "layer" / rc gossiper -*** TODO rewrite/refactor - -** hidden service data structures -*** IntroSet -**** decrypted plaintext hidden service descriptor -*** EncryptedIntroSet -**** public encrpyted / signed version of IntroSet - - -** service endpoint / outbound context connectivitybackend -*** service::Endpoint -**** backend for sending/recieving packets over the hidden service protocol layer -**** kitchen sink -*** service::SendContext -**** interface type for sending to a resource on the network -*** service::OutboundContext -**** implements SendContext extends path::Builder and path::PathSet -**** for maintaining a pathset that aligns on an introset's intros -~ - -** snode / exit connectivity backend -*** exit::BaseSession -**** extends path::Builder -**** obtains an exit/snode session from the router they are aligning to -*** exit::Endpoint -**** snode/exit side of an exit::Session - -** snapp / exit / mobile / null frontend handlers -*** handlers::TunEndpoint -**** extends service::Endpoint -**** provides tun interface frontend for hidden service backend -*** handlers::ExitEndpoint -**** provides tun interface frontend for exit/snode backend - - -** outbound message dispatcher -*** TODO tom please document these - diff --git a/docs/conf.py.in b/docs/conf.py.in deleted file mode 100644 index e99ba91ec..000000000 --- a/docs/conf.py.in +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'lokinet' -copyright = '2020, Jeff Becker' -author = 'Jeff Becker' - -# The short X.Y version -version = '' -# The full version, including alpha/beta/rc tags -release = '' - - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. - -extensions = ['breathe', 'exhale'] -breathe_projects = { "lokinet": "@CMAKE_CURRENT_BINARY_DIR@/doxyxml/" } -breathe_default_project = "lokinet" -breathe_domain_by_extension = {"h" : "cpp", "hpp": "cpp"} - -exhale_args = { - # These arguments are required - "containmentFolder": "./api", - "rootFileName": "lokinet.rst", - "rootFileTitle": "lokinet internals", - "doxygenStripFromPath": "..", - # Suggested optional arguments - "createTreeView": True, - # TIP: if using the sphinx-bootstrap-theme, you need - # "treeViewIsBootstrap": True, -} - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_suffix = ['.rst', '.md'] - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' -highlight_language = 'c++' -primary_domain = 'cpp' -default_role = 'cpp:any' -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'lokinetdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'lokinet.tex', 'lokinet Documentation', - 'Jeff Becker', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'lokinet', 'lokinet Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'lokinet', 'lokinet Documentation', - author, 'lokinet', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] diff --git a/docs/config.json b/docs/config.json new file mode 100644 index 000000000..a160cbfba --- /dev/null +++ b/docs/config.json @@ -0,0 +1,9 @@ +{ + "baseUrl": "", + "indexInFolders": false, + "linkSuffix": ".md", + "mainPageInRoot": false, + "mainPageName": "index", + "linkLowercase": false, + "foldersToGenerate": ["files", "classes", "namespaces"] +} diff --git a/docs/crypto_v0.txt b/docs/crypto_v0.txt deleted file mode 100644 index 84f234d4d..000000000 --- a/docs/crypto_v0.txt +++ /dev/null @@ -1,39 +0,0 @@ - - -cryptography: - -H(x) is 512 bit blake2b digest of x -HS(x) is 256 bit blake2b digest of x -MD(x, k) is 512 bit blake2b hmac of x with secret value k -MDS(x, k) is 256 bit blake2b hmac of x with secret value k -SE(k, n, x) is chacha20 encrypt data x using symettric key k and nounce n -SD(k, n, x) is chacha20 dectypt data x using symettric key k and nounce n -S(k, x) is sign x with ed25519 using secret key k -EDKG() is generate ec keypair (p, s) public key p (32 bytes), secret key s (64 bytes) -V(k, x, sig) is verify x data using signature sig using public key k -EDDH(a, b) is curve25519 scalar multiplication of a and b -HKE(a, b, x) is hashed key exchange between a and b using a secret key x HS(a + b + EDDH(x, b)) -TKE(a, b, x, n) is a transport shared secret kdf using MDS(n, HKE(a, b, x)) - -when A is client and B is server where n is a 32 bytes shared random - -client computes TKE(A.pk, B.pk, A.sk, n) -server computes TKE(A.pk, B.pk, B.sk, n) - -PDH(a, b, x) is path shared secret generation HS(a + b + EDDH(x, b)) - -PKE(a, b, x, n) is a path shared secret kdf using MDS(n, PDH(a, b, x)) - -given A is the path creator and B is a hop in the path and n is 32 bytes shared random - -A computes PKE(A.pk, B.pk, A.sk, n) as S_a -B computes PKE(A.pk, B.pk, B.sk, n) as S_b - -S_a is equal to S_b - -RAND(n) is n random bytes - -PQKG() is generate a sntrup4591761 key pair (sk, pk) -PQKE_A(pk) is alice generating (x, k) where x is sntrup4591761 ciphertext block and k is the session key -PQKE_B(x, sk) is bob calculating k where x is sntrup4591761 ciphertext block, sk is bob's sntrup4591761 secretkey and k is the session key - diff --git a/docs/dht_v0.txt b/docs/dht_v0.txt deleted file mode 100644 index 868ea3d57..000000000 --- a/docs/dht_v0.txt +++ /dev/null @@ -1,153 +0,0 @@ -DHT messages - -DHT messages can be either wrapped in a LIDM message or sent anonymously over a path inside a DHT routing message - -The distance function is A xor B (traditional kademlia) - -The dht implements both iterative and recursive lookups. - -Recursive lookups forward the request to a node closer if not found. -Iterative lookups return the key and of the DHT node who is closer. - -In the case of iterative FRCM the RC of the closer router is provided. -In the case of iterative FIM the pubkey of a dht node who is closer is provided in the GIM. - -find introduction message (FIM) - -variant 1: find an IS by SA - -{ - A: "F", - R: 0 for iterative and 1 for recurisve, - S: "<32 bytes SA>", - T: transaction_id_uint64, - V: 0 -} - -variant 2: recursively find many IS in a tag - -{ - A: "F", - E: [list, of, excluded, SA], - R: 0 for iterative and 1 for recurisve, - N: "<16 bytes topic tag>", - T: transaction_id_uint64, - V: 0 -} - -exclude adding service addresses in E if present - - -got introduction message (GIM) - -sent in reply to FIM containing the result of the search -sent in reply to PIM to acknoledge the publishing of an IS - -{ - A: "G", - I: [IS, IS, IS, ...], - K: "<32 bytes public key of router who is closer, provided ONLY if FIM.R is 0>", - T: transaction_id_uint64, - V: 0, -} - -The I value MUST NOT contain more than 4 IS. -The I value MUST contain either 1 or 0 IS for PIM and FIM variant 1. - - -publish introduction message (PIM) - -publish one IS to the DHT. - -version 0 uses the SA of the IS as the keyspace location. - -in the future the location will be determined by the dht kdf -which uses a shared random source to obfuscate keyspace location. - - -R is currently set to 0 by the sender. - -{ - A: "I", - I: IS, - R: random_walk_counter, - S: optional member 0 for immediate store otherwise non zero, - T: transaction_id_uint64, - V: 0 -} - -if R is greater than 0, decrement R and forward the request to a random DHT -node without storing the IS. -As of protocol version 0, R is always 0. - -If S is provided store the IS for later lookup unconditionally, then -decrement S by 1 and forward to dht peer who is next closest to -the SA of the IS. If S is greater than 3, don't store the IS and -discard this message. - -acknoledge introduction message (AIM) - -acknoledge the publishing of an introduction - -{ - A: "A", - P: published_to_counter, - T: transaction_id_uint64, - V: 0 -} - -increment P by 1 and forward to requester - - -find router contact message (FRCM) - -find a router by long term RC.k public key - -{ - A: "R", - E: 0 or 1 if exploritory lookup, - I: 0 or 1 if iterative lookup, - K: "<32 byte public key of router>", - T: transaction_id_uint64, - V: 0 -} - -if E is provided and non zero then return E dht nodes that are closest to K -if I is provided and non zero then this request is considered an iterative lookup -during an iterative lookup the response's GRCM.K is set to the pubkey of the router closer in key space. -during a recursive lookup the request is forwarded to a router who is closer in -keyspace to K. -If we have no peers that are closer to K and we don't have the RC known locally -we reply with a GRCM whose R value is emtpy. -In any case if we have a locally known RC with pubkey equal to K reply with -a GRCM with 1 RC in the R value corrisponding to that locally known RC. - -got router contact message (GRCM) - -R is a list containing a single RC if found or is an empty list if not found -sent in reply to FRCM only - -{ - A: "S", - K: "<32 bytes public identity key of router closer, provided ONLY if FRCM.I is 1>", - R: [RC], - T: transaction_id_uint64, - V: 0 -} - -in response to an exploritory router lookup, where FRCM.E is provided and non zero. - -{ - A: "S", - N: [list, of, router, publickeys, near, K], - T: transaction_id_uint64, - V: 0 -} - -sent in reply to a dht request to indicate transaction timeout - -{ - A: "T", - T: transaction_id_uint64, - V: 0 -} \ No newline at end of file diff --git a/docs/dht_v1.txt b/docs/dht_v1.txt deleted file mode 100644 index 5832caa7d..000000000 --- a/docs/dht_v1.txt +++ /dev/null @@ -1,94 +0,0 @@ -llarp's dht is a recusrive kademlia dht with optional request proxying via paths for requester anonymization. - -dht is separated into 2 different networks, one for router contacts, one for introsets. - - - - -format for consesus propagation messages: - -keys: A, H, K, N, O, T, U, V - -concensus request messages - -requester requests current table hash, H,N,O is set to zeros if not known - -C -> S - -{ - A: "C", - H: "<32 byte last hash of consensus table>", - N: uint64_number_of_entries_to_request, - O: uint64_offset_in_table, - T: uint64_txid, - V: [] -} - - -when H or N is set to zero from the requester, they are requesting the current consensus table's hash - -consensus response message is as follows for a zero H or N value - -S -> C - -{ - A: "C", - H: "<32 byte hash of current consensus table>", - N: uint64_number_of_entries_in_table, - T: uint64_txid, - U: uint64_ms_next_update_required, - V: [proto, major, minor, patch] -} - -requester requests a part of the current table for hash H - -N must be less than or equal to 512 - -C -> S - -{ - A: "C", - H: "<32 bytes current consensus table hash>", - N: 256, - O: 512, - T: uint64_txid, - V: [] -} - -consensus response message for routers 512 to 512 + 256 - -S -> C - -{ - A: "C", - H: "<32 bytes current concensus table hash>", - K: [list, of, N, pubkeys, from, request, starting, at, position, O], - T: uint64_txid, - V: [proto, major, minor, patch] -} - -consensus table is a concatination of all public keys in lexigraphical order. - -the hash function in use is 256 bit blake2 - - - -gossip RC message - -broadcast style RC publish message. sent to all peers infrequently. - -it is really an unwarrented GRCM, propagate to all peers. - -{ - A: "S", - R: [RC], - T: 0, - V: proto -} - -replays are dropped using a decaying hashset or decaying bloom filter. - - - -the introset dht has 3 message: GetIntroSet Message (GIM), PutIntroSet Message (PIM), FoundIntroSet Message (FIM) - diff --git a/docs/doxygen.md b/docs/doxygen.md new file mode 100644 index 000000000..f520f02b8 --- /dev/null +++ b/docs/doxygen.md @@ -0,0 +1,27 @@ + +# Doxygen + +building doxygen docs requires the following: + +* cmake +* doxygen +* sphinx-build +* sphinx readthedocs theme +* breathe +* exhale + +install packages: + + $ sudo apt install make cmake doxygen python3-sphinx python3-sphinx-rtd-theme python3-breathe python3-pip + $ pip3 install --user exhale + +build docs: + + $ mkdir -p build-docs + $ cd build-docs + $ cmake .. && make doc + +serve built docs via http, will be served at http://127.0.0.1:8000/ + + $ python3 -m http.server -d docs/html + diff --git a/docs/fix-markdown.sh b/docs/fix-markdown.sh new file mode 100644 index 000000000..23f35a31d --- /dev/null +++ b/docs/fix-markdown.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# apply markdown file content quarks + + +# rewrite br tags +sed -i 's|
|
|g' $@ diff --git a/docs/high-level.txt b/docs/high-level.txt deleted file mode 100644 index a7ae94076..000000000 --- a/docs/high-level.txt +++ /dev/null @@ -1,144 +0,0 @@ -LLARP - Low Latency Anon Routing Protocol - - TL;DR edition: an onion router with a tun interface for transporting ip packets - anonymously between you and the internet and internally inside itself to other users. - - - This document describes the high level outline of LLARP, specific all the - project's goals, non-goals and network architecture from a bird's eye view. - - - Preface: - - - Working on I2P has been a really big learning experience for everyone involved. - After much deliberation I have decided to start making a "next generation" onion - routing protocol. Specifically LLARP is (currently) a research project - to explore the question: - - - "What if I2P was made in the current year (2018)? What would be different?" - - - Project Non Goals: - - - This project does not attempt to solve traffic shape correlation or active nation - state sponsored network attacks. The former is an inherit property of low latency - computer networks that I personally do not think is possible to properly fully - "solve". The latter is a threat that lies outside the scope of what the current - toolset that is available to me at the moment provides. - - - This project does not attempt to be a magical application level cure-all for - application or end user security. At the end of the day that is a problem that - exists between chair and keyboard. - - - The Single Project Goal: - - - LLARP is a protocol suite meant to anonymize IP by providing an anonymous - network level (IPv4/IPv6) tunnel broker for both "hidden services" and - communication back to "the clearnet" (the normal internet). Both hidden service - and clearnet communication MUST permit both outbound and inbound traffic on the - network level without any NAT (except for IPv4 in which NAT is permitted due to - lack of address availability). - - - In short We want to permit both anonymous exit and entry network level traffic - between LLARP enabled networks and the internet. - - - Rationale for starting over: - - - Despite Tor Project's best efforts to popularize Tor use, Tor2Web seems to be - widely popular for people who do not wish to opt into the ecosystem. My proposed - solution would be to permit inbound traffic from "exit nodes" in addition to - allowing outbound exit traffic. I have no ideas on how this could be done with - the existing protocols in Tor or if it is possible or advisable to attempt such - as I am not familiar with their ecosystem. - - - I2P could have been used as a medium for encrypted anonymous IP transit but the - current network has issues with latency and throughput. Rebasing I2P atop more - modern cryptography has been going on internally inside I2P for at least 5 years - with less progress than desired. Like some before me, I have concluded that it - would be faster to redo the whole stack "the right way" than to wait for I2P to - finish rebasing. That being said, nothing is preventing I2P from be used for - encrypted anonymous IP transit traffic in a future where I2P finishes their - protocol migrations, I just don't want to wait. - - - In short, I want to take the "best parts" from Tor and I2P and make a new - protocol suite. - - - For both Tor and I2P I have 2 categories for the attributes they have. - - - the good - the bad and the ugly - - - The good (I2P): - - - I2P aims to provide an anonymous unspoofable load balanced network layer. - - - I want this feature. - - - I2P is trust agile, it does not have any hard coded trusts in its network - architecture. Even network boostrap can be done from a single router if the user - desires to (albeit this is currently ill advised). - - - I want this feature. - - - The good (Tor): - - - Tor embraces the reality of the current internet infrastructure by having a - client/server architecture. This allows very low barriers of entry in using the - Tor network and a higher barrier of entry for contributing routing - infrastructure. This promotes a healthy network shape of high capacity servers - serving low capacity clients that "dangle off of the side" of the network. - - - I want this feature. - - - The bad and the ugly (I2P): - - - Bad: I2P uses old cryptography, specially 2048 bit ElGamal using non standard primes. - The use of ElGamal is so pervasive throughout the I2P protocol stack that it - exists at every level of it. Removing it is a massive task that is taking a long - LONG time. - - - I don't want this feature. - - - Ugly: I2P cannot currently mitigate most sybil attacks with their current network - architecture. Recently I2P has added some blocklist solutions signed by release - signers but this probably won't scale in the event of a "big" attack. In - addition I2P isn't staffed for such attacks either. - - - This is a hard problem to solve that the Loki network may be able to help - with by creating a financial barrier to running multiple a relays. - - - - The bad and the ugly (Tor): - - - Bad: Tor is strictly TCP oriented. - I don't want this feature. - - diff --git a/docs/ideal-ux.md b/docs/ideal-ux.md new file mode 100644 index 000000000..2050892d5 --- /dev/null +++ b/docs/ideal-ux.md @@ -0,0 +1,3 @@ +# How Do I use lokinet? + +`// TODO: this` diff --git a/docs/index.md.in b/docs/index.md.in new file mode 100644 index 000000000..d7fe69d00 --- /dev/null +++ b/docs/index.md.in @@ -0,0 +1,7 @@ +# Lokinet @lokinet_VERSION@ (git rev: @GIT_VERSION@) + +summary goes here + +## Overview + +[code internals](index_namespaces.md) diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index a58c3ac24..000000000 --- a/docs/index.rst +++ /dev/null @@ -1,8 +0,0 @@ - -Welcome to Lokinet Internals -============================ - -.. toctree:: - :maxdepth: 2 - - api/lokinet diff --git a/docs/liblokinet-dev-guide.md b/docs/liblokinet-dev-guide.md new file mode 100644 index 000000000..f89f8e9f2 --- /dev/null +++ b/docs/liblokinet-dev-guide.md @@ -0,0 +1,11 @@ +# Embedding Lokinet into an existing application + +When all else fails and you want to deploy lokinet inside your app without the OS caring you can embed an entire lokinet client and a few of the upper network layers into your application manually. + +## Why you should avoid this route + +`// TODO: this` + +## When you cannot avoid this route, how do i use it? + +`// TODO: this` diff --git a/docs/linux-setcap-readme.txt b/docs/linux-setcap-readme.txt deleted file mode 100644 index 01b94b3c8..000000000 --- a/docs/linux-setcap-readme.txt +++ /dev/null @@ -1,16 +0,0 @@ -Lokinet needs certain capabilities to run to set up a virtual network interface and provide a DNS server. The preferred approach to using this is through the linux capabilities mechanism, which allows assigning limited capabilities without needing to run the entire process as root. - -There are two main ways to do this: - -1. If you are running lokinet via an init system such as systemd, you can specify the capabilities in the service file by adding: - -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE - - into the [Service] section of the systemd service file. This will assign the necessary permissions when running the process and allow lokinet to work while running as a non-root user. - -2. You can set the capabilities on the binary by using the setcap program (if not available you may need to install libcap2-bin on Debian/Ubuntu-based systems) and running: - -setcap cap_net_admin,cap_net_bind_service=+eip lokinet - - This grants the permissions whenever the lokinet binary is executed. diff --git a/docs/llarp.png b/docs/llarp.png deleted file mode 100644 index 781db1124..000000000 Binary files a/docs/llarp.png and /dev/null differ diff --git a/docs/llarp_structure.dot b/docs/llarp_structure.dot deleted file mode 100644 index 1052f0998..000000000 --- a/docs/llarp_structure.dot +++ /dev/null @@ -1,102 +0,0 @@ -digraph { - constants -> util; - - crypto -> constants; - crypto -> llarp; - crypto -> util; - - dht -> crypto; - dht -> messages; - dht -> llarp; - dht -> path; - dht -> routing; - dht -> service; - dht -> util; - - dns -> crypto; - dns -> ev; - dns -> handlers; - dns -> llarp; - dns -> net; - dns -> service; - dns -> util; - - ev -> net; - ev -> util; - - exit -> crypto; - exit -> handlers; - exit -> messages; - exit -> net; - exit -> path; - exit -> routing; - exit -> util; - - handlers -> dns; - handlers -> ev; - handlers -> exit; - handlers -> net; - handlers -> service; - handlers -> util; - - link -> constants; - link -> crypto; - link -> ev; - link -> messages; - link -> net; - link -> util; - - messages -> crypto; - messages -> dht; - messages -> exit; - messages -> link; - messages -> llarp; - messages -> path; - messages -> routing; - messages -> service; - messages -> util; - - net -> crypto; - net -> util; - - path -> crypto; - path -> dht; - path -> llarp; - path -> messages; - path -> routing; - path -> service; - path -> util; - - routing -> llarp; - routing -> messages; - routing -> path; - routing -> util; - - service -> crypto; - service -> dht; - service -> ev; - service -> exit; - service -> handlers; - service -> messages; - service -> net; - service -> path; - service -> routing; - service -> util; - - util -> constants; - - llarp -> constants; - llarp -> crypto; - llarp -> dht; - llarp -> dns; - llarp -> ev; - llarp -> exit; - llarp -> handlers; - llarp -> link; - llarp -> messages; - llarp -> net; - llarp -> path; - llarp -> routing; - llarp -> service; - llarp -> util; -} diff --git a/docs/logo.svg b/docs/logo.svg deleted file mode 100644 index 423f32691..000000000 --- a/docs/logo.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/lokinet_admin_v0.txt b/docs/lokinet_admin_v0.txt deleted file mode 100644 index 6ec5f2a5d..000000000 --- a/docs/lokinet_admin_v0.txt +++ /dev/null @@ -1,70 +0,0 @@ -LokiNET admin api - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in RFC 2119 [RFC2119]. - ------- - -the admin api currently uses jsonrpc 2.0 over http - -the methods currently provided are: - -llarp.nodedb.rc.getbykey - - get rc by public identity key - - required parameters: - - key: 32 bytes public identity key - - returns: - - a list of RCs (see protocol v0 spec) that have this public identity key - usually 0 or 1 RCs - - -llarp.nodedb.rc.getbycidr - - get a list of RCs in an address range - - required parameters: - - cidr: ipv6 network cidr string, i.e. "::ffff.21.0.0.0/8" or "fc00::/7" - limit: integer max number of items to fetch, zero or positive integer, - if zero no limit. - - returns: - - a list of 0 to limit RCs that advertise themselves as being reachble via an - address in the given CIDR. - - -llarp.admin.sys.uptime (authentication required) - - required paramters: - - (none) - - returns: - - an integer milliseconds since unix epoch we've been online - -llarp.admin.link.neighboors - - get a list of connected service nodes on all links - - required parameters: - - (none) - - returns: - - list of 0 to N dicts in the following format: - - { - "connected" : uint64_milliseconds_timestamp_connected_at - "ident" : "<64 hex encoded public identity key>", - "laddr" : "local address", - "raddr" : "remote address" - } diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 000000000..4ba3d76cd --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,5 @@ +site_name: Lokinet +theme: + name: 'readthedocs' +docs_dir: markdown +site_dir: html diff --git a/docs/net-comparisons.md b/docs/net-comparisons.md new file mode 100644 index 000000000..65264cd37 --- /dev/null +++ b/docs/net-comparisons.md @@ -0,0 +1,35 @@ +# How is lokinet different than ... + + +## Tor Browser + +Tor browser is a hardened Firefox Web Browser meant exclusively to surf http(s) sites via Tor. It is meant to be a complete self contained browser you open and run to surf the Web (not the internet) anonymously. +Lokinet does not provide a web browser at this time because that is not a small task to do at all, and an even larger task to do in a way that is secure, robust and private. Community Contribuitions Welcomed. + +## Tor/Onion Services + +While Tor Browser is the main user facing product made by Tor Project, the main powerhouse is Tor itself. Tor provides a way to anonymize tcp connections made by an initiator and optionally additionally the recipient, when using a .onion address. Lokinet provides a similar feature set but can carry anything that can be encapsulated in an IP packet (currently only unicast traffic). + +The Lokinet UX differs greatly from that of Tor. By default we do not provide exit connectivity. Because each user's threat model greatly varies in scope and breadth, there exists no one size fits all way to do exit connectivity. Users obtain their exit node information out-of-band at the moment. In the future we want to add decentralized network wide service discovery not limited to just exit providers, but this is currently unimplemented. We think that by being hands-off with respect to exit node requirements a far more diverse set of exit nodes can exist. In addition to having totally open unrestrcited exits, there is merit to permitting "specialized" exit providers that are allowed to do excessive filtering or geo blocking for security theatre checkbox compliance. + +Lokinet additionally encourages the manual selection and pinning of edge connections to fit each user's threat model. + +## I2P + +Integrating applications to utilize i2p's network layer is painful and greatly stunts mainstream adoption. +Lokinet takes the inverse approach of i2p: make app integration in lokinet should require zero custom shims or modifications to code to make it work. + +## DVPNs / Commercial VPN Proxies + +One Hop VPNs can see your real IP and all of the traffic you tunnel over them. They are able to turn your data over to authorities even if they claim to not log. + +Lokinet can see only 1 of these 2 things, but NEVER both: + +* Encrypted data coming from your real IP going to the first hop Lokinet Router forwarded to another Lokinet Router. +* A lokinet exit sees traffic coming from a `.loki` address but has no idea what the real IP is for it. + +One Hop Commericial VPN Tunnels are no log by **policy**. You just have to trust that they are telling the truth. + +Lokinet is no log by **design** it doesn't have a choice in this matter because the technology greatly hampers efforts to do so. + +Any Lokinet Client can be an exit if they want to and it requires no service node stakes. Exits are able to charge users for exiting to the internet, tooling for orechestrating this is in development. diff --git a/docs/old-wire-protocol.txt b/docs/old-wire-protocol.txt deleted file mode 100644 index 30f07659c..000000000 --- a/docs/old-wire-protocol.txt +++ /dev/null @@ -1,90 +0,0 @@ -Wire Protocol (version ½) - - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in RFC 2119 [RFC2119]. - -LLARP supports by default an authenticated and framed transport over UTP [1] - -Handshake: - -Alice establishes a UTP "connection" with Bob. - -Alice sends a LIM a_L encrpyted with the initial b_K key - -if Bob accepts Alice's router, Bob replies with a LIM b_L encrpyted with the -b_K key. - -next the session keys are generated via: - -a_h = HS(a_K + a_L.n) -b_h = HS(b_K + b_L.n) -a_K = TKE(A.p, B_a.e, sk, a_h) -b_K = TKE(A.p, B_a.e, sk, b_h) - -A.tx_K = b_K -A.rx_K = a_K -B.tx_K = a_K -B.rx_K = B_K - -the initial value of a_K is HS(A.k) and b_K is HS(B.k) - -608 byte fragments are sent over UTP in an ordered fashion. - -The each fragment F has the following structure: - -[ 32 bytes blake2 keyed hash of the following 576 bytes (h)] -[ 32 bytes random nonce (n)] -[ 544 bytes encrypted payload (p)] - -the recipiant verifies F.h == MDS(F.n + F.p, rx_K) and the UTP session -is reset if verification fails. - -the decrypted payload P has the following structure: - -[ 24 bytes random (A) ] -[ big endian unsigned 32 bit message id (I) ] -[ big endian unsigned 16 bit fragment length (N) ] -[ big endian unsigned 16 bit fragment remaining bytes (R) ] -[ N bytes of plaintext payload (X) ] -[ trailing bytes discarded ] - -link layer messages fragmented and delievered in any order the sender chooses. - -recipaint ensures a buffer for message number P.I exists, allocating one if it -does not exist. - -recipiant appends P.X to the end of the buffer for message P.I - -if P.R is zero then message number P.I is completed and processed as a link -layer messages. otherwise the recipiant expects P.R additional bytes. -P.R's value MUST decrease by P.N in the next fragment sent. - -message size MUST NOT exceed 8192 bytes. - -if a message is not received in 2 seconds it is discarded and any further -fragments for the message are also discarded. - -P.I MUST have the initial value 0 -P.I MUST be incremeneted by 1 for each new messsage transmitted -P.I MAY wrap around back to 0 - - -after every fragment F the session key K is mutated via: - -K = HS(K + P.A) - -Periodically the connection initiator MUST renegotiate the session key by -sending a LIM after L.p milliseconds have elapsed. - -If the local RC changes while a connection is established they MUST -renegotioate the session keys by sending a LIM to ensure the new RC is sent. - - -references: - -[1] http://www.bittorrent.org/beps/bep_0029.html - - - diff --git a/docs/proto_v0.txt b/docs/proto_v0.txt deleted file mode 100644 index 6a3bb0ecf..000000000 --- a/docs/proto_v0.txt +++ /dev/null @@ -1,903 +0,0 @@ -LLARP v0 - -LLARP (Low Latency Anon Routing Protocol) is a protocol for anonymizing senders and -recipiants of encrypted messages sent over the internet without a centralised -trusted party. - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in RFC 2119 [RFC2119]. - -basic structures: - -all structures are key, value dictionaries encoded with bittorrent encoding -notation: - -a + b is a concatanated with b - -a ^ b is a bitwise XOR b - -x[a:b] is a memory slice of x from index a to b - -BE(x) is bittorrent encode x - -BD(x) is bittorrent decode x - -{ a: b, y: z } is a dictionary with two keys a and y - whose values are b and z respectively - -[ a, b, c ... ] is a list containing a b c and more items in that order - -"" is a bytestring whose contents and length is described by the - quoted value - -"" * N is a bytestring containing the concatenated N times. - -cryptography: - -see crypto_v0.txt - ---- - -wire protocol - -see wire-protocol.txt - ---- - -datastructures: - -all datastructures are assumed version 0 if they lack a v value -otherwise version is provided by the v value - -all ip addresses can be ipv4 via hybrid dual stack ipv4 mapped ipv6 addresses, -i.e ::ffff.8.8.8.8. The underlying implementation MAY implement ipv4 as native -ipv4 instead of using a hybrid dual stack. - -net address: - -net addresses are a variable length byte string, if between 7 and 15 bytes it's -treated as a dot notation ipv4 address (xxx.xxx.xxx.xxx) -if it's exactly 16 bytes it's treated as a big endian encoding ipv6 address. - -address info (AI) - -An address info (AI) defines a publically reachable endpoint - -{ - c: transport_rank_uint16, - d: "", - e: "<32 bytes public encryption key>", - i: "", - p: port_uint16, - v: 0 -} - -example wank address info: - -{ - c: 1, - d: "wank", - e: "<32 bytes of 0x61>", - i: "123.123.123.123", - p: 1234, - v: 0 -} - -bencoded form: - -d1:ci1e1:d4:wank1:e32:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1:d3:iwp1:i15:123.123.123.1231:pi1234e1:vi0ee - -Traffic Policy (TP) - -Traffic policy (TP) defines port, protocol and QoS/drop policy. - -{ - a: protocol_integer, - b: port_integeger, - d: drop_optional_integer, - v: 0 -} - -drop d is set to 1 to indicate that packets of protocol a with source port b will be dropped. -if d is 0 or not provided this traffic policy does nothing. - -Exit Info (XI) - -An exit info (XI) defines a exit address that can relay exit traffic to the -internet. - -{ - a: "", - b: "", - k: "<32 bytes public encryption/signing key>", - p: [ list, of, traffic, policies], - v: 0 -} - - -Exit Route (XR) - -An exit route (XR) define an allocated exit address and any additional -information required to access the internet via that exit address. - -{ - a: "<16 bytes big endian ipv6 gateway address>", - b: "<16 bytes big endian ipv6 netmask>", - c: "<16 bytes big endian ipv6 source address>", - l: lifetime_in_milliseconds_uint64, - v: 0 -} - -router contact (RC) - -router's full identity - -{ - a: [ one, or, many, AI, here ... ], - e: extensions_supported, - i: "", - k: "<32 bytes public long term identity signing key>", - n: "", - p: "<32 bytes public path encryption key>", - s: [services, supported], - u: time_signed_at_milliseconds_since_epoch_uint64, - v: 0, - x: [ Exit, Infos ], - z: "<64 bytes signature using identity key>" -} - -e is a dict containing key/value of supported protocol extensions on this service node, keys are strings, values MUST be 0 or 1. - -s is a list of services on this service node: - -each service is a 6 item long, list of the following: - -["_service", "_proto", ttl_uint, priority_uint, weight_uint, port_uint] - -with the corrisponding SRV record: - -<_service>.<_proto>.router_pubkey_goes_here.snode. IN SRV router_pubkey_goes_here.snode - - -RC.t is the timestamp of when this RC was signed. -RC is valid for a maximum of 1 hour after which it MUST be resigned with the new -timestamp. - -RC.i is set to the network identifier. - -only routers with the same network identifier may connect to each other. - -"testnet" for testnet. -"lokinet" for the "official" lokinet mainline network. - -other values of RC.i indicate the router belongs to a network fork. - - -service info (SI) - -public information blob for a hidden service - -e is the long term public encryption key -s is the long term public signing key -v is the protocol version -x is a nounce value for generating vanity addresses that can be omitted - -if x is included it MUST be equal to 16 bytes - -{ - e: "<32 bytes public encryption key>", - s: "<32 bytes public signing key>", - v: 0, - x: "" -} - -service address (SA) - -the "network address" of a hidden service, which is computed as the blake2b -256 bit hash of the public infomration blob. - -HS(BE(SI)) - -when in string form it's encoded with z-base32 and uses the .loki tld - -introduction (I) - -a descriptor annoucing a path to a hidden service - -k is the rc.k value of the router to contact -p is the path id on the router that is owned by the service -v is the protocol version -x is the timestamp milliseconds since epoch that this introduction expires at - -{ - k: "<32 bytes public identity key of router>", - l: advertised_path_latency_ms_uint64, (optional) - p: "<16 bytes path id>", - v: 0, - x: time_expires_milliseconds_since_epoch_uint64 -} - -introduction set (IS) - -and introset is a signed set of introductions for a hidden service -a is the service info of the publisher -h is a list of srv records in same format as in RCs -i is the list of introductions that this service is advertising with -k is the public key to use when doing encryption to this hidden service -n is a 16 byte null padded utf-8 encoded string tagging the hidden service in - a topic searchable via a lookup (optional) -v is the protocol version -w is an optinal proof of work for DoS protection (slot for future) -z is the signature of the entire IS where z is set to zero signed by the hidden -service's signing key. - -{ - a: SI, - h: [list, of, advertised, services], - i: [ I, I, I, ... ], - k: "<1218 bytes sntrup4591761 public key block>", - n: "<16 bytes service topic (optional)>", - t: timestamp_uint64_milliseconds_since_epoch_published_at, - v: 0, - w: optional proof of work, - z: "<64 bytes signature using service info signing key>" -} - -h is a list of services on this endpoint - -each service is a 7 item long, list of the following: - -["_service", "_proto", ttl_uint, priority_uint, weight_uint, port_uint, "<32 bytes SA of the service>"] - -with the corrisponding SRV record: - -<_service>.<_proto>..loki. IN SRV .loki. - -recursion on SRV records is NOT permitted. - ---- - -Encrypted frames: - - -Encrypted frames are encrypted containers for link message records like LRCR. - -32 bytes hmac, h -32 bytes nounce, n -32 bytes ephmeral sender's public encryption key, k -remaining bytes ciphertext, x - -decryption: - -0) verify hmac - -S = PKE(n, k, our_RC.K) -verify h == MDS(n + k + x, S) - -If the hmac verification fails the entire parent message is discarded - -1) decrypt and decode - -new_x = SD(S, n[0:24], x) -msg = BD(new_x) - -If the decoding fails the entire parent message is discarded - -encryption: - -to encrypt a frame to a router with public key B.k - -0) prepare nounce n, ephemeral keypair (A.k, s) and derive shared secret S - -A.k, s = ECKG() -n = RAND(32) -S = PKE(p, A.k, B.k, n) - -1) encode and encrypt - -x = BE(msg) -new_x = SE(S, n[0:24], x) - -2) generate message authentication - -h = MDS(n + A.k + new_x, S) - -resulting frame is h + n + A.k + new_x - - ---- - -link layer messages: - -the link layer is responsible for anonymising the source and destination of -routing layer messages. - -any link layer message without a key v is assumed to be version 0 otherwise -indicates the protocol version in use. - - - -link introduce message (LIM) - -This message MUST be the first link message sent before any others. This message -identifies the sender as having the RC contained in r. The recipiant MUST -validate the RC's signature and ensure that the public key in use is listed in -the RC.a matching the ipv6 address it originated from. - -{ - a: "i", - e: "<32 bytes ephemeral public encryption key>", - n: "<32 bytes nonce for key exhcange>", - p: uint64_milliseconds_session_period, - r: RC, - v: 0, - z: "<64 bytes signature of entire message by r.k>" -} - -the link session will be kept open for p milliseconds after which -the session MUST be renegotiated. - -link relay commit message (LRCM) - -request a commit to relay traffic to another node. - -{ - a: "c", - c: [ list, of, encrypted, LRCR frames ], - v: 0 -} - -c MUST contain dummy records if the hop length is less than the maximum -hop length. - -link relay commit record (LRCR) - -record requesting relaying messages for 600 seconds to router -on network whose i is equal to RC.k and decrypt data any messages using -PKE(n, rc.p, c) as symmetric key for encryption and decryption. - -if l is provided and is less than 600 and greater than 10 then that lifespan -is used (in seconds) instead of 600 seconds. - -if w is provided and fits the required proof of work then the lifetime of -the path is extended by w.y seconds - -{ - c: "<32 byte public encryption key used for upstream>", - d: uint_optional_ms_delay, // TODO - i: "<32 byte RC.k of next hop>", - l: uint_optional_lifespan, - n: "<32 bytes nounce for key exchange>", - r: "<16 bytes rx path id>", - t: "<16 bytes tx path id>", - u: "", - v: 0, - w: proof of work -} - -w if provided is a dict with the following struct - -{ - t: time_created_seconds_since_epoch, - v: 0, - y: uint32_seconds_extended_lifetime, - z: "<32 bytes nonce>" -} - -the validity of the proof of work is that given - -h = HS(BE(w)) - -h has log_e(y) prefixed bytes being 0x00 - -this proof of work requirement is subject to change - -if i is equal to RC.k then any LRDM.x values are decrypted and interpreted as -routing layer messages. This indicates that we are the farthest hop in the path. - -link relay status message (LRSM) - -response to path creator about build status - -{ - a: "s", - c: [ list, of, encrypted, LRSR frames], - p: "<16 bytes rx path id>", - s: uint_status_flags, // for now, only set (or don't) success bit - v: 0 -} - -the creator of the LRSM MUST include dummy LRSR records -to pad out to the maximum path length - -link relay status record (LRSR) - -record indicating status of path build - -{ - s: uint_status_flags, - v: 0 -} - -uint_status_flags (bitwise booleans): - [0] = success - [1] = fail, hop timeout - [2] = fail, congestion - [3] = fail, refusal, next hop is not known to be a snode - [4] = fail, used by hop creator when decrypting frames if decryption fails - [5] = fail, used by hop creator when record decode fails - [4-63] reserved - -link relay upstream message (LRUM) - -sent to relay data via upstream direction of a previously created path. - -{ - a: "u", - p: "<16 bytes path id>", - v: 0, - x: "", - y: "<32 bytes nonce>" -} - -x1 = SD(k, y, x) - -if we are the farthest hop, process x1 as a routing message -otherwise transmit a LRUM to the next hop - -{ - a: "u", - p: p, - v: 0, - x: x1, - y: y ^ HS(k) -} - -link relay downstream message (LRDM) - -sent to relay data via downstream direction of a previously created path. - -{ - a: "d", - p: "<16 bytes path id>", - v: 0, - x: "", - y: "<32 bytes nonce>" -} - -if we are the creator of the path decrypt x for each hop key k - -x = SD(k, y, x) - -otherwise transmit LRDM to next hop - -x1 = SE(k, y, x) - -{ - a: "d", - p: p, - v: 0, - x: x1, - y: y ^ HS(k) -} - -link immediate dht message (LIDM): - -transfer one or more dht messages directly without a previously made path. - -{ - a: "m", - m: [many, dht, messages], - v: 0 -} - - -link immediate state message (LISM) - -transfer a state message between nodes - -{ - a: "s", - s: state_message, - v: 0 -} - ---- - -state message: - -NOTE: this message type is currently a documentation stub and remains unimplemented. - -state messages propagate changes to the service nodes concensous to thin clients. - -service node joined network - -{ - A: "J", - R: "<32 bytes public key>", - V: 0 -} - -service node parted network - -{ - A: "P", - R: "<32 bytes public key>", - V: 0 -} - -service node list request - -request the service node list starting at index I containing R entries - -{ - A: "R", - I: starting_offset_int, - R: number_of_entires_to_request_int, - V: 0 -} - -service node list response - -response to service node list request - -{ - A: "L", - S: { - "<32 bytes pubkey>" : 1, - "<32 bytes pubkey>" : 1, - .... - }, - V: 0 -} - ---- - -routing layer: - -the routing layer provides inter network communication between the LLARP link -layer and ip (internet protocol) for exit traffic or ap (anonymous protocol) for -hidden services. replies to messages are sent back via the path they -originated from inside a LRDM. all routing messages have an S value which -provides the sequence number of the message so the messages can be ordered. - -ipv4 addresses are allowed via ipv4 mapped ipv6 addresses, i.e. ::ffff.10.0.0.1 - -path confirm message (PCM) - -sent as the first message down a path after it's built to confirm it was built - -always the first message sent - -{ - A: "P", - L: uint64_milliseconds_path_lifetime, - S: 0, - T: uint64_milliseconds_sent_timestamp, - V: 0 -} - -path latency message (PLM) - -a latency measurement message, reply with a PLM response if we are the far end -of a path. - -variant 1, request, generated by the path creator: - -{ - A: "L", - S: uint64_sequence_number, - V: 0 -} - -variant 2, response, generated by the endpoint that recieved the request. - -{ - A: "L", - S: uint64_sequence_number, - T: uint64_timestamp_recieved, - V: 0 -} - -obtain exit message (OXM) - -sent to an exit router to obtain ip exit traffic context. -replies are sent down the path that messages originate from. - -{ - A: "X", - B: [list, of, permitted, blacklisted, traffic, policies], - E: 0 for snode communication or 1 for internet access, - I: "<32 bytes signing public key for future communication>", - S: uint64_sequence_number, - T: uint64_transaction_id, - V: 0, - W: [list, of, required, whitelisted, traffic, policies], - X: lifetime_of_address_mapping_in_seconds_uint64, - Z: "<64 bytes signature using I>" -} - -grant exit messsage (GXM) - -sent in response to an OXM to grant an ip for exit traffic from an external -ip address used for exit traffic. - -{ - A: "G", - S: uint64_sequence_number, - T: transaction_id_uint64, - Y: "<16 byte nonce>", - V: 0, - Z: "<64 bytes signature>" -} - -any TITM recieved on the same path will be forwarded out to the internet if -OXAM.E is not 0, otherwise it is interpreted as service node traffic. - - -reject exit message (RXM) - -sent in response to an OXAM to indicate that exit traffic is not allowed or -was denied. - -{ - A: "J", - B: backoff_milliseconds_uint64, - R: [list, of, rejected, traffic, policies], - S: uint64_sequence_number, - T: transaction_id_uint64, - V: 0, - Y: "<16 byte nonce>", - Z: "<64 bytes signature>" -} - - -discarded data fragment message (DDFM) - -sent in reply to TDFM when we don't have a path locally or are doing network -congestion control from a TITM. - -{ - A: "D", - P: "<16 bytes path id>", - S: uint64_sequence_number_of_fragment_dropped, - V: 0 -} - -transfer data fragment message (TDFM) - -transfer data between paths. - -{ - A: "T", - P: "<16 bytes path id>", - S: uint64_sequence_number, - T: hidden_service_frame, - V: 0 -} - -transfer data to another path with id P on the local router place a random 32 -byte and T values into y and z values into a LRDM message (respectively) and -send it in the downstream direction. if this path does not exist on the router -it is replied to with a DDFM. - - - -hidden service data (HSD) - -data sent anonymously over the network to a recipiant from a sender. -sent inside a HSFM encrypted with a shared secret. - -{ - a: protocol_number_uint, - d: "", - i: Introduction for reply, - n: uint_message_sequence_number, - o: N seconds until this converstation plans terminate, - s: SI of sender, - t: "<16 bytes converstation_tag>, - v: 0 -} - - -hidden service frame (HSF) - -TODO: document this better - -establish converstation tag message (variant 1) - -generate a new convotag that is contained inside an encrypted HSD - -{ - A: "H", - C: "<1048 bytes ciphertext block>", - D: "", - F: "<16 bytes source path_id>", - N: "<32 bytes nonce for key exchange>", - V: 0, - Z: "<64 bytes signature of entire message using sender's signing key>" -} - -alice (A) wants to talk to bob (B) over the network, both have hidden services -set up and are online on the network. - -A and B are both referring to alice and bob's SI respectively. -A_sk is alice's private signing key. - -for alice (A) to send the string "beep" to bob (B), alice picks an introduction -to use on one of her paths (I_A) such that I_A is aligning with one of bobs's -paths (I_B) - -alice generates: - -T = RAND(16) - -m = { - a: 0, - d: "beep", - i: I_A, - n: 0, - s: A, - t: T, - v: 0 -} - -X = BE(m) - -C, K = PQKE_A(I_B.k) -N = RAND(32) -D = SE(X, K, N) - -path = PickSendPath() - -M = { - A: "T", - P: I_B.P, - S: uint64_sequence_number, - T: { - A: "H", - C: C, - D: D, - F: path.lastHop.txID, - N: N, - V: 0, - Z: "\x00" * 64 - }, - V: 0 -} - -Z = S(A_sk, BE(M)) - -alice transmits a TDFM to router with public key I_B.K via her path that ends -with router with public key I_B.k - -path = PickSendPath() - -{ - A: "T", - P: I_B.P, - S: uint64_sequence_number, - T: { - A: "H", - C: C, - D: D, - F: path.lastHop.txID, - N: N, - V: 0, - Z: Z - }, - V: 0 -} - -the shared secret (S) for further message encryption is: - -S = HS(K + PKE(A, B, sk, N)) - -given sk is the local secret encryption key used by the current hidden service - -please note: -signature verification of the outer message can only be done after decryption -because the signing keys are inside the encrypted HSD. - -data from a previously made session (variant 2) - -transfer data on a converstation previously made - -{ - A: "H", - D: "", - F: "<16 bytes path id of soruce>", - N: "<32 bytes nonce for symettric cipher>", - T: "<16 bytes converstation tag>", - V: 0, - Z: "<64 bytes signature using sender's signing key>" -} - -reject a message sent on a convo tag, when a remote endpoint -sends this message a new converstation SHOULD be established. - -{ - A: "H", - F: "<16 bytes path id of soruce>", - R: 1, - T: "<16 bytes converstation tag>", - V: 0, - Z: "<64 bytes signature using sender's signing key>" -} - - -transfer ip traffic message (TITM) - -transfer ip traffic - -{ - A: "I", - S: uint64_sequence_number, - V: 0, - X: [list, of, ip, packet, buffers], -} - -an ip packet buffer is prefixed with a 64 bit big endian unsigned integer -denoting the sequence number for re-ordering followed by the ip packet itself. - -X is parsed as a list of IP packet buffers. -for each ip packet the source addresss is extracted and sent on the -appropriate network interface. - -When we receive an ip packet from the internet to an exit address, we put it -into a TITM, and send it downstream the corresponding path in an LRDM. - -update exit path message (UXPM) - -sent from a new path by client to indicate that a previously established exit -should use the new path that this message came from. - -{ - A: "U", - P: "<16 bytes previous tx path id>", - S: uint64_sequence_number, - T: uint64_txid, - V: 0, - Y: "<16 bytes nonce>", - Z: "<64 bytes signature using previously provided signing key>" -} - -close exit path message (CXPM) - -client sends a CXPM when the exit is no longer needed or by the exit if the -exit wants to close prematurely. -also sent by exit in reply to a CXPM to confirm close. - -{ - A: "C", - S: uint64_sequence_number, - V: 0, - Y: "<16 bytes nonce>", - Z: "<64 bytes signature>" -} - -update exit verify message (EXVM) - -sent in reply to a UXPM to verify that the path handover was accepted - -{ - A: "V", - S: uint64_sequence_number, - T: uint64_txid, - V: 0, - Y: "<16 bytes nonce>", - Z: "<64 bytes signature>" -} - - -DHT message holder message: - -wrapper message for sending many dht messages down a path. - -{ - A: "M", - M: [many, dht, messages, here], - S: uint64_sequence_number, - V: 0 -} diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 000000000..1b132b26f --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,25 @@ +# Lokinet Docs + +This is where Lokinet documentation lives. + +## High level + +[How is Lokinet different to \[insert network technology name here\] ?](net-comparisons.md) + +[How Do I use Lokinet?](ideal-ux.md) + +[What Lokinet can't do](we-cannot-make-sandwiches.md) + + +## Lokinet (SN)Application Developer Portal + + +[What are "SNApps" and how to develop them.](snapps-dev-guide.md) + +[How do I embed lokinet into my application?](liblokinet-dev-guide.md) + + +## Lokinet Internals + +[Build Doxygen Docs for internals](doxygen.md) + diff --git a/docs/snap-config-options.md b/docs/snap-config-options.md deleted file mode 100644 index 0979fe22a..000000000 --- a/docs/snap-config-options.md +++ /dev/null @@ -1,47 +0,0 @@ - -# snapp config options - -## required - -### ifname -network interface name -### ifaddr -ip range of network interface - -## optional - -### keyfile -the private key to persist address with. -if not specified the address will be ephemeral. -### reachable -bool value that sets if we publish our snapp to the dht -`true`: we are reachable via dht -`false`: we are not reachable via dht -### hops -number of hops in a path, min is `1`, max is `8`, defaults to `4` -### paths -number of paths to maintain at any given time, defaults to `6`. -### blacklist-snode -adds a `.snode` to path build blacklist -### exit-node -specify a `.snode` or `.loki` address to use as an exit broker -### local-dns -address to bind local dns resoler to, defaults to `127.3.2.1:53` -if port is omitted it uses port `53` -### upstream-dns -address to forward non lokinet related queries to. if not set lokinet dns will reply with srvfail. -### mapaddr -perma map `.loki` address to an ip owned by the snapp -to map `whatever.loki` to `10.0.10.10` it can be specified via: -``` -mapaddr=whatever.loki:10.0.10.10 -``` - -## compile time optional features - -### on-up -path to shell script to call when our interface is up -### on-down -path to shell script to call when our interface is down -### on-ready -path to shell script to call when snapp is first ready diff --git a/docs/snapps-dev-guide.md b/docs/snapps-dev-guide.md new file mode 100644 index 000000000..24ebb8ff3 --- /dev/null +++ b/docs/snapps-dev-guide.md @@ -0,0 +1,30 @@ +# (SN)Apps Development Guide + + +## Our approach + +`// TODO: this` + +## Differences From Other approaches + +`// TODO: this` + +### SOCKS Proxy/HTTP Tunneling + +`// TODO: this` + +### Embedding a network stack + +`// TODO: this` + +## High Level Code Practices + +`// TODO: this` + +### Goodisms + +`// TODO: this` + +### Badisms + +`// TODO: this` diff --git a/docs/tooling.txt b/docs/tooling.txt deleted file mode 100644 index e41325330..000000000 --- a/docs/tooling.txt +++ /dev/null @@ -1,38 +0,0 @@ -What is a RouterEvent? - - A RouterEvent is a way of representing a conceptual event that took place in a "router" (relay or client). - - RouterEvents are used in order to collect information about a network all in one place and preserve causality. - -How do I make a new RouterEvent? - - Add your event following the structure in llarp/tooling/router_event.{hpp,cpp} - - Add your event to pybind in pybind/llarp/tooling/router_event.cpp - -What if a class my event uses is missing members in pybind? - - Find the relevant file pybind/whatever/class.cpp and remedy that! - -What if I need to refer to classes which aren't available already in pybind? - - Add pybind/namespace/namespace/etc/class.cpp and pybind it! - - You will need to edit the following files accordingly: - pybind/common.hpp - pybind/module.cpp - pybind/CMakeLists.txt - -How do I use a RouterEvent? - - From the cpp side, find the place in the code where it makes the most logical sense - that the conceptual event has taken place (and you have the relevant information to - create the "event" instance) and create it there as follows: - - #include - - where the event takes place, do the following: - auto event = std::make_unique(constructor_args...); - somehow_get_a_router->NotifyRouterEvent(std::move(event)); - - From the Python side...it's a python object! diff --git a/docs/we-cannot-make-sandwiches.md b/docs/we-cannot-make-sandwiches.md new file mode 100644 index 000000000..3798f28b6 --- /dev/null +++ b/docs/we-cannot-make-sandwiches.md @@ -0,0 +1,19 @@ +# What Lokinet can't do + +Lokinet does a few things very well, but obviously can't do everything. + +## Anonymize OS/Application Fingerprints + +Mitigating OS/Application Fingerprinting is the responsibility of the OS and Applications. Lokinet is an Unspoofable IP Packet Onion router, tuning OS fingerprints to be uniform would be a great thing to have in general even outside of the context of Lokinet. The creation of such an OS bundle is a great idea, but outside the scope of Lokinet. We welcome others to develop a solution for this. + +## Malware + +Lokinet cannot prevent running of malicious programs. Computer security unfortunately cannot be solved unilaterally by networking software without simply dropping all incoming and outgoing traffic. + +## Phoning Home + +Lokinet cannot prevent software which sends arbitrary usage data or private information to Microsoft/Apple/Google/Amazon/Facebook/etc. If you are using a service that requires the ability to phone home in order to work, that is a price you pay to use that service. + +## Make Sandwiches + +At its core, Lokinet is technology that anonymizes and authenticates IP traffic. At this current time Lokinet cannot make you a sandwich. No, not even as root. diff --git a/docs/wire-protocol.txt b/docs/wire-protocol.txt deleted file mode 100644 index 0cb51cc0c..000000000 --- a/docs/wire-protocol.txt +++ /dev/null @@ -1,309 +0,0 @@ -Wire Protocol (version 1) - - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in RFC 2119 [RFC2119]. - -LLARP's wire protocol is Internet Wire Protocol (IWP) - -The main goal of iwp is to provide an authenticated encrypted -reliable semi-ordered durable datagram transfer protocol supporting -datagrams of larger size than link mtu. - -in iwp there is an initiator who initiates a session to a recipiant. - -iwp has 3 phases. the first phase is the proof of flow phase. -the second is a session handshake phase, the third is data transmission. - -proof of flow: - -the purpose of the proof of flow phase is to verify the existence -of the initiator's endpoint. - -At any time before the data transfer phase a reject message -is sent the session is reset. - -Alice (A) is the sender and Bob (B) is the recipiant. - -A asks for a flow id from B. - -B MAY send a flow id to A or MAY reject the message from A. - -session handshake: - -an encrypted session is established using establish wire session messages -using a newly created flow id. - -message format: - -there are 2 layers in this protocol, outer messages and inner messages. - -outer messages are sent in plaintext and / or obfsucated with symettric -encryption using a preshared key. - -inner messages are inside an encrypted and authenticated envelope -wrapped by an outer messages, which is always a data tranmssion message. - -outer message format: - -every outer message MAY be obfsucated via symettric encryption for dpi -resistance reasons, this is not authenticated encryption. - -the message is first assumed to be sent in clear first. -if parsing of clear variant fails then the recipiant MUST fall back to assuming -the protocol is in obfuscated mode. - - -<16 bytes nounce, n> - - -obfuscated via: - -K = HS(B_k) -N = HS(n + K) -X = SD(K, m, N[0:24]) - -where -B_k is the long term identity public key of the recipient. -HS is blake2 256 bit non keyed hash -SD is xchacha20 symettric stream cipher (decryption) - -outer-header: - -<1 byte command> -<1 byte reserved set to 0x3d> - -command 'O' - obtain flow id - -obtain a flow id - - -<6 magic bytes "netid?"> -<8 bytes netid, I> -<8 bytes timestamp milliseconds since epoch, T> -<32 bytes public identity key of sender, A_k> -<0-N bytes discarded> - - -the if the network id differs from the current network's id a reject message -MUST be sent - -MUST be replied to with a message rejected or a give handshake cookie - -command 'G' - give flow id - - -<6 magic bytes "netid!"> -<16 bytes new flow id> -<32 bytes public identiy key of sender, A_k> -<0-N bytes ignored but included in signature> - - -after recieving a give flow id message a session negotiation can happen with that flow id. - -command 'R' - flow rejected - -reject new flow - - -<14 ascii bytes reason for rejection null padded> -<8 bytes timestamp> -<32 bytes public identity key of sender, A_k> -<0-N bytes ignored but included in signature> - - -command 'E' - establish wire session - -establish an encrypted session using a flow id - - -<2 bytes 0x0a 0x0d> -<4 bytes flags, F> -<16 bytes flow id, B> -<32 bytes ephemeral public encryption key, E> -<8 bytes packet counter starting at 0> - - - - -F is currently set to all zeros - -every time we try establishing a wire session we increment the counter -by 1 for the next message we send. - -when we get an establish wire session message -we reply with an establish wire session message with counter being counter + 1 - -if A is provided that is interpreted as being generated via: - -h0 = HS('') -h1 = EDDH(us, them) -A = HS(B + h0 + h1) - -each side establishes their own rx key using this message. -when each side has both established thier rx key data can be transmitted. - -command 'D' - encrypted data transmission - -transmit encrypted data on a wire session - - -<16 bytes flow-id, F> -<24 bytes nonce, N> - - - - -B is the flow id from the recipiant (from outer header) -N is a random nounce -X is encrypted data -Z is keyed hash of entire message - -Z is generated via: - -msg.Z = MDS(outer-header + F + N + X, tx_K) - -data tranmission: - -inner message format of X (after decryption): - -inner header: - -<1 byte protocol version> -<1 byte command> - - -command: 'k' (keep alive) - -tell other side to acknoledge they are alive - - -<2 bytes resevered, set to 0> -<2 bytes attempt counter, set to 0 and incremented every retransmit, reset when we get a keepalive ack> -<2 bytes milliseconds ping timeout> -<8 bytes current session TX limit in bytes per second> -<8 bytes current session RX use in bytes per second> -<8 bytes milliseconds since epoch our current time> - - -command: 'l' (keep alive ack) - -acknolege keep alive message - - -<6 bytes reserved, set to 0> -<8 bytes current session RX limit in bytes per second> -<8 bytes current session TX use in bytes per second> -<8 bytes milliseconds since epoch our current time> - - - -command: 'n' (advertise neighboors) - -tell peer about neighboors, only sent by non service nodes to other non service -nodes. - - - -<0 or more intermediate routes> - - -route: - -<1 byte route version (currently 0)> -<1 byte flags, lsb set indicates src is a service node> -<2 bytes latency in ms> -<2 bytes backpressure> -<2 bytes number of connected peers> -<8 bytes publish timestamp ms since epoch> -<32 bytes pubkey neighboor> -<32 bytes pubkey src> -<64 bytes signature of entire route signed by src> - -command: 'c' (congestion) - -tell other side to slow down - - -<2 bytes reduce TX rate by this many 1024 bytes per second> -<4 bytes milliseconds slowdown lifetime> - - -command: 'd' (anti-congestion) - -tell other side to speed up - - -<2 bytes increase TX rate by this many 1024 bytes per second> -<4 bytes milliseconds speedup lifetime> - - - -command: 's' (start transmission) - -initate the transmission of a message to the remote peer - - -<1 byte flags F> -<1 byte reserved R set to zero> -<2 bytes total size of full message> -<4 bytes sequence number S> -<32 bytes blake2 hash of full message> - - -if F lsb is set then there is no further fragments - -command: 't' (continued transmission) - -continue transmission of a bigger message - - -<1 byte flags F> -<1 bytes reserved R set to zero> -<2 bytes 16 byte block offset in message> -<4 bytes sequence number S> - - - -command: 'q' (acknoledge transmission) - -acknoledges a transmitted message - - - -command: 'r' (rotate keys) - -inform remote that their RX key should be rotated - -given alice(A) sends this message to bob(B) the new keys are computed as such: - -n_K = TKE(K, B_e, K_seed, N) - -A.tx_K = n_K -B.rx_K = n_K - - -<2 bytes milliseconds lifetime of old keys, retain them for this long and then discard> -<4 bytes reserved, set to 0> -<32 bytes key exchange nounce, N> -<32 bytes next public encryption key, K> - - -command: 'u' (upgrade) - -request protocol upgrade - - -<1 byte protocol min version to upgrade to> -<1 byte protocol max version to upgrade to> - - -command: 'v' (version upgrade) - -sent in response to upgrade message - - -<1 byte protocol version selected> -<1 byte protocol version highest we support> - diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 2ddd0e96f..0f935161b 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -22,6 +22,7 @@ if(SUBMODULE_CHECK) check_submodule(pybind11) check_submodule(sqlite_orm) check_submodule(oxen-mq) + check_submodule(oxen-encoding) check_submodule(uvw) check_submodule(cpr) check_submodule(ngtcp2) @@ -56,26 +57,27 @@ add_ngtcp2_lib() # cpr configuration. Ideally we'd just do this via add_subdirectory, but cpr's cmake requires # 3.15+, and we target lower than that (and this is fairly simple to build). +if(WITH_BOOTSTRAP) + if(NOT BUILD_STATIC_DEPS) + find_package(CURL REQUIRED COMPONENTS HTTP HTTPS SSL) -if(NOT BUILD_STATIC_DEPS) - find_package(CURL REQUIRED COMPONENTS HTTP HTTPS SSL) - - # CURL::libcurl wasn't added to FindCURL until cmake 3.12, so add it if necessary - if (CMAKE_VERSION VERSION_LESS 3.12 AND NOT TARGET CURL::libcurl) - add_library(libcurl UNKNOWN IMPORTED GLOBAL) - set_target_properties(libcurl PROPERTIES - IMPORTED_LOCATION ${CURL_LIBRARIES} - INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}") - add_library(CURL_libcurl INTERFACE) - target_link_libraries(CURL_libcurl INTERFACE libcurl) - add_library(CURL::libcurl ALIAS CURL_libcurl) + # CURL::libcurl wasn't added to FindCURL until cmake 3.12, so add it if necessary + if (CMAKE_VERSION VERSION_LESS 3.12 AND NOT TARGET CURL::libcurl) + add_library(libcurl UNKNOWN IMPORTED GLOBAL) + set_target_properties(libcurl PROPERTIES + IMPORTED_LOCATION ${CURL_LIBRARIES} + INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}") + add_library(CURL_libcurl INTERFACE) + target_link_libraries(CURL_libcurl INTERFACE libcurl) + add_library(CURL::libcurl ALIAS CURL_libcurl) + endif() endif() -endif() -file(GLOB cpr_sources ${conf_depends} cpr/cpr/*.cpp) + file(GLOB cpr_sources ${conf_depends} cpr/cpr/*.cpp) -add_library(cpr STATIC EXCLUDE_FROM_ALL ${cpr_sources}) -target_link_libraries(cpr PUBLIC CURL::libcurl) -target_include_directories(cpr PUBLIC cpr/include) -target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL) -add_library(cpr::cpr ALIAS cpr) + add_library(cpr STATIC EXCLUDE_FROM_ALL ${cpr_sources}) + target_link_libraries(cpr PUBLIC CURL::libcurl) + target_include_directories(cpr PUBLIC cpr/include) + target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL) + add_library(cpr::cpr ALIAS cpr) +endif() diff --git a/external/clang-format-hooks b/external/clang-format-hooks deleted file mode 160000 index ac35e705e..000000000 --- a/external/clang-format-hooks +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ac35e705ebf20187b7ff92c29d1411f6a4c8d522 diff --git a/external/ngtcp2 b/external/ngtcp2 index 15ba6021c..026b8434e 160000 --- a/external/ngtcp2 +++ b/external/ngtcp2 @@ -1 +1 @@ -Subproject commit 15ba6021ca352e2e60f9b43f4b96d2e97a42f60b +Subproject commit 026b8434ebcbeec48939d1c7671a0a4d5c75202b diff --git a/external/oxen-encoding b/external/oxen-encoding new file mode 160000 index 000000000..79193e58f --- /dev/null +++ b/external/oxen-encoding @@ -0,0 +1 @@ +Subproject commit 79193e58fb26624d40cd2e95156f78160f2b9b3e diff --git a/external/oxen-mq b/external/oxen-mq index 51754037e..eadb37c76 160000 --- a/external/oxen-mq +++ b/external/oxen-mq @@ -1 +1 @@ -Subproject commit 51754037ea19204610751c2ea8ae72b7ed6c1818 +Subproject commit eadb37c7654150bef18497773718f15ef843734a diff --git a/include/lokinet/lokinet_misc.h b/include/lokinet/lokinet_misc.h index dea41f9ba..b09b20e2c 100644 --- a/include/lokinet/lokinet_misc.h +++ b/include/lokinet/lokinet_misc.h @@ -21,6 +21,17 @@ extern "C" int EXPORT lokinet_log_level(const char*); + typedef void (*lokinet_logger_func)(const char*, void*); + + /// set a custom logger function + void EXPORT + lokinet_set_logger(lokinet_logger_func func, void* user); + + /// @brief take in hex and turn it into base32z + /// @return value must be free()'d later + char* EXPORT + lokinet_hex_to_base32z(const char* hex); + #ifdef __cplusplus } #endif diff --git a/include/lokinet/lokinet_stream.h b/include/lokinet/lokinet_stream.h index d5d97754b..385a67fc8 100644 --- a/include/lokinet/lokinet_stream.h +++ b/include/lokinet/lokinet_stream.h @@ -8,7 +8,6 @@ extern "C" #endif /// the result of a lokinet stream mapping attempt -#pragma pack(1) struct lokinet_stream_result { /// set to zero on success otherwise the error that happened @@ -23,7 +22,6 @@ extern "C" /// the id of the stream we created int stream_id; }; -#pragma pack() /// connect out to a remote endpoint /// remoteAddr is in the form of "name:port" @@ -39,7 +37,7 @@ extern "C" /// return 0 to accept /// return -1 to explicitly reject /// return -2 to silently drop - typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void*); + typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void* userdata); /// set stream accepter filter /// passes user parameter into stream filter as void * @@ -53,6 +51,9 @@ extern "C" int EXPORT lokinet_inbound_stream(uint16_t port, struct lokinet_context* context); + void EXPORT + lokinet_close_stream(int stream_id, struct lokinet_context* context); + #ifdef __cplusplus } #endif diff --git a/include/lokinet/lokinet_udp.h b/include/lokinet/lokinet_udp.h index 27b00c3e7..9e520dcd3 100644 --- a/include/lokinet/lokinet_udp.h +++ b/include/lokinet/lokinet_udp.h @@ -2,54 +2,22 @@ #include "lokinet_context.h" -#ifdef _WIN32 -extern "C" -{ - struct iovec - { - void* iov_base; - size_t iov_len; - }; -} -#else -#include -#endif - #ifdef __cplusplus extern "C" { #endif /// information about a udp flow - struct lokinet_udp_flow + struct lokinet_udp_flowinfo { - /// the socket id for this flow used for i/o purposes and closing this socket - int socket_id; /// remote endpoint's .loki or .snode address - char remote_addr[256]; - /// local endpoint's ip address - char local_addr[64]; + char remote_host[256]; /// remote endpont's port - int remote_port; - /// local endpoint's port - int local_port; + uint16_t remote_port; + /// the socket id for this flow used for i/o purposes and closing this socket + int socket_id; }; - /// establish an outbound udp flow - /// remoteHost is the remote .loki or .snode address conneting to - /// remotePort is either a string integer or an srv record name to lookup, e.g. thingservice in - /// which we do a srv lookup for _udp.thingservice.remotehost.tld and use the "best" port provided - /// localAddr is the local ip:port to bind our socket to, if localAddr is NULL then - /// lokinet_udp_sendmmsg MUST be used to send packets return 0 on success return nonzero on fail, - /// containing an errno value - int EXPORT - lokinet_udp_establish( - char* remoteHost, - char* remotePort, - char* localAddr, - struct lokinet_udp_flow* flow, - struct lokinet_context* ctx); - /// a result from a lokinet_udp_bind call struct lokinet_udp_bind_result { @@ -57,48 +25,97 @@ extern "C" int socket_id; }; + /// flow acceptor hook, return 0 success, return nonzero with errno on failure + typedef int (*lokinet_udp_flow_filter)( + void* userdata, + const struct lokinet_udp_flowinfo* remote_address, + void** flow_userdata, + int* timeout_seconds); + + /// callback to make a new outbound flow + typedef void(lokinet_udp_create_flow_func)( + void* userdata, void** flow_userdata, int* timeout_seconds); + + /// hook function for handling packets + typedef void (*lokinet_udp_flow_recv_func)( + const struct lokinet_udp_flowinfo* remote_address, + const char* pkt_data, + size_t pkt_length, + void* flow_userdata); + + /// hook function for flow timeout + typedef void (*lokinet_udp_flow_timeout_func)( + const struct lokinet_udp_flowinfo* remote_address, void* flow_userdata); + /// inbound listen udp socket /// expose udp port exposePort to the void - /// if srv is not NULL add an srv record for this port, the format being "thingservice" in which - /// will add a srv record "_udp.thingservice.ouraddress.tld" that advertises this port provide - /// localAddr to forward inbound udp packets to "ip:port" if localAddr is NULL then the resulting - /// socket MUST be drained by lokinet_udp_recvmmsg + //// + /// @param filter MUST be non null, pointing to a flow filter for accepting new udp flows, called + /// with user data /// - /// returns 0 on success - /// returns nonzero on error in which it is an errno value + /// @param recv MUST be non null, pointing to a packet handler function for each flow, called + /// with per flow user data provided by filter function if accepted + /// + /// @param timeout MUST be non null, + /// pointing to a cleanup function to clean up a stale flow, staleness determined by the value + /// given by the filter function returns 0 on success + /// + /// @returns nonzero on error in which it is an errno value int EXPORT lokinet_udp_bind( - int exposedPort, - char* srv, - char* localAddr, - struct lokinet_udp_listen_result* result, + uint16_t exposedPort, + lokinet_udp_flow_filter filter, + lokinet_udp_flow_recv_func recv, + lokinet_udp_flow_timeout_func timeout, + void* user, + struct lokinet_udp_bind_result* result, struct lokinet_context* ctx); - /// poll many udp sockets for activity - /// returns 0 on sucess + /// @brief establish a udp flow to remote endpoint + /// + /// @param create_flow the callback to create the new flow if we establish one + /// + /// @param user passed to new_flow as user data + /// + /// @param remote the remote address to establish to /// - /// returns non zero errno on error + /// @param ctx the lokinet context to use + /// + /// @return 0 on success, non zero errno on fail int EXPORT - lokinet_udp_poll( - const int* socket_ids, - size_t numsockets, - const struct timespec* timeout, + lokinet_udp_establish( + lokinet_udp_create_flow_func create_flow, + void* user, + const struct lokinet_udp_flowinfo* remote, struct lokinet_context* ctx); - struct lokinet_udp_pkt - { - char remote_addr[256]; - int remote_port; - struct iovec pkt; - }; + /// @brief send on an established flow to remote endpoint + /// blocks until we have sent the packet + /// + /// @param flowinfo remote flow to use for sending + /// + /// @param ptr pointer to data to send + /// + /// @param len the length of the data + /// + /// @param ctx the lokinet context to use + /// + /// @returns 0 on success and non zero errno on fail + int EXPORT + lokinet_udp_flow_send( + const struct lokinet_udp_flowinfo* remote, + const void* ptr, + size_t len, + struct lokinet_context* ctx); - /// analog to recvmmsg - ssize_t EXPORT - lokinet_udp_recvmmsg( - int socket_id, - struct lokinet_udp_pkt* events, - size_t max_events, - struct lokient_context* ctx); + /// @brief close a bound udp socket + /// closes all flows immediately + /// + /// @param socket_id the bound udp socket's id + /// + /// @param ctx lokinet context + void EXPORT + lokinet_udp_close(int socket_id, struct lokinet_context* ctx); #ifdef __cplusplus } diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 05a60bbc3..40786322b 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -34,7 +34,7 @@ target_link_libraries(lokinet-util PUBLIC nlohmann_json::nlohmann_json filesystem date::date - oxenmq::oxenmq + oxenc::oxenc ) if(ANDROID) @@ -54,11 +54,12 @@ add_library(lokinet-platform net/net_int.cpp net/sock_addr.cpp vpn/packet_router.cpp + vpn/egres_packet_router.cpp vpn/platform.cpp ) target_link_libraries(lokinet-platform PUBLIC lokinet-cryptography lokinet-util Threads::Threads base_libs uvw) - +target_link_libraries(lokinet-platform PRIVATE oxenmq::oxenmq) if (ANDROID) target_sources(lokinet-platform PRIVATE android/ifaddrs.c) @@ -241,8 +242,16 @@ if(WITH_HIVE) ) endif() -target_link_libraries(liblokinet PUBLIC cxxopts lokinet-platform lokinet-util lokinet-cryptography sqlite_orm ngtcp2) +target_link_libraries(liblokinet PUBLIC cxxopts oxenc::oxenc lokinet-platform lokinet-util lokinet-cryptography sqlite_orm ngtcp2_static oxenmq::oxenmq) target_link_libraries(liblokinet PRIVATE libunbound) +pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET) +if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING) + add_definitions(-DHAVE_CRYPT) + add_library(libcrypt INTERFACE) + target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT) + target_link_libraries(liblokinet PRIVATE libcrypt) + message(STATUS "using libcrypt ${CRYPT_VERSION}") +endif() if(BUILD_LIBLOKINET) diff --git a/llarp/apple/vpn_interface.cpp b/llarp/apple/vpn_interface.cpp index 079e24aa9..c54cef00a 100644 --- a/llarp/apple/vpn_interface.cpp +++ b/llarp/apple/vpn_interface.cpp @@ -1,12 +1,18 @@ #include "vpn_interface.hpp" #include "context.hpp" +#include namespace llarp::apple { VPNInterface::VPNInterface( - Context& ctx, packet_write_callback packet_writer, on_readable_callback on_readable) - : m_PacketWriter{std::move(packet_writer)}, m_OnReadable{std::move(on_readable)} + Context& ctx, + packet_write_callback packet_writer, + on_readable_callback on_readable, + AbstractRouter* router) + : m_PacketWriter{std::move(packet_writer)} + , m_OnReadable{std::move(on_readable)} + , _router{router} { ctx.loop->call_soon([this] { m_OnReadable(*this); }); } @@ -21,6 +27,12 @@ namespace llarp::apple return true; } + void + VPNInterface::MaybeWakeUpperLayers() const + { + _router->TriggerPump(); + } + int VPNInterface::PollFD() const { diff --git a/llarp/apple/vpn_interface.hpp b/llarp/apple/vpn_interface.hpp index c1dff8dbf..762227f91 100644 --- a/llarp/apple/vpn_interface.hpp +++ b/llarp/apple/vpn_interface.hpp @@ -17,7 +17,10 @@ namespace llarp::apple using on_readable_callback = std::function; explicit VPNInterface( - Context& ctx, packet_write_callback packet_writer, on_readable_callback on_readable); + Context& ctx, + packet_write_callback packet_writer, + on_readable_callback on_readable, + AbstractRouter* router); // Method to call when a packet has arrived to deliver the packet to lokinet bool @@ -35,6 +38,9 @@ namespace llarp::apple bool WritePacket(net::IPPacket pkt) override; + void + MaybeWakeUpperLayers() const override; + private: // Function for us to call when we have a packet to emit. Should return true if the packet was // handed off to the OS successfully. @@ -46,6 +52,8 @@ namespace llarp::apple static inline constexpr auto PacketQueueSize = 1024; thread::Queue m_ReadQueue{PacketQueueSize}; + + AbstractRouter* const _router; }; } // namespace llarp::apple diff --git a/llarp/apple/vpn_platform.cpp b/llarp/apple/vpn_platform.cpp index b11c0b05b..1d1eafb8e 100644 --- a/llarp/apple/vpn_platform.cpp +++ b/llarp/apple/vpn_platform.cpp @@ -15,8 +15,9 @@ namespace llarp::apple , m_OnReadable{std::move(on_readable)} {} - std::shared_ptr VPNPlatform::ObtainInterface(vpn::InterfaceInfo) + std::shared_ptr + VPNPlatform::ObtainInterface(vpn::InterfaceInfo, AbstractRouter* router) { - return std::make_shared(m_Context, m_PacketWriter, m_OnReadable); + return std::make_shared(m_Context, m_PacketWriter, m_OnReadable, router); } } // namespace llarp::apple diff --git a/llarp/apple/vpn_platform.hpp b/llarp/apple/vpn_platform.hpp index 04ce75646..0cf7f469b 100644 --- a/llarp/apple/vpn_platform.hpp +++ b/llarp/apple/vpn_platform.hpp @@ -16,7 +16,8 @@ namespace llarp::apple llarp_route_callbacks route_callbacks, void* callback_context); - std::shared_ptr ObtainInterface(vpn::InterfaceInfo) override; + std::shared_ptr + ObtainInterface(vpn::InterfaceInfo, AbstractRouter*) override; vpn::IRouteManager& RouteManager() override diff --git a/llarp/bootstrap.cpp b/llarp/bootstrap.cpp index f21f3a025..32f273de4 100644 --- a/llarp/bootstrap.cpp +++ b/llarp/bootstrap.cpp @@ -1,5 +1,7 @@ #include "bootstrap.hpp" #include "util/bencode.hpp" +#include "util/logging/logger.hpp" +#include "util/logging/buffer.hpp" namespace llarp { @@ -16,9 +18,12 @@ namespace llarp [&](llarp_buffer_t* b, bool more) -> bool { if (more) { - RouterContact rc; + RouterContact rc{}; if (not rc.BDecode(b)) + { + LogError("invalid rc in bootstrap list: ", llarp::buffer_printer{*b}); return false; + } emplace(std::move(rc)); } return true; diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 3a2719343..6bff96112 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -126,32 +126,23 @@ namespace llarp "provided the public-port option must also be specified.", }, [this](std::string arg) { - if (not arg.empty()) - { - llarp::LogInfo("public ip ", arg, " size ", arg.size()); + if (arg.empty()) + return; + nuint32_t addr{}; + if (not addr.FromString(arg)) + throw std::invalid_argument{stringify(arg, " is not a valid IPv4 address")}; - if (arg.size() > 15) - throw std::invalid_argument(stringify("Not a valid IPv4 addr: ", arg)); + if (IsIPv4Bogon(addr)) + throw std::invalid_argument{ + stringify(addr, " looks like it is not a publicly routable ip address")}; - m_publicAddress.setAddress(arg); - } + m_PublicIP = addr; }); - conf.defineOption("router", "public-address", Hidden, [this](std::string arg) { - if (not arg.empty()) - { - llarp::LogWarn( - "*** WARNING: The config option [router]:public-address=", - arg, - " is deprecated, use public-ip=", - arg, - " instead to avoid this warning and avoid future configuration problems."); - - if (arg.size() > 15) - throw std::invalid_argument(stringify("Not a valid IPv4 addr: ", arg)); - - m_publicAddress.setAddress(arg); - } + conf.defineOption("router", "public-address", Hidden, [](std::string) { + throw std::invalid_argument{ + "[router]:public-address option no longer supported, use [router]:public-ip and " + "[router]:public-port instead"}; }); conf.defineOption( @@ -166,8 +157,7 @@ namespace llarp [this](int arg) { if (arg <= 0 || arg > std::numeric_limits::max()) throw std::invalid_argument("public-port must be >= 0 and <= 65536"); - - m_publicAddress.setPort(arg); + m_PublicPort = ToNet(huint16_t{static_cast(arg)}); }); conf.defineOption( @@ -317,7 +307,7 @@ namespace llarp ClientOnly, Comment{ "Set the endpoint authentication mechanism.", - "none/whitelist/lmq", + "none/whitelist/lmq/file", }, [this](std::string arg) { if (arg.empty()) @@ -366,6 +356,42 @@ namespace llarp m_AuthWhitelist.emplace(std::move(addr)); }); + conf.defineOption( + "network", + "auth-file", + ClientOnly, + MultiValue, + Comment{ + "Read auth tokens from file to accept endpoint auth", + "Can be provided multiple times", + }, + [this](fs::path arg) { + if (not fs::exists(arg)) + throw std::invalid_argument{ + stringify("cannot load auth file ", arg, " as it does not seem to exist")}; + m_AuthFiles.emplace(std::move(arg)); + }); + conf.defineOption( + "network", + "auth-file-type", + ClientOnly, + Comment{ + "How to interpret the contents of an auth file.", + "Possible values: hashes, plaintext", + }, + [this](std::string arg) { m_AuthFileType = service::ParseAuthFileType(std::move(arg)); }); + + conf.defineOption( + "network", + "auth-static", + ClientOnly, + MultiValue, + Comment{ + "Manually add a static auth code to accept for endpoint auth", + "Can be provided multiple times", + }, + [this](std::string arg) { m_AuthStaticTokens.emplace(std::move(arg)); }); + conf.defineOption( "network", "reachable", @@ -454,6 +480,7 @@ namespace llarp "network", "exit-node", ClientOnly, + MultiValue, Comment{ "Specify a `.loki` address and an optional ip range to use as an exit broker.", "Example:", @@ -496,12 +523,13 @@ namespace llarp "network", "exit-auth", ClientOnly, + MultiValue, Comment{ "Specify an optional authentication code required to use a non-public exit node.", "For example:", " exit-auth=myfavouriteexit.loki:abc", "uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.", - "Can be specified multiple time to store codes for different exit nodes.", + "Can be specified multiple times to store codes for different exit nodes.", }, [this](std::string arg) { if (arg.empty()) @@ -531,6 +559,30 @@ namespace llarp m_ExitAuths.emplace(exit, auth); }); + conf.defineOption( + "network", + "auto-routing", + ClientOnly, + Default{true}, + Comment{ + "Enable / disable automatic route configuration.", + "When this is enabled and an exit is used Lokinet will automatically configure " + "operating system routes to route traffic through the exit node.", + "This is enabled by default, but can be disabled to perform advanced exit routing " + "configuration manually."}, + AssignmentAcceptor(m_EnableRoutePoker)); + + conf.defineOption( + "network", + "blackhole-routes", + ClientOnly, + Default{true}, + Comment{ + "Enable / disable route configuration blackholes.", + "When enabled lokinet will drop ip4 and ip6 not included in exit config.", + "Enabled by default."}, + AssignmentAcceptor(m_BlackholeRoutes)); + conf.defineOption( "network", "ifname", @@ -714,7 +766,7 @@ namespace llarp #endif // Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it - constexpr Default DefaultUpstreamDNS{"1.1.1.1"}; + constexpr Default DefaultUpstreamDNS{"9.9.9.10"}; m_upstreamDNS.emplace_back(DefaultUpstreamDNS.val); if (!m_upstreamDNS.back().getPort()) m_upstreamDNS.back().setPort(53); @@ -798,12 +850,12 @@ namespace llarp const IpAddress addr{value}; if (not addr.hasPort()) throw std::invalid_argument("no port provided in link address"); - info.interface = addr.toHost(); + info.m_interface = addr.toHost(); info.port = *addr.getPort(); } else { - info.interface = std::string{name}; + info.m_interface = std::string{name}; std::vector splits = split(value, ","); for (std::string_view str : splits) diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 03f7a000c..975a8ac99 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -53,7 +54,8 @@ namespace llarp bool m_blockBogons = false; - IpAddress m_publicAddress; + std::optional m_PublicIP; + nuint16_t m_PublicPort; int m_workerThreads = -1; int m_numNetThreads = -1; @@ -114,9 +116,12 @@ namespace llarp std::unordered_map m_mapAddrs; service::AuthType m_AuthType = service::AuthType::eAuthTypeNone; + service::AuthFileType m_AuthFileType = service::AuthFileType::eAuthFileHashes; std::optional m_AuthUrl; std::optional m_AuthMethod; std::unordered_set m_AuthWhitelist; + std::unordered_set m_AuthStaticTokens; + std::set m_AuthFiles; std::vector m_SRVRecords; @@ -129,6 +134,9 @@ namespace llarp std::optional m_AddrMapPersistFile; + bool m_EnableRoutePoker; + bool m_BlackholeRoutes; + void defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); }; @@ -147,7 +155,7 @@ namespace llarp { struct LinkInfo { - std::string interface; + std::string m_interface; int addressFamily = -1; uint16_t port = -1; }; @@ -193,7 +201,7 @@ namespace llarp struct BootstrapConfig { std::vector files; - std::set routers; + BootstrapList routers; bool seednode; void defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); diff --git a/llarp/constants/path.hpp b/llarp/constants/path.hpp index 5b8f15cfa..849fabf59 100644 --- a/llarp/constants/path.hpp +++ b/llarp/constants/path.hpp @@ -24,6 +24,9 @@ namespace llarp constexpr auto intro_spread_slices = 4; /// spacing frequency at which we try to build paths for introductions constexpr std::chrono::milliseconds intro_path_spread = default_lifetime / intro_spread_slices; + /// how long away from expiration in millseconds do we consider an intro to become stale + constexpr std::chrono::milliseconds intro_stale_threshold = + default_lifetime - intro_path_spread; /// Minimum paths to keep around for intros; mainly used at startup (the /// spread, above, should be able to maintain more than this number of paths /// normally once things are going). diff --git a/llarp/constants/platform.hpp b/llarp/constants/platform.hpp new file mode 100644 index 000000000..eaaf14084 --- /dev/null +++ b/llarp/constants/platform.hpp @@ -0,0 +1,65 @@ +#pragma once + +/// namespace for platform feature detection constexprs +namespace llarp::platform +{ + /// are we linux ? + inline constexpr bool is_linux = +#ifdef __linux__ + true +#else + false +#endif + ; + + /// are we freebsd ? + inline constexpr bool is_freebsd = +#ifdef __FreeBSD__ + true +#else + false +#endif + ; + + /// are we windows ? + inline constexpr bool is_windows = +#ifdef _WIN32 + true +#else + false +#endif + ; + + /// are we an apple platform ? + inline constexpr bool is_apple = +#ifdef __apple__ + true +#else + false +#endif + ; + + /// are we an android platform ? + inline constexpr bool is_android = +#ifdef ANDROID + true +#else + false +#endif + ; + + /// are we an iphone ? + inline constexpr bool is_iphone = +#ifdef IOS + true +#else + false +#endif + ; + + /// are we a mobile phone ? + inline constexpr bool is_mobile = is_android or is_iphone; + + /// does this platform support native ipv6 ? + inline constexpr bool supports_ipv6 = not is_windows; +} // namespace llarp::platform diff --git a/llarp/constants/proto.hpp b/llarp/constants/proto.hpp index fe2bb03ec..0ed9b53f7 100644 --- a/llarp/constants/proto.hpp +++ b/llarp/constants/proto.hpp @@ -1,13 +1,9 @@ #pragma once -#ifndef LLARP_PROTO_VERSION -#define LLARP_PROTO_VERSION (0) -#endif +namespace llarp::constants +{ + /// current network wide protocol version + // TODO: enum class + constexpr auto proto_version = 0; -#ifndef LLARP_ETH_PROTO -#define LLARP_ETH_PROTO (0xD1CE) -#endif - -#ifndef LLARP_KEYFILE_VERSION -#define LLARP_KEYFILE_VERSION (1) -#endif +} // namespace llarp::constants diff --git a/llarp/constants/time.hpp b/llarp/constants/time.hpp new file mode 100644 index 000000000..69866e2c8 --- /dev/null +++ b/llarp/constants/time.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace llarp +{ + using namespace std::literals; + /// how big of a time skip before we reset network state + constexpr auto TimeskipDetectedDuration = 1min; +} // namespace llarp diff --git a/llarp/constants/version.cpp.in b/llarp/constants/version.cpp.in index 67c3cfa9c..57156338d 100644 --- a/llarp/constants/version.cpp.in +++ b/llarp/constants/version.cpp.in @@ -1,27 +1,16 @@ #include -#include #include -// clang-format off -#define LLARP_STRINGIFY2(val) #val -#define LLARP_STRINGIFY(val) LLARP_STRINGIFY2(val) - -#define LLARP_VERSION_STR \ - LLARP_STRINGIFY(LLARP_VERSION_MAJOR) \ - "." LLARP_STRINGIFY(LLARP_VERSION_MINOR) "." LLARP_STRINGIFY( \ - LLARP_VERSION_PATCH) -#define LLARP_VERSION_FULL LLARP_VERSION_STR "-@VERSIONTAG@" - namespace llarp { // clang-format off - const std::array VERSION{{LLARP_VERSION_MAJOR, LLARP_VERSION_MINOR, LLARP_VERSION_PATCH}}; - const std::array ROUTER_VERSION{{LLARP_PROTO_VERSION, LLARP_VERSION_MAJOR, LLARP_VERSION_MINOR, LLARP_VERSION_PATCH}}; - const char* const VERSION_STR = LLARP_VERSION_STR; + const std::array VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; + const std::array ROUTER_VERSION{{llarp::constants::proto_version, @lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}}; + const char* const VERSION_STR = "@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@"; const char* const VERSION_TAG = "@VERSIONTAG@"; - const char* const VERSION_FULL = LLARP_NAME "-" LLARP_VERSION_STR "-@VERSIONTAG@"; + const char* const VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@VERSIONTAG@"; - const char* const RELEASE_MOTTO = LLARP_RELEASE_MOTTO; - const char* const DEFAULT_NETID = LLARP_DEFAULT_NETID; + const char* const RELEASE_MOTTO = "@RELEASE_MOTTO@"; + const char* const DEFAULT_NETID = "lokinet"; // clang-format on } // namespace llarp diff --git a/llarp/constants/version.h b/llarp/constants/version.h deleted file mode 100644 index 30893701e..000000000 --- a/llarp/constants/version.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// Don't include this file directly but rather go through version.hpp instead. -// This is only here so version.cpp.in and the weird archaic windows build -// recipies can use the version. - -#define LLARP_NAME "lokinet" - -#define LLARP_DEFAULT_NETID "lokinet" - -#ifndef LLARP_RELEASE_MOTTO -#define LLARP_RELEASE_MOTTO "(dev build)" -#endif - -#if defined(_WIN32) && defined(RC_INVOKED) -#define LLARP_VERSION LLARP_VERSION_MAJOR, LLARP_VERSION_MINOR, LLARP_VERSION_PATCH, 0 - -#define MAKE_TRIPLET(X, Y, Z) TRIPLET_CAT(X, ., Y, ., Z) -#define TRIPLET_CAT(X, D1, Y, D2, Z) X##D1##Y##D2##Z - -#define LLARP_VERSION_TRIPLET \ - MAKE_TRIPLET(LLARP_VERSION_MAJOR, LLARP_VERSION_MINOR, LLARP_VERSION_PATCH) - -#endif diff --git a/llarp/context.cpp b/llarp/context.cpp index 59a54dc28..ecba8ee09 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -166,6 +166,14 @@ namespace llarp SigINT(); } #ifndef _WIN32 + if (sig == SIGUSR1) + { + if (router and not router->IsServiceNode()) + { + LogInfo("SIGUSR1: resetting network state"); + router->Thaw(); + } + } if (sig == SIGHUP) { Reload(); diff --git a/llarp/crypto/crypto.hpp b/llarp/crypto/crypto.hpp index 56cc6c60f..1af34e6b4 100644 --- a/llarp/crypto/crypto.hpp +++ b/llarp/crypto/crypto.hpp @@ -100,6 +100,10 @@ namespace llarp virtual bool check_identity_privkey(const SecretKey&) = 0; + + /// check if a password hash string matches the challenge + virtual bool + check_passwd_hash(std::string pwhash, std::string challenge) = 0; }; inline Crypto::~Crypto() = default; diff --git a/llarp/crypto/crypto_libsodium.cpp b/llarp/crypto/crypto_libsodium.cpp index 8a14f66af..a5671a151 100644 --- a/llarp/crypto/crypto_libsodium.cpp +++ b/llarp/crypto/crypto_libsodium.cpp @@ -8,11 +8,16 @@ #include #include #include +#include #include -#include #include #include #include +#ifdef HAVE_CRYPT +#include +#endif + +#include extern "C" { @@ -290,7 +295,7 @@ namespace llarp std::array buf; std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin()); std::copy(k.begin(), k.end(), buf.begin() + 160); - htole64buf(buf.data() + 160 + K::SIZE, i); + oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE); // n = H(b) // h = make_point(n) ShortHash n; @@ -463,6 +468,25 @@ namespace llarp auto d = keypair.data(); crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d); } + + bool + CryptoLibSodium::check_passwd_hash(std::string pwhash, std::string challenge) + { + (void)pwhash; + (void)challenge; + bool ret = false; +#ifdef HAVE_CRYPT + auto pos = pwhash.find_last_of('$'); + auto settings = pwhash.substr(0, pos); + crypt_data data{}; + if (char* ptr = crypt_r(challenge.c_str(), settings.c_str(), &data)) + { + ret = ptr == pwhash; + } + sodium_memzero(&data, sizeof(data)); +#endif + return ret; + } } // namespace sodium const byte_t* diff --git a/llarp/crypto/crypto_libsodium.hpp b/llarp/crypto/crypto_libsodium.hpp index f085aee1e..6248e9484 100644 --- a/llarp/crypto/crypto_libsodium.hpp +++ b/llarp/crypto/crypto_libsodium.hpp @@ -104,6 +104,9 @@ namespace llarp bool check_identity_privkey(const SecretKey&) override; + + bool + check_passwd_hash(std::string pwhash, std::string challenge) override; }; } // namespace sodium diff --git a/llarp/crypto/encrypted_frame.hpp b/llarp/crypto/encrypted_frame.hpp index 7731d74e5..3bdd58b68 100644 --- a/llarp/crypto/encrypted_frame.hpp +++ b/llarp/crypto/encrypted_frame.hpp @@ -45,7 +45,6 @@ namespace llarp EncryptInPlace(const SecretKey& seckey, const PubKey& other); }; - /// TODO: can only handle 1 frame at a time template struct AsyncFrameDecrypter { diff --git a/llarp/crypto/types.cpp b/llarp/crypto/types.cpp index 3db017647..b2f190b83 100644 --- a/llarp/crypto/types.cpp +++ b/llarp/crypto/types.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include #include @@ -20,14 +20,14 @@ namespace llarp { if (str.size() != 2 * size()) return false; - oxenmq::from_hex(str.begin(), str.end(), begin()); + oxenc::from_hex(str.begin(), str.end(), begin()); return true; } std::string PubKey::ToString() const { - return oxenmq::to_hex(begin(), end()); + return oxenc::to_hex(begin(), end()); } bool diff --git a/llarp/dht/message.hpp b/llarp/dht/message.hpp index 338c98c33..13fdd9229 100644 --- a/llarp/dht/message.hpp +++ b/llarp/dht/message.hpp @@ -34,7 +34,7 @@ namespace llarp Key_t From; PathID_t pathID; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; }; IMessage::Ptr_t diff --git a/llarp/dht/messages/findintro.cpp b/llarp/dht/messages/findintro.cpp index 0e21523b7..a898b78c1 100644 --- a/llarp/dht/messages/findintro.cpp +++ b/llarp/dht/messages/findintro.cpp @@ -28,7 +28,7 @@ namespace llarp if (!BEncodeMaybeReadDictInt("T", txID, read, k, val)) return false; - if (!BEncodeMaybeVerifyVersion("V", version, LLARP_PROTO_VERSION, read, k, val)) + if (!BEncodeMaybeVerifyVersion("V", version, llarp::constants::proto_version, read, k, val)) return false; return read; @@ -66,7 +66,7 @@ namespace llarp if (!BEncodeWriteDictInt("T", txID, buf)) return false; // protocol version - if (!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf)) return false; return bencode_end(buf); diff --git a/llarp/dht/messages/findname.cpp b/llarp/dht/messages/findname.cpp index 929db0ed7..049cc0724 100644 --- a/llarp/dht/messages/findname.cpp +++ b/llarp/dht/messages/findname.cpp @@ -1,5 +1,5 @@ #include "findname.hpp" -#include +#include #include #include "gotname.hpp" #include @@ -16,7 +16,7 @@ namespace llarp::dht bool FindNameMessage::BEncode(llarp_buffer_t* buf) const { - const auto data = oxenmq::bt_serialize(oxenmq::bt_dict{ + const auto data = oxenc::bt_serialize(oxenc::bt_dict{ {"A", "N"sv}, {"H", std::string_view{(char*)NameHash.data(), NameHash.size()}}, {"T", TxID}}); diff --git a/llarp/dht/messages/gotname.cpp b/llarp/dht/messages/gotname.cpp index 47efe5854..2f1720938 100644 --- a/llarp/dht/messages/gotname.cpp +++ b/llarp/dht/messages/gotname.cpp @@ -1,5 +1,5 @@ #include "gotname.hpp" -#include +#include #include #include #include @@ -19,8 +19,8 @@ namespace llarp::dht GotNameMessage::BEncode(llarp_buffer_t* buf) const { const std::string nonce((const char*)result.nonce.data(), result.nonce.size()); - const auto data = oxenmq::bt_serialize( - oxenmq::bt_dict{{"A", "M"sv}, {"D", result.ciphertext}, {"N", nonce}, {"T", TxID}}); + const auto data = oxenc::bt_serialize( + oxenc::bt_dict{{"A", "M"sv}, {"D", result.ciphertext}, {"N", nonce}, {"T", TxID}}); return buf->write(data.begin(), data.end()); } diff --git a/llarp/dht/messages/gotrouter.cpp b/llarp/dht/messages/gotrouter.cpp index 4ab90a7d4..9fcb56c31 100644 --- a/llarp/dht/messages/gotrouter.cpp +++ b/llarp/dht/messages/gotrouter.cpp @@ -73,7 +73,7 @@ namespace llarp return bencode_read_integer(val, &txid); } bool read = false; - if (!BEncodeMaybeVerifyVersion("V", version, LLARP_PROTO_VERSION, read, key, val)) + if (!BEncodeMaybeVerifyVersion("V", version, llarp::constants::proto_version, read, key, val)) return false; return read; diff --git a/llarp/dht/messages/gotrouter.hpp b/llarp/dht/messages/gotrouter.hpp index 259911313..55a74f98e 100644 --- a/llarp/dht/messages/gotrouter.hpp +++ b/llarp/dht/messages/gotrouter.hpp @@ -30,7 +30,7 @@ namespace llarp /// gossip message GotRouterMessage(const RouterContact rc) : IMessage({}), foundRCs({rc}), txid(0) { - version = LLARP_PROTO_VERSION; + version = llarp::constants::proto_version; } GotRouterMessage(const GotRouterMessage& other) diff --git a/llarp/dht/messages/pubintro.cpp b/llarp/dht/messages/pubintro.cpp index 07598f9cc..b36f9b342 100644 --- a/llarp/dht/messages/pubintro.cpp +++ b/llarp/dht/messages/pubintro.cpp @@ -195,7 +195,7 @@ namespace llarp return false; if (!BEncodeWriteDictInt("T", txID, buf)) return false; - if (!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf)) return false; return bencode_end(buf); } diff --git a/llarp/dns/message.cpp b/llarp/dns/message.cpp index 0d78d1fdb..65b30c78d 100644 --- a/llarp/dns/message.cpp +++ b/llarp/dns/message.cpp @@ -1,9 +1,9 @@ #include "message.hpp" +#include #include "dns.hpp" #include "srv_data.hpp" #include -#include #include #include #include @@ -197,7 +197,7 @@ namespace llarp const auto addr = net::TruncateV6(ip); rec.rr_type = qTypeA; rec.rData.resize(4); - htobe32buf(rec.rData.data(), addr.h); + oxenc::write_host_as_big(addr.h, rec.rData.data()); } answers.emplace_back(std::move(rec)); } diff --git a/llarp/dns/srv_data.cpp b/llarp/dns/srv_data.cpp index 883d9c68c..e46b90732 100644 --- a/llarp/dns/srv_data.cpp +++ b/llarp/dns/srv_data.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include "llarp/util/bencode.h" #include "llarp/util/types.hpp" @@ -106,7 +106,7 @@ namespace llarp::dns bool SRVData::BEncode(llarp_buffer_t* buf) const { - const std::string data = oxenmq::bt_serialize(toTuple()); + const std::string data = oxenc::bt_serialize(toTuple()); return buf->write(data.begin(), data.end()); } @@ -122,11 +122,11 @@ namespace llarp::dns try { SRVTuple tuple{}; - oxenmq::bt_deserialize(srvString, tuple); + oxenc::bt_deserialize(srvString, tuple); *this = fromTuple(std::move(tuple)); return IsValid(); } - catch (const oxenmq::bt_deserialize_invalid&) + catch (const oxenc::bt_deserialize_invalid&) { return false; }; diff --git a/llarp/dns/unbound_resolver.cpp b/llarp/dns/unbound_resolver.cpp index 50c56f5fc..82022cce0 100644 --- a/llarp/dns/unbound_resolver.cpp +++ b/llarp/dns/unbound_resolver.cpp @@ -106,6 +106,9 @@ namespace llarp::dns return false; } + // disable ip6 for upstream dns + ub_ctx_set_option(unboundContext, "prefer-ip6", "0"); + // enable async ub_ctx_async(unboundContext, 1); #ifdef _WIN32 runner = std::thread{[&]() { diff --git a/llarp/endpoint_base.hpp b/llarp/endpoint_base.hpp index ce13786ac..e924ab523 100644 --- a/llarp/endpoint_base.hpp +++ b/llarp/endpoint_base.hpp @@ -14,7 +14,7 @@ #include #include #include -#include "oxenmq/variant.h" +#include "oxenc/variant.h" namespace llarp { diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp index b491f42b0..c7356f432 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/ev_libuv.cpp @@ -241,8 +241,11 @@ namespace llarp::uv using event_t = uvw::PollEvent; auto handle = m_Impl->resource(netif->PollFD()); #else - using event_t = uvw::CheckEvent; - auto handle = m_Impl->resource(); + // we use a uv_prepare_t because it fires before blocking for new io events unconditionally + // we want to match what linux does, using a uv_check_t does not suffice as the order of + // operations is not what we need. + using event_t = uvw::PrepareEvent; + auto handle = m_Impl->resource(); #endif if (!handle) return false; @@ -254,6 +257,10 @@ namespace llarp::uv LogDebug("got packet ", pkt.sz); if (handler) handler(std::move(pkt)); + // on windows/apple, vpn packet io does not happen as an io action that wakes up the event + // loop thus, we must manually wake up the event loop when we get a packet on our interface. + // on linux this is a nop + netif->MaybeWakeUpperLayers(); } }); diff --git a/llarp/ev/vpn.hpp b/llarp/ev/vpn.hpp index c3346fcdd..64da9f50d 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/ev/vpn.hpp @@ -4,12 +4,13 @@ #include #include -#include +#include namespace llarp { struct Context; -} + struct AbstractRouter; +} // namespace llarp namespace llarp::vpn { @@ -59,6 +60,10 @@ namespace llarp::vpn /// returns false if we dropped it virtual bool WritePacket(net::IPPacket pkt) = 0; + + /// idempotently wake up the upper layers as needed (platform dependant) + virtual void + MaybeWakeUpperLayers() const {}; }; class IRouteManager @@ -112,7 +117,7 @@ namespace llarp::vpn /// get a new network interface fully configured given the interface info /// blocks until ready, throws on error virtual std::shared_ptr - ObtainInterface(InterfaceInfo info) = 0; + ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; /// get owned ip route manager for managing routing table virtual IRouteManager& diff --git a/llarp/exit/policy.hpp b/llarp/exit/policy.hpp index ed8e857fd..de41e1ebc 100644 --- a/llarp/exit/policy.hpp +++ b/llarp/exit/policy.hpp @@ -11,7 +11,7 @@ namespace llarp uint64_t proto = 0; uint64_t port = 0; uint64_t drop = 0; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; bool BDecode(llarp_buffer_t* buf) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 8c88c3753..73902ac04 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -23,8 +23,6 @@ namespace llarp , m_Name(std::move(name)) , m_LocalResolverAddr{"127.0.0.1:53"} , m_QUIC{std::make_shared(*this)} - , m_InetToNetwork(name + "_exit_rx", r->loop(), r->loop()) - { m_ShouldInitTun = true; m_QUIC = std::make_shared(*this); @@ -363,7 +361,11 @@ namespace llarp void ExitEndpoint::Flush() { - m_InetToNetwork.Process([&](Pkt_t& pkt) { + while (not m_InetToNetwork.empty()) + { + net::IPPacket pkt{m_InetToNetwork.top()}; + m_InetToNetwork.pop(); + PubKey pk; { auto itr = m_IPToKey.find(pkt.dstv6()); @@ -371,7 +373,7 @@ namespace llarp { // drop LogWarn(Name(), " dropping packet, has no session at ", pkt.dstv6()); - return; + continue; } pk = itr->second; } @@ -385,7 +387,7 @@ namespace llarp if (itr != m_SNodeSessions.end()) { itr->second->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::TrafficV4); - return; + continue; } } auto tryFlushingTraffic = [&](exit::Endpoint* const ep) -> bool { @@ -412,7 +414,8 @@ namespace llarp pk, " as we have no working endpoints"); } - }); + } + for (auto& [pubkey, endpoint] : m_ActiveExits) { if (!endpoint->Flush()) @@ -443,7 +446,7 @@ namespace llarp info.ifname = m_ifname; info.addrs.emplace(m_OurRange); - m_NetIf = GetRouter()->GetVPNPlatform()->ObtainInterface(std::move(info)); + m_NetIf = GetRouter()->GetVPNPlatform()->ObtainInterface(std::move(info), m_Router); if (not m_NetIf) { llarp::LogError("Could not create interface"); @@ -633,7 +636,7 @@ namespace llarp void ExitEndpoint::OnInetPacket(net::IPPacket pkt) { - m_InetToNetwork.Emplace(std::move(pkt)); + m_InetToNetwork.emplace(std::move(pkt)); } bool diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index d5053b8d2..5c2f3ec41 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -214,15 +214,8 @@ namespace llarp std::shared_ptr m_QUIC; - using Pkt_t = net::IPPacket; - using PacketQueue_t = util::CoDelQueue< - Pkt_t, - Pkt_t::GetTime, - Pkt_t::PutTime, - Pkt_t::CompareOrder, - Pkt_t::GetNow, - util::NullMutex, - util::NullLock>; + using PacketQueue_t = std:: + priority_queue, net::IPPacket::CompareOrder>; /// internet to llarp packet queue PacketQueue_t m_InetToNetwork; diff --git a/llarp/handlers/null.hpp b/llarp/handlers/null.hpp index e8f209dc6..e0464b041 100644 --- a/llarp/handlers/null.hpp +++ b/llarp/handlers/null.hpp @@ -5,87 +5,120 @@ #include #include #include +#include -namespace llarp +namespace llarp::handlers { - namespace handlers + struct NullEndpoint final : public llarp::service::Endpoint, + public std::enable_shared_from_this { - struct NullEndpoint final : public llarp::service::Endpoint, - public std::enable_shared_from_this + NullEndpoint(AbstractRouter* r, llarp::service::Context* parent) + : llarp::service::Endpoint{r, parent} + , m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) { + var::visit( + [&pkt](auto&& from) { + LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes"); + }, + from); + }}} { - NullEndpoint(AbstractRouter* r, llarp::service::Context* parent) - : llarp::service::Endpoint(r, parent) + r->loop()->add_ticker([this] { Pump(Now()); }); + } + + virtual bool + HandleInboundPacket( + const service::ConvoTag tag, + const llarp_buffer_t& buf, + service::ProtocolType t, + uint64_t) override + { + LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag); + if (t == service::ProtocolType::Control) { - r->loop()->add_ticker([this] { Pump(Now()); }); + return true; } - - virtual bool - HandleInboundPacket( - const service::ConvoTag tag, - const llarp_buffer_t& buf, - service::ProtocolType t, - uint64_t) override + if (t == service::ProtocolType::TrafficV4 or t == service::ProtocolType::TrafficV6) { - LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag); - if (t == service::ProtocolType::Control) + if (auto from = GetEndpointWithConvoTag(tag)) { + net::IPPacket pkt{}; + if (not pkt.Load(buf)) + { + LogWarn("invalid ip packet from remote T=", tag); + return false; + } + m_PacketRouter->HandleIPPacketFrom(std::move(*from), std::move(pkt)); return true; } - if (t != service::ProtocolType::QUIC) - return false; - - auto* quic = GetQUICTunnel(); - if (!quic) - { - LogWarn("incoming quic packet but this endpoint is not quic capable; dropping"); - return false; - } - if (buf.sz < 4) + else { - LogWarn("invalid incoming quic packet, dropping"); + LogWarn("did not handle packet, no endpoint with convotag T=", tag); return false; } - quic->receive_packet(tag, buf); - return true; } + if (t != service::ProtocolType::QUIC) + return false; - std::string - GetIfName() const override + auto* quic = GetQUICTunnel(); + if (!quic) { - return ""; + LogWarn("incoming quic packet but this endpoint is not quic capable; dropping"); + return false; } - - path::PathSet_ptr - GetSelf() override + if (buf.sz < 4) { - return shared_from_this(); + LogWarn("invalid incoming quic packet, dropping"); + return false; } + quic->receive_packet(tag, buf); + return true; + } - std::weak_ptr - GetWeak() override - { - return weak_from_this(); - } + std::string + GetIfName() const override + { + return ""; + } - bool - SupportsV6() const override - { - return false; - } + path::PathSet_ptr + GetSelf() override + { + return shared_from_this(); + } - void - SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{}; + std::weak_ptr + GetWeak() override + { + return weak_from_this(); + } - huint128_t ObtainIPForAddr(std::variant) override - { - return {0}; - } + bool + SupportsV6() const override + { + return false; + } - std::optional> ObtainAddrForIP( - huint128_t) const override - { - return std::nullopt; - } - }; - } // namespace handlers -} // namespace llarp + void + SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{}; + + huint128_t ObtainIPForAddr(std::variant) override + { + return {0}; + } + + std::optional> ObtainAddrForIP( + huint128_t) const override + { + return std::nullopt; + } + + vpn::EgresPacketRouter* + EgresPacketRouter() override + { + return m_PacketRouter.get(); + } + + private: + std::unique_ptr m_PacketRouter; + }; +} // namespace llarp::handlers diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 5677fc12b..c31a300e6 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -1,7 +1,5 @@ #include #include -// harmless on other platforms -#define __USE_MINGW_ANSI_STDIO 1 #include "tun.hpp" #include #ifndef _WIN32 @@ -24,12 +22,11 @@ #include #include #include - #include -#include - #include +#include + namespace llarp { namespace handlers @@ -176,7 +173,11 @@ namespace llarp LogInfo(Name(), " setting to be not reachable by default"); } - if (conf.m_AuthType != service::AuthType::eAuthTypeNone) + if (conf.m_AuthType == service::AuthType::eAuthTypeFile) + { + m_AuthPolicy = service::MakeFileAuthPolicy(m_router, conf.m_AuthFiles, conf.m_AuthFileType); + } + else if (conf.m_AuthType != service::AuthType::eAuthTypeNone) { std::string url, method; if (conf.m_AuthUrl.has_value() and conf.m_AuthMethod.has_value()) @@ -185,7 +186,12 @@ namespace llarp method = *conf.m_AuthMethod; } auto auth = std::make_shared( - url, method, conf.m_AuthWhitelist, Router()->lmq(), shared_from_this()); + url, + method, + conf.m_AuthWhitelist, + conf.m_AuthStaticTokens, + Router()->lmq(), + shared_from_this()); auth->Start(); m_AuthPolicy = std::move(auth); } @@ -276,7 +282,7 @@ namespace llarp { std::string_view bdata{data.data(), data.size()}; LogDebug(Name(), " parsing address map data: ", bdata); - const auto parsed = oxenmq::bt_deserialize(bdata); + const auto parsed = oxenc::bt_deserialize(bdata); for (const auto& [key, value] : parsed) { huint128_t ip{}; @@ -908,7 +914,7 @@ namespace llarp try { - m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info)); + m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info), Router()); } catch (std::exception& ex) { @@ -1013,7 +1019,7 @@ namespace llarp addrmap[ip.ToString()] = a.ToString(); } } - const auto data = oxenmq::bt_serialize(addrmap); + const auto data = oxenc::bt_serialize(addrmap); maybe->write(data.data(), data.size()); } } @@ -1023,6 +1029,38 @@ namespace llarp return llarp::service::Endpoint::Stop(); } + std::optional + TunEndpoint::ObtainExitAddressFor( + huint128_t ip, + std::function)> exitSelectionStrat) + { + // is it already mapped? return the mapping + if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end()) + return itr->second; + // build up our candidates to choose + std::unordered_set candidates; + for (const auto& entry : m_ExitMap.FindAllEntries(ip)) + { + // make sure it is allowed by the range if the ip is a bogon + if (not IsBogon(ip) or entry.first.BogonContains(ip)) + candidates.emplace(entry.second); + } + // no candidates? bail. + if (candidates.empty()) + return std::nullopt; + if (not exitSelectionStrat) + { + // default strat to random choice + exitSelectionStrat = [](auto candidates) { + auto itr = candidates.begin(); + std::advance(itr, llarp::randint() % candidates.size()); + return *itr; + }; + } + // map the exit and return the endpoint we mapped it to + return m_ExitIPToExitAddress.emplace(ip, exitSelectionStrat(candidates)).first->second; + } + void TunEndpoint::HandleGotUserPacket(net::IPPacket pkt) { @@ -1037,25 +1075,7 @@ namespace llarp dst = pkt.dstv6(); src = pkt.srcv6(); } - // this is for ipv6 slaac on ipv6 exits - /* - constexpr huint128_t ipv6_multicast_all_nodes = - huint128_t{uint128_t{0xff01'0000'0000'0000UL, 1UL}}; - constexpr huint128_t ipv6_multicast_all_routers = - huint128_t{uint128_t{0xff01'0000'0000'0000UL, 2UL}}; - if (dst == ipv6_multicast_all_nodes and m_state->m_ExitEnabled) - { - // send ipv6 multicast - for (const auto& [ip, addr] : m_IPToAddr) - { - (void)ip; - SendToOrQueue( - service::Address{addr.as_array()}, pkt.ConstBuffer(), service::ProtocolType::Exit); - } - return; - } - */ if (m_state->m_ExitEnabled) { dst = net::ExpandV4(net::TruncateV6(dst)); @@ -1063,28 +1083,18 @@ namespace llarp auto itr = m_IPToAddr.find(dst); if (itr == m_IPToAddr.end()) { - // find all ranges that match the destination ip - const auto exitEntries = m_ExitMap.FindAllEntries(dst); - if (exitEntries.empty()) + service::Address addr{}; + + if (auto maybe = ObtainExitAddressFor(dst)) + addr = *maybe; + else { // send icmp unreachable as we dont have any exits for this ip if (const auto icmp = pkt.MakeICMPUnreachable()) - { HandleWriteIPPacket(icmp->ConstBuffer(), dst, src, 0); - } + return; } - service::Address addr{}; - for (const auto& [range, exitAddr] : exitEntries) - { - if (not IsBogon(dst) or range.BogonContains(dst)) - { - addr = exitAddr; - } - // we do not permit bogons when they don't explicitly match a permitted bogon range - } - if (addr.IsZero()) // drop becase no exit was found that matches our rules - return; pkt.ZeroSourceAddress(); MarkAddressOutbound(addr); EnsurePathToService( @@ -1266,24 +1276,14 @@ namespace llarp } else // don't allow snode return false; - const auto mapped = m_ExitMap.FindAllEntries(src); - bool allow = false; - for (const auto& [range, exitAddr] : mapped) + // make sure the mapping matches + if (auto itr = m_ExitIPToExitAddress.find(src); itr != m_ExitIPToExitAddress.end()) { - if (not IsBogon(src) or range.BogonContains(src)) - { - // allow if this address matches the endpoint we think it should be - allow = exitAddr == fromAddr; - break; - } + if (itr->second != fromAddr) + return false; } - if (not allow) - { - var::visit( - [&](auto&& address) { LogWarn(Name(), " does not allow ", src, " from ", address); }, - addr); + else return false; - } } else { diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index a5173c17d..74161754b 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -7,15 +7,16 @@ #include #include #include -#include #include #include #include -#include + #include #include -#include "service/protocol_type.hpp" + +#include +#include namespace llarp { @@ -172,27 +173,20 @@ namespace llarp ResetInternalState() override; protected: - using PacketQueue_t = llarp::util::CoDelQueue< - net::IPPacket, - net::IPPacket::GetTime, - net::IPPacket::PutTime, - net::IPPacket::CompareOrder, - net::IPPacket::GetNow>; - struct WritePacket { uint64_t seqno; net::IPPacket pkt; bool - operator<(const WritePacket& other) const + operator>(const WritePacket& other) const { - return other.seqno < seqno; + return seqno > other.seqno; } }; /// queue for sending packets to user from network - std::priority_queue m_NetworkToUserPktQueue; + util::ascending_priority_queue m_NetworkToUserPktQueue; void Pump(llarp_time_t now) override; @@ -222,7 +216,20 @@ namespace llarp /// a hidden service std::unordered_map, bool> m_SNodes; + /// maps ip address to an exit endpoint, useful when we have multiple exits on a range + std::unordered_map m_ExitIPToExitAddress; + private: + /// given an ip address that is not mapped locally find the address it shall be forwarded to + /// optionally provide a custom selection strategy, if none is provided it will choose a + /// random entry from the available choices + /// return std::nullopt if we cannot route this address to an exit + std::optional + ObtainExitAddressFor( + huint128_t ip, + std::function)> exitSelectionStrat = + nullptr); + template void SendDNSReply( diff --git a/llarp/iwp/linklayer.cpp b/llarp/iwp/linklayer.cpp index 7615a28d2..03e2259eb 100644 --- a/llarp/iwp/linklayer.cpp +++ b/llarp/iwp/linklayer.cpp @@ -76,28 +76,13 @@ namespace llarp::iwp bool success = session->Recv_LL(std::move(pkt)); if (not success and isNewSession) { - LogWarn("Brand new session failed; removing from pending sessions list"); + LogDebug("Brand new session failed; removing from pending sessions list"); m_Pending.erase(from); } WakeupPlaintext(); } } - bool - LinkLayer::MapAddr(const RouterID& r, ILinkSession* s) - { - if (not ILinkLayer::MapAddr(r, s)) - return false; - m_AuthedAddrs.emplace(s->GetRemoteEndpoint(), r); - return true; - } - - void - LinkLayer::UnmapAddr(const SockAddr& addr) - { - m_AuthedAddrs.erase(addr); - } - std::shared_ptr LinkLayer::NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) { diff --git a/llarp/iwp/linklayer.hpp b/llarp/iwp/linklayer.hpp index 52bfd34e6..6d58b046c 100644 --- a/llarp/iwp/linklayer.hpp +++ b/llarp/iwp/linklayer.hpp @@ -44,12 +44,6 @@ namespace llarp::iwp void RecvFrom(const SockAddr& from, ILinkSession::Packet_t pkt) override; - bool - MapAddr(const RouterID& pk, ILinkSession* s) override; - - void - UnmapAddr(const SockAddr& addr); - void WakeupPlaintext(); @@ -61,7 +55,6 @@ namespace llarp::iwp HandleWakeupPlaintext(); const std::shared_ptr m_Wakeup; - std::unordered_map m_AuthedAddrs; std::vector m_WakingUp; const bool m_Inbound; }; diff --git a/llarp/iwp/message_buffer.cpp b/llarp/iwp/message_buffer.cpp index 495fb076b..000f80a6a 100644 --- a/llarp/iwp/message_buffer.cpp +++ b/llarp/iwp/message_buffer.cpp @@ -10,12 +10,14 @@ namespace llarp uint64_t msgid, ILinkSession::Message_t msg, llarp_time_t now, - ILinkSession::CompletionHandler handler) + ILinkSession::CompletionHandler handler, + uint16_t priority) : m_Data{std::move(msg)} , m_MsgID{msgid} , m_Completed{handler} , m_LastFlush{now} , m_StartedAt{now} + , m_ResendPriority{priority} { const llarp_buffer_t buf(m_Data); CryptoManager::instance()->shorthash(m_Digest, buf); @@ -27,8 +29,9 @@ namespace llarp { size_t extra = std::min(m_Data.size(), FragmentSize); auto xmit = CreatePacket(Command::eXMIT, 10 + 32 + extra, 0, 0); - htobe16buf(xmit.data() + CommandOverhead + PacketOverhead, m_Data.size()); - htobe64buf(xmit.data() + 2 + CommandOverhead + PacketOverhead, m_MsgID); + oxenc::write_host_as_big( + static_cast(m_Data.size()), xmit.data() + CommandOverhead + PacketOverhead); + oxenc::write_host_as_big(m_MsgID, xmit.data() + 2 + CommandOverhead + PacketOverhead); std::copy_n( m_Digest.begin(), m_Digest.size(), xmit.data() + 10 + CommandOverhead + PacketOverhead); std::copy_n(m_Data.data(), extra, xmit.data() + 10 + CommandOverhead + PacketOverhead + 32); @@ -71,8 +74,8 @@ namespace llarp { const size_t fragsz = idx + FragmentSize < datasz ? FragmentSize : datasz - idx; auto frag = CreatePacket(Command::eDATA, fragsz + Overhead, 0, 0); - htobe16buf(frag.data() + 2 + PacketOverhead, idx); - htobe64buf(frag.data() + 4 + PacketOverhead, m_MsgID); + oxenc::write_host_as_big(idx, frag.data() + 2 + PacketOverhead); + oxenc::write_host_as_big(m_MsgID, frag.data() + 4 + PacketOverhead); std::copy( m_Data.begin() + idx, m_Data.begin() + idx + fragsz, @@ -136,7 +139,7 @@ namespace llarp InboundMessage::ACKS() const { auto acks = CreatePacket(Command::eACKS, 9); - htobe64buf(acks.data() + CommandOverhead + PacketOverhead, m_MsgID); + oxenc::write_host_as_big(m_MsgID, acks.data() + CommandOverhead + PacketOverhead); acks[PacketOverhead + 10] = AcksBitmask(); return acks; } diff --git a/llarp/iwp/message_buffer.hpp b/llarp/iwp/message_buffer.hpp index e760a8ac5..3ea352064 100644 --- a/llarp/iwp/message_buffer.hpp +++ b/llarp/iwp/message_buffer.hpp @@ -40,7 +40,8 @@ namespace llarp uint64_t msgid, ILinkSession::Message_t data, llarp_time_t now, - ILinkSession::CompletionHandler handler); + ILinkSession::CompletionHandler handler, + uint16_t priority); ILinkSession::Message_t m_Data; uint64_t m_MsgID = 0; @@ -49,6 +50,15 @@ namespace llarp llarp_time_t m_LastFlush = 0s; ShortHash m_Digest; llarp_time_t m_StartedAt = 0s; + uint16_t m_ResendPriority; + + bool + operator<(const OutboundMessage& msg) const + { + // yes, the first order is reversed as higher means more important + // second part is for queue order + return msg.m_ResendPriority < m_ResendPriority or m_MsgID < msg.m_MsgID; + } ILinkSession::Packet_t XMIT() const; diff --git a/llarp/iwp/session.cpp b/llarp/iwp/session.cpp index a3e504222..9aa88d01a 100644 --- a/llarp/iwp/session.cpp +++ b/llarp/iwp/session.cpp @@ -5,6 +5,8 @@ #include #include +#include + namespace llarp { namespace iwp @@ -22,7 +24,7 @@ namespace llarp } // randomize nounce CryptoManager::instance()->randbytes(pkt.data() + HMACSIZE, TUNNONCESIZE); - pkt[PacketOverhead] = LLARP_PROTO_VERSION; + pkt[PacketOverhead] = llarp::constants::proto_version; pkt[PacketOverhead + 1] = cmd; return pkt; } @@ -126,10 +128,12 @@ namespace llarp if (not msg.BEncode(&buf)) { LogError("failed to encode LIM for ", m_RemoteAddr); + return; } - if (!SendMessageBuffer(std::move(data), h)) + if (not SendMessageBuffer(std::move(data), h)) { LogError("failed to send LIM to ", m_RemoteAddr); + return; } LogTrace("sent LIM to ", m_RemoteAddr); } @@ -171,16 +175,18 @@ namespace llarp if (m_State == State::Closed) return; auto close_msg = CreatePacket(Command::eCLOS, 0, 16, 16); - if (m_State == State::Ready) - m_Parent->UnmapAddr(m_RemoteAddr); + m_Parent->UnmapAddr(m_RemoteAddr); m_State = State::Closed; + if (m_SentClosed.test_and_set()) + return; EncryptAndSend(std::move(close_msg)); + LogInfo(m_Parent->PrintableName(), " closing connection to ", m_RemoteAddr); } bool Session::SendMessageBuffer( - ILinkSession::Message_t buf, ILinkSession::CompletionHandler completed) + ILinkSession::Message_t buf, ILinkSession::CompletionHandler completed, uint16_t priority) { if (m_TXMsgs.size() >= MaxSendQueueSize) { @@ -191,8 +197,9 @@ namespace llarp const auto now = m_Parent->Now(); const auto msgid = m_TXID++; const auto bufsz = buf.size(); - auto& msg = m_TXMsgs.emplace(msgid, OutboundMessage{msgid, std::move(buf), now, completed}) - .first->second; + auto& msg = + m_TXMsgs.emplace(msgid, OutboundMessage{msgid, std::move(buf), now, completed, priority}) + .first->second; TriggerPump(); EncryptAndSend(msg.XMIT()); if (bufsz > FragmentSize) @@ -220,7 +227,7 @@ namespace llarp const auto& itr = m_SendMACKs.top(); while (numAcks > 0) { - htobe64buf(ptr, itr); + oxenc::write_host_as_big(itr, ptr); m_SendMACKs.pop(); numAcks--; ptr += sizeof(uint64_t); @@ -250,15 +257,22 @@ namespace llarp msg.SendACKS(util::memFn(&Session::EncryptAndSend, this), now); } } + std::priority_queue< + OutboundMessage*, + std::vector, + ComparePtr> + to_resend; for (auto& [id, msg] : m_TXMsgs) { if (msg.ShouldFlush(now)) - { - msg.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now); - } + to_resend.push(&msg); + } + if (not to_resend.empty()) + { + for (auto& msg = to_resend.top(); not to_resend.empty(); to_resend.pop()) + msg->FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now); } } - assert(shared_from_this().use_count() > 1); if (not m_EncryptNext.empty()) { m_Parent->QueueWork( @@ -338,7 +352,7 @@ namespace llarp bool Session::TimedOut(llarp_time_t now) const { - if (m_State == State::Ready || m_State == State::LinkIntro) + if (m_State == State::Ready) { return now > m_LastRX && now - m_LastRX @@ -590,7 +604,7 @@ namespace llarp const ShortHash expected{buf.base}; if (H != expected) { - LogError( + LogDebug( m_Parent->PrintableName(), " keyed hash mismatch ", H, @@ -639,10 +653,13 @@ namespace llarp LogError("failed to decrypt session data from ", m_RemoteAddr); continue; } - if (pkt[PacketOverhead] != LLARP_PROTO_VERSION) + if (pkt[PacketOverhead] != llarp::constants::proto_version) { LogError( - "protocol version mismatch ", int(pkt[PacketOverhead]), " != ", LLARP_PROTO_VERSION); + "protocol version mismatch ", + int(pkt[PacketOverhead]), + " != ", + llarp::constants::proto_version); itr = msgs.erase(itr); continue; } @@ -713,7 +730,7 @@ namespace llarp byte_t* ptr = data.data() + CommandOverhead + PacketOverhead + 1; while (numAcks > 0) { - uint64_t acked = bufbe64toh(ptr); + auto acked = oxenc::load_big_to_host(ptr); LogTrace("mack containing txid=", acked, " from ", m_RemoteAddr); auto itr = m_TXMsgs.find(acked); if (itr != m_TXMsgs.end()) @@ -740,7 +757,7 @@ namespace llarp LogError("short nack from ", m_RemoteAddr); return; } - uint64_t txid = bufbe64toh(data.data() + CommandOverhead + PacketOverhead); + auto txid = oxenc::load_big_to_host(data.data() + CommandOverhead + PacketOverhead); LogTrace("got nack on ", txid, " from ", m_RemoteAddr); auto itr = m_TXMsgs.find(txid); if (itr != m_TXMsgs.end()) @@ -762,13 +779,13 @@ namespace llarp return; } auto* pos = data.data() + CommandOverhead + PacketOverhead; - uint16_t sz = bufbe16toh(pos); + auto sz = oxenc::load_big_to_host(pos); pos += sizeof(sz); - uint64_t rxid = bufbe64toh(pos); + auto rxid = oxenc::load_big_to_host(pos); pos += sizeof(rxid); auto p2 = pos + ShortHash::SIZE; assert(p2 == data.data() + XMITOverhead); - LogTrace("rxid=", rxid, " sz=", sz, " h=", oxenmq::to_hex(pos, p2), " from ", m_RemoteAddr); + LogTrace("rxid=", rxid, " sz=", sz, " h=", oxenc::to_hex(pos, p2), " from ", m_RemoteAddr); m_LastRX = m_Parent->Now(); { // check for replay @@ -823,8 +840,9 @@ namespace llarp return; } m_LastRX = m_Parent->Now(); - uint16_t sz = bufbe16toh(data.data() + CommandOverhead + PacketOverhead); - uint64_t rxid = bufbe64toh(data.data() + CommandOverhead + sizeof(uint16_t) + PacketOverhead); + auto sz = oxenc::load_big_to_host(data.data() + CommandOverhead + PacketOverhead); + auto rxid = oxenc::load_big_to_host( + data.data() + CommandOverhead + sizeof(uint16_t) + PacketOverhead); auto itr = m_RXMsgs.find(rxid); if (itr == m_RXMsgs.end()) { @@ -832,7 +850,7 @@ namespace llarp { LogTrace("no rxid=", rxid, " for ", m_RemoteAddr); auto nack = CreatePacket(Command::eNACK, 8); - htobe64buf(nack.data() + PacketOverhead + CommandOverhead, rxid); + oxenc::write_host_as_big(rxid, nack.data() + PacketOverhead + CommandOverhead); EncryptAndSend(std::move(nack)); } else @@ -885,7 +903,7 @@ namespace llarp } const auto now = m_Parent->Now(); m_LastRX = now; - uint64_t txid = bufbe64toh(data.data() + 2 + PacketOverhead); + auto txid = oxenc::load_big_to_host(data.data() + 2 + PacketOverhead); auto itr = m_TXMsgs.find(txid); if (itr == m_TXMsgs.end()) { @@ -954,7 +972,7 @@ namespace llarp } else { - LogWarn("bad intro from ", m_RemoteAddr); + LogDebug("bad intro from ", m_RemoteAddr); return false; } } diff --git a/llarp/iwp/session.hpp b/llarp/iwp/session.hpp index 460063005..210a37a1e 100644 --- a/llarp/iwp/session.hpp +++ b/llarp/iwp/session.hpp @@ -8,8 +8,8 @@ #include #include #include -#include +#include #include namespace llarp @@ -60,7 +60,10 @@ namespace llarp Tick(llarp_time_t now) override; bool - SendMessageBuffer(ILinkSession::Message_t msg, CompletionHandler resultHandler) override; + SendMessageBuffer( + ILinkSession::Message_t msg, + CompletionHandler resultHandler, + uint16_t priority = 0) override; void Send_LL(const byte_t* buf, size_t sz); @@ -194,7 +197,7 @@ namespace llarp /// maps rxid to time recieved std::unordered_map m_ReplayFilter; /// rx messages to send in next round of multiacks - std::priority_queue, std::greater<>> m_SendMACKs; + util::ascending_priority_queue m_SendMACKs; using CryptoQueue_t = std::vector; @@ -203,6 +206,7 @@ namespace llarp std::atomic_flag m_PlaintextEmpty; llarp::thread::Queue m_PlaintextRecv; + std::atomic_flag m_SentClosed; void EncryptWorker(CryptoQueue_t msgs); diff --git a/llarp/link/i_link_manager.hpp b/llarp/link/i_link_manager.hpp index abf8b8081..48ffd3d94 100644 --- a/llarp/link/i_link_manager.hpp +++ b/llarp/link/i_link_manager.hpp @@ -30,7 +30,8 @@ namespace llarp SendTo( const RouterID& remote, const llarp_buffer_t& buf, - ILinkSession::CompletionHandler completed) = 0; + ILinkSession::CompletionHandler completed, + uint16_t priority = 0) = 0; virtual bool HasSessionTo(const RouterID& remote) const = 0; diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index a5682ebdd..116bfb392 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -19,7 +19,7 @@ namespace llarp // TODO: may want to add some memory of session failures for a given // router on a given link and not return that link here for a // duration - if (!link->IsCompatable(rc)) + if (not link->IsCompatable(rc)) continue; return link; @@ -36,7 +36,10 @@ namespace llarp bool LinkManager::SendTo( - const RouterID& remote, const llarp_buffer_t& buf, ILinkSession::CompletionHandler completed) + const RouterID& remote, + const llarp_buffer_t& buf, + ILinkSession::CompletionHandler completed, + uint16_t priority) { if (stopping) return false; @@ -51,7 +54,7 @@ namespace llarp return false; } - return link->SendTo(remote, buf, completed); + return link->SendTo(remote, buf, completed, priority); } bool diff --git a/llarp/link/link_manager.hpp b/llarp/link/link_manager.hpp index 0f8eeac69..77179b713 100644 --- a/llarp/link/link_manager.hpp +++ b/llarp/link/link_manager.hpp @@ -28,7 +28,8 @@ namespace llarp SendTo( const RouterID& remote, const llarp_buffer_t& buf, - ILinkSession::CompletionHandler completed) override; + ILinkSession::CompletionHandler completed, + uint16_t priority) override; bool HasSessionTo(const RouterID& remote) const override; diff --git a/llarp/link/server.cpp b/llarp/link/server.cpp index 083bd71ea..44ac8b4a3 100644 --- a/llarp/link/server.cpp +++ b/llarp/link/server.cpp @@ -8,6 +8,7 @@ #include #include #include +#include static constexpr auto LINK_LAYER_TICK_INTERVAL = 100ms; @@ -129,7 +130,7 @@ namespace llarp } bool - ILinkLayer::Configure(AbstractRouter* router, const std::string& ifname, int af, uint16_t port) + ILinkLayer::Configure(AbstractRouter* router, std::string ifname, int af, uint16_t port) { m_Router = router; m_udp = m_Router->loop()->make_udp( @@ -142,11 +143,42 @@ namespace llarp if (ifname == "*") { - if (!AllInterfaces(af, m_ourAddr)) + if (router->IsServiceNode()) + { + if (auto maybe = router->OurPublicIP()) + { + auto addr = var::visit([](auto&& addr) { return SockAddr{addr}; }, *maybe); + // service node outbound link + if (HasInterfaceAddress(addr.getIP())) + { + // we have our ip claimed on a local net interface + m_ourAddr = addr; + } + else if (auto maybe = net::AllInterfaces(addr)) + { + // we do not have our claimed ip, nat or something? + m_ourAddr = *maybe; + } + else + return false; // the ultimate failure case + } + else + return false; + } + else if (auto maybe = net::AllInterfaces(SockAddr{"0.0.0.0"})) + { + // client outbound link + m_ourAddr = *maybe; + } + else return false; } else { + if (ifname == "0.0.0.0" and not GetBestNetIF(ifname)) + throw std::invalid_argument{ + "0.0.0.0 provided and we cannot find a valid ip to use, please set one " + "explicitly instead in the bind section instead of 0.0.0.0"}; if (const auto maybe = GetInterfaceAddr(ifname, af)) { m_ourAddr = *maybe; @@ -157,10 +189,11 @@ namespace llarp { m_ourAddr = SockAddr{ifname + ":0"}; } - catch (const std::exception& e) + catch (const std::exception& ex) { - LogError(stringify("Could not use ifname ", ifname, " to configure ILinkLayer")); - throw e; + LogError( + stringify("Could not use ifname ", ifname, " to configure ILinkLayer: ", ex.what())); + throw ex; } } } @@ -192,6 +225,7 @@ namespace llarp llarp::LogInfo("session to ", RouterID(itr->second->GetPubKey()), " timed out"); itr->second->Close(); closedSessions.emplace(itr->first); + UnmapAddr(itr->second->GetRemoteEndpoint()); itr = m_AuthedLinks.erase(itr); } } @@ -210,6 +244,7 @@ namespace llarp else { LogInfo("pending session at ", itr->first, " timed out"); + UnmapAddr(itr->second->GetRemoteEndpoint()); // defer call so we can acquire mutexes later closedPending.emplace_back(std::move(itr->second)); itr = m_Pending.erase(itr); @@ -234,6 +269,12 @@ namespace llarp } } + void + ILinkLayer::UnmapAddr(const SockAddr& addr) + { + m_AuthedAddrs.erase(addr); + } + bool ILinkLayer::MapAddr(const RouterID& pk, ILinkSession* s) { @@ -249,6 +290,7 @@ namespace llarp s->Close(); return false; } + m_AuthedAddrs.emplace(addr, pk); m_AuthedLinks.emplace(pk, itr->second); itr = m_Pending.erase(itr); m_Router->TriggerPump(); @@ -435,12 +477,16 @@ namespace llarp void ILinkLayer::SendTo_LL(const SockAddr& to, const llarp_buffer_t& pkt) { - m_udp->send(to, pkt); + if (not m_udp->send(to, pkt)) + LogError("could not send udp packet to ", to); } bool ILinkLayer::SendTo( - const RouterID& remote, const llarp_buffer_t& buf, ILinkSession::CompletionHandler completed) + const RouterID& remote, + const llarp_buffer_t& buf, + ILinkSession::CompletionHandler completed, + uint16_t priority) { std::shared_ptr s; { @@ -459,7 +505,7 @@ namespace llarp } ILinkSession::Message_t pkt(buf.sz); std::copy_n(buf.base, buf.sz, pkt.begin()); - return s && s->SendMessageBuffer(std::move(pkt), completed); + return s && s->SendMessageBuffer(std::move(pkt), completed, priority); } bool diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index 803120454..02cc17f83 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -101,11 +101,14 @@ namespace llarp void ForEachSession(std::function visit) EXCLUDES(m_AuthedLinksMutex); + void + UnmapAddr(const SockAddr& addr); + void SendTo_LL(const SockAddr& to, const llarp_buffer_t& pkt); virtual bool - Configure(AbstractRouter* loop, const std::string& ifname, int af, uint16_t port); + Configure(AbstractRouter* loop, std::string ifname, int af, uint16_t port); virtual std::shared_ptr NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) = 0; @@ -148,7 +151,8 @@ namespace llarp SendTo( const RouterID& remote, const llarp_buffer_t& buf, - ILinkSession::CompletionHandler completed); + ILinkSession::CompletionHandler completed, + uint16_t priority); virtual bool GetOurAddressInfo(AddressInfo& addr) const; @@ -182,7 +186,7 @@ namespace llarp return false; } - virtual bool + bool MapAddr(const RouterID& pk, ILinkSession* s); void @@ -254,7 +258,7 @@ namespace llarp AuthedLinks m_AuthedLinks GUARDED_BY(m_AuthedLinksMutex); mutable DECLARE_LOCK(Mutex_t, m_PendingMutex, ACQUIRED_AFTER(m_AuthedLinksMutex)); Pending m_Pending GUARDED_BY(m_PendingMutex); - + std::unordered_map m_AuthedAddrs; std::unordered_map m_RecentlyClosed; private: diff --git a/llarp/link/session.hpp b/llarp/link/session.hpp index ab8b5d4f5..fc8df2414 100644 --- a/llarp/link/session.hpp +++ b/llarp/link/session.hpp @@ -57,7 +57,7 @@ namespace llarp /// send a message buffer to the remote endpoint virtual bool - SendMessageBuffer(Message_t, CompletionHandler handler) = 0; + SendMessageBuffer(Message_t, CompletionHandler handler, uint16_t priority) = 0; /// start the connection virtual void diff --git a/llarp/lokinet_shared.cpp b/llarp/lokinet_shared.cpp index 7a4891032..2075ba421 100644 --- a/llarp/lokinet_shared.cpp +++ b/llarp/lokinet_shared.cpp @@ -1,7 +1,5 @@ - - -#include "lokinet.h" -#include "llarp.hpp" +#include +#include #include #include @@ -10,7 +8,13 @@ #include #include +#include + +#include + #include +#include +#include #ifdef _WIN32 #define EHOSTDOWN ENETDOWN @@ -18,6 +22,34 @@ namespace { + struct Logger : public llarp::ILogStream + { + lokinet_logger_func func; + void* user; + + explicit Logger(lokinet_logger_func _func, void* _user) : func{_func}, user{_user} + {} + + void + PreLog(std::stringstream&, llarp::LogLevel, std::string_view, int, const std::string&) + const override + {} + + void + Print(llarp::LogLevel, std::string_view, const std::string& msg) override + { + func(msg.c_str(), user); + } + + void + PostLog(std::stringstream&) const override{}; + + void + ImmediateFlush() override{}; + + void Tick(llarp_time_t) override{}; + }; + struct Context : public llarp::Context { using llarp::Context::Context; @@ -28,6 +60,162 @@ namespace return std::make_shared(); } }; + + struct UDPFlow + { + using Clock_t = std::chrono::steady_clock; + void* m_FlowUserData; + std::chrono::seconds m_FlowTimeout; + std::chrono::time_point m_ExpiresAt; + lokinet_udp_flowinfo m_FlowInfo; + lokinet_udp_flow_recv_func m_Recv; + + /// call timeout hook for this flow + void + TimedOut(lokinet_udp_flow_timeout_func timeout) + { + timeout(&m_FlowInfo, m_FlowUserData); + } + + /// mark this flow as active + /// updates the expires at timestamp + void + MarkActive() + { + m_ExpiresAt = Clock_t::now() + m_FlowTimeout; + } + + /// returns true if we think this flow is expired + bool + IsExpired() const + { + return Clock_t::now() >= m_ExpiresAt; + } + + void + HandlePacket(const llarp::net::IPPacket& pkt) + { + if (auto maybe = pkt.L4Data()) + { + MarkActive(); + m_Recv(&m_FlowInfo, maybe->first, maybe->second, m_FlowUserData); + } + } + }; + + struct UDPHandler + { + using AddressVariant_t = llarp::vpn::AddressVariant_t; + int m_SocketID; + llarp::nuint16_t m_LocalPort; + lokinet_udp_flow_filter m_Filter; + lokinet_udp_flow_recv_func m_Recv; + lokinet_udp_flow_timeout_func m_Timeout; + void* m_User; + std::weak_ptr m_Endpoint; + + std::unordered_map m_Flows; + + std::mutex m_Access; + + explicit UDPHandler( + int socketid, + llarp::nuint16_t localport, + lokinet_udp_flow_filter filter, + lokinet_udp_flow_recv_func recv, + lokinet_udp_flow_timeout_func timeout, + void* user, + std::weak_ptr ep) + : m_SocketID{socketid} + , m_LocalPort{localport} + , m_Filter{filter} + , m_Recv{recv} + , m_Timeout{timeout} + , m_User{user} + , m_Endpoint{ep} + {} + + void + KillAllFlows() + { + std::unique_lock lock{m_Access}; + for (auto& item : m_Flows) + { + item.second.TimedOut(m_Timeout); + } + m_Flows.clear(); + } + + void + AddFlow( + const AddressVariant_t& from, + const lokinet_udp_flowinfo& flow_addr, + void* flow_userdata, + int flow_timeoutseconds, + std::optional firstPacket = std::nullopt) + { + std::unique_lock lock{m_Access}; + auto& flow = m_Flows[from]; + flow.m_FlowInfo = flow_addr; + flow.m_FlowTimeout = std::chrono::seconds{flow_timeoutseconds}; + flow.m_FlowUserData = flow_userdata; + flow.m_Recv = m_Recv; + if (firstPacket) + flow.HandlePacket(*firstPacket); + } + + void + ExpireOldFlows() + { + std::unique_lock lock{m_Access}; + for (auto itr = m_Flows.begin(); itr != m_Flows.end();) + { + if (itr->second.IsExpired()) + { + itr->second.TimedOut(m_Timeout); + itr = m_Flows.erase(itr); + } + else + ++itr; + } + } + + void + HandlePacketFrom(AddressVariant_t from, llarp::net::IPPacket pkt) + { + { + std::unique_lock lock{m_Access}; + if (m_Flows.count(from)) + { + m_Flows[from].HandlePacket(pkt); + return; + } + } + lokinet_udp_flowinfo flow_addr{}; + // set flow remote address + std::string addrstr = var::visit([](auto&& from) { return from.ToString(); }, from); + + std::copy_n( + addrstr.data(), + std::min(addrstr.size(), sizeof(flow_addr.remote_host)), + flow_addr.remote_host); + // set socket id + flow_addr.socket_id = m_SocketID; + // get source port + if (const auto srcport = pkt.SrcPort()) + { + flow_addr.remote_port = ToHost(*srcport).h; + } + else + return; // invalid data so we bail + void* flow_userdata = nullptr; + int flow_timeoutseconds{}; + // got a new flow, let's check if we want it + if (m_Filter(m_User, &flow_addr, &flow_userdata, &flow_timeoutseconds)) + return; + AddFlow(from, flow_addr, flow_userdata, flow_timeoutseconds, pkt); + } + }; } // namespace struct lokinet_context @@ -39,7 +227,10 @@ struct lokinet_context std::unique_ptr runner; - lokinet_context() : impl{std::make_shared()}, config{llarp::Config::EmbeddedConfig()} + int _socket_id; + + lokinet_context() + : impl{std::make_shared()}, config{llarp::Config::EmbeddedConfig()}, _socket_id{0} {} ~lokinet_context() @@ -48,6 +239,91 @@ struct lokinet_context runner->join(); } + int + next_socket_id() + { + int id = ++_socket_id; + // handle overflow + if (id < 0) + { + _socket_id = 0; + id = ++_socket_id; + } + return id; + } + + /// make a udp handler and hold onto it + /// return its id + [[nodiscard]] std::optional + make_udp_handler( + const std::shared_ptr& ep, + llarp::huint16_t exposePort, + lokinet_udp_flow_filter filter, + lokinet_udp_flow_recv_func recv, + lokinet_udp_flow_timeout_func timeout, + void* user) + { + if (udp_sockets.empty()) + { + // start udp flow expiration timer + impl->router->loop()->call_every(1s, std::make_shared(0), [this]() { + std::unique_lock lock{m_access}; + for (auto& item : udp_sockets) + { + item.second->ExpireOldFlows(); + } + }); + } + + auto udp = std::make_shared( + next_socket_id(), llarp::ToNet(exposePort), filter, recv, timeout, user, std::weak_ptr{ep}); + auto id = udp->m_SocketID; + std::promise result; + + impl->router->loop()->call([ep, &result, udp, exposePort]() { + if (auto pkt = ep->EgresPacketRouter()) + { + pkt->AddUDPHandler(exposePort, [udp](auto from, auto pkt) { + udp->HandlePacketFrom(std::move(from), std::move(pkt)); + }); + result.set_value(true); + } + else + result.set_value(false); + }); + + if (result.get_future().get()) + { + udp_sockets[udp->m_SocketID] = std::move(udp); + return id; + } + return std::nullopt; + } + + void + remove_udp_handler(int socket_id) + { + std::shared_ptr udp; + { + std::unique_lock lock{m_access}; + if (auto itr = udp_sockets.find(socket_id); itr != udp_sockets.end()) + { + udp = std::move(itr->second); + udp_sockets.erase(itr); + } + } + if (udp) + { + udp->KillAllFlows(); + // remove packet handler + impl->router->loop()->call( + [ep = udp->m_Endpoint.lock(), localport = llarp::ToHost(udp->m_LocalPort)]() { + if (auto pkt = ep->EgresPacketRouter()) + pkt->RemoveUDPHandler(localport); + }); + } + } + /// acquire mutex for accessing this context [[nodiscard]] auto acquire() @@ -62,6 +338,7 @@ struct lokinet_context } std::unordered_map streams; + std::unordered_map> udp_sockets; void inbound_stream(int id) @@ -78,8 +355,6 @@ struct lokinet_context namespace { - std::unique_ptr g_context; - void stream_error(lokinet_stream_result* result, int err) { @@ -230,18 +505,39 @@ extern "C" int EXPORT lokinet_add_bootstrap_rc(const char* data, size_t datalen, struct lokinet_context* ctx) { + if (data == nullptr or datalen == 0) + return -3; llarp_buffer_t buf{data, datalen}; - llarp::RouterContact rc{}; if (ctx == nullptr) return -3; auto lock = ctx->acquire(); // add a temp cryptography implementation here so rc.Verify works llarp::CryptoManager instance{new llarp::sodium::CryptoLibSodium{}}; - if (not rc.BDecode(&buf)) - return -1; - if (not rc.Verify(llarp::time_now_ms())) - return -2; - ctx->config->bootstrap.routers.insert(std::move(rc)); + if (data[0] == 'l') + { + if (not ctx->config->bootstrap.routers.BDecode(&buf)) + { + llarp::LogError("Cannot decode bootstrap list: ", llarp::buffer_printer{buf}); + return -1; + } + for (const auto& rc : ctx->config->bootstrap.routers) + { + if (not rc.Verify(llarp::time_now_ms())) + return -2; + } + } + else + { + llarp::RouterContact rc{}; + if (not rc.BDecode(&buf)) + { + llarp::LogError("failed to decode signle RC: ", llarp::buffer_printer{buf}); + return -1; + } + if (not rc.Verify(llarp::time_now_ms())) + return -2; + ctx->config->bootstrap.routers.insert(std::move(rc)); + } return 0; } @@ -334,11 +630,11 @@ extern "C" return; auto lock = ctx->acquire(); - if (not ctx->impl->IsStopping()) - { - ctx->impl->CloseAsync(); - ctx->impl->Wait(); - } + if (ctx->impl->IsStopping()) + return; + + ctx->impl->CloseAsync(); + ctx->impl->Wait(); if (ctx->runner) ctx->runner->join(); @@ -523,6 +819,27 @@ extern "C" return id; } + char* EXPORT + lokinet_hex_to_base32z(const char* hex) + { + std::string_view hexview{hex}; + if (not oxenc::is_hex(hexview)) + return nullptr; + + const size_t byte_len = hexview.size() / 2; + const size_t b32z_len = (byte_len * 8 + 4) / 5; // = ⌈N×8÷5⌉ because 5 bits per 32z char + auto buf = std::make_unique(b32z_len + 1); + char* end = buf.get() + b32z_len; + *end = 0; // null terminate + // Write the bytes into the *end* of the buffer so that when we rewrite the final b32z chars + // into the buffer we won't overwrite any byte values until after we've consumed them. + char* bytepos = end - byte_len; + oxenc::from_hex(hexview.begin(), hexview.end(), bytepos); + // In-place conversion into the buffer + oxenc::to_base32z(bytepos, end, buf.get()); + return buf.release(); // leak the buffer to the caller + } + void EXPORT lokinet_close_stream(int stream_id, struct lokinet_context* ctx) { @@ -594,4 +911,168 @@ extern "C" delete result->internal; result->internal = nullptr; } + + int EXPORT + lokinet_udp_bind( + uint16_t exposedPort, + lokinet_udp_flow_filter filter, + lokinet_udp_flow_recv_func recv, + lokinet_udp_flow_timeout_func timeout, + void* user, + struct lokinet_udp_bind_result* result, + struct lokinet_context* ctx) + { + if (filter == nullptr or recv == nullptr or timeout == nullptr or result == nullptr + or ctx == nullptr) + return EINVAL; + + auto lock = ctx->acquire(); + if (auto ep = ctx->endpoint()) + { + if (auto maybe = + ctx->make_udp_handler(ep, llarp::huint16_t{exposedPort}, filter, recv, timeout, user)) + { + result->socket_id = *maybe; + return 0; + } + } + return EINVAL; + } + + void EXPORT + lokinet_udp_close(int socket_id, struct lokinet_context* ctx) + { + if (ctx) + { + ctx->remove_udp_handler(socket_id); + } + } + + int EXPORT + lokinet_udp_flow_send( + const struct lokinet_udp_flowinfo* remote, + const void* ptr, + size_t len, + struct lokinet_context* ctx) + { + if (remote == nullptr or remote->remote_port == 0 or ptr == nullptr or len == 0 + or ctx == nullptr) + return EINVAL; + std::shared_ptr ep; + llarp::nuint16_t srcport{0}; + llarp::nuint16_t dstport{llarp::ToNet(llarp::huint16_t{remote->remote_port})}; + { + auto lock = ctx->acquire(); + if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end()) + { + ep = itr->second->m_Endpoint.lock(); + srcport = itr->second->m_LocalPort; + } + else + return EHOSTUNREACH; + } + if (auto maybe = llarp::service::ParseAddress(std::string{remote->remote_host})) + { + llarp::net::IPPacket pkt = llarp::net::IPPacket::UDP( + llarp::nuint32_t{0}, + srcport, + llarp::nuint32_t{0}, + dstport, + llarp_buffer_t{reinterpret_cast(ptr), len}); + + if (pkt.sz == 0) + return EINVAL; + std::promise ret; + ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() { + if (auto tag = ep->GetBestConvoTagFor(addr)) + { + if (ep->SendToOrQueue(*tag, pkt.ConstBuffer(), llarp::service::ProtocolType::TrafficV4)) + { + ret.set_value(0); + return; + } + } + ret.set_value(ENETUNREACH); + }); + return ret.get_future().get(); + } + return EINVAL; + } + + int EXPORT + lokinet_udp_establish( + lokinet_udp_create_flow_func create_flow, + void* user, + const struct lokinet_udp_flowinfo* remote, + struct lokinet_context* ctx) + { + if (create_flow == nullptr or remote == nullptr or ctx == nullptr) + return EINVAL; + std::shared_ptr ep; + { + auto lock = ctx->acquire(); + if (ctx->impl->router->loop()->inEventLoop()) + { + llarp::LogError("cannot call udp_establish from internal event loop"); + return EINVAL; + } + if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end()) + { + ep = itr->second->m_Endpoint.lock(); + } + else + return EHOSTUNREACH; + } + if (auto maybe = llarp::service::ParseAddress(std::string{remote->remote_host})) + { + { + // check for pre existing flow + auto lock = ctx->acquire(); + if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end()) + { + auto& udp = itr->second; + if (udp->m_Flows.count(*maybe)) + { + // we already have a flow. + return EADDRINUSE; + } + } + } + std::promise gotten; + ctx->impl->router->loop()->call([addr = *maybe, ep, &gotten]() { + ep->MarkAddressOutbound(addr); + auto res = ep->EnsurePathTo( + addr, [&gotten](auto result) { gotten.set_value(result.has_value()); }, 5s); + if (not res) + { + gotten.set_value(false); + } + }); + if (gotten.get_future().get()) + { + void* flow_data{nullptr}; + int flow_timeoutseconds{}; + create_flow(user, &flow_data, &flow_timeoutseconds); + { + auto lock = ctx->acquire(); + if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end()) + { + itr->second->AddFlow(*maybe, *remote, flow_data, flow_timeoutseconds); + return 0; + } + else + return EADDRINUSE; + } + } + else + return ETIMEDOUT; + } + return EINVAL; + } + + void EXPORT + lokinet_set_logger(lokinet_logger_func func, void* user) + { + llarp::LogContext::Instance().logStream.reset(new Logger{func, user}); + } } diff --git a/llarp/messages/dht_immediate.cpp b/llarp/messages/dht_immediate.cpp index 1af4a9c47..2ab9f13e3 100644 --- a/llarp/messages/dht_immediate.cpp +++ b/llarp/messages/dht_immediate.cpp @@ -20,7 +20,7 @@ namespace llarp { if (!bencode_read_integer(buf, &version)) return false; - return version == LLARP_PROTO_VERSION; + return version == llarp::constants::proto_version; } // bad key return false; @@ -54,7 +54,7 @@ namespace llarp return false; // protocol version - if (!bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION)) + if (!bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version)) return false; return bencode_end(buf); diff --git a/llarp/messages/discard.hpp b/llarp/messages/discard.hpp index 2466cb3d0..46d24d429 100644 --- a/llarp/messages/discard.hpp +++ b/llarp/messages/discard.hpp @@ -69,7 +69,7 @@ namespace llarp DataDiscardMessage(const PathID_t& dst, uint64_t s) : P(dst) { S = s; - version = LLARP_PROTO_VERSION; + version = llarp::constants::proto_version; } void diff --git a/llarp/messages/link_intro.cpp b/llarp/messages/link_intro.cpp index f334e2aba..9144baf20 100644 --- a/llarp/messages/link_intro.cpp +++ b/llarp/messages/link_intro.cpp @@ -43,9 +43,10 @@ namespace llarp { if (!bencode_read_integer(buf, &version)) return false; - if (version != LLARP_PROTO_VERSION) + if (version != llarp::constants::proto_version) { - llarp::LogWarn("llarp protocol version mismatch ", version, " != ", LLARP_PROTO_VERSION); + llarp::LogWarn( + "llarp protocol version mismatch ", version, " != ", llarp::constants::proto_version); return false; } llarp::LogDebug("LIM version ", version); @@ -86,7 +87,7 @@ namespace llarp if (!rc.BEncode(buf)) return false; - if (!bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION)) + if (!bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version)) return false; if (!bencode_write_bytestring(buf, "z", 1)) diff --git a/llarp/messages/link_message.hpp b/llarp/messages/link_message.hpp index 9ed1cf2f9..af5af6413 100644 --- a/llarp/messages/link_message.hpp +++ b/llarp/messages/link_message.hpp @@ -17,7 +17,7 @@ namespace llarp { /// who did this message come from or is going to ILinkSession* session = nullptr; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; PathID_t pathid; diff --git a/llarp/messages/relay.cpp b/llarp/messages/relay.cpp index fdbac1a1f..ace3d7df2 100644 --- a/llarp/messages/relay.cpp +++ b/llarp/messages/relay.cpp @@ -25,7 +25,7 @@ namespace llarp if (!BEncodeWriteDictEntry("p", pathid, buf)) return false; - if (!BEncodeWriteDictInt("v", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("v", llarp::constants::proto_version, buf)) return false; if (!BEncodeWriteDictEntry("x", X, buf)) return false; @@ -40,7 +40,7 @@ namespace llarp bool read = false; if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf)) return false; - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, key, buf)) + if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf)) return false; if (!BEncodeMaybeReadDictEntry("x", X, read, key, buf)) return false; @@ -79,7 +79,7 @@ namespace llarp if (!BEncodeWriteDictEntry("p", pathid, buf)) return false; - if (!BEncodeWriteDictInt("v", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("v", llarp::constants::proto_version, buf)) return false; if (!BEncodeWriteDictEntry("x", X, buf)) return false; @@ -94,7 +94,7 @@ namespace llarp bool read = false; if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf)) return false; - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, key, buf)) + if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf)) return false; if (!BEncodeMaybeReadDictEntry("x", X, read, key, buf)) return false; diff --git a/llarp/messages/relay_commit.cpp b/llarp/messages/relay_commit.cpp index 22fd0e33a..3dc67d7a5 100644 --- a/llarp/messages/relay_commit.cpp +++ b/llarp/messages/relay_commit.cpp @@ -29,7 +29,7 @@ namespace llarp return BEncodeReadArray(frames, buf); } bool read = false; - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, key, buf)) + if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf)) return false; return read; @@ -54,7 +54,7 @@ namespace llarp if (!BEncodeWriteDictArray("c", frames, buf)) return false; // version - if (!bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION)) + if (!bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version)) return false; return bencode_end(buf); @@ -102,9 +102,10 @@ namespace llarp if (!BEncodeWriteDictEntry("u", *nextRC, buf)) return false; } - if (!bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION)) + + if (not bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version)) return false; - if (work && !BEncodeWriteDictEntry("w", *work, buf)) + if (work and not BEncodeWriteDictEntry("w", *work, buf)) return false; return bencode_end(buf); @@ -135,7 +136,8 @@ namespace llarp nextRC = std::make_unique(); return nextRC->BDecode(buffer); } - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, *key, buffer)) + if (!BEncodeMaybeVerifyVersion( + "v", version, llarp::constants::proto_version, read, *key, buffer)) return false; if (*key == "w") { @@ -319,6 +321,8 @@ namespace llarp self->hop = nullptr; }; self->context->ForwardLRCM(self->hop->info.upstream, self->frames, func); + // trigger idempotent pump to ensure that the build messages propagate + self->context->Router()->TriggerPump(); } // this is called from the logic thread @@ -456,6 +460,8 @@ namespace llarp self->decrypter = nullptr; }); } + // trigger idempotent pump to ensure that the build messages propagate + self->context->Router()->TriggerPump(); } }; diff --git a/llarp/messages/relay_status.cpp b/llarp/messages/relay_status.cpp index 9ea58a55b..67dcb3294 100644 --- a/llarp/messages/relay_status.cpp +++ b/llarp/messages/relay_status.cpp @@ -80,7 +80,7 @@ namespace llarp } else if (key == "v") { - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, key, buf)) + if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf)) { return false; } @@ -115,7 +115,7 @@ namespace llarp if (!BEncodeWriteDictInt("s", status, buf)) return false; // version - if (!bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION)) + if (!bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version)) return false; return bencode_end(buf); @@ -145,8 +145,8 @@ namespace llarp void LR_StatusMessage::SetDummyFrames() { - // TODO - return; + for (auto& f : frames) + f.Randomize(); } // call this from a worker thread @@ -190,7 +190,7 @@ namespace llarp LR_StatusRecord record; record.status = newStatus; - record.version = LLARP_PROTO_VERSION; + record.version = llarp::constants::proto_version; llarp_buffer_t buf(frame.data(), frame.size()); buf.cur = buf.base + EncryptedFrameOverheadSize; @@ -221,31 +221,43 @@ namespace llarp std::shared_ptr hop) { router->loop()->call([router, nextHop, msg = std::move(msg), hop = std::move(hop)] { - SendMessage(router, nextHop, msg); - // destroy hop as needed - if ((msg->status & LR_StatusRecord::SUCCESS) != LR_StatusRecord::SUCCESS) - { - hop->QueueDestroySelf(router); - } + SendMessage(router, nextHop, msg, hop); }); } void LR_StatusMessage::SendMessage( - AbstractRouter* router, const RouterID nextHop, std::shared_ptr msg) + AbstractRouter* router, + const RouterID nextHop, + std::shared_ptr msg, + std::shared_ptr hop) { llarp::LogDebug("Attempting to send LR_Status message to (", nextHop, ")"); - if (not router->SendToOrQueue(nextHop, *msg)) - { - llarp::LogError("Sending LR_Status message, SendToOrQueue to ", nextHop, " failed"); - } + + auto resultCallback = [hop, router, msg, nextHop](auto status) { + if ((msg->status & LR_StatusRecord::SUCCESS) != LR_StatusRecord::SUCCESS + or status != SendStatus::Success) + { + llarp::LogError("Failed to propagate LR_Status message to ", nextHop); + hop->QueueDestroySelf(router); + } + }; + + // send the status message to previous hop + // if it fails we are hitting a failure case we can't cope with so ... drop. + if (not router->SendToOrQueue(nextHop, *msg, resultCallback)) + resultCallback(SendStatus::Congestion); + + // trigger idempotent pump to make sure stuff gets sent + router->TriggerPump(); } bool LR_StatusRecord::BEncode(llarp_buffer_t* buf) const { return bencode_start_dict(buf) && BEncodeWriteDictInt("s", status, buf) - && bencode_write_uint64_entry(buf, "v", 1, LLARP_PROTO_VERSION) && bencode_end(buf); + && bencode_write_uint64_entry(buf, "v", 1, llarp::constants::proto_version) + && bencode_end(buf); } bool @@ -258,7 +270,8 @@ namespace llarp if (!BEncodeMaybeReadDictInt("s", status, read, *key, buffer)) return false; - if (!BEncodeMaybeVerifyVersion("v", version, LLARP_PROTO_VERSION, read, *key, buffer)) + if (!BEncodeMaybeVerifyVersion( + "v", version, llarp::constants::proto_version, read, *key, buffer)) return false; return read; diff --git a/llarp/messages/relay_status.hpp b/llarp/messages/relay_status.hpp index b331cd622..265ce6a08 100644 --- a/llarp/messages/relay_status.hpp +++ b/llarp/messages/relay_status.hpp @@ -105,7 +105,10 @@ namespace llarp static void SendMessage( - AbstractRouter* router, const RouterID nextHop, std::shared_ptr msg); + AbstractRouter* router, + const RouterID nextHop, + std::shared_ptr msg, + std::shared_ptr hop); const char* Name() const override diff --git a/llarp/net/address_info.cpp b/llarp/net/address_info.cpp index c4aad84fe..d4b8cd398 100644 --- a/llarp/net/address_info.cpp +++ b/llarp/net/address_info.cpp @@ -27,6 +27,12 @@ namespace llarp return lhs.rank < rhs.rank || lhs.ip < rhs.ip || lhs.port < rhs.port; } + std::variant + AddressInfo::IP() const + { + return SockAddr{ip}.getIP(); + } + bool AddressInfo::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { @@ -99,7 +105,7 @@ namespace llarp { if (!bencode_read_integer(buf, &i)) return false; - return i == LLARP_PROTO_VERSION; + return i == llarp::constants::proto_version; } // bad key @@ -143,7 +149,7 @@ namespace llarp return false; /** version */ - if (!bencode_write_uint64_entry(buff, "v", 1, LLARP_PROTO_VERSION)) + if (!bencode_write_uint64_entry(buff, "v", 1, llarp::constants::proto_version)) return false; /** end */ return bencode_end(buff); diff --git a/llarp/net/address_info.hpp b/llarp/net/address_info.hpp index 5851ad07c..a66f77112 100644 --- a/llarp/net/address_info.hpp +++ b/llarp/net/address_info.hpp @@ -9,6 +9,8 @@ #include #include +#include + /** * address_info.hpp * @@ -25,7 +27,7 @@ namespace llarp llarp::PubKey pubkey; in6_addr ip = {}; uint16_t port; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; bool BDecode(llarp_buffer_t* buf) @@ -47,6 +49,10 @@ namespace llarp void fromSockAddr(const SockAddr& address); + /// get this as an explicit v4 or explicit v6 + std::variant + IP() const; + std::ostream& print(std::ostream& stream, int level, int spaces) const; }; diff --git a/llarp/net/exit_info.hpp b/llarp/net/exit_info.hpp index 7265e181a..b3727c4f5 100644 --- a/llarp/net/exit_info.hpp +++ b/llarp/net/exit_info.hpp @@ -21,7 +21,7 @@ namespace llarp IpAddress ipAddress; IpAddress netmask; PubKey pubkey; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; ExitInfo() = default; diff --git a/llarp/net/ip_packet.cpp b/llarp/net/ip_packet.cpp index c31c2332d..d6e339ee5 100644 --- a/llarp/net/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -2,619 +2,668 @@ #include "ip.hpp" #include -#include #include #include #ifndef _WIN32 #include #endif +#include + #include #include -constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; - -void -ipv6_header::FlowLabel(llarp::nuint32_t label) +namespace llarp::net { - // the ipv6 flow label is the last 20 bits in the first 32 bits of the header - preamble.flowlabel = - (htonl(ipv6_flowlabel_mask) & label.n) | (preamble.flowlabel & htonl(~ipv6_flowlabel_mask)); -} + constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; -llarp::nuint32_t -ipv6_header::FlowLabel() const -{ - return llarp::nuint32_t{preamble.flowlabel & htonl(ipv6_flowlabel_mask)}; -} + template + struct ipv6_header_preamble + { + unsigned char pad_small : 4; + unsigned char version : 4; + uint8_t pad[3]; + }; -namespace llarp -{ - namespace net + template <> + struct ipv6_header_preamble { - std::string - IPProtocolName(IPProtocol proto) - { - if (const auto* ent = ::getprotobynumber(static_cast(proto))) - { - return ent->p_name; - } - throw std::invalid_argument{ - "cannot determine protocol name for ip proto '" + std::to_string(static_cast(proto)) - + "'"}; - } + unsigned char version : 4; + unsigned char pad_small : 4; + uint8_t pad[3]; + }; + + /// get 20 bit truncated flow label in network order + llarp::nuint32_t + ipv6_header::FlowLabel() const + { + return llarp::nuint32_t{preamble.flowlabel & htonl(ipv6_flowlabel_mask)}; + } - IPProtocol - ParseIPProtocol(std::string data) + /// put 20 bit truncated flow label network order + void + ipv6_header::FlowLabel(llarp::nuint32_t label) + { + // the ipv6 flow label is the last 20 bits in the first 32 bits of the header + preamble.flowlabel = + (htonl(ipv6_flowlabel_mask) & label.n) | (preamble.flowlabel & htonl(~ipv6_flowlabel_mask)); + }; + + std::string + IPProtocolName(IPProtocol proto) + { + if (const auto* ent = ::getprotobynumber(static_cast(proto))) { - if (const auto* ent = ::getprotobyname(data.c_str())) - { - return static_cast(ent->p_proto); - } - if (starts_with(data, "0x")) - { - if (const int intVal = std::stoi(data.substr(2), nullptr, 16); intVal > 0) - return static_cast(intVal); - } - throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; + return ent->p_name; } + throw std::invalid_argument{ + "cannot determine protocol name for ip proto '" + std::to_string(static_cast(proto)) + + "'"}; + } - inline static uint32_t* - in6_uint32_ptr(in6_addr& addr) + IPProtocol + ParseIPProtocol(std::string data) + { + if (const auto* ent = ::getprotobyname(data.c_str())) { - return (uint32_t*)addr.s6_addr; + return static_cast(ent->p_proto); } - - inline static const uint32_t* - in6_uint32_ptr(const in6_addr& addr) + if (starts_with(data, "0x")) { - return (uint32_t*)addr.s6_addr; + if (const int intVal = std::stoi(data.substr(2), nullptr, 16); intVal > 0) + return static_cast(intVal); } + throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; + } - huint128_t - IPPacket::srcv6() const - { - if (IsV6()) - return In6ToHUInt(HeaderV6()->srcaddr); + inline static uint32_t* + in6_uint32_ptr(in6_addr& addr) + { + return (uint32_t*)addr.s6_addr; + } - return ExpandV4(srcv4()); - } + inline static const uint32_t* + in6_uint32_ptr(const in6_addr& addr) + { + return (uint32_t*)addr.s6_addr; + } - huint128_t - IPPacket::dstv6() const - { - if (IsV6()) - return In6ToHUInt(HeaderV6()->dstaddr); + huint128_t + IPPacket::srcv6() const + { + if (IsV6()) + return In6ToHUInt(HeaderV6()->srcaddr); - return ExpandV4(dstv4()); - } + return ExpandV4(srcv4()); + } - bool - IPPacket::Load(const llarp_buffer_t& pkt) - { - if (pkt.sz > sizeof(buf) or pkt.sz == 0) - return false; - sz = pkt.sz; - std::copy_n(pkt.base, sz, buf); - return true; - } + huint128_t + IPPacket::dstv6() const + { + if (IsV6()) + return In6ToHUInt(HeaderV6()->dstaddr); - ManagedBuffer - IPPacket::ConstBuffer() const - { - const byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); - } + return ExpandV4(dstv4()); + } - ManagedBuffer - IPPacket::Buffer() - { - byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); - } + bool + IPPacket::Load(const llarp_buffer_t& pkt) + { + if (pkt.sz > sizeof(buf) or pkt.sz == 0) + return false; + sz = pkt.sz; + std::copy_n(pkt.base, sz, buf); + return true; + } + + ManagedBuffer + IPPacket::ConstBuffer() const + { + const byte_t* ptr = buf; + llarp_buffer_t b(ptr, sz); + return ManagedBuffer(b); + } - std::optional - IPPacket::DstPort() const - { - switch (IPProtocol{Header()->protocol}) - { - case IPProtocol::TCP: - case IPProtocol::UDP: - return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4) + 2)}; - default: - return std::nullopt; - } - } + ManagedBuffer + IPPacket::Buffer() + { + byte_t* ptr = buf; + llarp_buffer_t b(ptr, sz); + return ManagedBuffer(b); + } - huint32_t - IPPacket::srcv4() const + std::optional + IPPacket::DstPort() const + { + switch (IPProtocol{Header()->protocol}) { - return huint32_t{ntohl(Header()->saddr)}; + case IPProtocol::TCP: + case IPProtocol::UDP: + return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4) + 2)}; + default: + return std::nullopt; } + } - huint32_t - IPPacket::dstv4() const + std::optional + IPPacket::SrcPort() const + { + switch (IPProtocol{Header()->protocol}) { - return huint32_t{ntohl(Header()->daddr)}; + case IPProtocol::TCP: + case IPProtocol::UDP: + return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4))}; + default: + return std::nullopt; } + } - huint128_t - IPPacket::dst4to6() const - { - return ExpandV4(dstv4()); - } + huint32_t + IPPacket::srcv4() const + { + return huint32_t{ntohl(Header()->saddr)}; + } - huint128_t - IPPacket::src4to6() const - { - return ExpandV4(srcv4()); - } + huint32_t + IPPacket::dstv4() const + { + return huint32_t{ntohl(Header()->daddr)}; + } - huint128_t - IPPacket::dst4to6Lan() const - { - return ExpandV4Lan(dstv4()); - } + huint128_t + IPPacket::dst4to6() const + { + return ExpandV4(dstv4()); + } + + huint128_t + IPPacket::src4to6() const + { + return ExpandV4(srcv4()); + } + + huint128_t + IPPacket::dst4to6Lan() const + { + return ExpandV4Lan(dstv4()); + } - huint128_t - IPPacket::src4to6Lan() const + huint128_t + IPPacket::src4to6Lan() const + { + return ExpandV4Lan(srcv4()); + } + + uint16_t + ipchksum(const byte_t* buf, size_t sz, uint32_t sum) + { + while (sz > 1) { - return ExpandV4Lan(srcv4()); + sum += *(const uint16_t*)buf; + sz -= sizeof(uint16_t); + buf += sizeof(uint16_t); } - - uint16_t - ipchksum(const byte_t* buf, size_t sz, uint32_t sum) + if (sz != 0) { - while (sz > 1) - { - sum += *(const uint16_t*)buf; - sz -= sizeof(uint16_t); - buf += sizeof(uint16_t); - } - if (sz != 0) - { - uint16_t x = 0; + uint16_t x = 0; - *(byte_t*)&x = *(const byte_t*)buf; - sum += x; - } + *(byte_t*)&x = *(const byte_t*)buf; + sum += x; + } - // only need to do it 2 times to be sure - // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff - sum = (sum & 0xFFff) + (sum >> 16); - sum += sum >> 16; + // only need to do it 2 times to be sure + // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff + sum = (sum & 0xFFff) + (sum >> 16); + sum += sum >> 16; - return uint16_t((~sum) & 0xFFff); - } + return uint16_t((~sum) & 0xFFff); + } #define ADD32CS(x) ((uint32_t)(x & 0xFFff) + (uint32_t)(x >> 16)) #define SUB32CS(x) ((uint32_t)((~x) & 0xFFff) + (uint32_t)((~x) >> 16)) - static nuint16_t - deltaIPv4Checksum( - nuint16_t old_sum, - nuint32_t old_src_ip, - nuint32_t old_dst_ip, - nuint32_t new_src_ip, - nuint32_t new_dst_ip) - { - uint32_t sum = uint32_t(old_sum.n) + ADD32CS(old_src_ip.n) + ADD32CS(old_dst_ip.n) - + SUB32CS(new_src_ip.n) + SUB32CS(new_dst_ip.n); - - // only need to do it 2 times to be sure - // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff - sum = (sum & 0xFFff) + (sum >> 16); - sum += sum >> 16; - - return nuint16_t{uint16_t(sum & 0xFFff)}; - } - - static nuint16_t - deltaIPv6Checksum( - nuint16_t old_sum, - const uint32_t old_src_ip[4], - const uint32_t old_dst_ip[4], - const uint32_t new_src_ip[4], - const uint32_t new_dst_ip[4]) - { - /* we don't actually care in what way integers are arranged in memory - * internally */ - /* as long as uint16 pairs are swapped in correct direction, result will - * be correct (assuming there are no gaps in structure) */ - /* we represent 128bit stuff there as 4 32bit ints, that should be more or - * less correct */ - /* we could do 64bit ints too but then we couldn't reuse 32bit macros and - * that'd suck for 32bit cpus */ + static nuint16_t + deltaIPv4Checksum( + nuint16_t old_sum, + nuint32_t old_src_ip, + nuint32_t old_dst_ip, + nuint32_t new_src_ip, + nuint32_t new_dst_ip) + { + uint32_t sum = uint32_t(old_sum.n) + ADD32CS(old_src_ip.n) + ADD32CS(old_dst_ip.n) + + SUB32CS(new_src_ip.n) + SUB32CS(new_dst_ip.n); + + // only need to do it 2 times to be sure + // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff + sum = (sum & 0xFFff) + (sum >> 16); + sum += sum >> 16; + + return nuint16_t{uint16_t(sum & 0xFFff)}; + } + + static nuint16_t + deltaIPv6Checksum( + nuint16_t old_sum, + const uint32_t old_src_ip[4], + const uint32_t old_dst_ip[4], + const uint32_t new_src_ip[4], + const uint32_t new_dst_ip[4]) + { + /* we don't actually care in what way integers are arranged in memory + * internally */ + /* as long as uint16 pairs are swapped in correct direction, result will + * be correct (assuming there are no gaps in structure) */ + /* we represent 128bit stuff there as 4 32bit ints, that should be more or + * less correct */ + /* we could do 64bit ints too but then we couldn't reuse 32bit macros and + * that'd suck for 32bit cpus */ #define ADDN128CS(x) (ADD32CS(x[0]) + ADD32CS(x[1]) + ADD32CS(x[2]) + ADD32CS(x[3])) #define SUBN128CS(x) (SUB32CS(x[0]) + SUB32CS(x[1]) + SUB32CS(x[2]) + SUB32CS(x[3])) - uint32_t sum = uint32_t(old_sum.n) + ADDN128CS(old_src_ip) + ADDN128CS(old_dst_ip) - + SUBN128CS(new_src_ip) + SUBN128CS(new_dst_ip); + uint32_t sum = uint32_t(old_sum.n) + ADDN128CS(old_src_ip) + ADDN128CS(old_dst_ip) + + SUBN128CS(new_src_ip) + SUBN128CS(new_dst_ip); #undef ADDN128CS #undef SUBN128CS - // only need to do it 2 times to be sure - // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff - sum = (sum & 0xFFff) + (sum >> 16); - sum += sum >> 16; + // only need to do it 2 times to be sure + // proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff + sum = (sum & 0xFFff) + (sum >> 16); + sum += sum >> 16; - return nuint16_t{uint16_t(sum & 0xFFff)}; - } + return nuint16_t{uint16_t(sum & 0xFFff)}; + } #undef ADD32CS #undef SUB32CS - static void - deltaChecksumIPv4TCP( - byte_t* pld, - size_t psz, - size_t fragoff, - size_t chksumoff, - nuint32_t oSrcIP, - nuint32_t oDstIP, - nuint32_t nSrcIP, - nuint32_t nDstIP) - { - if (fragoff > chksumoff || psz < chksumoff - fragoff + 2) - return; - - auto check = (nuint16_t*)(pld + chksumoff - fragoff); - - *check = deltaIPv4Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); - // usually, TCP checksum field cannot be 0xFFff, - // because one's complement addition cannot result in 0x0000, - // and there's inversion in the end; - // emulate that. - if (check->n == 0xFFff) - check->n = 0x0000; - } - - static void - deltaChecksumIPv6TCP( - byte_t* pld, - size_t psz, - size_t fragoff, - size_t chksumoff, - const uint32_t oSrcIP[4], - const uint32_t oDstIP[4], - const uint32_t nSrcIP[4], - const uint32_t nDstIP[4]) - { - if (fragoff > chksumoff || psz < chksumoff - fragoff + 2) - return; - - auto check = (nuint16_t*)(pld + chksumoff - fragoff); - - *check = deltaIPv6Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); - // usually, TCP checksum field cannot be 0xFFff, - // because one's complement addition cannot result in 0x0000, - // and there's inversion in the end; - // emulate that. - if (check->n == 0xFFff) - check->n = 0x0000; - } + static void + deltaChecksumIPv4TCP( + byte_t* pld, + size_t psz, + size_t fragoff, + size_t chksumoff, + nuint32_t oSrcIP, + nuint32_t oDstIP, + nuint32_t nSrcIP, + nuint32_t nDstIP) + { + if (fragoff > chksumoff || psz < chksumoff - fragoff + 2) + return; + + auto check = (nuint16_t*)(pld + chksumoff - fragoff); + + *check = deltaIPv4Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); + // usually, TCP checksum field cannot be 0xFFff, + // because one's complement addition cannot result in 0x0000, + // and there's inversion in the end; + // emulate that. + if (check->n == 0xFFff) + check->n = 0x0000; + } + + static void + deltaChecksumIPv6TCP( + byte_t* pld, + size_t psz, + size_t fragoff, + size_t chksumoff, + const uint32_t oSrcIP[4], + const uint32_t oDstIP[4], + const uint32_t nSrcIP[4], + const uint32_t nDstIP[4]) + { + if (fragoff > chksumoff || psz < chksumoff - fragoff + 2) + return; + + auto check = (nuint16_t*)(pld + chksumoff - fragoff); + + *check = deltaIPv6Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); + // usually, TCP checksum field cannot be 0xFFff, + // because one's complement addition cannot result in 0x0000, + // and there's inversion in the end; + // emulate that. + if (check->n == 0xFFff) + check->n = 0x0000; + } + + static void + deltaChecksumIPv4UDP( + byte_t* pld, + size_t psz, + size_t fragoff, + nuint32_t oSrcIP, + nuint32_t oDstIP, + nuint32_t nSrcIP, + nuint32_t nDstIP) + { + if (fragoff > 6 || psz < 6 + 2) + return; + + auto check = (nuint16_t*)(pld + 6); + if (check->n == 0x0000) + return; // 0 is used to indicate "no checksum", don't change + + *check = deltaIPv4Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); + // 0 is used to indicate "no checksum" + // 0xFFff and 0 are equivalent in one's complement math + // 0xFFff + 1 = 0x10000 -> 0x0001 (same as 0 + 1) + // infact it's impossible to get 0 with such addition, + // when starting from non-0 value. + // inside deltachksum we don't invert so it's safe to skip check there + // if(check->n == 0x0000) + // check->n = 0xFFff; + } + + static void + deltaChecksumIPv6UDP( + byte_t* pld, + size_t psz, + size_t fragoff, + const uint32_t oSrcIP[4], + const uint32_t oDstIP[4], + const uint32_t nSrcIP[4], + const uint32_t nDstIP[4]) + { + if (fragoff > 6 || psz < 6 + 2) + return; + + auto check = (nuint16_t*)(pld + 6); + // 0 is used to indicate "no checksum", don't change + // even tho this shouldn't happen for IPv6, handle it properly + // we actually should drop/log 0-checksum packets per spec + // but that should be done at upper level than this function + // it's better to do correct thing there regardless + // XXX or maybe we should change this function to be able to return error? + // either way that's not a priority + if (check->n == 0x0000) + return; + + *check = deltaIPv6Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); + // 0 is used to indicate "no checksum" + // 0xFFff and 0 are equivalent in one's complement math + // 0xFFff + 1 = 0x10000 -> 0x0001 (same as 0 + 1) + // infact it's impossible to get 0 with such addition, + // when starting from non-0 value. + // inside deltachksum we don't invert so it's safe to skip check there + // if(check->n == 0x0000) + // check->n = 0xFFff; + } + + void + IPPacket::UpdateIPv4Address(nuint32_t nSrcIP, nuint32_t nDstIP) + { + llarp::LogDebug("set src=", nSrcIP, " dst=", nDstIP); - static void - deltaChecksumIPv4UDP( - byte_t* pld, - size_t psz, - size_t fragoff, - nuint32_t oSrcIP, - nuint32_t oDstIP, - nuint32_t nSrcIP, - nuint32_t nDstIP) - { - if (fragoff > 6 || psz < 6 + 2) - return; - - auto check = (nuint16_t*)(pld + 6); - if (check->n == 0x0000) - return; // 0 is used to indicate "no checksum", don't change - - *check = deltaIPv4Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); - // 0 is used to indicate "no checksum" - // 0xFFff and 0 are equivalent in one's complement math - // 0xFFff + 1 = 0x10000 -> 0x0001 (same as 0 + 1) - // infact it's impossible to get 0 with such addition, - // when starting from non-0 value. - // inside deltachksum we don't invert so it's safe to skip check there - // if(check->n == 0x0000) - // check->n = 0xFFff; - } + auto hdr = Header(); - static void - deltaChecksumIPv6UDP( - byte_t* pld, - size_t psz, - size_t fragoff, - const uint32_t oSrcIP[4], - const uint32_t oDstIP[4], - const uint32_t nSrcIP[4], - const uint32_t nDstIP[4]) - { - if (fragoff > 6 || psz < 6 + 2) - return; - - auto check = (nuint16_t*)(pld + 6); - // 0 is used to indicate "no checksum", don't change - // even tho this shouldn't happen for IPv6, handle it properly - // we actually should drop/log 0-checksum packets per spec - // but that should be done at upper level than this function - // it's better to do correct thing there regardless - // XXX or maybe we should change this function to be able to return error? - // either way that's not a priority - if (check->n == 0x0000) - return; - - *check = deltaIPv6Checksum(*check, oSrcIP, oDstIP, nSrcIP, nDstIP); - // 0 is used to indicate "no checksum" - // 0xFFff and 0 are equivalent in one's complement math - // 0xFFff + 1 = 0x10000 -> 0x0001 (same as 0 + 1) - // infact it's impossible to get 0 with such addition, - // when starting from non-0 value. - // inside deltachksum we don't invert so it's safe to skip check there - // if(check->n == 0x0000) - // check->n = 0xFFff; - } + auto oSrcIP = nuint32_t{hdr->saddr}; + auto oDstIP = nuint32_t{hdr->daddr}; - void - IPPacket::UpdateIPv4Address(nuint32_t nSrcIP, nuint32_t nDstIP) + // L4 checksum + auto ihs = size_t(hdr->ihl * 4); + if (ihs <= sz) { - llarp::LogDebug("set src=", nSrcIP, " dst=", nDstIP); - - auto hdr = Header(); + auto pld = buf + ihs; + auto psz = sz - ihs; - auto oSrcIP = nuint32_t{hdr->saddr}; - auto oDstIP = nuint32_t{hdr->daddr}; + auto fragoff = size_t((ntohs(hdr->frag_off) & 0x1Fff) * 8); - // L4 checksum - auto ihs = size_t(hdr->ihl * 4); - if (ihs <= sz) + switch (hdr->protocol) { - auto pld = buf + ihs; - auto psz = sz - ihs; - - auto fragoff = size_t((ntohs(hdr->frag_off) & 0x1Fff) * 8); - - switch (hdr->protocol) - { - case 6: // TCP - deltaChecksumIPv4TCP(pld, psz, fragoff, 16, oSrcIP, oDstIP, nSrcIP, nDstIP); - break; - case 17: // UDP - case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition - deltaChecksumIPv4UDP(pld, psz, fragoff, oSrcIP, oDstIP, nSrcIP, nDstIP); - break; - case 33: // DCCP - deltaChecksumIPv4TCP(pld, psz, fragoff, 6, oSrcIP, oDstIP, nSrcIP, nDstIP); - break; - } + case 6: // TCP + deltaChecksumIPv4TCP(pld, psz, fragoff, 16, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; + case 17: // UDP + case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition + deltaChecksumIPv4UDP(pld, psz, fragoff, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; + case 33: // DCCP + deltaChecksumIPv4TCP(pld, psz, fragoff, 6, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; } + } - // IPv4 checksum - auto v4chk = (nuint16_t*)&(hdr->check); - *v4chk = deltaIPv4Checksum(*v4chk, oSrcIP, oDstIP, nSrcIP, nDstIP); + // IPv4 checksum + auto v4chk = (nuint16_t*)&(hdr->check); + *v4chk = deltaIPv4Checksum(*v4chk, oSrcIP, oDstIP, nSrcIP, nDstIP); - // write new IP addresses - hdr->saddr = nSrcIP.n; - hdr->daddr = nDstIP.n; - } + // write new IP addresses + hdr->saddr = nSrcIP.n; + hdr->daddr = nDstIP.n; + } - void - IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional flowlabel) - { - const size_t ihs = 4 + 4 + 16 + 16; + void + IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional flowlabel) + { + const size_t ihs = 4 + 4 + 16 + 16; - // XXX should've been checked at upper level? - if (sz <= ihs) - return; + // XXX should've been checked at upper level? + if (sz <= ihs) + return; - auto hdr = HeaderV6(); - if (flowlabel.has_value()) - { - // set flow label if desired - hdr->FlowLabel(*flowlabel); - } + auto hdr = HeaderV6(); + if (flowlabel.has_value()) + { + // set flow label if desired + hdr->FlowLabel(*flowlabel); + } - const auto oldSrcIP = hdr->srcaddr; - const auto oldDstIP = hdr->dstaddr; - const uint32_t* oSrcIP = in6_uint32_ptr(oldSrcIP); - const uint32_t* oDstIP = in6_uint32_ptr(oldDstIP); + const auto oldSrcIP = hdr->srcaddr; + const auto oldDstIP = hdr->dstaddr; + const uint32_t* oSrcIP = in6_uint32_ptr(oldSrcIP); + const uint32_t* oDstIP = in6_uint32_ptr(oldDstIP); - // IPv6 address - hdr->srcaddr = HUIntToIn6(src); - hdr->dstaddr = HUIntToIn6(dst); - const uint32_t* nSrcIP = in6_uint32_ptr(hdr->srcaddr); - const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr); + // IPv6 address + hdr->srcaddr = HUIntToIn6(src); + hdr->dstaddr = HUIntToIn6(dst); + const uint32_t* nSrcIP = in6_uint32_ptr(hdr->srcaddr); + const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr); - // TODO IPv6 header options - auto pld = buf + ihs; - auto psz = sz - ihs; + // TODO IPv6 header options + auto pld = buf + ihs; + auto psz = sz - ihs; - size_t fragoff = 0; - auto nextproto = hdr->proto; - for (;;) + size_t fragoff = 0; + auto nextproto = hdr->proto; + for (;;) + { + switch (nextproto) { - switch (nextproto) + case 0: // Hop-by-Hop Options + case 43: // Routing Header + case 60: // Destination Options { - case 0: // Hop-by-Hop Options - case 43: // Routing Header - case 60: // Destination Options - { - nextproto = pld[0]; - auto addlen = (size_t(pld[1]) + 1) * 8; - if (psz < addlen) - return; - pld += addlen; - psz -= addlen; - break; - } - - case 44: // Fragment Header - /* - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Next Header | Reserved | Fragment Offset |Res|M| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Identification | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - nextproto = pld[0]; - fragoff = (uint16_t(pld[2]) << 8) | (uint16_t(pld[3]) & 0xFC); - if (psz < 8) - return; - pld += 8; - psz -= 8; - - // jump straight to payload processing - if (fragoff != 0) - goto endprotohdrs; - break; - - default: - goto endprotohdrs; + nextproto = pld[0]; + auto addlen = (size_t(pld[1]) + 1) * 8; + if (psz < addlen) + return; + pld += addlen; + psz -= addlen; + break; } - } - endprotohdrs: - switch (nextproto) - { - case 6: // TCP - deltaChecksumIPv6TCP(pld, psz, fragoff, 16, oSrcIP, oDstIP, nSrcIP, nDstIP); - break; - case 17: // UDP - case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition - deltaChecksumIPv6UDP(pld, psz, fragoff, oSrcIP, oDstIP, nSrcIP, nDstIP); - break; - case 33: // DCCP - deltaChecksumIPv6TCP(pld, psz, fragoff, 6, oSrcIP, oDstIP, nSrcIP, nDstIP); + case 44: // Fragment Header + /* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Reserved | Fragment Offset |Res|M| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identification | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + nextproto = pld[0]; + fragoff = (uint16_t(pld[2]) << 8) | (uint16_t(pld[3]) & 0xFC); + if (psz < 8) + return; + pld += 8; + psz -= 8; + + // jump straight to payload processing + if (fragoff != 0) + goto endprotohdrs; break; + + default: + goto endprotohdrs; } } + endprotohdrs: - void - IPPacket::ZeroAddresses(std::optional flowlabel) + switch (nextproto) { - if (IsV4()) - { - UpdateIPv4Address({0}, {0}); - } - else if (IsV6()) - { - UpdateIPv6Address({0}, {0}, flowlabel); - } + case 6: // TCP + deltaChecksumIPv6TCP(pld, psz, fragoff, 16, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; + case 17: // UDP + case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition + deltaChecksumIPv6UDP(pld, psz, fragoff, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; + case 33: // DCCP + deltaChecksumIPv6TCP(pld, psz, fragoff, 6, oSrcIP, oDstIP, nSrcIP, nDstIP); + break; } + } - void - IPPacket::ZeroSourceAddress(std::optional flowlabel) + void + IPPacket::ZeroAddresses(std::optional flowlabel) + { + if (IsV4()) { - if (IsV4()) - { - UpdateIPv4Address({0}, xhtonl(dstv4())); - } - else if (IsV6()) - { - UpdateIPv6Address({0}, dstv6(), flowlabel); - } + UpdateIPv4Address({0}, {0}); } - - std::optional - IPPacket::MakeICMPUnreachable() const + else if (IsV6()) { - if (IsV4()) - { - constexpr auto icmp_Header_size = 8; - constexpr auto ip_Header_size = 20; - net::IPPacket pkt{}; - auto* pkt_Header = pkt.Header(); - - pkt_Header->version = 4; - pkt_Header->ihl = 0x05; - pkt_Header->tos = 0; - pkt_Header->check = 0; - pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size); - pkt_Header->saddr = Header()->daddr; - pkt_Header->daddr = Header()->saddr; - pkt_Header->protocol = 1; // ICMP - pkt_Header->ttl = 1; - pkt_Header->frag_off = htons(0b0100000000000000); - // size pf ip header - const size_t l3_HeaderSize = Header()->ihl * 4; - // size of l4 packet to reflect back - const size_t l4_PacketSize = 8; - pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize); - - uint16_t* checksum; - uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4); - uint8_t* icmp_begin = itr; // type 'destination unreachable' - *itr++ = 3; - // code 'Destination host unknown error' - *itr++ = 7; - // checksum + unused - htobe32buf(itr, 0); - checksum = (uint16_t*)itr; - itr += 4; - // next hop mtu is ignored but let's put something here anyways just in case tm - htobe16buf(itr, 1500); - itr += 2; - // copy ip header and first 8 bytes of datagram for icmp rject - std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr); - itr += l4_PacketSize + l3_HeaderSize; - // calculate checksum of ip header - pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4); - const auto icmp_size = std::distance(icmp_begin, itr); - // calculate icmp checksum - *checksum = ipchksum(icmp_begin, icmp_size); - pkt.sz = ntohs(pkt_Header->tot_len); - return pkt; - } - return std::nullopt; + UpdateIPv6Address({0}, {0}, flowlabel); } + } - IPPacket - IPPacket::UDP( - nuint32_t srcaddr, - nuint16_t srcport, - nuint32_t dstaddr, - nuint16_t dstport, - const llarp_buffer_t& buf) + void + IPPacket::ZeroSourceAddress(std::optional flowlabel) + { + if (IsV4()) + { + UpdateIPv4Address({0}, xhtonl(dstv4())); + } + else if (IsV6()) { - net::IPPacket pkt; + UpdateIPv6Address({0}, dstv6(), flowlabel); + } + } - if (buf.sz + 28 > sizeof(pkt.buf)) - { - pkt.sz = 0; - return pkt; - } - auto* hdr = pkt.Header(); - pkt.buf[1] = 0; - hdr->version = 4; - hdr->ihl = 5; - hdr->tot_len = htons(buf.sz + 28); - hdr->protocol = 0x11; // udp - hdr->ttl = 64; - hdr->frag_off = htons(0b0100000000000000); - - hdr->saddr = srcaddr.n; - hdr->daddr = dstaddr.n; - - // make udp packet - uint8_t* ptr = pkt.buf + 20; - std::memcpy(ptr, &srcport.n, 2); - ptr += 2; - std::memcpy(ptr, &dstport.n, 2); - ptr += 2; - htobe16buf(ptr, static_cast(buf.sz + 8)); - ptr += 2; - htobe16buf(ptr, uint16_t{0}); // checksum - ptr += 2; - std::copy_n(buf.base, buf.sz, ptr); - - hdr->check = 0; - hdr->check = net::ipchksum(pkt.buf, 20); - pkt.sz = 28 + buf.sz; + std::optional + IPPacket::MakeICMPUnreachable() const + { + if (IsV4()) + { + constexpr auto icmp_Header_size = 8; + constexpr auto ip_Header_size = 20; + net::IPPacket pkt{}; + auto* pkt_Header = pkt.Header(); + + pkt_Header->version = 4; + pkt_Header->ihl = 0x05; + pkt_Header->tos = 0; + pkt_Header->check = 0; + pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size); + pkt_Header->saddr = Header()->daddr; + pkt_Header->daddr = Header()->saddr; + pkt_Header->protocol = 1; // ICMP + pkt_Header->ttl = 1; + pkt_Header->frag_off = htons(0b0100000000000000); + // size pf ip header + const size_t l3_HeaderSize = Header()->ihl * 4; + // size of l4 packet to reflect back + const size_t l4_PacketSize = 8; + pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize); + + uint16_t* checksum; + uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4); + uint8_t* icmp_begin = itr; // type 'destination unreachable' + *itr++ = 3; + // code 'Destination host unknown error' + *itr++ = 7; + // checksum + unused + oxenc::write_host_as_big(0, itr); + checksum = (uint16_t*)itr; + itr += 4; + // next hop mtu is ignored but let's put something here anyways just in case tm + oxenc::write_host_as_big(1500, itr); + itr += 2; + // copy ip header and first 8 bytes of datagram for icmp rject + std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr); + itr += l4_PacketSize + l3_HeaderSize; + // calculate checksum of ip header + pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4); + const auto icmp_size = std::distance(icmp_begin, itr); + // calculate icmp checksum + *checksum = ipchksum(icmp_begin, icmp_size); + pkt.sz = ntohs(pkt_Header->tot_len); return pkt; } + return std::nullopt; + } + + std::optional> + IPPacket::L4Data() const + { + const auto* hdr = Header(); + size_t l4_HeaderSize = 0; + if (hdr->protocol == 0x11) + { + l4_HeaderSize = 8; + } + else + return std::nullopt; + + // check for invalid size + if (sz < (hdr->ihl * 4) + l4_HeaderSize) + return std::nullopt; - } // namespace net -} // namespace llarp + const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize); + return std::make_pair(reinterpret_cast(ptr), std::distance(ptr, buf + sz)); + } + + IPPacket + IPPacket::UDP( + nuint32_t srcaddr, + nuint16_t srcport, + nuint32_t dstaddr, + nuint16_t dstport, + const llarp_buffer_t& buf) + { + net::IPPacket pkt; + + if (buf.sz + 28 > sizeof(pkt.buf)) + { + pkt.sz = 0; + return pkt; + } + auto* hdr = pkt.Header(); + pkt.buf[1] = 0; + hdr->version = 4; + hdr->ihl = 5; + hdr->tot_len = htons(buf.sz + 28); + hdr->protocol = 0x11; // udp + hdr->ttl = 64; + hdr->frag_off = htons(0b0100000000000000); + + hdr->saddr = srcaddr.n; + hdr->daddr = dstaddr.n; + + // make udp packet + uint8_t* ptr = pkt.buf + 20; + std::memcpy(ptr, &srcport.n, 2); + ptr += 2; + std::memcpy(ptr, &dstport.n, 2); + ptr += 2; + oxenc::write_host_as_big(static_cast(buf.sz + 8), ptr); + ptr += 2; + oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum + ptr += 2; + std::copy_n(buf.base, buf.sz, ptr); + + hdr->check = 0; + hdr->check = net::ipchksum(pkt.buf, 20); + pkt.sz = 28 + buf.sz; + return pkt; + } + +} // namespace llarp::net diff --git a/llarp/net/ip_packet.hpp b/llarp/net/ip_packet.hpp index 9f4b91fb6..a9db47904 100644 --- a/llarp/net/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -4,317 +4,262 @@ #include "net.hpp" #include #include - -#ifndef _WIN32 -// unix, linux -#include // FreeBSD needs this for uchar for ip.h -#include -#include -// anything not win32 -struct ip_header -{ -#ifdef __LITTLE_ENDIAN__ - unsigned int ihl : 4; - unsigned int version : 4; -#elif defined(__BIG_ENDIAN__) - unsigned int version : 4; - unsigned int ihl : 4; -#else -#error "Please fix " -#endif - -#if defined(__linux__) -#define ip_version version -#endif - uint8_t tos; - uint16_t tot_len; - uint16_t id; - uint16_t frag_off; - uint8_t ttl; - uint8_t protocol; - uint16_t check; - uint32_t saddr; - uint32_t daddr; -}; -#else -// windows nt -#include -typedef struct ip_hdr -{ - unsigned char ip_header_len : 4; // 4-bit header length (in 32-bit words) normally=5 - // (Means 20 Bytes may be 24 also) - unsigned char version : 4; // 4-bit IPv4 version - unsigned char ip_tos; // IP type of service - unsigned short ip_total_length; // Total length - unsigned short ip_id; // Unique identifier - - unsigned char ip_frag_offset : 5; // Fragment offset field - - unsigned char ip_more_fragment : 1; - unsigned char ip_dont_fragment : 1; - unsigned char ip_reserved_zero : 1; - - unsigned char ip_frag_offset1; // fragment offset - - unsigned char ip_ttl; // Time to live - unsigned char ip_protocol; // Protocol(TCP,UDP etc) - unsigned short ip_checksum; // IP checksum - unsigned int ip_srcaddr; // Source address - unsigned int ip_destaddr; // Source address -} IPV4_HDR; -#define ip_header IPV4_HDR -#define saddr ip_srcaddr -#define daddr ip_destaddr -#define check ip_checksum -#define ihl ip_header_len -#define protocol ip_protocol -#define frag_off ip_frag_offset -#define tos ip_tos -#define ttl ip_ttl -#define tot_len ip_total_length -#endif - -struct ipv6_header_preamble -{ - unsigned char version : 4; - unsigned char pad_small : 4; - uint8_t pad[3]; -}; - -struct ipv6_header -{ - union - { - ipv6_header_preamble preamble; - uint32_t flowlabel; - } preamble; - - uint16_t payload_len; - uint8_t proto; - uint8_t hoplimit; - in6_addr srcaddr; - in6_addr dstaddr; - - /// get 20 bit truncated flow label in network order - llarp::nuint32_t - FlowLabel() const; - - /// put 20 bit truncated flow label network order - void - FlowLabel(llarp::nuint32_t flowlabel); -}; - #include #include #include -namespace llarp +namespace llarp::net { - namespace net + template + struct ip_header_le { - /// "well known" ip protocols - /// TODO: extend this to non "well known values" - enum class IPProtocol : uint8_t - { - ICMP = 0x01, - IGMP = 0x02, - IPIP = 0x04, - TCP = 0x06, - UDP = 0x11, - GRE = 0x2F, - ICMP6 = 0x3A, - OSFP = 0x59, - PGM = 0x71, - }; - - /// get string representation of this protocol - /// throws std::invalid_argument if we don't know the name of this ip protocol - std::string - IPProtocolName(IPProtocol proto); + unsigned int ihl : 4; + unsigned int version : 4; + uint8_t tos; + uint16_t tot_len; + uint16_t id; + uint16_t frag_off; + uint8_t ttl; + uint8_t protocol; + uint16_t check; + uint32_t saddr; + uint32_t daddr; + }; + + template <> + struct ip_header_le + { + unsigned int version : 4; + unsigned int ihl : 4; + uint8_t tos; + uint16_t tot_len; + uint16_t id; + uint16_t frag_off; + uint8_t ttl; + uint8_t protocol; + uint16_t check; + uint32_t saddr; + uint32_t daddr; + }; + + using ip_header = ip_header_le; + + template + struct ipv6_header_preamble_le + { + unsigned char pad_small : 4; + unsigned char version : 4; + uint8_t pad[3]; + }; - /// parse a string to an ip protocol - /// throws std::invalid_argument if cannot be parsed - IPProtocol - ParseIPProtocol(std::string data); + template <> + struct ipv6_header_preamble_le + { + unsigned char version : 4; + unsigned char pad_small : 4; + uint8_t pad[3]; + }; - /// an Packet - struct IPPacket + struct ipv6_header + { + union { - static constexpr size_t MaxSize = 1500; - llarp_time_t timestamp; - size_t sz; - byte_t buf[MaxSize]; + ipv6_header_preamble_le preamble; + uint32_t flowlabel; + } preamble; + + uint16_t payload_len; + uint8_t proto; + uint8_t hoplimit; + in6_addr srcaddr; + in6_addr dstaddr; + llarp::nuint32_t + FlowLabel() const; + + /// put 20 bit truncated flow label network order + void + FlowLabel(llarp::nuint32_t label); + }; + + /// "well known" ip protocols + /// TODO: extend this to non "well known values" + enum class IPProtocol : uint8_t + { + ICMP = 0x01, + IGMP = 0x02, + IPIP = 0x04, + TCP = 0x06, + UDP = 0x11, + GRE = 0x2F, + ICMP6 = 0x3A, + OSFP = 0x59, + PGM = 0x71, + }; + + /// get string representation of this protocol + /// throws std::invalid_argument if we don't know the name of this ip protocol + std::string + IPProtocolName(IPProtocol proto); + + /// parse a string to an ip protocol + /// throws std::invalid_argument if cannot be parsed + IPProtocol + ParseIPProtocol(std::string data); + + /// an Packet + struct IPPacket + { + static constexpr size_t MaxSize = 1500; + llarp_time_t timestamp; + size_t sz; - static IPPacket - UDP(nuint32_t srcaddr, - nuint16_t srcport, - nuint32_t dstaddr, - nuint16_t dstport, - const llarp_buffer_t& data); + alignas(ip_header) byte_t buf[MaxSize]; - ManagedBuffer - Buffer(); + static IPPacket + UDP(nuint32_t srcaddr, + nuint16_t srcport, + nuint32_t dstaddr, + nuint16_t dstport, + const llarp_buffer_t& data); - ManagedBuffer - ConstBuffer() const; + ManagedBuffer + Buffer(); - bool - Load(const llarp_buffer_t& buf); + ManagedBuffer + ConstBuffer() const; - struct GetTime - { - llarp_time_t - operator()(const IPPacket& pkt) const - { - return pkt.timestamp; - } - }; - - struct PutTime - { - EventLoop_ptr loop; - PutTime(EventLoop_ptr evloop) : loop(std::move(evloop)) - {} - void - operator()(IPPacket& pkt) const - { - pkt.timestamp = loop->time_now(); - } - }; - - struct GetNow - { - EventLoop_ptr loop; - GetNow(EventLoop_ptr evloop) : loop(std::move(evloop)) - {} - llarp_time_t - operator()() const - { - return loop->time_now(); - } - }; - - struct CompareSize - { - bool - operator()(const IPPacket& left, const IPPacket& right) - { - return left.sz < right.sz; - } - }; - - struct CompareOrder - { - bool - operator()(const IPPacket& left, const IPPacket& right) - { - return left.timestamp < right.timestamp; - } - }; - - inline ip_header* - Header() - { - return (ip_header*)&buf[0]; - } + bool + Load(const llarp_buffer_t& buf); - inline const ip_header* - Header() const + struct CompareSize + { + bool + operator()(const IPPacket& left, const IPPacket& right) { - return (ip_header*)&buf[0]; + return left.sz < right.sz; } + }; - inline ipv6_header* - HeaderV6() + struct CompareOrder + { + bool + operator()(const IPPacket& left, const IPPacket& right) { - return (ipv6_header*)&buf[0]; + return left.timestamp < right.timestamp; } + }; - inline const ipv6_header* - HeaderV6() const - { - return (ipv6_header*)&buf[0]; - } + inline ip_header* + Header() + { + return reinterpret_cast(&buf[0]); + } - inline int - Version() const - { - return Header()->version; - } + inline const ip_header* + Header() const + { + return reinterpret_cast(&buf[0]); + } - inline bool - IsV4() const - { - return Version() == 4; - } + inline ipv6_header* + HeaderV6() + { + return reinterpret_cast(&buf[0]); + } - inline bool - IsV6() const - { - return Version() == 6; - } + inline const ipv6_header* + HeaderV6() const + { + return reinterpret_cast(&buf[0]); + } - inline service::ProtocolType - ServiceProtocol() const - { - if (IsV4()) - return service::ProtocolType::TrafficV4; - if (IsV6()) - return service::ProtocolType::TrafficV6; + inline int + Version() const + { + return Header()->version; + } - return service::ProtocolType::Control; - } + inline bool + IsV4() const + { + return Version() == 4; + } + + inline bool + IsV6() const + { + return Version() == 6; + } - huint128_t - srcv6() const; + inline service::ProtocolType + ServiceProtocol() const + { + if (IsV4()) + return service::ProtocolType::TrafficV4; + if (IsV6()) + return service::ProtocolType::TrafficV6; - huint128_t - dstv6() const; + return service::ProtocolType::Control; + } - huint32_t - srcv4() const; + huint128_t + srcv6() const; - huint32_t - dstv4() const; + huint128_t + dstv6() const; - huint128_t - src4to6() const; + huint32_t + srcv4() const; - huint128_t - dst4to6() const; + huint32_t + dstv4() const; - huint128_t - src4to6Lan() const; + huint128_t + src4to6() const; - huint128_t - dst4to6Lan() const; + huint128_t + dst4to6() const; - /// get destination port if applicable - std::optional - DstPort() const; + huint128_t + src4to6Lan() const; - void - UpdateIPv4Address(nuint32_t src, nuint32_t dst); + huint128_t + dst4to6Lan() const; - void - UpdateIPv6Address( - huint128_t src, huint128_t dst, std::optional flowlabel = std::nullopt); + /// get destination port if applicable + std::optional + DstPort() const; - /// set addresses to zero and recacluate checksums - void - ZeroAddresses(std::optional flowlabel = std::nullopt); + /// get source port if applicable + std::optional + SrcPort() const; - /// zero out source address - void - ZeroSourceAddress(std::optional flowlabel = std::nullopt); + /// get pointer and size of layer 4 data + std::optional> + L4Data() const; - /// make an icmp unreachable reply packet based of this ip packet - std::optional - MakeICMPUnreachable() const; - }; + void + UpdateIPv4Address(nuint32_t src, nuint32_t dst); + + void + UpdateIPv6Address( + huint128_t src, huint128_t dst, std::optional flowlabel = std::nullopt); + + /// set addresses to zero and recacluate checksums + void + ZeroAddresses(std::optional flowlabel = std::nullopt); + + /// zero out source address + void + ZeroSourceAddress(std::optional flowlabel = std::nullopt); + + /// make an icmp unreachable reply packet based of this ip packet + std::optional + MakeICMPUnreachable() const; + }; + + /// generate ip checksum + uint16_t + ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0); - /// generate ip checksum - uint16_t - ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0); - } // namespace net -} // namespace llarp +} // namespace llarp::net diff --git a/llarp/net/ip_range.cpp b/llarp/net/ip_range.cpp index 4b83e9d02..08d234584 100644 --- a/llarp/net/ip_range.cpp +++ b/llarp/net/ip_range.cpp @@ -1,6 +1,6 @@ #include "ip_range.hpp" -#include "oxenmq/bt_serialize.h" +#include "oxenc/bt_serialize.h" #include "llarp/util/bencode.h" @@ -9,7 +9,7 @@ namespace llarp bool IPRange::BEncode(llarp_buffer_t* buf) const { - const auto str = oxenmq::bt_serialize(ToString()); + const auto str = oxenc::bt_serialize(ToString()); return buf->write(str.begin(), str.end()); } @@ -24,7 +24,7 @@ namespace llarp std::string str; try { - oxenmq::bt_deserialize(data, str); + oxenc::bt_deserialize(data, str); } catch (std::exception&) { diff --git a/llarp/net/ip_range_map.hpp b/llarp/net/ip_range_map.hpp index a5644e78c..bf659ae78 100644 --- a/llarp/net/ip_range_map.hpp +++ b/llarp/net/ip_range_map.hpp @@ -10,8 +10,8 @@ namespace llarp { /// a container that maps an ip range to a value that allows you to lookup /// key by range hit - /// TODO: do some kind of magic shit to ensure near constant time for - /// lookups + /// + /// in the future we should do some kind of magic shit to ensure near constant time for lookups template struct IPRangeMap { diff --git a/llarp/net/net.cpp b/llarp/net/net.cpp index fe2b883c1..0c73e3167 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/net.cpp @@ -576,29 +576,60 @@ namespace llarp return addr.asIPv6(); } - bool - AllInterfaces(int af, SockAddr& result) + namespace net { - if (af == AF_INET) + namespace { - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - result = SockAddr{addr}; - return true; - } - if (af == AF_INET6) + SockAddr + All(int af) + { + if (af == AF_INET) + { + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + return SockAddr{addr}; + } + if (af == AF_INET6) + { + sockaddr_in6 addr6{}; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(0); + addr6.sin6_addr = IN6ADDR_ANY_INIT; + return SockAddr{addr6}; + } + throw llarp::make_exception(af, " is not a valid address family"); + } + } // namespace + + std::optional + AllInterfaces(SockAddr pub) { - sockaddr_in6 addr6; - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(0); - addr6.sin6_addr = IN6ADDR_ANY_INIT; - result = SockAddr{addr6}; - return true; + std::optional found; + IterAllNetworkInterfaces([pub, &found](auto* ifa) { + if (found) + return; + if (auto ifa_addr = ifa->ifa_addr) + { + if (ifa_addr->sa_family != pub.Family()) + return; + + SockAddr addr{*ifa->ifa_addr}; + + if (addr == pub) + found = addr; + } + }); + + // 0.0.0.0 is used in our compat shim as our public ip so we check for that special case + const auto zero = IPRange::FromIPv4(0, 0, 0, 0, 8); + // when we cannot find an address but we are looking for 0.0.0.0 just default to the old style + if (not found and (pub.isIPv4() and zero.Contains(pub.asIPv4()))) + found = All(pub.Family()); + return found; } - return false; - } + } // namespace net #if !defined(TESTNET) static constexpr std::array bogonRanges_v6 = { @@ -692,5 +723,20 @@ namespace llarp return false; } #endif + bool + HasInterfaceAddress(std::variant ip) + { + bool found{false}; + IterAllNetworkInterfaces([ip, &found](const auto* iface) { + if (found or iface == nullptr) + return; + if (auto addr = iface->ifa_addr; + addr and (addr->sa_family == AF_INET or addr->sa_family == AF_INET6)) + { + found = SockAddr{*iface->ifa_addr}.getIP() == ip; + } + }); + return found; + } } // namespace llarp diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index 95141f3a9..7c40617bc 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -52,6 +52,12 @@ namespace llarp bool IsIPv4Bogon(const huint32_t& addr); + inline bool + IsIPv4Bogon(const nuint32_t& addr) + { + return IsIPv4Bogon(ToHost(addr)); + } + bool IsBogon(const in6_addr& addr); @@ -61,8 +67,25 @@ namespace llarp bool IsBogonRange(const in6_addr& host, const in6_addr& mask); - bool - AllInterfaces(int af, SockAddr& addr); + /// get a sock addr we can use for all interfaces given our public address + namespace net + { + std::optional + AllInterfaces(SockAddr pubaddr); + } + + /// compat shim + // TODO: remove me + inline bool + AllInterfaces(int af, SockAddr& addr) + { + if (auto maybe = net::AllInterfaces(SockAddr{af == AF_INET ? "0.0.0.0" : "::"})) + { + addr = *maybe; + return true; + } + return false; + } /// get first network interface with public address bool @@ -92,4 +115,8 @@ namespace llarp } #endif + /// return true if we have a network interface with this ip + bool + HasInterfaceAddress(std::variant ip); + } // namespace llarp diff --git a/llarp/net/net_int.cpp b/llarp/net/net_int.cpp index 973d6ed4e..566e11b70 100644 --- a/llarp/net/net_int.cpp +++ b/llarp/net/net_int.cpp @@ -2,6 +2,8 @@ #include "ip.hpp" #include +#include + namespace llarp { huint16_t @@ -46,7 +48,7 @@ namespace llarp { c.resize(16); std::fill(c.begin(), c.end(), 0); - htobe32buf(c.data() + 12, h); + oxenc::write_host_as_big(h, c.data() + 12); c[11] = 0xff; c[10] = 0xff; } diff --git a/llarp/net/net_int.hpp b/llarp/net/net_int.hpp index 616e912d8..b5ec0e7cb 100644 --- a/llarp/net/net_int.hpp +++ b/llarp/net/net_int.hpp @@ -15,7 +15,6 @@ #include // for itoa #include -#include #include #include "uint128.hpp" diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index a09a88527..dbb1734a2 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -351,6 +351,14 @@ namespace llarp return a; } + std::variant + SockAddr::getIP() const + { + if (isIPv4()) + return getIPv4(); + return getIPv6(); + } + void SockAddr::setIPv4(nuint32_t ip) { diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 2e18451a2..a51379b13 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -7,16 +7,12 @@ #include #include #include -extern "C" const char* -inet_ntop(int af, const void* src, char* dst, size_t size); -extern "C" int -inet_pton(int af, const char* src, void* dst); -#define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif #include #include #include "net_int.hpp" +#include namespace llarp { @@ -86,6 +82,14 @@ namespace llarp std::string hostString() const; + inline int + Family() const + { + if (isIPv6()) + return AF_INET6; + return AF_INET; + } + /// Returns true if this is an empty SockAddr, defined by having no IP address set. An empty IP /// address with a valid port is still considered empty. /// @@ -138,6 +142,8 @@ namespace llarp getIPv6() const; nuint32_t getIPv4() const; + std::variant + getIP() const; /// in host order huint128_t diff --git a/llarp/net/uint128.hpp b/llarp/net/uint128.hpp index aa1c96d08..8340000b2 100644 --- a/llarp/net/uint128.hpp +++ b/llarp/net/uint128.hpp @@ -5,7 +5,8 @@ #include #include "../util/meta/traits.hpp" -#include "../util/endian.hpp" + +#include namespace llarp { @@ -309,9 +310,9 @@ ntoh128(llarp::uint128_t i) #ifdef __BIG_ENDIAN__ return i; #else - const auto loSwapped = htobe64(i.lower); - const auto hiSwapped = htobe64(i.upper); - return {loSwapped, hiSwapped}; + const auto loSwapped = oxenc::big_to_host(i.lower); + const auto hiSwapped = oxenc::big_to_host(i.upper); + return {/*upper=*/loSwapped, /*lower=*/hiSwapped}; #endif } diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index 671ad2fc3..22feb80f3 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -93,7 +93,7 @@ namespace llarp fs::path NodeDB::GetPathForPubkey(RouterID pubkey) const { - std::string hexString = oxenmq::to_hex(pubkey.begin(), pubkey.end()); + std::string hexString = oxenc::to_hex(pubkey.begin(), pubkey.end()); std::string skiplistDir; const llarp::RouterID r{pubkey}; diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index d0b5ea72f..1e0540f0f 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -14,9 +14,10 @@ #include #include #include -#include #include +#include + #include #include @@ -472,6 +473,11 @@ namespace llarp EnterState(ePathTimeout, now); } } + if (_status == ePathIgnore and now - m_LastRecvMessage >= path::alive_timeout) + { + // clean up this path as we dont use it anymore + EnterState(ePathExpired, now); + } } void @@ -640,7 +646,7 @@ namespace llarp llarp_buffer_t buf(tmp); // should help prevent bad paths with uninitialized members // FIXME: Why would we get uninitialized IMessages? - if (msg.version != LLARP_PROTO_VERSION) + if (msg.version != llarp::constants::proto_version) return false; if (!msg.BEncode(&buf)) { @@ -889,7 +895,7 @@ namespace llarp { if (pkt.size() <= 8) return false; - uint64_t counter = bufbe64toh(pkt.data()); + auto counter = oxenc::load_big_to_host(pkt.data()); if (m_ExitTrafficHandler( self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol)) { diff --git a/llarp/path/path_context.cpp b/llarp/path/path_context.cpp index 028104696..f856ec3e8 100644 --- a/llarp/path/path_context.cpp +++ b/llarp/path/path_context.cpp @@ -319,10 +319,24 @@ namespace llarp PathContext::CurrentTransitPaths() { SyncTransitMap_t::Lock_t lock(m_TransitPaths.first); - auto& map = m_TransitPaths.second; + const auto& map = m_TransitPaths.second; return map.size() / 2; } + uint64_t + PathContext::CurrentOwnedPaths(path::PathStatus st) + { + uint64_t num{}; + util::Lock lock{m_OurPaths.first}; + auto& map = m_OurPaths.second; + for (auto itr = map.begin(); itr != map.end(); ++itr) + { + if (itr->second->Status() == st) + num++; + } + return num / 2; + } + void PathContext::PutTransitHop(std::shared_ptr hop) { diff --git a/llarp/path/path_context.hpp b/llarp/path/path_context.hpp index b53e11a06..af5b9e175 100644 --- a/llarp/path/path_context.hpp +++ b/llarp/path/path_context.hpp @@ -173,6 +173,10 @@ namespace llarp uint64_t CurrentTransitPaths(); + /// current number of paths we created in status + uint64_t + CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ePathEstablished); + private: AbstractRouter* m_Router; SyncTransitMap_t m_TransitPaths; diff --git a/llarp/path/pathbuilder.cpp b/llarp/path/pathbuilder.cpp index 7800a12d4..c401965aa 100644 --- a/llarp/path/pathbuilder.cpp +++ b/llarp/path/pathbuilder.cpp @@ -68,7 +68,7 @@ namespace llarp } // build record record.lifetime = path::default_lifetime; - record.version = LLARP_PROTO_VERSION; + record.version = llarp::constants::proto_version; record.txid = hop.txID; record.rxid = hop.rxID; record.tunnelNonce = hop.nonce; @@ -191,10 +191,11 @@ namespace llarp lastBuild = 0s; } - void Builder::Tick(llarp_time_t) + void + Builder::Tick(llarp_time_t now) { - const auto now = llarp::time_now_ms(); - + PathSet::Tick(now); + now = llarp::time_now_ms(); m_router->pathBuildLimiter().Decay(now); ExpirePaths(now, m_router); diff --git a/llarp/path/pathset.cpp b/llarp/path/pathset.cpp index 75cecff39..50a40ef75 100644 --- a/llarp/path/pathset.cpp +++ b/llarp/path/pathset.cpp @@ -66,13 +66,31 @@ namespace llarp PathSet::TickPaths(AbstractRouter* r) { const auto now = llarp::time_now_ms(); - Lock_t l(m_PathsMutex); + Lock_t l{m_PathsMutex}; for (auto& item : m_Paths) { item.second->Tick(now, r); } } + void PathSet::Tick(llarp_time_t) + { + std::unordered_set endpoints; + for (auto& item : m_Paths) + { + endpoints.emplace(item.second->Endpoint()); + } + + m_PathCache.clear(); + for (const auto& ep : endpoints) + { + if (auto path = GetPathByRouter(ep)) + { + m_PathCache[ep] = path->weak_from_this(); + } + } + } + void PathSet::ExpirePaths(llarp_time_t now, AbstractRouter* router) { @@ -150,6 +168,13 @@ namespace llarp { Lock_t l(m_PathsMutex); Path_ptr chosen = nullptr; + if (roles == ePathRoleAny) + { + if (auto itr = m_PathCache.find(id); itr != m_PathCache.end()) + { + return itr->second.lock(); + } + } auto itr = m_Paths.begin(); while (itr != m_Paths.end()) { diff --git a/llarp/path/pathset.hpp b/llarp/path/pathset.hpp index 8457441b2..5bef0a643 100644 --- a/llarp/path/pathset.hpp +++ b/llarp/path/pathset.hpp @@ -130,7 +130,7 @@ namespace llarp /// tick owned paths virtual void - Tick(llarp_time_t now) = 0; + Tick(llarp_time_t now); /// count the number of paths that will exist at this timestamp in future size_t @@ -320,6 +320,9 @@ namespace llarp using PathMap_t = std::unordered_map, Path_ptr>; mutable Mtx_t m_PathsMutex; PathMap_t m_Paths; + + private: + std::unordered_map> m_PathCache; }; } // namespace path diff --git a/llarp/path/transit_hop.cpp b/llarp/path/transit_hop.cpp index f2cd2d1aa..e52251996 100644 --- a/llarp/path/transit_hop.cpp +++ b/llarp/path/transit_hop.cpp @@ -14,7 +14,8 @@ #include #include #include -#include + +#include namespace llarp { @@ -396,7 +397,7 @@ namespace llarp // check short packet buffer if (pkt.size() <= 8) continue; - uint64_t counter = bufbe64toh(pkt.data()); + auto counter = oxenc::load_big_to_host(pkt.data()); sent &= endpoint->QueueOutboundTraffic( info.rxID, ManagedBuffer(llarp_buffer_t(pkt.data() + 8, pkt.size() - 8)), diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index e66918b57..89b0129cc 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -1,7 +1,7 @@ #include "types.hpp" #include -#include +#include #include namespace llarp @@ -103,7 +103,7 @@ namespace llarp { if (not buf) throw std::runtime_error("PeerStats: Can't use null buf"); - const oxenmq::bt_dict data = { + const oxenc::bt_dict data = { {NumConnectionAttemptsKey, numConnectionAttempts}, {NumConnectionSuccessesKey, numConnectionSuccesses}, {NumConnectionRejectionsKey, numConnectionRejections}, @@ -120,7 +120,7 @@ namespace llarp {LeastRCRemainingLifetimeKey, leastRCRemainingLifetime.count()}, {LastRCUpdatedKey, lastRCUpdated.count()}, }; - const auto serialized = oxenmq::bt_serialize(data); + const auto serialized = oxenc::bt_serialize(data); if (not buf->write(serialized.begin(), serialized.end())) throw std::runtime_error("PeerStats: buffer too small"); } diff --git a/llarp/pow.hpp b/llarp/pow.hpp index c1e947c1e..be075ca6c 100644 --- a/llarp/pow.hpp +++ b/llarp/pow.hpp @@ -12,7 +12,7 @@ namespace llarp llarp_time_t timestamp = 0s; llarp_time_t extendedLifetime = 0s; AlignedBuffer<32> nonce; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; ~PoW(); diff --git a/llarp/profiling.hpp b/llarp/profiling.hpp index 03372d3fb..df26b6bcb 100644 --- a/llarp/profiling.hpp +++ b/llarp/profiling.hpp @@ -20,7 +20,7 @@ namespace llarp uint64_t pathTimeoutCount = 0; llarp_time_t lastUpdated = 0s; llarp_time_t lastDecay = 0s; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; bool BEncode(llarp_buffer_t* buf) const; diff --git a/llarp/quic/address.hpp b/llarp/quic/address.hpp index 5f7bc3522..a6b56a243 100644 --- a/llarp/quic/address.hpp +++ b/llarp/quic/address.hpp @@ -21,7 +21,7 @@ namespace llarp::quic class Address { sockaddr_in6 saddr{}; - ngtcp2_addr a{sizeof(saddr), reinterpret_cast(&saddr)}; + ngtcp2_addr a{reinterpret_cast(&saddr), sizeof(saddr)}; public: Address() = default; @@ -102,7 +102,7 @@ namespace llarp::quic Address local_, remote_; public: - ngtcp2_path path{{local_.sockaddr_size(), local_}, {remote_.sockaddr_size(), remote_}, nullptr}; + ngtcp2_path path{{local_, local_.sockaddr_size()}, {remote_, remote_.sockaddr_size()}, nullptr}; // Public accessors are const: const Address& local = local_; diff --git a/llarp/quic/client.cpp b/llarp/quic/client.cpp index dd01e4dc0..a2a081edf 100644 --- a/llarp/quic/client.cpp +++ b/llarp/quic/client.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/llarp/quic/connection.cpp b/llarp/quic/connection.cpp index 778908385..fed88401e 100644 --- a/llarp/quic/connection.cpp +++ b/llarp/quic/connection.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include extern "C" { @@ -35,7 +35,7 @@ namespace llarp::quic std::ostream& operator<<(std::ostream& o, const ConnectionID& c) { - return o << oxenmq::to_hex(c.data, c.data + c.datalen); + return o << oxenc::to_hex(c.data, c.data + c.datalen); } ConnectionID @@ -297,7 +297,7 @@ namespace llarp::quic conn.endpoint.make_stateless_reset_token(cid, token); LogDebug( "make stateless reset token ", - oxenmq::to_hex(token, token + NGTCP2_STATELESS_RESET_TOKENLEN)); + oxenc::to_hex(token, token + NGTCP2_STATELESS_RESET_TOKENLEN)); return 0; } @@ -1093,7 +1093,7 @@ namespace llarp::quic uint16_t port; try { - oxenmq::bt_dict_consumer meta{lokinet_metadata}; + oxenc::bt_dict_consumer meta{lokinet_metadata}; // '#' contains the port the client wants us to forward to if (!meta.skip_until("#")) { @@ -1108,7 +1108,7 @@ namespace llarp::quic } LogDebug("decoded lokinet tunnel port = ", port); } - catch (const oxenmq::bt_deserialize_invalid& c) + catch (const oxenc::bt_deserialize_invalid& c) { LogWarn("transport params lokinet metadata is invalid: ", c.what()); return NGTCP2_ERR_TRANSPORT_PARAM; @@ -1191,7 +1191,7 @@ namespace llarp::quic // reserved field code that QUIC parsers must ignore); currently we only include the port in // here (from the client to tell the server what it's trying to reach, and reflected from // the server for the client to verify). - std::string lokinet_metadata = bt_serialize(oxenmq::bt_dict{ + std::string lokinet_metadata = bt_serialize(oxenc::bt_dict{ {"#", tunnel_port}, }); copy_and_advance(buf, lokinet_metadata_code); diff --git a/llarp/quic/endpoint.cpp b/llarp/quic/endpoint.cpp index 5d57e9010..037011883 100644 --- a/llarp/quic/endpoint.cpp +++ b/llarp/quic/endpoint.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include extern "C" { @@ -225,7 +225,8 @@ namespace llarp::quic } void - Endpoint::close_connection(Connection& conn, uint64_t code, bool application) + Endpoint::close_connection( + Connection& conn, uint64_t code, bool application, std::string_view close_reason) { LogDebug("Closing connection ", conn.base_cid); if (!conn.closing) @@ -245,6 +246,8 @@ namespace llarp::quic u8data(conn.conn_buffer), conn.conn_buffer.size(), code, + reinterpret_cast(close_reason.data()), + close_reason.size(), get_timestamp()); if (written <= 0) { diff --git a/llarp/quic/endpoint.hpp b/llarp/quic/endpoint.hpp index cfbf92c54..f49460593 100644 --- a/llarp/quic/endpoint.hpp +++ b/llarp/quic/endpoint.hpp @@ -200,10 +200,16 @@ namespace llarp::quic // // Takes the iterator to the connection pair from `conns` and optional error parameters: if // `application` is false (the default) then we do a hard connection close because of transport - // error, if true we do a graceful application close. For application closes the code is - // application-defined; for hard closes the code should be one of the NGTCP2_*_ERROR values. + // error, if true we do a graceful application close. `close_reason` can be provided for + // propagating reason for close to remote, defaults to empty string. For application closes the + // code is application-defined; for hard closes the code should be one of the NGTCP2_*_ERROR + // values. void - close_connection(Connection& conn, uint64_t code = NGTCP2_NO_ERROR, bool application = false); + close_connection( + Connection& conn, + uint64_t code = NGTCP2_NO_ERROR, + bool application = false, + std::string_view close_reason = ""sv); /// Puts a connection into draining mode (i.e. after getting a connection close). This will /// keep the connection registered for the recommended 3*Probe Timeout, during which we drop diff --git a/llarp/quic/server.cpp b/llarp/quic/server.cpp index 369426699..690a97fac 100644 --- a/llarp/quic/server.cpp +++ b/llarp/quic/server.cpp @@ -2,8 +2,7 @@ #include #include -#include -#include +#include #include #include diff --git a/llarp/quic/stream.hpp b/llarp/quic/stream.hpp index 20caa51c3..176a2ab84 100644 --- a/llarp/quic/stream.hpp +++ b/llarp/quic/stream.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/llarp/quic/tunnel.cpp b/llarp/quic/tunnel.cpp index fa5e22029..6954e20f4 100644 --- a/llarp/quic/tunnel.cpp +++ b/llarp/quic/tunnel.cpp @@ -170,7 +170,7 @@ namespace llarp::quic { LogWarn( "Remote connection returned invalid initial byte (0x", - oxenmq::to_hex(bdata.begin(), bdata.begin() + 1), + oxenc::to_hex(bdata.begin(), bdata.begin() + 1), "); dropping connection"); stream.close(tunnel::ERROR_BAD_INIT); client.close(); diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 3d0d1e747..a0e761b78 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -220,6 +220,10 @@ namespace llarp virtual const byte_t* pubkey() const = 0; + /// get what our real public ip is if we can know it + virtual std::optional> + OurPublicIP() const = 0; + /// connect to N random routers virtual void ConnectToRandomRouters(int N) = 0; diff --git a/llarp/router/i_gossiper.hpp b/llarp/router/i_gossiper.hpp index 86954930f..c0d6fd51f 100644 --- a/llarp/router/i_gossiper.hpp +++ b/llarp/router/i_gossiper.hpp @@ -1,5 +1,7 @@ #pragma once #include +#include +#include namespace llarp { @@ -15,7 +17,7 @@ namespace llarp virtual bool GossipRC(const RouterContact& rc) = 0; - using Time_t = std::chrono::milliseconds; + using Time_t = Duration_t; virtual void Decay(Time_t now) = 0; @@ -27,5 +29,17 @@ namespace llarp /// return true if that rc is owned by us virtual bool IsOurRC(const RouterContact& rc) const = 0; + + /// forget the replay filter entry given pubkey + virtual void + Forget(const RouterID& router) = 0; + + /// returns the time point when we will send our next gossip at + virtual TimePoint_t + NextGossipAt() const = 0; + + /// returns the time point when we sent our last gossip at or nullopt if we never did + virtual std::optional + LastGossipAt() const = 0; }; } // namespace llarp diff --git a/llarp/router/outbound_message_handler.cpp b/llarp/router/outbound_message_handler.cpp index 2a8639d09..1abbb8c05 100644 --- a/llarp/router/outbound_message_handler.cpp +++ b/llarp/router/outbound_message_handler.cpp @@ -30,25 +30,28 @@ namespace llarp DoCallback(callback, SendStatus::InvalidRouter); return true; } - const uint16_t priority = msg.Priority(); + MessageQueueEntry ent; + ent.router = remote; + ent.inform = std::move(callback); + ent.pathid = msg.pathid; + ent.priority = msg.Priority(); + std::array linkmsg_buffer; - llarp_buffer_t buf(linkmsg_buffer); + llarp_buffer_t buf{linkmsg_buffer}; if (!EncodeBuffer(msg, buf)) { return false; } - Message message; - message.first.resize(buf.sz); - message.second = callback; + ent.message.resize(buf.sz); - std::copy_n(buf.base, buf.sz, message.first.data()); + std::copy_n(buf.base, buf.sz, ent.message.data()); // if we have a session to the destination, queue the message and return if (_router->linkManager().HasSessionTo(remote)) { - QueueOutboundMessage(remote, std::move(message), msg.pathid, priority); + QueueOutboundMessage(std::move(ent)); return true; } @@ -58,16 +61,11 @@ namespace llarp // in progress. bool shouldCreateSession = false; { - util::Lock l(_mutex); + util::Lock l{_mutex}; // create queue for if it doesn't exist, and get iterator auto [queue_itr, is_new] = pendingSessionMessageQueues.emplace(remote, MessageQueue()); - - MessageQueueEntry entry; - entry.priority = priority; - entry.message = message; - entry.router = remote; - queue_itr->second.push(std::move(entry)); + queue_itr->second.push(std::move(ent)); shouldCreateSession = is_new; } @@ -86,6 +84,9 @@ namespace llarp m_Killer.TryAccess([this]() { recentlyRemovedPaths.Decay(); ProcessOutboundQueue(); + // TODO: this probably shouldn't be pumping, as it defeats the purpose + // of having a limit on sends per tick, but chaning it is potentially bad + // and requires testing so it should be changed later. if (/*bool more = */ SendRoundRobin()) _router->TriggerPump(); }); @@ -190,52 +191,48 @@ namespace llarp } bool - OutboundMessageHandler::Send(const RouterID& remote, const Message& msg) + OutboundMessageHandler::Send(const MessageQueueEntry& ent) { - const llarp_buffer_t buf(msg.first); - auto callback = msg.second; + const llarp_buffer_t buf{ent.message}; m_queueStats.sent++; - return _router->linkManager().SendTo(remote, buf, [=](ILinkSession::DeliveryStatus status) { - if (status == ILinkSession::DeliveryStatus::eDeliverySuccess) - DoCallback(callback, SendStatus::Success); - else - { - DoCallback(callback, SendStatus::Congestion); - } - }); + SendStatusHandler callback = ent.inform; + return _router->linkManager().SendTo( + ent.router, + buf, + [this, callback](ILinkSession::DeliveryStatus status) { + if (status == ILinkSession::DeliveryStatus::eDeliverySuccess) + DoCallback(callback, SendStatus::Success); + else + { + DoCallback(callback, SendStatus::Congestion); + } + }, + ent.priority); } bool - OutboundMessageHandler::SendIfSession(const RouterID& remote, const Message& msg) + OutboundMessageHandler::SendIfSession(const MessageQueueEntry& ent) { - if (_router->linkManager().HasSessionTo(remote)) + if (_router->linkManager().HasSessionTo(ent.router)) { - return Send(remote, msg); + return Send(ent); } return false; } bool - OutboundMessageHandler::QueueOutboundMessage( - const RouterID& remote, Message&& msg, const PathID_t& pathid, uint16_t priority) + OutboundMessageHandler::QueueOutboundMessage(MessageQueueEntry entry) { - MessageQueueEntry entry; - entry.message = std::move(msg); - // copy callback in case we need to call it, so we can std::move(entry) - auto callback_copy = entry.message.second; - entry.router = remote; - entry.pathid = pathid; - entry.priority = priority; + auto callback = entry.inform; if (outboundQueue.tryPushBack(std::move(entry)) != llarp::thread::QueueReturn::Success) { m_queueStats.dropped++; - DoCallback(callback_copy, SendStatus::Congestion); + DoCallback(callback, SendStatus::Congestion); } else { m_queueStats.queued++; - uint32_t queueSize = outboundQueue.size(); m_queueStats.queueWatermark = std::max(queueSize, m_queueStats.queueWatermark); } @@ -272,7 +269,7 @@ namespace llarp } else { - DoCallback(entry.message.second, SendStatus::Congestion); + DoCallback(entry.inform, SendStatus::Congestion); m_queueStats.dropped++; } } @@ -288,7 +285,7 @@ namespace llarp while (not routing_mq.empty()) { const MessageQueueEntry& entry = routing_mq.top(); - Send(entry.router, entry.message); + Send(entry); routing_mq.pop(); } @@ -331,7 +328,7 @@ namespace llarp { const MessageQueueEntry& entry = message_queue.top(); - Send(entry.router, entry.message); + Send(entry); message_queue.pop(); consecutive_empty = 0; @@ -380,11 +377,11 @@ namespace llarp if (status == SendStatus::Success) { - Send(entry.router, entry.message); + Send(entry); } else { - DoCallback(entry.message.second, status); + DoCallback(entry.inform, status); } movedMessages.pop(); } diff --git a/llarp/router/outbound_message_handler.hpp b/llarp/router/outbound_message_handler.hpp index 3edb6d917..232568f64 100644 --- a/llarp/router/outbound_message_handler.hpp +++ b/llarp/router/outbound_message_handler.hpp @@ -6,12 +6,12 @@ #include #include #include +#include #include #include #include #include -#include struct llarp_buffer_t; @@ -74,22 +74,21 @@ namespace llarp Init(AbstractRouter* router); private: - using Message = std::pair, SendStatusHandler>; - /* A message that has been queued for sending, but not yet * processed into an individual path's message queue. */ struct MessageQueueEntry { uint16_t priority; - Message message; + std::vector message; + SendStatusHandler inform; PathID_t pathid; RouterID router; bool - operator<(const MessageQueueEntry& other) const + operator>(const MessageQueueEntry& other) const { - return other.priority < priority; + return priority > other.priority; } }; @@ -104,7 +103,7 @@ namespace llarp uint32_t numTicks = 0; }; - using MessageQueue = std::priority_queue; + using MessageQueue = util::ascending_priority_queue; /* If a session is not yet created with the destination router for a message, * a special queue is created for that router and an attempt is made to @@ -131,14 +130,14 @@ namespace llarp * returns the result of the call to LinkManager::SendTo() */ bool - Send(const RouterID& remote, const Message& msg); + Send(const MessageQueueEntry& ent); /* Sends the message along to the link layer if we have a session to the remote * * returns the result of the Send() call, or false if no session. */ bool - SendIfSession(const RouterID& remote, const Message& msg); + SendIfSession(const MessageQueueEntry& ent); /* queues a message to the shared outbound message queue. * @@ -149,8 +148,7 @@ namespace llarp * are placed in their paths' respective individual queues. */ bool - QueueOutboundMessage( - const RouterID& remote, Message&& msg, const PathID_t& pathid, uint16_t priority = 0); + QueueOutboundMessage(MessageQueueEntry entry); /* Processes messages on the shared message queue into their paths' respective * individual queues. diff --git a/llarp/router/rc_gossiper.cpp b/llarp/router/rc_gossiper.cpp index 7eb8aa88a..bd9e99b7b 100644 --- a/llarp/router/rc_gossiper.cpp +++ b/llarp/router/rc_gossiper.cpp @@ -27,9 +27,7 @@ namespace llarp bool RCGossiper::ShouldGossipOurRC(Time_t now) const { - bool should = now >= (m_LastGossipedOurRC + GossipOurRCInterval); - LogWarn("ShouldGossipOurRC: ", should); - return should; + return now >= (m_LastGossipedOurRC + GossipOurRCInterval); } bool @@ -44,6 +42,30 @@ namespace llarp m_Filter.Decay(now); } + void + RCGossiper::Forget(const RouterID& pk) + { + m_Filter.Remove(pk); + if (m_OurRouterID == pk) + m_LastGossipedOurRC = 0s; + } + + TimePoint_t + RCGossiper::NextGossipAt() const + { + if (auto maybe = LastGossipAt()) + return *maybe + GossipOurRCInterval; + return DateClock_t::now(); + } + + std::optional + RCGossiper::LastGossipAt() const + { + if (m_LastGossipedOurRC == 0s) + return std::nullopt; + return DateClock_t::time_point{m_LastGossipedOurRC}; + } + bool RCGossiper::GossipRC(const RouterContact& rc) { @@ -116,7 +138,7 @@ namespace llarp m_router->NotifyRouterEvent(m_router->pubkey(), rc); // send message - peerSession->SendMessageBuffer(std::move(msg), nullptr); + peerSession->SendMessageBuffer(std::move(msg), nullptr, gossip.Priority()); }); return true; } diff --git a/llarp/router/rc_gossiper.hpp b/llarp/router/rc_gossiper.hpp index 7b836b218..948abad32 100644 --- a/llarp/router/rc_gossiper.hpp +++ b/llarp/router/rc_gossiper.hpp @@ -29,6 +29,15 @@ namespace llarp void Init(ILinkManager*, const RouterID&, AbstractRouter*); + void + Forget(const RouterID& router) override; + + TimePoint_t + NextGossipAt() const override; + + std::optional + LastGossipAt() const override; + private: RouterID m_OurRouterID; Time_t m_LastGossipedOurRC = 0s; diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 9017125bc..b24b88476 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -87,6 +87,9 @@ namespace llarp RoutePoker::~RoutePoker() { + if (not m_Router or not m_Router->GetVPNPlatform()) + return; + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); for (const auto& [ip, gateway] : m_PokedRoutes) { @@ -162,10 +165,13 @@ namespace llarp if (m_Enabled) return; - m_Enabling = true; - Update(); - m_Enabling = false; - m_Enabled = true; + if (m_Router->GetConfig()->network.m_EnableRoutePoker) + { + m_Enabling = true; + Update(); + m_Enabling = false; + m_Enabled = true; + } systemd_resolved_set_dns( m_Router->hiddenServiceContext().GetDefault()->GetIfName(), @@ -191,10 +197,15 @@ namespace llarp void RoutePoker::Up() { + if (not m_Router->GetConfig()->network.m_EnableRoutePoker) + return; + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - // black hole all routes by default - route.AddBlackhole(); + // black hole all routes if enabled + if (m_Router->GetConfig()->network.m_BlackholeRoutes) + route.AddBlackhole(); + // explicit route pokes for first hops m_Router->ForEachPeer( [&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); }, @@ -207,6 +218,9 @@ namespace llarp void RoutePoker::Down() { + if (not m_Router->GetConfig()->network.m_EnableRoutePoker) + return; + // unpoke routes for first hops m_Router->ForEachPeer( [&](auto session, auto) mutable { DelRoute(session->GetRemoteEndpoint().asIPv4()); }, diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 8729bf0a0..b6ca5c6ba 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -94,18 +95,17 @@ namespace llarp util::StatusObject Router::ExtractStatus() const { - if (_running) - { - return util::StatusObject{ - {"running", true}, - {"numNodesKnown", _nodedb->NumLoaded()}, - {"dht", _dht->impl->ExtractStatus()}, - {"services", _hiddenServiceContext.ExtractStatus()}, - {"exit", _exitContext.ExtractStatus()}, - {"links", _linkManager.ExtractStatus()}, - {"outboundMessages", _outboundMessageHandler.ExtractStatus()}}; - } - return util::StatusObject{{"running", false}}; + if (not _running) + util::StatusObject{{"running", false}}; + + return util::StatusObject{ + {"running", true}, + {"numNodesKnown", _nodedb->NumLoaded()}, + {"dht", _dht->impl->ExtractStatus()}, + {"services", _hiddenServiceContext.ExtractStatus()}, + {"exit", _exitContext.ExtractStatus()}, + {"links", _linkManager.ExtractStatus()}, + {"outboundMessages", _outboundMessageHandler.ExtractStatus()}}; } util::StatusObject @@ -115,6 +115,7 @@ namespace llarp return util::StatusObject{{"running", false}}; auto services = _hiddenServiceContext.ExtractStatus(); + auto link_types = _linkManager.ExtractStatus(); uint64_t tx_rate = 0; @@ -138,45 +139,69 @@ namespace llarp // Compute all stats on all path builders on the default endpoint // Merge snodeSessions, remoteSessions and default into a single array std::vector builders; - auto snode_sessions = services["default"]["snodeSessions"]; - for (const auto& session : snode_sessions) - builders.push_back(session["buildStats"]); - auto remote_sessions = services["default"]["remoteSessions"]; - for (const auto& session : remote_sessions) - builders.push_back(session["buildStats"]); + if (services.is_object()) + { + const auto& serviceDefault = services.at("default"); + builders.push_back(serviceDefault); + + auto snode_sessions = serviceDefault.at("snodeSessions"); + for (const auto& session : snode_sessions) + builders.push_back(session); - builders.push_back(services["default"]["buildStats"]); + auto remote_sessions = serviceDefault.at("remoteSessions"); + for (const auto& session : remote_sessions) + builders.push_back(session); + } // Iterate over all items on this array to build the global pathStats - uint64_t paths = 0; + uint64_t pathsCount = 0; uint64_t success = 0; uint64_t attempts = 0; for (const auto& builder : builders) { if (builder.is_null()) continue; - if (builder["length"].is_number()) - paths += builder["length"].get(); - if (builder["success"].is_number()) - success += builder["success"].get(); - if (builder["attempts"].is_number()) - attempts += builder["attempts"].get(); + + const auto& paths = builder.at("paths"); + if (paths.is_array()) + { + for (const auto& [key, value] : paths.items()) + { + if (value.is_object() && value.at("status").is_string() + && value.at("status") == "established") + pathsCount++; + } + } + + const auto& buildStats = builder.at("buildStats"); + if (buildStats.is_null()) + continue; + + success += buildStats.at("success").get(); + attempts += buildStats.at("attempts").get(); } double ratio = static_cast(success) / (attempts + 1); - return util::StatusObject{ + util::StatusObject stats{ {"running", true}, - {"authCodes", services["default"]["authCodes"]}, - {"exitMap", services["default"]["exitMap"]}, - {"lokiAddress", services["default"]["identity"]}, - {"numPathsBuilt", paths}, + {"version", llarp::VERSION_FULL}, + {"uptime", to_json(Uptime())}, + {"numPathsBuilt", pathsCount}, {"numPeersConnected", peers}, {"numRoutersKnown", _nodedb->NumLoaded()}, {"ratio", ratio}, {"txRate", tx_rate}, {"rxRate", rx_rate}, }; + + if (services.is_object()) + { + stats["authCodes"] = services["default"]["authCodes"]; + stats["exitMap"] = services["default"]["exitMap"]; + stats["lokiAddress"] = services["default"]["identity"]; + } + return stats; } bool @@ -390,9 +415,6 @@ namespace llarp if (!FromConfig(conf)) throw std::runtime_error("FromConfig() failed"); - if (!InitOutboundLinks()) - throw std::runtime_error("InitOutboundLinks() failed"); - if (not EnsureIdentity()) throw std::runtime_error("EnsureIdentity() failed"); @@ -418,6 +440,8 @@ namespace llarp LogError("RC is invalid, not saving"); return false; } + if (m_isServiceNode) + _nodedb->Put(_rc); QueueDiskIO([&]() { HandleSaveRC(); }); return true; } @@ -452,6 +476,25 @@ namespace llarp and _rcLookupHandler.IsGreylisted(pubkey()); } + bool + Router::LooksDeregistered() const + { + return IsServiceNode() and whitelistRouters and _rcLookupHandler.HaveReceivedWhitelist() + and not _rcLookupHandler.SessionIsAllowed(pubkey()); + } + + bool + Router::ShouldTestOtherRouters() const + { + if (not IsServiceNode()) + return false; + if (not whitelistRouters) + return true; + if (not _rcLookupHandler.HaveReceivedWhitelist()) + return false; + return _rcLookupHandler.SessionIsAllowed(pubkey()); + } + bool Router::SessionToRouterAllowed(const RouterID& router) const { @@ -550,8 +593,8 @@ namespace llarp transport_keyfile = m_keyManager->m_transportKeyPath; ident_keyfile = m_keyManager->m_idKeyPath; - if (not conf.router.m_publicAddress.isEmpty()) - _ourAddress = conf.router.m_publicAddress.createSockAddr(); + if (auto maybe = conf.router.m_PublicIP) + _ourAddress = SockAddr{*maybe, conf.router.m_PublicPort}; RouterContact::BlockBogons = conf.router.m_blockBogons; @@ -682,14 +725,15 @@ namespace llarp if (inboundLinks.empty() and m_isServiceNode) { - const auto& publicAddr = conf.router.m_publicAddress; - if (publicAddr.isEmpty() or not publicAddr.hasPort()) + if (_ourAddress) { - throw std::runtime_error( - "service node enabled but could not find a public IP to bind to; you need to set the " - "public-ip= and public-port= options"); + inboundLinks.push_back(LinksConfig::LinkInfo{ + _ourAddress->hostString(), _ourAddress->Family(), _ourAddress->getPort()}); } - inboundLinks.push_back(LinksConfig::LinkInfo{"0.0.0.0", AF_INET, *publicAddr.getPort()}); + else + throw std::runtime_error{ + "service node enabled but could not find a public IP to bind to; you need to set the " + "public-ip= and public-port= options"}; } // create inbound links, if we are a service node @@ -709,7 +753,7 @@ namespace llarp util::memFn(&AbstractRouter::TriggerPump, this), util::memFn(&AbstractRouter::QueueWork, this)); - const std::string& key = serverConfig.interface; + const std::string& key = serverConfig.m_interface; int af = serverConfig.addressFamily; uint16_t port = serverConfig.port; if (!server->Configure(this, key, af, port)) @@ -807,6 +851,12 @@ namespace llarp return; // LogDebug("tick router"); const auto now = Now(); + if (const auto delta = now - _lastTick; _lastTick != 0s and delta > TimeskipDetectedDuration) + { + // we detected a time skip into the futre, thaw the network + LogWarn("Timeskip of ", delta, " detected. Resetting network state"); + Thaw(); + } #if defined(WITH_SYSTEMD) { @@ -817,17 +867,33 @@ namespace llarp ss << " snode | known/svc/clients: " << nodedb()->NumLoaded() << "/" << NumberOfConnectedRouters() << "/" << NumberOfConnectedClients() << " | " << pathContext().CurrentTransitPaths() << " active paths | " - << "block " << (m_lokidRpcClient ? m_lokidRpcClient->BlockHeight() : 0); + << "block " << (m_lokidRpcClient ? m_lokidRpcClient->BlockHeight() : 0) << " | gossip: " + << "(next/last) " << time_delta{_rcGossiper.NextGossipAt()} + << " / "; + if (auto maybe = _rcGossiper.LastGossipAt()) + { + ss << time_delta{*maybe}; + } + else + { + ss << "never"; + } } else { ss << " client | known/connected: " << nodedb()->NumLoaded() << "/" - << NumberOfConnectedRouters() << " | path success: "; - hiddenServiceContext().ForEachService([&ss](const auto& name, const auto& ep) { - ss << " [" << name << " " << std::setprecision(4) - << (100.0 * ep->CurrentBuildStats().SuccessRatio()) << "%]"; - return true; - }); + << NumberOfConnectedRouters(); + if (auto ep = hiddenServiceContext().GetDefault()) + { + ss << " | paths/endpoints " << pathContext().CurrentOwnedPaths() << "/" + << ep->UniqueEndpoints(); + auto success_rate = ep->CurrentBuildStats().SuccessRatio(); + if (success_rate < 0.5) + { + ss << " [ !!! Low Build Success Rate (" << std::setprecision(4) + << (100.0 * success_rate) << "%) !!! ] "; + } + }; } const auto status = ss.str(); ::sd_notify(0, status.c_str()); @@ -850,15 +916,24 @@ namespace llarp const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist(); const bool isSvcNode = IsServiceNode(); const bool decom = LooksDecommissioned(); + bool shouldGossip = isSvcNode and whitelistRouters and gotWhitelist + and _rcLookupHandler.SessionIsAllowed(pubkey()); - if (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000)) - || (now - _rc.last_updated) > rcRegenInterval) + if (isSvcNode + and (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000)) or (now - _rc.last_updated) > rcRegenInterval)) { LogInfo("regenerating RC"); - if (!UpdateOurRC(false)) - LogError("Failed to update our RC"); + if (UpdateOurRC()) + { + // our rc changed so we should gossip it + shouldGossip = true; + // remove our replay entry so it goes out + _rcGossiper.Forget(pubkey()); + } + else + LogError("failed to update our RC"); } - else if (whitelistRouters and gotWhitelist and _rcLookupHandler.SessionIsAllowed(pubkey())) + if (shouldGossip) { // if we have the whitelist enabled, we have fetched the list and we are in either // the white or grey list, we want to gossip our RC @@ -943,17 +1018,19 @@ namespace llarp connectToNum = strictConnect; } - if (decom) + if (auto dereg = LooksDeregistered(); (dereg or decom) and now >= m_NextDecommissionWarn) { // complain about being deregistered - if (now >= m_NextDecommissionWarn) - { - constexpr auto DecommissionWarnInterval = 30s; - LogError("We are running as a service node but we seem to be decommissioned"); - m_NextDecommissionWarn = now + DecommissionWarnInterval; - } + constexpr auto DecommissionWarnInterval = 30s; + LogError( + "We are running as a service node but we seem to be ", + dereg ? "deregistered" : "decommissioned"); + m_NextDecommissionWarn = now + DecommissionWarnInterval; } - else if (connected < connectToNum) + + // if we need more sessions to routers and we are not a service node kicked from the network + // we shall connect out to others + if (connected < connectToNum and not LooksDeregistered()) { size_t dlt = connectToNum - connected; LogDebug("connecting to ", dlt, " random routers to keep alive"); @@ -974,7 +1051,8 @@ namespace llarp if (m_peerDb) { // TODO: throttle this? - // TODO: need to capture session stats when session terminates / is removed from link manager + // TODO: need to capture session stats when session terminates / is removed from link + // manager _linkManager.updatePeerDb(m_peerDb); if (m_peerDb->shouldFlush(now)) @@ -1132,7 +1210,7 @@ namespace llarp // set router version if service node if (IsServiceNode()) { - _rc.routerVersion = RouterVersion(llarp::VERSION, LLARP_PROTO_VERSION); + _rc.routerVersion = RouterVersion(llarp::VERSION, llarp::constants::proto_version); } _linkManager.ForEachInboundLink([&](LinkLayer_ptr link) { @@ -1173,6 +1251,12 @@ namespace llarp return false; } + if (not InitOutboundLinks()) + { + LogError("failed to init outbound links"); + return false; + } + if (IsServiceNode()) { if (!SaveRC()) @@ -1241,10 +1325,6 @@ namespace llarp LogInfo("have ", _nodedb->NumLoaded(), " routers"); -#ifdef _WIN32 - // windows uses proactor event loop so we need to constantly pump - _loop->add_ticker([this] { PumpLL(); }); -#endif _loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); }); _running.store(true); _startedAt = Now(); @@ -1258,8 +1338,10 @@ namespace llarp // dont run tests if we are not running or we are stopping if (not _running) return; - // dont run tests if we are decommissioned - if (LooksDecommissioned()) + // dont run tests if we think we should not test other routers + // this occurs when we are deregistered or do not have the service node list + // yet when we expect to have one. + if (not ShouldTestOtherRouters()) return; auto tests = m_routerTesting.get_failing(); if (auto maybe = m_routerTesting.next_random(this)) @@ -1485,6 +1567,22 @@ namespace llarp return ep and ep->HasExit(); } + std::optional> + Router::OurPublicIP() const + { + if (_ourAddress) + return _ourAddress->getIP(); + std::optional> found; + _linkManager.ForEachInboundLink([&found](const auto& link) { + if (found) + return; + AddressInfo ai; + if (link->GetOurAddressInfo(ai)) + found = ai.IP(); + }); + return found; + } + bool Router::InitOutboundLinks() { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 0e56e4920..f8578c024 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -102,6 +102,9 @@ namespace llarp return _dht; } + std::optional> + OurPublicIP() const override; + util::StatusObject ExtractStatus() const override; @@ -197,6 +200,14 @@ namespace llarp bool LooksDecommissioned() const; + /// return true if we look like we are a deregistered service node + bool + LooksDeregistered() const; + + /// return true if we look like we are allowed and able to test other routers + bool + ShouldTestOtherRouters() const; + std::optional _ourAddress; EventLoop_ptr _loop; diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index a4c7ff1a9..76dac45fa 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -10,7 +10,7 @@ #include "util/printer.hpp" #include "util/time.hpp" -#include +#include #include #include "util/fs.hpp" @@ -221,7 +221,7 @@ namespace llarp routerVersion = std::optional{}; last_updated = 0s; srvRecords.clear(); - version = LLARP_PROTO_VERSION; + version = llarp::constants::proto_version; } util::StatusObject @@ -267,7 +267,7 @@ namespace llarp try { std::string_view buf_view(reinterpret_cast(buf->cur), buf->size_left()); - oxenmq::bt_list_consumer btlist(buf_view); + oxenc::bt_list_consumer btlist(buf_view); uint64_t outer_version = btlist.consume_integer(); @@ -301,7 +301,7 @@ namespace llarp } bool - RouterContact::DecodeVersion_1(oxenmq::bt_list_consumer& btlist) + RouterContact::DecodeVersion_1(oxenc::bt_list_consumer& btlist) { auto signature_string = btlist.consume_string_view(); signed_bt_dict = btlist.consume_dict_data(); diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index bb500dfba..53f29bd38 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -18,10 +18,10 @@ #define MAX_RC_SIZE (1024) #define NICKLEN (32) -namespace oxenmq +namespace oxenc { class bt_list_consumer; -} // namespace oxenmq +} // namespace oxenc namespace llarp { @@ -101,7 +101,7 @@ namespace llarp llarp::AlignedBuffer nickname; llarp_time_t last_updated = 0s; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; std::optional routerVersion; /// should we serialize the exit info? const static bool serializeExit = true; @@ -226,7 +226,7 @@ namespace llarp DecodeVersion_0(llarp_buffer_t* buf); bool - DecodeVersion_1(oxenmq::bt_list_consumer& btlist); + DecodeVersion_1(oxenc::bt_list_consumer& btlist); }; inline std::ostream& diff --git a/llarp/router_id.cpp b/llarp/router_id.cpp index c6e3b3e33..987525137 100644 --- a/llarp/router_id.cpp +++ b/llarp/router_id.cpp @@ -1,5 +1,5 @@ #include "router_id.hpp" -#include +#include namespace llarp { @@ -8,7 +8,7 @@ namespace llarp std::string RouterID::ToString() const { - std::string b32 = oxenmq::to_base32z(begin(), end()); + std::string b32 = oxenc::to_base32z(begin(), end()); b32 += SNODE_TLD; return b32; } @@ -17,7 +17,7 @@ namespace llarp RouterID::ShortString() const { // 5 bytes produces exactly 8 base32z characters: - return oxenmq::to_base32z(begin(), begin() + 5); + return oxenc::to_base32z(begin(), begin() + 5); } util::StatusObject @@ -38,9 +38,9 @@ namespace llarp // - must end in a 1-bit value: 'o' or 'y' (i.e. 10000 or 00000) // - must have 51 preceeding base32z chars // - thus we get 51*5+1 = 256 bits = 32 bytes of output - if (str.size() != 52 || !oxenmq::is_base32z(str) || !(str.back() == 'o' || str.back() == 'y')) + if (str.size() != 52 || !oxenc::is_base32z(str) || !(str.back() == 'o' || str.back() == 'y')) return false; - oxenmq::from_base32z(str.begin(), str.end(), begin()); + oxenc::from_base32z(str.begin(), str.end(), begin()); return true; } } // namespace llarp diff --git a/llarp/router_version.hpp b/llarp/router_version.hpp index 80632c328..0df69a7d1 100644 --- a/llarp/router_version.hpp +++ b/llarp/router_version.hpp @@ -57,7 +57,7 @@ namespace llarp private: Version_t m_Version = {{0, 0, 0}}; - int64_t m_ProtoVersion = LLARP_PROTO_VERSION; + int64_t m_ProtoVersion = llarp::constants::proto_version; }; inline std::ostream& diff --git a/llarp/routing/dht_message.cpp b/llarp/routing/dht_message.cpp index a581e5d73..510754049 100644 --- a/llarp/routing/dht_message.cpp +++ b/llarp/routing/dht_message.cpp @@ -39,7 +39,7 @@ namespace llarp return false; if (!BEncodeWriteDictInt("S", S, buf)) return false; - if (!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf)) return false; return bencode_end(buf); diff --git a/llarp/routing/message.hpp b/llarp/routing/message.hpp index 8274b956b..6544792d5 100644 --- a/llarp/routing/message.hpp +++ b/llarp/routing/message.hpp @@ -16,7 +16,7 @@ namespace llarp { PathID_t from; uint64_t S{0}; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; IMessage() = default; diff --git a/llarp/routing/path_transfer_message.cpp b/llarp/routing/path_transfer_message.cpp index 8667fbd52..ddb5ac82d 100644 --- a/llarp/routing/path_transfer_message.cpp +++ b/llarp/routing/path_transfer_message.cpp @@ -40,7 +40,7 @@ namespace llarp if (!BEncodeWriteDictEntry("T", T, buf)) return false; - if (!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf)) return false; if (!BEncodeWriteDictEntry("Y", Y, buf)) return false; diff --git a/llarp/routing/transfer_traffic_message.cpp b/llarp/routing/transfer_traffic_message.cpp index b3b66e207..b1a7beeba 100644 --- a/llarp/routing/transfer_traffic_message.cpp +++ b/llarp/routing/transfer_traffic_message.cpp @@ -2,7 +2,8 @@ #include "handler.hpp" #include -#include + +#include namespace llarp { @@ -15,7 +16,7 @@ namespace llarp return false; X.emplace_back(buf.sz + 8); byte_t* ptr = X.back().data(); - htobe64buf(ptr, counter); + oxenc::write_host_as_big(counter, ptr); ptr += 8; memcpy(ptr, buf.base, buf.sz); // 8 bytes encoding overhead and 8 bytes counter diff --git a/llarp/rpc/endpoint_rpc.cpp b/llarp/rpc/endpoint_rpc.cpp index 302f14d65..8fa90bf95 100644 --- a/llarp/rpc/endpoint_rpc.cpp +++ b/llarp/rpc/endpoint_rpc.cpp @@ -6,14 +6,16 @@ namespace llarp::rpc EndpointAuthRPC::EndpointAuthRPC( std::string url, std::string method, - Whitelist_t whitelist, + Whitelist_t whitelist_addrs, + std::unordered_set whitelist_tokens, LMQ_ptr lmq, Endpoint_ptr endpoint) - : m_AuthURL(std::move(url)) - , m_AuthMethod(std::move(method)) - , m_AuthWhitelist(std::move(whitelist)) - , m_LMQ(std::move(lmq)) - , m_Endpoint(std::move(endpoint)) + : m_AuthURL{std::move(url)} + , m_AuthMethod{std::move(method)} + , m_AuthWhitelist{std::move(whitelist_addrs)} + , m_AuthStaticTokens{std::move(whitelist_tokens)} + , m_LMQ{std::move(lmq)} + , m_Endpoint{std::move(endpoint)} {} void @@ -57,13 +59,6 @@ namespace llarp::rpc reply(service::AuthResult{service::AuthResultCode::eAuthAccepted, "explicitly whitelisted"}); return; } - if (not m_Conn.has_value()) - { - // we don't have a connection to the backend so it's failed - reply(service::AuthResult{ - service::AuthResultCode::eAuthFailed, "remote has no connection to auth backend"}); - return; - } if (msg->proto != llarp::service::ProtocolType::Auth) { @@ -72,9 +67,32 @@ namespace llarp::rpc return; } + std::string payload{(char*)msg->payload.data(), msg->payload.size()}; + + if (m_AuthStaticTokens.count(payload)) + { + reply(service::AuthResult{service::AuthResultCode::eAuthAccepted, "explicitly whitelisted"}); + return; + } + + if (not m_Conn.has_value()) + { + if (m_AuthStaticTokens.empty()) + { + // we don't have a connection to the backend so it's failed + reply(service::AuthResult{ + service::AuthResultCode::eAuthFailed, "remote has no connection to auth backend"}); + } + else + { + // static auth mode + reply(service::AuthResult{service::AuthResultCode::eAuthRejected, "access not permitted"}); + } + return; + } + const auto authinfo = msg->EncodeAuthInfo(); std::string_view metainfo{authinfo.data(), authinfo.size()}; - std::string_view payload{(char*)msg->payload.data(), msg->payload.size()}; // call method with 2 parameters: metainfo and userdata m_LMQ->request( *m_Conn, diff --git a/llarp/rpc/endpoint_rpc.hpp b/llarp/rpc/endpoint_rpc.hpp index c84cf6183..02e573d6d 100644 --- a/llarp/rpc/endpoint_rpc.hpp +++ b/llarp/rpc/endpoint_rpc.hpp @@ -20,7 +20,8 @@ namespace llarp::rpc explicit EndpointAuthRPC( std::string url, std::string method, - Whitelist_t whitelist, + Whitelist_t addr_whitelist, + std::unordered_set token_whitelist, LMQ_ptr lmq, Endpoint_ptr endpoint); virtual ~EndpointAuthRPC() = default; @@ -40,6 +41,7 @@ namespace llarp::rpc const std::string m_AuthURL; const std::string m_AuthMethod; const Whitelist_t m_AuthWhitelist; + const std::unordered_set m_AuthStaticTokens; LMQ_ptr m_LMQ; Endpoint_ptr m_Endpoint; std::optional m_Conn; diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index ea7122e79..44e5c9520 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -6,7 +6,8 @@ #include #include - +#include +#include #include namespace llarp @@ -147,7 +148,12 @@ namespace llarp constexpr auto PingInterval = 30s; auto makePingRequest = [self = shared_from_this()]() { // send a ping - nlohmann::json payload = {{"version", {VERSION[0], VERSION[1], VERSION[2]}}}; + PubKey pk{}; + if (auto r = self->m_Router.lock()) + pk = r->pubkey(); + nlohmann::json payload = { + {"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())}, + {"version", {VERSION[0], VERSION[1], VERSION[2]}}}; self->Request( "admin.lokinet_ping", [](bool success, std::vector data) { @@ -334,8 +340,8 @@ namespace llarp { service::EncryptedName result; const auto j = nlohmann::json::parse(data[1]); - result.ciphertext = oxenmq::from_hex(j["encrypted_value"].get()); - const auto nonce = oxenmq::from_hex(j["nonce"].get()); + result.ciphertext = oxenc::from_hex(j["encrypted_value"].get()); + const auto nonce = oxenc::from_hex(j["nonce"].get()); if (nonce.size() != result.nonce.size()) { throw std::invalid_argument(stringify( @@ -392,7 +398,7 @@ namespace llarp } std::vector routerIdStrings; - oxenmq::bt_deserialize(msg.data[0], routerIdStrings); + oxenc::bt_deserialize(msg.data[0], routerIdStrings); std::vector routerIds; routerIds.reserve(routerIdStrings.size()); diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 5bb14e84e..7bc2f3f42 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -1,5 +1,6 @@ #include "rpc_server.hpp" #include +#include #include #include #include @@ -432,13 +433,27 @@ namespace llarp::rpc const auto range_itr = obj.find("range"); if (range_itr == obj.end()) { - range.FromString("0.0.0.0/0"); + // platforms without ipv6 support will shit themselves + // here if we give them an exit mapping that is ipv6 + if constexpr (platform::supports_ipv6) + { + range.FromString("::/0"); + } + else + { + range.FromString("0.0.0.0/0"); + } } else if (not range.FromString(range_itr->get())) { reply(CreateJSONError("invalid ip range")); return; } + if (not platform::supports_ipv6 and not range.IsV4()) + { + reply(CreateJSONError("ipv6 ranges not supported on this platform")); + return; + } std::optional token; const auto token_itr = obj.find("token"); if (token_itr != obj.end()) @@ -512,8 +527,7 @@ namespace llarp::rpc } onGoodResult(result.reason); }); - }, - 5s); + }); }; if (exit.has_value()) { @@ -564,8 +578,8 @@ namespace llarp::rpc { r->routePoker().Down(); ep->UnmapExitRange(range); + reply(CreateJSONResponse("OK")); } - reply(CreateJSONResponse("OK")); }); }); }) diff --git a/llarp/service/address.cpp b/llarp/service/address.cpp index 7166f1ca6..940658c28 100644 --- a/llarp/service/address.cpp +++ b/llarp/service/address.cpp @@ -1,6 +1,6 @@ #include "address.hpp" #include -#include +#include #include namespace llarp::service @@ -26,7 +26,7 @@ namespace llarp::service str = subdomain; str += '.'; } - str += oxenmq::to_base32z(begin(), end()); + str += oxenc::to_base32z(begin(), end()); str += tld; return str; } @@ -56,10 +56,10 @@ namespace llarp::service // - must end in a 1-bit value: 'o' or 'y' (i.e. 10000 or 00000) // - must have 51 preceeding base32z chars // - thus we get 51*5+1 = 256 bits = 32 bytes of output - if (str.size() != 52 || !oxenmq::is_base32z(str) || !(str.back() == 'o' || str.back() == 'y')) + if (str.size() != 52 || !oxenc::is_base32z(str) || !(str.back() == 'o' || str.back() == 'y')) return false; - oxenmq::from_base32z(str.begin(), str.end(), begin()); + oxenc::from_base32z(str.begin(), str.end(), begin()); return true; } diff --git a/llarp/service/async_key_exchange.cpp b/llarp/service/async_key_exchange.cpp index 43a765a75..bf814daac 100644 --- a/llarp/service/async_key_exchange.cpp +++ b/llarp/service/async_key_exchange.cpp @@ -70,7 +70,7 @@ namespace llarp // set sender self->msg.sender = self->m_LocalIdentity.pub; // set version - self->msg.version = LLARP_PROTO_VERSION; + self->msg.version = llarp::constants::proto_version; // encrypt and sign if (frame->EncryptAndSign(self->msg, K, self->m_LocalIdentity)) self->loop->call([self, frame] { AsyncKeyExchange::Result(self, frame); }); diff --git a/llarp/service/auth.cpp b/llarp/service/auth.cpp index 2f631f3fb..1c6ce91fb 100644 --- a/llarp/service/auth.cpp +++ b/llarp/service/auth.cpp @@ -1,6 +1,11 @@ #include "auth.hpp" #include +#include +#include "protocol.hpp" +#include +#include + namespace llarp::service { /// maybe get auth result from string @@ -22,6 +27,7 @@ namespace llarp::service ParseAuthType(std::string data) { std::unordered_map values = { + {"file", AuthType::eAuthTypeFile}, {"lmq", AuthType::eAuthTypeLMQ}, {"whitelist", AuthType::eAuthTypeWhitelist}, {"none", AuthType::eAuthTypeNone}}; @@ -31,6 +37,25 @@ namespace llarp::service return itr->second; } + AuthFileType + ParseAuthFileType(std::string data) + { + std::unordered_map values = { + {"plain", AuthFileType::eAuthFilePlain}, + {"plaintext", AuthFileType::eAuthFilePlain}, + {"hashed", AuthFileType::eAuthFileHashes}, + {"hashes", AuthFileType::eAuthFileHashes}, + {"hash", AuthFileType::eAuthFileHashes}}; + const auto itr = values.find(data); + if (itr == values.end()) + throw std::invalid_argument("no such auth file type: " + data); +#ifndef HAVE_CRYPT + if (itr->second == AuthFileType::eAuthFileHashes) + throw std::invalid_argument("unsupported auth file type: " + data); +#endif + return itr->second; + } + /// turn an auth result code into an int uint64_t AuthResultCodeAsInt(AuthResultCode code) @@ -58,4 +83,106 @@ namespace llarp::service } } + class FileAuthPolicy : public IAuthPolicy, public std::enable_shared_from_this + { + const std::set m_Files; + const AuthFileType m_Type; + AbstractRouter* const m_Router; + mutable util::Mutex m_Access; + std::unordered_set m_Pending; + /// returns an auth result for a auth info challange, opens every file until it finds a token + /// matching it + /// this is expected to be done in the IO thread + AuthResult + CheckFiles(const AuthInfo& info) const + { + for (const auto& f : m_Files) + { + fs::ifstream i{f}; + std::string line{}; + while (std::getline(i, line)) + { + // split off comments + const auto parts = split_any(line, "#;", true); + if (auto part = parts[0]; not parts.empty() and not parts[0].empty()) + { + // split off whitespaces and check password + if (CheckPasswd(std::string{TrimWhitespace(part)}, info.token)) + return AuthResult{AuthResultCode::eAuthAccepted, "accepted by whitelist"}; + } + } + } + return AuthResult{AuthResultCode::eAuthRejected, "rejected by whitelist"}; + } + + bool + CheckPasswd(std::string hash, std::string challenge) const + { + switch (m_Type) + { + case AuthFileType::eAuthFilePlain: + return hash == challenge; + case AuthFileType::eAuthFileHashes: + return CryptoManager::instance()->check_passwd_hash( + std::move(hash), std::move(challenge)); + default: + return false; + } + } + + public: + FileAuthPolicy(AbstractRouter* r, std::set files, AuthFileType filetype) + : m_Files{std::move(files)}, m_Type{filetype}, m_Router{r} + {} + + void + AuthenticateAsync( + std::shared_ptr msg, std::function hook) override + { + auto reply = m_Router->loop()->make_caller( + [tag = msg->tag, hook, self = shared_from_this()](AuthResult result) { + { + util::Lock _lock{self->m_Access}; + self->m_Pending.erase(tag); + } + hook(result); + }); + { + util::Lock _lock{m_Access}; + m_Pending.emplace(msg->tag); + } + if (msg->proto == ProtocolType::Auth) + { + m_Router->QueueDiskIO( + [self = shared_from_this(), + auth = AuthInfo{std::string{ + reinterpret_cast(msg->payload.data()), msg->payload.size()}}, + reply]() { + try + { + reply(self->CheckFiles(auth)); + } + catch (std::exception& ex) + { + reply(AuthResult{AuthResultCode::eAuthFailed, ex.what()}); + } + }); + } + else + reply(AuthResult{AuthResultCode::eAuthRejected, "protocol error"}); + } + bool + AsyncAuthPending(ConvoTag tag) const override + { + util::Lock _lock{m_Access}; + return m_Pending.count(tag); + } + }; + + std::shared_ptr + MakeFileAuthPolicy(AbstractRouter* r, std::set files, AuthFileType filetype) + { + return std::make_shared(r, std::move(files), filetype); + } + } // namespace llarp::service diff --git a/llarp/service/auth.hpp b/llarp/service/auth.hpp index 98e515e02..b87f9eab7 100644 --- a/llarp/service/auth.hpp +++ b/llarp/service/auth.hpp @@ -44,7 +44,7 @@ namespace llarp::service struct IAuthPolicy { - ~IAuthPolicy() = default; + virtual ~IAuthPolicy() = default; /// asynchronously determine if we accept new convotag from remote service, call hook with /// result later @@ -71,7 +71,16 @@ namespace llarp::service /// manual whitelist eAuthTypeWhitelist, /// LMQ server - eAuthTypeLMQ + eAuthTypeLMQ, + /// static file + eAuthTypeFile, + }; + + /// how to interpret an file for auth + enum class AuthFileType + { + eAuthFilePlain, + eAuthFileHashes, }; /// get an auth type from a string @@ -79,4 +88,13 @@ namespace llarp::service AuthType ParseAuthType(std::string arg); + /// get an auth file type from a string + /// throws std::invalid_argument if arg is invalid + AuthFileType + ParseAuthFileType(std::string arg); + + /// make an IAuthPolicy that reads out of a static file + std::shared_ptr + MakeFileAuthPolicy(AbstractRouter*, std::set files, AuthFileType fileType); + } // namespace llarp::service diff --git a/llarp/service/context.hpp b/llarp/service/context.hpp index a3ef35f3f..3694d9772 100644 --- a/llarp/service/context.hpp +++ b/llarp/service/context.hpp @@ -12,7 +12,7 @@ namespace llarp namespace service { /// holds all the hidden service endpoints we own - /// TODO: this should be refactored (removed entirely...?) now that lokinet + /// this should be refactored (removed entirely...?) now that lokinet /// only supports one endpoint per instance struct Context { diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index aae60423d..18e7876df 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -104,8 +105,7 @@ namespace llarp std::set intros; if (const auto maybe = GetCurrentIntroductionsWithFilter([now](const service::Introduction& intro) -> bool { - return not intro.ExpiresSoon( - now, path::default_lifetime - path::min_intro_lifetime); + return not intro.ExpiresSoon(now, path::intro_stale_threshold); })) { intros.insert(maybe->begin(), maybe->end()); @@ -696,6 +696,12 @@ namespace llarp } } + size_t + Endpoint::UniqueEndpoints() const + { + return m_state->m_RemoteSessions.size() + m_state->m_SNodeSessions.size(); + } + constexpr auto PublishIntrosetTimeout = 20s; bool @@ -734,13 +740,13 @@ namespace llarp if (not m_PublishIntroSet) return false; - auto next_pub = m_state->m_LastPublishAttempt - + (m_state->m_IntroSet.HasStaleIntros( - now, path::default_lifetime - path::intro_path_spread) + const auto lastEventAt = std::max(m_state->m_LastPublishAttempt, m_state->m_LastPublish); + const auto next_pub = lastEventAt + + (m_state->m_IntroSet.HasStaleIntros(now, path::intro_stale_threshold) ? IntrosetPublishRetryCooldown : IntrosetPublishInterval); - return now >= next_pub and m_LastIntrosetRegenAttempt + 1s <= now; + return now >= next_pub; } void @@ -1624,7 +1630,7 @@ namespace llarp session->FlushDownstream(); // handle inbound traffic sorted - std::priority_queue queue; + util::ascending_priority_queue queue; while (not m_InboundTrafficQueue.empty()) { // succ it out diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index cfb5a2ef9..e7364d1f3 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -20,13 +20,13 @@ #include #include #include -#include +#include #include "endpoint_types.hpp" #include "llarp/endpoint_base.hpp" #include "auth.hpp" -#include +#include // minimum time between introset shifts #ifndef MIN_SHIFT_INTERVAL @@ -168,6 +168,12 @@ namespace llarp void HandlePathDied(path::Path_ptr p) override; + virtual vpn::EgresPacketRouter* + EgresPacketRouter() + { + return nullptr; + }; + bool PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override; @@ -323,10 +329,15 @@ namespace llarp // nullptr if the path was not made before the timeout using PathEnsureHook = std::function; + static constexpr auto DefaultPathEnsureTimeout = 2s; + /// return false if we have already called this function before for this /// address bool - EnsurePathToService(const Address remote, PathEnsureHook h, llarp_time_t timeoutMS); + EnsurePathToService( + const Address remote, + PathEnsureHook h, + llarp_time_t timeoutMS = DefaultPathEnsureTimeout); using SNodeEnsureHook = std::function; @@ -391,6 +402,10 @@ namespace llarp std::optional GetSeqNoForConvo(const ConvoTag& tag); + /// count unique endpoints we are talking to + size_t + UniqueEndpoints() const; + bool HasExit() const; diff --git a/llarp/service/endpoint_state.hpp b/llarp/service/endpoint_state.hpp index 704cba110..c256acf88 100644 --- a/llarp/service/endpoint_state.hpp +++ b/llarp/service/endpoint_state.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include namespace llarp { diff --git a/llarp/service/identity.hpp b/llarp/service/identity.hpp index 7fd1b72a7..5278f1af6 100644 --- a/llarp/service/identity.hpp +++ b/llarp/service/identity.hpp @@ -22,7 +22,7 @@ namespace llarp SecretKey signkey; PrivateKey derivedSignKey; PQKeyPair pq; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; VanityNonce vanity; // public service info diff --git a/llarp/service/info.cpp b/llarp/service/info.cpp index ad5191758..3e2d15fd4 100644 --- a/llarp/service/info.cpp +++ b/llarp/service/info.cpp @@ -56,7 +56,7 @@ namespace llarp return false; if (!BEncodeWriteDictEntry("s", signkey, buf)) return false; - if (!BEncodeWriteDictInt("v", LLARP_PROTO_VERSION, buf)) + if (!BEncodeWriteDictInt("v", llarp::constants::proto_version, buf)) return false; if (!vanity.IsZero()) { diff --git a/llarp/service/info.hpp b/llarp/service/info.hpp index cf9a5d757..1a56f13bf 100644 --- a/llarp/service/info.hpp +++ b/llarp/service/info.hpp @@ -20,7 +20,7 @@ namespace llarp public: VanityNonce vanity; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; void RandomizeVanity() diff --git a/llarp/service/intro.hpp b/llarp/service/intro.hpp index 507d0ffc9..36ea34c2a 100644 --- a/llarp/service/intro.hpp +++ b/llarp/service/intro.hpp @@ -17,7 +17,7 @@ namespace llarp PathID_t pathID; llarp_time_t latency = 0s; llarp_time_t expiresAt = 0s; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; util::StatusObject ExtractStatus() const; diff --git a/llarp/service/intro_set.cpp b/llarp/service/intro_set.cpp index ca6e58414..e0deda9e4 100644 --- a/llarp/service/intro_set.cpp +++ b/llarp/service/intro_set.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace llarp::service { @@ -235,9 +235,9 @@ namespace llarp::service try { - oxenmq::bt_deserialize(srvString, SRVs); + oxenc::bt_deserialize(srvString, SRVs); } - catch (const oxenmq::bt_deserialize_invalid& err) + catch (const oxenc::bt_deserialize_invalid& err) { LogError("Error decoding SRV records from IntroSet: ", err.what()); return false; @@ -321,7 +321,7 @@ namespace llarp::service // srv records if (not SRVs.empty()) { - std::string serial = oxenmq::bt_serialize(SRVs); + std::string serial = oxenc::bt_serialize(SRVs); if (!bencode_write_bytestring(buf, "s", 1)) return false; if (!buf->write(serial.begin(), serial.end())) diff --git a/llarp/service/intro_set.hpp b/llarp/service/intro_set.hpp index 66e88fb14..c39a08404 100644 --- a/llarp/service/intro_set.hpp +++ b/llarp/service/intro_set.hpp @@ -49,7 +49,7 @@ namespace llarp std::optional exitTrafficPolicy; Signature signature; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; bool OtherIsNewer(const IntroSet& other) const diff --git a/llarp/service/lns_tracker.hpp b/llarp/service/lns_tracker.hpp index 9aa65904f..aba31c509 100644 --- a/llarp/service/lns_tracker.hpp +++ b/llarp/service/lns_tracker.hpp @@ -8,7 +8,7 @@ #include "address.hpp" #include -#include +#include namespace llarp::service { diff --git a/llarp/service/outbound_context.cpp b/llarp/service/outbound_context.cpp index e2ac2cb6e..a67b70fe7 100644 --- a/llarp/service/outbound_context.cpp +++ b/llarp/service/outbound_context.cpp @@ -45,11 +45,10 @@ namespace llarp if (dst == remoteIntro.pathID && remoteIntro.router == p->Endpoint()) { LogWarn(Name(), " message ", seq, " dropped by endpoint ", p->Endpoint(), " via ", dst); + markedBad = remoteIntro.IsExpired(Now()); MarkCurrentIntroBad(Now()); - ShiftIntroduction(false); + ShiftIntroRouter(p->Endpoint()); UpdateIntroSet(); - SwapIntros(); - markedBad = remoteIntro.IsExpired(Now()); } return true; } @@ -479,7 +478,8 @@ namespace llarp { if (markedBad or path::Builder::BuildCooldownHit(now)) return false; - if (NumInStatus(path::ePathBuilding) >= numDesiredPaths) + + if (NumInStatus(path::ePathBuilding) >= std::max(numDesiredPaths / size_t{2}, size_t{1})) return false; size_t numValidPaths = 0; diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp index 8ecf32ac8..0b9939c59 100644 --- a/llarp/service/protocol.cpp +++ b/llarp/service/protocol.cpp @@ -191,7 +191,7 @@ namespace llarp return false; if (!BEncodeMaybeReadDictEntry("T", T, read, key, val)) return false; - if (!BEncodeMaybeVerifyVersion("V", version, LLARP_PROTO_VERSION, read, key, val)) + if (!BEncodeMaybeVerifyVersion("V", version, llarp::constants::proto_version, read, key, val)) return false; if (!BEncodeMaybeReadDictEntry("Z", Z, read, key, val)) return false; diff --git a/llarp/service/protocol.hpp b/llarp/service/protocol.hpp index f2485c3af..4286ce221 100644 --- a/llarp/service/protocol.hpp +++ b/llarp/service/protocol.hpp @@ -45,7 +45,7 @@ namespace llarp Endpoint* handler = nullptr; ConvoTag tag; uint64_t seqno = 0; - uint64_t version = LLARP_PROTO_VERSION; + uint64_t version = llarp::constants::proto_version; /// encode metainfo for lmq endpoint auth std::vector @@ -64,9 +64,9 @@ namespace llarp ProcessAsync(path::Path_ptr p, PathID_t from, std::shared_ptr self); bool - operator<(const ProtocolMessage& other) const + operator>(const ProtocolMessage& other) const { - return other.seqno < seqno; + return seqno > other.seqno; } }; @@ -155,7 +155,7 @@ namespace llarp N.Zero(); Z.Zero(); R = 0; - version = LLARP_PROTO_VERSION; + version = llarp::constants::proto_version; } bool diff --git a/llarp/util/aligned.hpp b/llarp/util/aligned.hpp index d588d154a..713220614 100644 --- a/llarp/util/aligned.hpp +++ b/llarp/util/aligned.hpp @@ -5,7 +5,7 @@ #include #include "printer.hpp" -#include +#include #include #include @@ -72,7 +72,7 @@ namespace llarp friend std::ostream& operator<<(std::ostream& out, const AlignedBuffer& self) { - return out << oxenmq::to_hex(self.begin(), self.end()); + return out << oxenc::to_hex(self.begin(), self.end()); } /// bitwise NOT @@ -270,21 +270,21 @@ namespace llarp std::string ToHex() const { - return oxenmq::to_hex(begin(), end()); + return oxenc::to_hex(begin(), end()); } std::string ShortHex() const { - return oxenmq::to_hex(begin(), begin() + 4); + return oxenc::to_hex(begin(), begin() + 4); } bool FromHex(std::string_view str) { - if (str.size() != 2 * size() || !oxenmq::is_hex(str)) + if (str.size() != 2 * size() || !oxenc::is_hex(str)) return false; - oxenmq::from_hex(str.begin(), str.end(), begin()); + oxenc::from_hex(str.begin(), str.end(), begin()); return true; } diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 2967024eb..f185e6347 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -1,5 +1,5 @@ #include "buffer.hpp" -#include "endian.hpp" +#include #include #include @@ -33,64 +33,66 @@ llarp_buffer_t::writef(const char* fmt, ...) return true; } +namespace +{ + template + bool + put(llarp_buffer_t& buf, UInt i) + { + if (buf.size_left() < sizeof(UInt)) + return false; + oxenc::write_host_as_big(i, buf.cur); + buf.cur += sizeof(UInt); + return true; + } + + template + bool + read(llarp_buffer_t& buf, UInt& i) + { + if (buf.size_left() < sizeof(UInt)) + return false; + i = oxenc::load_big_to_host(buf.cur); + buf.cur += sizeof(UInt); + return true; + } + +} // namespace + bool llarp_buffer_t::put_uint16(uint16_t i) { - if (size_left() < sizeof(uint16_t)) - return false; - htobe16buf(cur, i); - cur += sizeof(uint16_t); - return true; + return put(*this, i); } bool llarp_buffer_t::put_uint64(uint64_t i) { - if (size_left() < sizeof(uint64_t)) - return false; - htobe64buf(cur, i); - cur += sizeof(uint64_t); - return true; + return put(*this, i); } bool llarp_buffer_t::put_uint32(uint32_t i) { - if (size_left() < sizeof(uint32_t)) - return false; - htobe32buf(cur, i); - cur += sizeof(uint32_t); - return true; + return put(*this, i); } bool llarp_buffer_t::read_uint16(uint16_t& i) { - if (size_left() < sizeof(uint16_t)) - return false; - i = bufbe16toh(cur); - cur += sizeof(uint16_t); - return true; + return read(*this, i); } bool llarp_buffer_t::read_uint32(uint32_t& i) { - if (size_left() < sizeof(uint32_t)) - return false; - i = bufbe32toh(cur); - cur += sizeof(uint32_t); - return true; + return read(*this, i); } bool llarp_buffer_t::read_uint64(uint64_t& i) { - if (size_left() < sizeof(uint64_t)) - return false; - i = bufbe64toh(cur); - cur += sizeof(uint64_t); - return true; + return read(*this, i); } size_t diff --git a/llarp/util/codel.hpp b/llarp/util/codel.hpp deleted file mode 100644 index a18be23c2..000000000 --- a/llarp/util/codel.hpp +++ /dev/null @@ -1,167 +0,0 @@ -#pragma once - -#include -#include "mem.hpp" -#include -#include "time.hpp" - -#include -#include -#include -#include -#include -#include - -namespace llarp -{ - namespace util - { - struct GetNowSyscall - { - llarp_time_t - operator()() const - { - return llarp::time_now_ms(); - } - }; - - template < - typename T, - typename GetTime, - typename PutTime, - typename Compare, - typename GetNow = GetNowSyscall, - typename Mutex_t = util::Mutex, - typename Lock_t = std::lock_guard, - size_t MaxSize = 1024> - struct CoDelQueue - { - CoDelQueue(std::string name, PutTime put, GetNow now) - : m_QueueIdx(0) - , m_name(std::move(name)) - , _putTime(std::move(put)) - , _getNow(std::move(now)) - {} - - size_t - Size() EXCLUDES(m_QueueMutex) - { - Lock_t lock(m_QueueMutex); - return m_QueueIdx; - } - - template - bool - EmplaceIf(std::function pred, Args&&... args) EXCLUDES(m_QueueMutex) - { - Lock_t lock(m_QueueMutex); - if (m_QueueIdx == MaxSize) - return false; - T* t = &m_Queue[m_QueueIdx]; - new (t) T(std::forward(args)...); - if (!pred(*t)) - { - t->~T(); - return false; - } - - _putTime(m_Queue[m_QueueIdx]); - if (firstPut == 0s) - firstPut = _getTime(m_Queue[m_QueueIdx]); - ++m_QueueIdx; - - return true; - } - - template - void - Emplace(Args&&... args) EXCLUDES(m_QueueMutex) - { - Lock_t lock(m_QueueMutex); - if (m_QueueIdx == MaxSize) - return; - T* t = &m_Queue[m_QueueIdx]; - new (t) T(std::forward(args)...); - _putTime(m_Queue[m_QueueIdx]); - if (firstPut == 0s) - firstPut = _getTime(m_Queue[m_QueueIdx]); - ++m_QueueIdx; - } - - template - void - Process(Visit v) - { - return Process(v, [](T&) -> bool { return false; }); - } - - template - void - Process(Visit visitor, Filter f) EXCLUDES(m_QueueMutex) - { - llarp_time_t lowest = std::numeric_limits::max(); - if (_getNow() < nextTickAt) - return; - // llarp::LogInfo("CoDelQueue::Process - start at ", start); - Lock_t lock(m_QueueMutex); - auto start = firstPut; - - if (m_QueueIdx == 1) - { - visitor(m_Queue[0]); - T* t = &m_Queue[0]; - t->~T(); - m_QueueIdx = 0; - firstPut = 0s; - return; - } - size_t idx = 0; - while (m_QueueIdx) - { - llarp::LogDebug(m_name, " - queue has ", m_QueueIdx); - T* item = &m_Queue[idx++]; - if (f(*item)) - break; - --m_QueueIdx; - const llarp_time_t dlt = start - _getTime(*item); - // llarp::LogInfo("CoDelQueue::Process - dlt ", dlt); - lowest = std::min(dlt, lowest); - if (m_QueueIdx == 0) - { - // llarp::LogInfo("CoDelQueue::Process - single item: lowest ", - // lowest, " dropMs: ", dropMs); - if (lowest > dropMs) - { - item->~T(); - nextTickInterval += initialIntervalMs / uint64_t(std::sqrt(++dropNum)); - firstPut = 0s; - nextTickAt = start + nextTickInterval; - return; - } - - nextTickInterval = initialIntervalMs; - dropNum = 0; - } - visitor(*item); - item->~T(); - } - firstPut = 0s; - nextTickAt = start + nextTickInterval; - } - - const llarp_time_t initialIntervalMs = 5ms; - const llarp_time_t dropMs = 100ms; - llarp_time_t firstPut = 0s; - size_t dropNum = 0; - llarp_time_t nextTickInterval = initialIntervalMs; - llarp_time_t nextTickAt = 0s; - Mutex_t m_QueueMutex; - size_t m_QueueIdx GUARDED_BY(m_QueueMutex); - std::array m_Queue GUARDED_BY(m_QueueMutex); - std::string m_name; - GetTime _getTime; - PutTime _putTime; - GetNow _getNow; - }; // namespace util - } // namespace util -} // namespace llarp diff --git a/llarp/util/decaying_hashset.hpp b/llarp/util/decaying_hashset.hpp index 9618f4089..e7c0a0a72 100644 --- a/llarp/util/decaying_hashset.hpp +++ b/llarp/util/decaying_hashset.hpp @@ -72,6 +72,12 @@ namespace llarp m_CacheInterval = interval; } + void + Remove(const Val_t& val) + { + m_Values.erase(val); + } + private: template void diff --git a/llarp/util/endian.hpp b/llarp/util/endian.hpp deleted file mode 100644 index eb2b534c7..000000000 --- a/llarp/util/endian.hpp +++ /dev/null @@ -1,203 +0,0 @@ -#pragma once - -// adapted from libi2pd - -#include -#include - -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -#include -#elif defined(__sun) -#include -#include -#define htobe16(x) htons(x) -#define htole16(x) (x) -#define be16toh(x) ntohs(x) -#define le16toh(x) (x) - -#define htobe32(x) htonl(x) -#define htole32(x) (x) -#define be32toh(x) ntohl(x) -#define le32toh(x) (x) - -#define htobe64(x) \ - (((uint64_t)htonl(((uint32_t)(((uint64_t)(x)) >> 32)))) \ - | (((uint64_t)htonl(((uint32_t)(x)))) << 32)) -#define htole64(x) (x) -#define be64toh(x) \ - (((uint64_t)ntohl(((uint32_t)(((uint64_t)(x)) >> 32)))) \ - | (((uint64_t)ntohl(((uint32_t)(x)))) << 32)) -#define le64toh(x) (x) -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GLIBC__) -#include -#elif defined(__APPLE__) && defined(__MACH__) -#include -#define htobe16(x) OSSwapHostToBigInt16(x) -#define htole16(x) OSSwapHostToLittleInt16(x) -#define be16toh(x) OSSwapBigToHostInt16(x) -#define le16toh(x) OSSwapLittleToHostInt16(x) - -#define htobe32(x) OSSwapHostToBigInt32(x) -#define htole32(x) OSSwapHostToLittleInt32(x) -#define be32toh(x) OSSwapBigToHostInt32(x) -#define le32toh(x) OSSwapLittleToHostInt32(x) - -#define htobe64(x) OSSwapHostToBigInt64(x) -#define htole64(x) OSSwapHostToLittleInt64(x) -#define be64toh(x) OSSwapBigToHostInt64(x) -#define le64toh(x) OSSwapLittleToHostInt64(x) -#elif defined(_WIN32) -#include -#ifndef __LITTLE_ENDIAN__ -#define __LITTLE_ENDIAN__ -#endif -#define htobe16(x) htons(x) -#define htole16(x) (x) -#define be16toh(x) ntohs(x) -#define le16toh(x) (x) - -#define htobe32(x) htonl(x) -#define htole32(x) (x) -#define be32toh(x) ntohl(x) -#define le32toh(x) (x) - -#define htobe64(x) \ - (((uint64_t)htonl(((uint32_t)(((uint64_t)(x)) >> 32)))) \ - | (((uint64_t)htonl(((uint32_t)(x)))) << 32)) -#define htole64(x) (x) -#define be64toh(x) \ - (((uint64_t)ntohl(((uint32_t)(((uint64_t)(x)) >> 32)))) \ - | (((uint64_t)ntohl(((uint32_t)(x)))) << 32)) -#define le64toh(x) (x) -#else -#define NEEDS_LOCAL_ENDIAN -#include -uint16_t -htobe16(uint16_t int16); -uint32_t -htobe32(uint32_t int32); -uint64_t -htobe64(uint64_t int64); - -uint16_t -be16toh(uint16_t big16); -uint32_t -be32toh(uint32_t big32); -uint64_t -be64toh(uint64_t big64); - -// assume LittleEndine -#define htole16 -#define htole32 -#define htole64 -#define le16toh -#define le32toh -#define le64toh - -#endif - -#if !defined(__LITTLE_ENDIAN__) && defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) \ - && __BYTE_ORDER == __LITTLE_ENDIAN -#define __LITTLE_ENDIAN__ -#elif !defined(__BIG_ENDIAN__) && defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) \ - && __BYTE_ORDER == __BIG_ENDIAN -#define __BIG_ENDIAN__ -#elif !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) -#error "Error: don't know which endian this is" -#endif - -inline uint16_t -buf16toh(const void* buf) -{ - uint16_t b16; - memcpy(&b16, buf, sizeof(uint16_t)); - return b16; -} - -inline uint32_t -buf32toh(const void* buf) -{ - uint32_t b32; - memcpy(&b32, buf, sizeof(uint32_t)); - return b32; -} - -inline uint64_t -buf64toh(const void* buf) -{ - uint64_t b64; - memcpy(&b64, buf, sizeof(uint64_t)); - return b64; -} - -inline uint16_t -bufbe16toh(const void* buf) -{ - return be16toh(buf16toh(buf)); -} - -inline uint32_t -bufbe32toh(const void* buf) -{ - return be32toh(buf32toh(buf)); -} - -inline uint64_t -bufbe64toh(const void* buf) -{ - return be64toh(buf64toh(buf)); -} - -inline void -htobuf16(void* buf, uint16_t b16) -{ - memcpy(buf, &b16, sizeof(uint16_t)); -} - -inline void -htobuf32(void* buf, uint32_t b32) -{ - memcpy(buf, &b32, sizeof(uint32_t)); -} - -inline void -htobuf64(void* buf, uint64_t b64) -{ - memcpy(buf, &b64, sizeof(uint64_t)); -} - -inline void -htobe16buf(void* buf, uint16_t big16) -{ - htobuf16(buf, htobe16(big16)); -} - -inline void -htobe32buf(void* buf, uint32_t big32) -{ - htobuf32(buf, htobe32(big32)); -} - -inline void -htobe64buf(void* buf, uint64_t big64) -{ - htobuf64(buf, htobe64(big64)); -} - -inline void -htole16buf(void* buf, uint16_t big16) -{ - htobuf16(buf, htole16(big16)); -} - -inline void -htole32buf(void* buf, uint32_t big32) -{ - htobuf32(buf, htole32(big32)); -} - -inline void -htole64buf(void* buf, uint64_t big64) -{ - htobuf64(buf, htole64(big64)); -} diff --git a/llarp/util/priority_queue.hpp b/llarp/util/priority_queue.hpp new file mode 100644 index 000000000..19ef98f55 --- /dev/null +++ b/llarp/util/priority_queue.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace llarp::util +{ + /// priority queue that uses operator > instead of operator < + template > + using ascending_priority_queue = + std::priority_queue>; + +} // namespace llarp::util diff --git a/llarp/util/str.hpp b/llarp/util/str.hpp index c90781c9f..1e564b6cf 100644 --- a/llarp/util/str.hpp +++ b/llarp/util/str.hpp @@ -35,6 +35,17 @@ namespace llarp return o.str(); } + /// util for constructing an exception with a message constructed from a set of whatever passed + /// into stringify + /// E must be derived from std::exception here + template + E + make_exception(T&&... stuff) + { + static_assert(std::is_base_of_v); + return E{stringify(std::forward(stuff)...)}; + } + using namespace std::literals; /// Returns true if the first string is equal to the second string, compared case-insensitively. diff --git a/llarp/util/time.cpp b/llarp/util/time.cpp index 4c9cde1f8..cd6a3b4fd 100644 --- a/llarp/util/time.cpp +++ b/llarp/util/time.cpp @@ -1,20 +1,24 @@ #include "time.hpp" #include +#include namespace llarp { - using Clock_t = std::chrono::system_clock; - - template - static Duration_t - time_since_epoch(std::chrono::time_point point) + namespace { - return std::chrono::duration_cast(point.time_since_epoch()); - } + using Clock_t = std::chrono::system_clock; + + template + static Duration_t + time_since_epoch(std::chrono::time_point point) + { + return std::chrono::duration_cast(point.time_since_epoch()); + } - const static auto started_at_system = Clock_t::now(); + const static auto started_at_system = Clock_t::now(); - const static auto started_at_steady = std::chrono::steady_clock::now(); + const static auto started_at_steady = std::chrono::steady_clock::now(); + } // namespace uint64_t ToMS(Duration_t ms) @@ -74,4 +78,11 @@ namespace llarp out.fill(old_fill); return out << "s"; } + + std::ostream& + operator<<(std::ostream& out, const TimePoint_t& tp) + { + auto t = TimePoint_t::clock::to_time_t(tp); + return out << std::put_time(std::localtime(&t), "%c %Z"); + } } // namespace llarp diff --git a/llarp/util/time.hpp b/llarp/util/time.hpp index d5f558f8c..abd725f05 100644 --- a/llarp/util/time.hpp +++ b/llarp/util/time.hpp @@ -26,4 +26,36 @@ namespace llarp nlohmann::json to_json(const Duration_t& t); + std::ostream& + operator<<(std::ostream& out, const TimePoint_t& t); + + template + struct time_delta + { + const TimePoint_t at; + + std::ostream& + operator()(std::ostream& out) const + { + const auto dlt = std::chrono::duration_cast(TimePoint_t::clock::now() - at); + if (dlt > 0s) + return out << std::chrono::duration_cast(dlt) << " ago "; + else if (dlt < 0s) + return out << "in " << std::chrono::duration_cast(-dlt); + else + return out << "now"; + } + }; + + inline std::ostream& + operator<<(std::ostream& out, const time_delta& td) + { + return td(out); + } + + inline std::ostream& + operator<<(std::ostream& out, const time_delta& td) + { + return td(out); + } } // namespace llarp diff --git a/llarp/util/types.hpp b/llarp/util/types.hpp index cd86b4d04..be6a0a286 100644 --- a/llarp/util/types.hpp +++ b/llarp/util/types.hpp @@ -14,6 +14,9 @@ namespace llarp /// convert to milliseconds uint64_t ToMS(Duration_t duration); + + using DateClock_t = std::chrono::system_clock; + using TimePoint_t = DateClock_t::time_point; } // namespace llarp using llarp_time_t = llarp::Duration_t; diff --git a/llarp/vpn/android.hpp b/llarp/vpn/android.hpp index c9792a009..043249b0d 100644 --- a/llarp/vpn/android.hpp +++ b/llarp/vpn/android.hpp @@ -92,7 +92,7 @@ namespace llarp::vpn {} std::shared_ptr - ObtainInterface(InterfaceInfo info) override + ObtainInterface(InterfaceInfo info, AbstractRouter*) override { return std::make_shared(std::move(info), fd); } diff --git a/llarp/vpn/egres_packet_router.cpp b/llarp/vpn/egres_packet_router.cpp new file mode 100644 index 000000000..655aaeb49 --- /dev/null +++ b/llarp/vpn/egres_packet_router.cpp @@ -0,0 +1,102 @@ +#include "egres_packet_router.hpp" + +namespace llarp::vpn +{ + struct EgresUDPPacketHandler : public EgresLayer4Handler + { + EgresPacketHandlerFunc m_BaseHandler; + std::unordered_map m_LocalPorts; + + explicit EgresUDPPacketHandler(EgresPacketHandlerFunc baseHandler) + : m_BaseHandler{std::move(baseHandler)} + {} + + void + AddSubHandler(nuint16_t localport, EgresPacketHandlerFunc handler) override + { + m_LocalPorts.emplace(std::move(localport), std::move(handler)); + } + + void + RemoveSubHandler(nuint16_t localport) override + { + m_LocalPorts.erase(localport); + } + + void + HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override + { + if (auto dstPort = pkt.DstPort()) + { + if (auto itr = m_LocalPorts.find(*dstPort); itr != m_LocalPorts.end()) + { + itr->second(std::move(from), std::move(pkt)); + return; + } + } + m_BaseHandler(std::move(from), std::move(pkt)); + } + }; + + struct EgresGenericLayer4Handler : public EgresLayer4Handler + { + EgresPacketHandlerFunc m_BaseHandler; + + explicit EgresGenericLayer4Handler(EgresPacketHandlerFunc baseHandler) + : m_BaseHandler{std::move(baseHandler)} + {} + + void + HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override + { + m_BaseHandler(std::move(from), std::move(pkt)); + } + }; + + EgresPacketRouter::EgresPacketRouter(EgresPacketHandlerFunc baseHandler) + : m_BaseHandler{std::move(baseHandler)} + {} + + void + EgresPacketRouter::HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) + { + const auto proto = pkt.Header()->protocol; + if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end()) + { + itr->second->HandleIPPacketFrom(std::move(from), std::move(pkt)); + } + else + m_BaseHandler(std::move(from), std::move(pkt)); + } + + namespace + { + constexpr byte_t udp_proto = 0x11; + } + + void + EgresPacketRouter::AddUDPHandler(huint16_t localport, EgresPacketHandlerFunc func) + { + if (m_IPProtoHandler.find(udp_proto) == m_IPProtoHandler.end()) + { + m_IPProtoHandler.emplace(udp_proto, std::make_unique(m_BaseHandler)); + } + m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), std::move(func)); + } + + void + EgresPacketRouter::AddIProtoHandler(uint8_t proto, EgresPacketHandlerFunc func) + { + m_IPProtoHandler[proto] = std::make_unique(std::move(func)); + } + + void + EgresPacketRouter::RemoveUDPHandler(huint16_t localport) + { + if (auto itr = m_IPProtoHandler.find(udp_proto); itr != m_IPProtoHandler.end()) + { + itr->second->RemoveSubHandler(ToNet(localport)); + } + } + +} // namespace llarp::vpn diff --git a/llarp/vpn/egres_packet_router.hpp b/llarp/vpn/egres_packet_router.hpp new file mode 100644 index 000000000..8b074267d --- /dev/null +++ b/llarp/vpn/egres_packet_router.hpp @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace llarp::vpn +{ + using AddressVariant_t = llarp::EndpointBase::AddressVariant_t; + using EgresPacketHandlerFunc = std::function; + + struct EgresLayer4Handler + { + virtual ~EgresLayer4Handler() = default; + + virtual void + HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) = 0; + + virtual void AddSubHandler(nuint16_t, EgresPacketHandlerFunc){}; + virtual void RemoveSubHandler(nuint16_t){}; + }; + + class EgresPacketRouter + { + EgresPacketHandlerFunc m_BaseHandler; + std::unordered_map> m_IPProtoHandler; + + public: + /// baseHandler will be called if no other handlers matches a packet + explicit EgresPacketRouter(EgresPacketHandlerFunc baseHandler); + + /// feed in an ip packet for handling + void + HandleIPPacketFrom(AddressVariant_t, net::IPPacket pkt); + + /// add a non udp packet handler using ip protocol proto + void + AddIProtoHandler(uint8_t proto, EgresPacketHandlerFunc func); + + /// helper that adds a udp packet handler for UDP destinted for localport + void + AddUDPHandler(huint16_t localport, EgresPacketHandlerFunc func); + + /// remove a udp handler that is already set up by bound port + void + RemoveUDPHandler(huint16_t localport); + }; +} // namespace llarp::vpn diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 0f47636e9..cad48fe91 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -19,6 +19,8 @@ #include #include +#include + namespace llarp::vpn { struct in6_ifreq @@ -181,7 +183,7 @@ namespace llarp::vpn { family = AF_INET; bitlen = bits; - htobe32buf(data, addr.h); + oxenc::write_host_as_big(addr.h, data); } _inet_addr(huint128_t addr, size_t bits = 128) @@ -416,10 +418,10 @@ namespace llarp::vpn if (parts[1].find_first_not_of('0') == std::string::npos and parts[0] != ifname) { const auto& ip = parts[2]; - if ((ip.size() == sizeof(uint32_t) * 2) and oxenmq::is_hex(ip)) + if ((ip.size() == sizeof(uint32_t) * 2) and oxenc::is_hex(ip)) { huint32_t x{}; - oxenmq::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); + oxenc::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); gateways.emplace_back(x); } } @@ -448,7 +450,7 @@ namespace llarp::vpn public: std::shared_ptr - ObtainInterface(InterfaceInfo info) override + ObtainInterface(InterfaceInfo info, AbstractRouter*) override { return std::make_shared(std::move(info)); }; diff --git a/llarp/vpn/packet_router.hpp b/llarp/vpn/packet_router.hpp index e84454eae..ee0721a05 100644 --- a/llarp/vpn/packet_router.hpp +++ b/llarp/vpn/packet_router.hpp @@ -17,7 +17,6 @@ namespace llarp::vpn virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){}; }; - class PacketRouter { PacketHandlerFunc m_BaseHandler; @@ -38,5 +37,9 @@ namespace llarp::vpn /// helper that adds a udp packet handler for UDP destinted for localport void AddUDPHandler(huint16_t localport, PacketHandlerFunc func); + + /// remove a udp handler that is already set up by bound port + void + RemoveUDPHandler(huint16_t localport); }; } // namespace llarp::vpn diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index 9ecd15e79..55d4d9f93 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -6,6 +6,7 @@ #include #include #include +#include // DDK macros #define CTL_CODE(DeviceType, Function, Method, Access) \ @@ -168,16 +169,9 @@ namespace llarp::vpn return ret; } - class Win32Interface final : public NetworkInterface + namespace { - std::atomic m_Run; - HANDLE m_Device, m_IOCP; - std::vector m_Threads; - thread::Queue m_ReadQueue; - - InterfaceInfo m_Info; - - static std::wstring + std::wstring get_win_sys_path() { wchar_t win_sys_path[MAX_PATH] = {0}; @@ -190,6 +184,18 @@ namespace llarp::vpn } return win_sys_path; } + } // namespace + + class Win32Interface final : public NetworkInterface + { + std::atomic m_Run; + HANDLE m_Device, m_IOCP; + std::vector m_Threads; + thread::Queue m_ReadQueue; + + InterfaceInfo m_Info; + + AbstractRouter* const _router; static std::string NetSHCommand() @@ -220,7 +226,8 @@ namespace llarp::vpn return converter.to_bytes(wcmd); } - Win32Interface(InterfaceInfo info) : m_ReadQueue{1024}, m_Info{std::move(info)} + Win32Interface(InterfaceInfo info, AbstractRouter* router) + : m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router} { DWORD len; @@ -401,6 +408,12 @@ namespace llarp::vpn thread.join(); } + virtual void + MaybeWakeUpperLayers() const override + { + _router->TriggerPump(); + } + int PollFD() const override { @@ -504,6 +517,17 @@ namespace llarp::vpn ::system(cmd.c_str()); } + static std::string + PowerShell() + { + std::wstring wcmd = + get_win_sys_path() + L"\\WindowsPowerShell\\v1.0\\powershell.exe -Command "; + + using convert_type = std::codecvt_utf8; + std::wstring_convert converter; + return converter.to_bytes(wcmd); + } + static std::string RouteCommand() { @@ -541,8 +565,8 @@ namespace llarp::vpn Execute(RouteCommand() + " " + cmd + " c000::/2 " + ipv6.ToString()); ifname.back()++; - Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname); - Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname); + Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); + Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); } void @@ -613,12 +637,16 @@ namespace llarp::vpn void AddDefaultRouteViaInterface(std::string ifname) override { + // kill ipv6 + Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); DefaultRouteViaInterface(ifname, "ADD"); } void DelDefaultRouteViaInterface(std::string ifname) override { + // restore ipv6 + Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); DefaultRouteViaInterface(ifname, "DELETE"); } }; @@ -629,12 +657,13 @@ namespace llarp::vpn public: std::shared_ptr - ObtainInterface(InterfaceInfo info) override + ObtainInterface(InterfaceInfo info, AbstractRouter* router) override { - auto netif = std::make_shared(std::move(info)); + auto netif = std::make_shared(std::move(info), router); netif->Start(); return netif; }; + IRouteManager& RouteManager() override { diff --git a/llarp/win32/resource.h b/llarp/win32/resource.h deleted file mode 100644 index d90e55303..000000000 --- a/llarp/win32/resource.h +++ /dev/null @@ -1,15 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by version.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/llarp/win32/version.rc b/llarp/win32/version.rc.in similarity index 64% rename from llarp/win32/version.rc rename to llarp/win32/version.rc.in index 8b024dccd..d0021caa3 100644 --- a/llarp/win32/version.rc +++ b/llarp/win32/version.rc.in @@ -6,19 +6,16 @@ // // Microsoft Visual C++ generated resource script. // -#include "resource.h" -#include + +// clang-format off +#define lokinet_VERSION @lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@, 0 + #ifdef __GNUC__ // make windows rc accept this #include #endif ///////////////////////////////////////////////////////////////////////////// // English (United States) resources -#define STRINGIZER(version) #version - -#define VERSION_STRING(version, codename, revision) \ - STRINGIZER(version) "-release [" STRINGIZER(codename) "] (rev-" STRINGIZER(revision) ")" - #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE 1033,1 @@ -56,8 +53,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION LLARP_VERSION - PRODUCTVERSION LLARP_VERSION + FILEVERSION lokinet_VERSION + PRODUCTVERSION lokinet_VERSION FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x3L @@ -72,15 +69,15 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "includes relay/exit functionality, such code is highly experimental on non-Linux targets" - VALUE "CompanyName", "Loki Foundation" - VALUE "FileDescription", "LokiNET daemon for Microsoft Windows NT" - VALUE "FileVersion", VERSION_STRING(LLARP_VERSION_TRIPLET, LLARP_RELEASE_MOTTO, VERSIONTAG) - VALUE "InternalName", "llarpd" - VALUE "LegalCopyright", "Copyright 2018-2020 Jeff Becker, Rick V for the Loki Foundation. All rights reserved. This software is provided under the terms of the zlib-libpng licence; see the file LICENSE for details." - VALUE "OriginalFilename", "llarpd.exe" + VALUE "Comments", "This comment has invoked its 5th ammendment constitutional right to remain silent" + VALUE "CompanyName", "OPTF" + VALUE "FileDescription", "LokiNET daemon for Windows" + VALUE "FileVersion", "@lokinet_VERSION@" + VALUE "InternalName", "lokinet" + VALUE "LegalCopyright", "Copyright (c) 2018-2022 Jeff Becker, Rick V for the OPTF. This software is provided under the terms of the GPL3; see the file LICENSE for details." + VALUE "OriginalFilename", "@lokinet_EXE_NAME@" VALUE "ProductName", "LokiNET for Windows" - VALUE "ProductVersion", VERSION_STRING(LLARP_VERSION_TRIPLET, LLARP_RELEASE_MOTTO, VERSIONTAG) + VALUE "ProductVersion", "@lokinet_VERSION@" END END BLOCK "VarFileInfo" diff --git a/lokinet-docker.ini b/lokinet-docker.ini deleted file mode 100644 index c55480eec..000000000 --- a/lokinet-docker.ini +++ /dev/null @@ -1,82 +0,0 @@ -# this configuration was auto generated with 'sane' defaults -# change these values as desired - - -[router] -# number of crypto worker threads -threads=4 -# path to store signed RC -contact-file=/root/.lokinet/self.signed -# path to store transport private key -transport-privkey=/root/.lokinet/transport.private -# path to store identity signing key -ident-privkey=/root/.lokinet/identity.private -# encryption key for onion routing -encryption-privkey=/root/.lokinet/encryption.private - -# uncomment following line to set router nickname to 'lokinet' -#nickname=lokinet - - -[logging] -level=info -# uncomment for logging to file -#type=file -#file=/path/to/logfile -# uncomment for syslog logging -#type=syslog - -[metrics] -json-metrics-path=/root/.lokinet/metrics.json - -# admin api (disabled by default) -[api] -enabled=true -#authkey=insertpubkey1here -#authkey=insertpubkey2here -#authkey=insertpubkey3here -bind=127.0.0.1:1190 - -# system settings for privileges and such -[system] -user=lokinet -group=lokinet -pidfile=/root/.lokinet/lokinet.pid - -# dns provider configuration section -[dns] -# resolver -upstream=1.1.1.1 -bind=127.3.2.1:53 - -# network database settings block -[netdb] -# directory for network database skiplist storage -dir=/netdb - -# bootstrap settings -[bootstrap] -# add a bootstrap node's signed identity to the list of nodes we want to bootstrap from -# if we don't have any peers we connect to this router -add-node=/root/.lokinet/bootstrap.signed - -# lokid settings (disabled by default) -[lokid] -enabled=false -jsonrpc=127.0.0.1:22023 -#service-node-seed=/path/to/servicenode/seed - -# network settings -[network] -profiles=/root/.lokinet/profiles.dat -enabled=true -exit=false -#exit-blacklist=tcp:25 -#exit-whitelist=tcp:* -#exit-whitelist=udp:* -ifaddr=10.200.0.1/8 -ifname=loki-docker0 - -# ROUTERS ONLY: publish network interfaces for handling inbound traffic -[bind] -eth0=1090 diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 3ca6fe306..9083f213d 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -39,11 +39,6 @@ namespace llarp [](RouterConfig& self) { return self.m_dataDir.c_str(); }, [](RouterConfig& self, std::string dir) { self.m_dataDir = dir; }) .def_readwrite("blockBogons", &RouterConfig::m_blockBogons) - .def( - "overrideAddress", - [](RouterConfig& self, std::string addr) { - self.m_publicAddress = llarp::IpAddress(addr); - }) .def_readwrite("workerThreads", &RouterConfig::m_workerThreads) .def_readwrite("numNetThreads", &RouterConfig::m_numNetThreads) .def_readwrite("JobQueueSize", &RouterConfig::m_JobQueueSize); @@ -75,18 +70,18 @@ namespace llarp .def(py::init<>()) .def( "setOutboundLink", - [](LinksConfig& self, std::string interface, int family, uint16_t port) { + [](LinksConfig& self, std::string _interface, int family, uint16_t port) { LinksConfig::LinkInfo info; - info.interface = std::move(interface); + info.m_interface = std::move(_interface); info.addressFamily = family; info.port = port; self.m_OutboundLink = std::move(info); }) .def( "addInboundLink", - [](LinksConfig& self, std::string interface, int family, uint16_t port) { + [](LinksConfig& self, std::string _interface, int family, uint16_t port) { LinksConfig::LinkInfo info; - info.interface = std::move(interface); + info.m_interface = std::move(_interface); info.addressFamily = family; info.port = port; self.m_InboundLinks.push_back(info); diff --git a/pybind/llarp/router_id.cpp b/pybind/llarp/router_id.cpp index 2612c531f..1cb2c56b7 100644 --- a/pybind/llarp/router_id.cpp +++ b/pybind/llarp/router_id.cpp @@ -11,9 +11,9 @@ namespace llarp .def( "FromHex", [](RouterID* r, const std::string& hex) { - if (hex.size() != 2 * r->size() || !oxenmq::is_hex(hex)) + if (hex.size() != 2 * r->size() || !oxenc::is_hex(hex)) throw std::runtime_error("FromHex requires a 64-digit hex string"); - oxenmq::from_hex(hex.begin(), hex.end(), r->data()); + oxenc::from_hex(hex.begin(), hex.end(), r->data()); }) .def("__repr__", &RouterID::ToString) .def("__str__", &RouterID::ToString) diff --git a/readme.md b/readme.md index bd4e7a722..cbded271b 100644 --- a/readme.md +++ b/readme.md @@ -4,15 +4,30 @@ Lokinet is the reference implementation of LLARP (low latency anonymous routing protocol), a layer 3 onion routing protocol. -You can learn more about the high level design of LLARP [here](docs/high-level.txt) +You can learn more about the high level design of LLARP [here](docs/) -And you can read the LLARP protocol specification [here](docs/proto_v0.txt) +[![Build Status](https://ci.oxen.rocks/api/badges/oxen-io/lokinet/status.svg?ref=refs/heads/dev)](https://ci.oxen.rocks/oxen-io/lokinet) -You can view documentation on how to get started [here](https://docs.oxen.io/products-built-on-oxen/lokinet) . +## Installing -A simple demo application that is lokinet "aware" can be found [here](https://github.com/majestrate/lokinet-aware-demos) +If you are simply looking to install Lokinet and don't want to compile it yourself we provide several options for platforms to run on: -[![Build Status](https://ci.oxen.rocks/api/badges/oxen-io/lokinet/status.svg?ref=refs/heads/dev)](https://ci.oxen.rocks/oxen-io/lokinet) +Tier 1: + +* [Linux](#linux-install) +* [Android](#apk-install) + +Tier 2: + +* [Windows](#windows-install) +* [MacOS](#mac-install) +* [FreeBSD](#freebsd-install) + +Currently Unsupported Platforms: (maintainers welcome) + +* Apple iPhone +* Homebrew +* \[Insert Flavor of the Month windows package manager here\] ## Building @@ -23,17 +38,18 @@ Build requirements: * C++ 17 capable C++ compiler * libuv >= 1.27.0 * libsodium >= 1.0.18 +* libssl (for lokinet-bootstrap) * libcurl (for lokinet-bootstrap) * libunbound * libzmq * cppzmq * sqlite3 -### Linux +### Linux -You do not have to build from source if you are on debian or ubuntu as we have apt repositories with pre-built lokinet packages on `deb.oxen.io`. +You do not have to build from source if you are on debian or ubuntu as we have apt repositories with pre-built lokinet packages on `deb.oxen.io` or `rpm.oxen.io`. -You can install these using: +You can install debian packages using: $ sudo curl -so /etc/apt/trusted.gpg.d/oxen.gpg https://deb.oxen.io/pub.gpg $ echo "deb https://deb.oxen.io $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/oxen.list @@ -41,34 +57,53 @@ You can install these using: $ sudo apt install lokinet -If you are not on a platform supported by the debian packages or if you want to build a dev build, this is the most "portable" way to do it: +If you want to build from source: - $ sudo apt install build-essential cmake git libcap-dev pkg-config automake libtool libuv1-dev libsodium-dev libzmq3-dev libcurl4-openssl-dev libevent-dev nettle-dev libunbound-dev libsqlite3-dev + $ sudo apt install build-essential cmake git libcap-dev pkg-config automake libtool libuv1-dev libsodium-dev libzmq3-dev libcurl4-openssl-dev libevent-dev nettle-dev libunbound-dev libsqlite3-dev libssl-dev nlohmann-json3-dev $ git clone --recursive https://github.com/oxen-io/lokinet $ cd lokinet $ mkdir build $ cd build - $ cmake .. -DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DCMAKE_BUILD_TYPE=Release - $ make -j$(nproc) - -If you dont want to do a static build install the dependancies and run: - $ cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF $ make -j$(nproc) + $ sudo make install -install: +#### Arch Linux - $ sudo make install +Due to [circumstances beyond our control](https://github.com/oxen-io/lokinet/discussions/1823) a working `PKGBUILD` can be found [here](https://raw.githubusercontent.com/oxen-io/lokinet/makepkg/contrib/archlinux/PKGBUILD). + +#### Cross Compile For Linux + +current cross targets: -### macOS +* aarch64-linux-gnu +* arm-linux-gnueabihf +* mips-linux-gnu +* mips64-linux-gnuabi64 +* mipsel-linux-gnu +* powerpc64le-linux-gnu -Lokinet ~~is~~ will be available on the Apple App store. +install the toolchain (this one is for `aarch64-linux-gnu`, you can provide your own toolchain if you want) + + $ sudo apt install g{cc,++}-aarch64-linux-gnu + +build 1 or many cross targets: + + $ ./contrib/cross.sh arch_1 arch_2 ... arch_n + +### MacOS + +Lokinet ~~is~~ will be available on the Apple App store. Source code compilation of Lokinet by end users is not supported or permitted by apple on their platforms, see [this](contrib/macos/README.txt) for more information. If you find this disagreeable consider using a platform that permits compiling from source. -### Windows +### Windows + +You can get the latest stable windows release from https://lokinet.org/ or check the [releases page on github](https://github.com/oxen-io/lokinet/releases). + +nightly builds for the brave or impatient can be found from our CI pipeline [here](https://oxen.rocks/oxen-io/lokinet/) -You can get the latest stable windows release from https://lokinet.org/ or check the releases page on github. +#### Building For Windows windows builds are cross compiled from debian/ubuntu linux @@ -80,6 +115,8 @@ additional build requirements: setup: $ sudo apt install build-essential cmake git pkg-config mingw-w64 nsis cpack automake libtool + $ sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix + $ sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix building: @@ -87,7 +124,9 @@ building: $ cd lokinet $ ./contrib/windows.sh -### FreeBSD +### FreeBSD + +Currently has no VPN Platform code, see #1513 build: @@ -96,26 +135,37 @@ build: $ cd lokinet $ mkdir build $ cd build - $ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DBUILD_SHARED_DEPS=ON .. + $ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DBUILD_STATIC_DEPS=ON .. $ make install (root): # make install + +### Android + +We have an Android APK for lokinet VPN via android VPN API. + +Coming to F-Droid whenever that happens. [[issue]](https://github.com/oxen-io/lokinet-flutter-app/issues/8) + +* [source code](https://github.com/oxen-io/lokinet-flutter-app) +* [CI builds](https://oxen.rocks/oxen-io/lokinet/) ## Usage -### Debian / Ubuntu packages +### Debian / Ubuntu packages -When running from debian package the following steps are not needed as it is already ready to use. +When running from debian package the following steps are not needed as it is already running and +ready to use. You can stop/start/restart it using `systemctl start lokinet`, `systemctl stop +lokinet`, etc. -## Running on Linux (without debs) +### Running on Linux (without debs) -**DO NOT RUN AS ROOT**, run as normal user. +**DO NOT RUN AS ROOT**, run as normal user. set up the initial configs: - $ lokinet -g + $ lokinet -g $ lokinet-bootstrap after you create default config, run it: @@ -127,12 +177,17 @@ This requires the binary to have the proper capabilities which is usually set by $ sudo setcap cap_net_admin,cap_net_bind_service=+eip /usr/local/bin/lokinet -## Running on macOS/UNIX/BSD - -**YOU HAVE TO RUN AS ROOT**, run using sudo. Elevated privileges are needed to create the virtual tunnel interface. +---- -The macOS installer places the normal binaries (`lokinet` and `lokinet-bootstrap`) in `/usr/local/bin` which should be in your path, so you can easily use the binaries from your terminal. The installer also nukes your previous config and keys and sets up a fresh config and downloads the latest bootstrap seed. +# License -to run, after you create default config: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - $ sudo lokinet +``` +Copyright © 2018-2022 The Oxen Project +Copyright © 2018-2022 Jeff Becker +Copyright © 2018-2020 Rick V. (Historical Windows NT port and portions) +``` diff --git a/release.md b/release.md deleted file mode 100644 index 1ff06b51b..000000000 --- a/release.md +++ /dev/null @@ -1,55 +0,0 @@ - -The windows release is signed by rick, his public key is: - - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEXBhAthYJKwYBBAHaRw8BAQdACj8fXcXB+ktPL/gNRBGZajE9ycsQOiMPXigH -0uP6BCW0G1JpY2sgViA8cmlja0Bzbm93bGlnaHQubmV0PoiQBBMWCAA4FiEEsbLw -yIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGyMFCwkIBwIGFQoJCAsCBBYCAwECHgEC -F4AACgkQwO3Icj/cNGUeCwEAuyFfehigul3So0xOuRIxldiHoqLJfSEp4kjU+8b5 -NjsBAIOC4KFpdv8CTPa/aQgRIx/UlOjJ8vMnS94XPSs2vRcDuDgEXBhAthIKKwYB -BAGXVQEFAQEHQKT2GHP2O+q5vgXd6D4IiOu8rI+kcGllVY/0DEqGesJYAwEIB4h4 -BBgWCAAgFiEEsbLwyIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGwwACgkQwO3Icj/c -NGX/tgD9GES37acIhovhMzDj0u9oU/1HqNyx4A45EQ90dP8KMN4BALBRzWXgB23t -9r6g3ZWHQJEpF4RnmcbDbR0SxdyoCkQG -=RdBx ------END PGP PUBLIC KEY BLOCK----- - -``` - -The linux and macos releases are signed by jeff, his public key is: - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEWZx2ERYJKwYBBAHaRw8BAQdAKxsq4dGzYzKJqU8Vin5d8vJF10/NG4Hziw+f -WTbM8nC0MEplZmYgQmVja2VyIChwcm9iYWJseSBub3QgZXZpbCkgPGplZmZAaTJw -LnJvY2tzPoh5BBMWCAAhBQJZnHYRAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheA -AAoJEPNXs7Qvb5sFP2MBAIcL8KOd/RupEtSMyb2f4OBsaE8oFU+NsvfevW0XrBBQ -AQDhjax9f2D0k30pj4uYBJRb/L0JJFfbzI+uwgTtgRp1DLg4BFmcdhESCisGAQQB -l1UBBQEBB0BJOuegxPmX1Ma/nv4O2lZp0rA89EazPgtUrR3e1846DQMBCAeIYQQY -FggACQUCWZx2EQIbDAAKCRDzV7O0L2+bBUgkAPsEeiiut+gGECP/63m7NyTwruNP -oVZUYE1m8XXbHr28UgEA4nXGIAHDRuIUY4sRcVQz2Um9O6kaCdQHH0eSPE48VQ8= -=gFkp ------END PGP PUBLIC KEY BLOCK----- - -``` - -To verify the releases first import those keys into gpg key database - -run the following, copy paste both keys and press `^D` (control - D) to finish - - $ gpg --import - -Alternatively you can get jeff's key off a key server: - - $ gpg --recv-key 67EF6BA68E7B0B0D6EB4F7D4F357B3B42F6F9B05 # jeff's key - -then verify the signatures, make sure that the `.sig` file and the release file -are in the same directory. - - $ gpg --verify release-file.exe.sig diff --git a/release_es.md b/release_es.md deleted file mode 100644 index 5d0a9bca1..000000000 --- a/release_es.md +++ /dev/null @@ -1,55 +0,0 @@ - -La version lanzada para windows esta firmada por rick, su llave plublica es: - - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEXBhAthYJKwYBBAHaRw8BAQdACj8fXcXB+ktPL/gNRBGZajE9ycsQOiMPXigH -0uP6BCW0G1JpY2sgViA8cmlja0Bzbm93bGlnaHQubmV0PoiQBBMWCAA4FiEEsbLw -yIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGyMFCwkIBwIGFQoJCAsCBBYCAwECHgEC -F4AACgkQwO3Icj/cNGUeCwEAuyFfehigul3So0xOuRIxldiHoqLJfSEp4kjU+8b5 -NjsBAIOC4KFpdv8CTPa/aQgRIx/UlOjJ8vMnS94XPSs2vRcDuDgEXBhAthIKKwYB -BAGXVQEFAQEHQKT2GHP2O+q5vgXd6D4IiOu8rI+kcGllVY/0DEqGesJYAwEIB4h4 -BBgWCAAgFiEEsbLwyIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGwwACgkQwO3Icj/c -NGX/tgD9GES37acIhovhMzDj0u9oU/1HqNyx4A45EQ90dP8KMN4BALBRzWXgB23t -9r6g3ZWHQJEpF4RnmcbDbR0SxdyoCkQG -=RdBx ------END PGP PUBLIC KEY BLOCK----- - -``` - -La version lanzada para linux y macos estan firmadas por jeff, su llave publica es: - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEWZx2ERYJKwYBBAHaRw8BAQdAKxsq4dGzYzKJqU8Vin5d8vJF10/NG4Hziw+f -WTbM8nC0MEplZmYgQmVja2VyIChwcm9iYWJseSBub3QgZXZpbCkgPGplZmZAaTJw -LnJvY2tzPoh5BBMWCAAhBQJZnHYRAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheA -AAoJEPNXs7Qvb5sFP2MBAIcL8KOd/RupEtSMyb2f4OBsaE8oFU+NsvfevW0XrBBQ -AQDhjax9f2D0k30pj4uYBJRb/L0JJFfbzI+uwgTtgRp1DLg4BFmcdhESCisGAQQB -l1UBBQEBB0BJOuegxPmX1Ma/nv4O2lZp0rA89EazPgtUrR3e1846DQMBCAeIYQQY -FggACQUCWZx2EQIbDAAKCRDzV7O0L2+bBUgkAPsEeiiut+gGECP/63m7NyTwruNP -oVZUYE1m8XXbHr28UgEA4nXGIAHDRuIUY4sRcVQz2Um9O6kaCdQHH0eSPE48VQ8= -=gFkp ------END PGP PUBLIC KEY BLOCK----- - -``` - -Para verificar las versiones lanzada, primero importar esas llaves en la base de datos de llaves de gpg - -correr lo proximo, copie y pegue ambas llaves y presione `^D` (control - D) para completar - - $ gpg --import - -De forma alternativa puede obtener la llave de jeff desde un servidor de llaves: - - $ gpg --recv-key 67EF6BA68E7B0B0D6EB4F7D4F357B3B42F6F9B05 # jeff's key - -luego verifique la llave, este seguro de que el archivo `.sig` y el archivo de la version lanzada esta -en el mismo directorio. - - $ gpg --verify release-file.exe.sig diff --git a/release_ru.md b/release_ru.md deleted file mode 100644 index 804d97ddd..000000000 --- a/release_ru.md +++ /dev/null @@ -1,53 +0,0 @@ - -Релиз для Windows подписан Риком, его открытый ключ: - - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEXBhAthYJKwYBBAHaRw8BAQdACj8fXcXB+ktPL/gNRBGZajE9ycsQOiMPXigH -0uP6BCW0G1JpY2sgViA8cmlja0Bzbm93bGlnaHQubmV0PoiQBBMWCAA4FiEEsbLw -yIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGyMFCwkIBwIGFQoJCAsCBBYCAwECHgEC -F4AACgkQwO3Icj/cNGUeCwEAuyFfehigul3So0xOuRIxldiHoqLJfSEp4kjU+8b5 -NjsBAIOC4KFpdv8CTPa/aQgRIx/UlOjJ8vMnS94XPSs2vRcDuDgEXBhAthIKKwYB -BAGXVQEFAQEHQKT2GHP2O+q5vgXd6D4IiOu8rI+kcGllVY/0DEqGesJYAwEIB4h4 -BBgWCAAgFiEEsbLwyIc/Y1IT8TNLwO3Icj/cNGUFAlwYQLYCGwwACgkQwO3Icj/c -NGX/tgD9GES37acIhovhMzDj0u9oU/1HqNyx4A45EQ90dP8KMN4BALBRzWXgB23t -9r6g3ZWHQJEpF4RnmcbDbR0SxdyoCkQG -=RdBx ------END PGP PUBLIC KEY BLOCK----- - -``` -Релизы linux и macos подписаны Джеффом, его открытый ключ: - -``` - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEWZx2ERYJKwYBBAHaRw8BAQdAKxsq4dGzYzKJqU8Vin5d8vJF10/NG4Hziw+f -WTbM8nC0MEplZmYgQmVja2VyIChwcm9iYWJseSBub3QgZXZpbCkgPGplZmZAaTJw -LnJvY2tzPoh5BBMWCAAhBQJZnHYRAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheA -AAoJEPNXs7Qvb5sFP2MBAIcL8KOd/RupEtSMyb2f4OBsaE8oFU+NsvfevW0XrBBQ -AQDhjax9f2D0k30pj4uYBJRb/L0JJFfbzI+uwgTtgRp1DLg4BFmcdhESCisGAQQB -l1UBBQEBB0BJOuegxPmX1Ma/nv4O2lZp0rA89EazPgtUrR3e1846DQMBCAeIYQQY -FggACQUCWZx2EQIbDAAKCRDzV7O0L2+bBUgkAPsEeiiut+gGECP/63m7NyTwruNP -oVZUYE1m8XXbHr28UgEA4nXGIAHDRuIUY4sRcVQz2Um9O6kaCdQHH0eSPE48VQ8= -=gFkp ------END PGP PUBLIC KEY BLOCK----- - -``` - -Чтобы проверить релизы, сначала импортируйте эти ключи в базу данных ключей gpg. - -выполните следующее, скопируйте и вставьте оба ключа и нажмите `^D` (control - D), чтобы закончить - - $ gpg --import - -В качестве альтернативы вы можете получить ключ Джеффа на сервере ключей: - - $ gpg --recv-key 67EF6BA68E7B0B0D6EB4F7D4F357B3B42F6F9B05 # jeff's key - -затем проверьте подписи, убедитесь, что файл `.sig` и файл релиза находятся в том же каталоге. - - $ gpg --verify release-file.exe.sig diff --git a/test/crypto/test_llarp_crypto.cpp b/test/crypto/test_llarp_crypto.cpp index 34747e53d..13105a87e 100644 --- a/test/crypto/test_llarp_crypto.cpp +++ b/test/crypto/test_llarp_crypto.cpp @@ -47,3 +47,48 @@ TEST_CASE("PQ crypto") REQUIRE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys))); REQUIRE(otherShared == shared); } + +#ifdef HAVE_CRYPT + +TEST_CASE("passwd hash valid") +{ + llarp::sodium::CryptoLibSodium crypto; + + // poggers password hashes + std::set valid_hashes; + // UNIX DES + valid_hashes.emplace("CVu85Ms694POo"); + // sha256 salted + valid_hashes.emplace( + "$5$cIghotiBGjfPC7Fu$" + "TXXxPhpUcEiF9tMnjhEVJFi9AlNDSkNRQFTrXPQTKS9"); + // sha512 salted + valid_hashes.emplace( + "$6$qB77ms3wCIo.xVKP$Hl0RLuDgWNmIW4s." + "5KUbFmnauoTfrWSPJzDCD8ZTSSfwRbMgqgG6F9y3K.YEYVij8g/" + "Js0DRT2RhgXoX0sHGb."); + + for (const auto& hash : valid_hashes) + { + // make sure it is poggers ... + REQUIRE(crypto.check_passwd_hash(hash, "poggers")); + // ... and not inscrutible + REQUIRE(not crypto.check_passwd_hash(hash, "inscrutible")); + } +} + +TEST_CASE("passwd hash malformed") +{ + llarp::sodium::CryptoLibSodium crypto; + + std::set invalid_hashes = { + "stevejobs", + "$JKEDbzgzym1N6", // crypt() for "stevejobs" with a $ at the begining + "$0$zero$AAAAAAAAAAA", + "$$$AAAAAAAAAAAA", + "$LIGMA$BALLS$LMAOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO."}; + for (const auto& hash : invalid_hashes) + REQUIRE(not crypto.check_passwd_hash(hash, "stevejobs")); +} + +#endif diff --git a/test/router/test_llarp_router_version.cpp b/test/router/test_llarp_router_version.cpp index 6230b2161..57e436347 100644 --- a/test/router/test_llarp_router_version.cpp +++ b/test/router/test_llarp_router_version.cpp @@ -24,14 +24,14 @@ TEST_CASE("Compatibility when protocol unequal", "[RouterVersion]") TEST_CASE("Empty compatibility", "[RouterVersion]") { - llarp::RouterVersion v1({0, 0, 1}, LLARP_PROTO_VERSION); + llarp::RouterVersion v1({0, 0, 1}, llarp::constants::proto_version); CHECK_FALSE(v1.IsCompatableWith(llarp::emptyRouterVersion)); } TEST_CASE("IsEmpty", "[RouterVersion]") { - llarp::RouterVersion notEmpty({0, 0, 1}, LLARP_PROTO_VERSION); + llarp::RouterVersion notEmpty({0, 0, 1}, llarp::constants::proto_version); CHECK_FALSE(notEmpty.IsEmpty()); CHECK(llarp::emptyRouterVersion.IsEmpty()); @@ -39,7 +39,7 @@ TEST_CASE("IsEmpty", "[RouterVersion]") TEST_CASE("Clear", "[RouterVersion]") { - llarp::RouterVersion version({0, 0, 1}, LLARP_PROTO_VERSION); + llarp::RouterVersion version({0, 0, 1}, llarp::constants::proto_version); CHECK_FALSE(version.IsEmpty()); version.Clear(); diff --git a/test/routing/test_llarp_routing_obtainexitmessage.cpp b/test/routing/test_llarp_routing_obtainexitmessage.cpp index 8a5c2ca2b..fca9842e6 100644 --- a/test/routing/test_llarp_routing_obtainexitmessage.cpp +++ b/test/routing/test_llarp_routing_obtainexitmessage.cpp @@ -28,6 +28,6 @@ TEST_CASE_METHOD(LlarpTest<>, "Sign-verify") CHECK(msg.Sign(alice)); CHECK(msg.Verify()); CHECK(msg.I == PubKey{seckey_topublic(alice)}); - CHECK(msg.version == LLARP_PROTO_VERSION); + CHECK(msg.version == llarp::constants::proto_version); CHECK_FALSE(msg.Z.IsZero()); } diff --git a/win32-setup/extra_create_icons.nsis b/win32-setup/extra_create_icons.nsis new file mode 100644 index 000000000..75cc048f1 --- /dev/null +++ b/win32-setup/extra_create_icons.nsis @@ -0,0 +1 @@ +CreateShortCut '$SMPROGRAMS\$STARTMENU_FOLDER\Lokinet.lnk' '$INSTDIR\share\gui\lokinet-gui.exe' diff --git a/win32-setup/extra_delete_icons.nsis b/win32-setup/extra_delete_icons.nsis new file mode 100644 index 000000000..75cc048f1 --- /dev/null +++ b/win32-setup/extra_delete_icons.nsis @@ -0,0 +1 @@ +CreateShortCut '$SMPROGRAMS\$STARTMENU_FOLDER\Lokinet.lnk' '$INSTDIR\share\gui\lokinet-gui.exe' diff --git a/win32-setup/extra_install.nsis b/win32-setup/extra_install.nsis new file mode 100644 index 000000000..ec07fb540 --- /dev/null +++ b/win32-setup/extra_install.nsis @@ -0,0 +1,7 @@ +ifFileExists $INSTDIR\bin\tuntap-install.exe 0 +2 +ExecWait '$INSTDIR\bin\tuntap-install.exe /S' +ExecWait '$INSTDIR\bin\lokinet.exe --install' +ExecWait 'sc failure lokinet reset= 60 actions= restart/1000' +ExecWait '$INSTDIR\bin\lokinet.exe -g C:\ProgramData\lokinet\lokinet.ini' +CopyFiles '$INSTDIR\share\bootstrap.signed' C:\ProgramData\lokinet\bootstrap.signed + diff --git a/win32-setup/extra_preinstall.nsis b/win32-setup/extra_preinstall.nsis new file mode 100644 index 000000000..de3904f6f --- /dev/null +++ b/win32-setup/extra_preinstall.nsis @@ -0,0 +1,6 @@ +IfFileExists $INSTDIR\bin\lokinet.exe 0 +3 +ExecWait 'net stop lokinet' +ExecWait '$INSTDIR\bin\lokinet.exe --remove' + +IfFileExists $INSTDIR\share\gui\lokinet.exe 0 +2 +ExecWait 'taskkill /f /t /im lokinet-gui.exe' diff --git a/win32-setup/extra_uninstall.nsis b/win32-setup/extra_uninstall.nsis new file mode 100644 index 000000000..ea8664219 --- /dev/null +++ b/win32-setup/extra_uninstall.nsis @@ -0,0 +1,5 @@ +ExecWait 'net stop lokinet' +ExecWait 'taskkill /f /t /im lokinet-gui.exe' +ExecWait '$INSTDIR\bin\lokinet.exe --remove' +RMDir /r /REBOOTOK C:\ProgramData\lokinet +