Merge pull request #2204 from dr7ana/quic-wip

Draft: Wire Protocol Overhaul, Message Handling Refactor
pull/2214/head
Jason Rhinelander 8 months ago committed by GitHub
commit 690ec5fbca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,6 +9,7 @@ local default_deps_base = [
'libsqlite3-dev',
'libcurl4-openssl-dev',
'libzmq3-dev',
'libgnutls28-dev',
'make',
];
local default_deps_nocxx = ['libsodium-dev'] + default_deps_base; // libsodium-dev needs to be >= 1.0.18
@ -30,11 +31,19 @@ local ci_dep_mirror(want_mirror) = (if want_mirror then ' -DLOCAL_MIRROR=https:/
local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q';
local kitware_repo(distro) = [
'eatmydata ' + apt_get_quiet + ' install -y curl ca-certificates',
'curl -sSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - >/usr/share/keyrings/kitware-archive-keyring.gpg',
'echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ' + distro + ' main" >/etc/apt/sources.list.d/kitware.list',
'eatmydata ' + apt_get_quiet + ' update',
];
// Regular build on a debian-like system:
local debian_pipeline(name,
image,
arch='amd64',
deps=default_deps,
extra_setup=[],
build_type='Release',
lto=false,
werror=true,
@ -70,13 +79,14 @@ local debian_pipeline(name,
'echo deb http://deb.oxen.io $$(lsb_release -sc) main >/etc/apt/sources.list.d/oxen.list',
'eatmydata ' + apt_get_quiet + ' update',
] else []
) + [
) + extra_setup
+ [
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y gdb cmake git pkg-config ccache ' + std.join(' ', deps),
'mkdir build',
'cd build',
'cmake .. -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE=' + build_type + ' ' +
(if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') +
'-DWARN_DEPRECATED=OFF ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
'-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') +
@ -90,6 +100,24 @@ local debian_pipeline(name,
},
],
};
local local_gnutls(jobs=6, prefix='/usr/local') = [
apt_get_quiet + ' install -y curl ca-certificates',
'curl -sSL https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz | tar xfz -',
'curl -sSL https://www.gnupg.org/ftp/gcrypt/gnutls/v3.8/gnutls-3.8.0.tar.xz | tar xfJ -',
'export CC="ccache gcc"',
'export PKG_CONFIG_PATH=' + prefix + '/lib/pkgconfig:' + prefix + '/lib64/pkgconfig',
'export LD_LIBRARY_PATH=' + prefix + '/lib:' + prefix + '/lib64',
'cd nettle-3.9.1',
'./configure --prefix=' + prefix,
'make -j' + jobs,
'make install',
'cd ..',
'cd gnutls-3.8.0',
'./configure --prefix=' + prefix + ' --with-included-libtasn1 --with-included-unistring --without-p11-kit --disable-libdane --disable-cxx --without-tpm --without-tpm2',
'make -j' + jobs,
'make install',
'cd ..',
];
local apk_builder(name, image, extra_cmds=[], allow_fail=false, jobs=6) = {
kind: 'pipeline',
type: 'docker',
@ -301,7 +329,7 @@ local mac_builder(name,
'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk
'./contrib/mac-configure.sh ' +
ci_dep_mirror(local_mirror) +
(if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') +
'-DWARN_DEPRECATED=OFF ' +
codesign,
'cd build-mac',
// We can't use the 'package' target here because making a .dmg requires an active logged in
@ -351,28 +379,29 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
'echo "Building on ${DRONE_STAGE_MACHINE}"',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y git clang-format-14 jsonnet',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y git clang-format-15 jsonnet',
'./contrib/ci/drone-format-verify.sh',
],
}],
},
// documentation builder
docs_pipeline('Documentation',
docker_base + 'docbuilder',
extra_cmds=['UPLOAD_OS=docs ./contrib/ci/drone-static-upload.sh']),
//docs_pipeline('Documentation',
// docker_base + 'docbuilder',
// extra_cmds=['UPLOAD_OS=docs ./contrib/ci/drone-static-upload.sh']),
// Various debian builds
debian_pipeline('Debian sid (amd64)', docker_base + 'debian-sid'),
debian_pipeline('Debian sid/Debug (amd64)', docker_base + 'debian-sid', build_type='Debug'),
clang(13),
full_llvm(13),
clang(16),
full_llvm(16),
debian_pipeline('Debian stable (i386)', docker_base + 'debian-stable/i386'),
debian_pipeline('Debian buster (amd64)', docker_base + 'debian-buster', cmake_extra='-DDOWNLOAD_SODIUM=ON'),
debian_pipeline('Debian buster (amd64)', docker_base + 'debian-buster', extra_setup=kitware_repo('bionic') + local_gnutls(), cmake_extra='-DDOWNLOAD_SODIUM=ON'),
debian_pipeline('Ubuntu latest (amd64)', docker_base + 'ubuntu-rolling'),
debian_pipeline('Ubuntu LTS (amd64)', docker_base + 'ubuntu-lts'),
debian_pipeline('Ubuntu bionic (amd64)',
docker_base + 'ubuntu-bionic',
deps=['g++-8'] + default_deps_nocxx,
extra_setup=kitware_repo('bionic') + local_gnutls(),
cmake_extra='-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8',
oxen_repo=true),
@ -403,6 +432,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
debian_pipeline('Static (bionic amd64)',
docker_base + 'ubuntu-bionic',
deps=['g++-8', 'python3-dev', 'automake', 'libtool'],
extra_setup=kitware_repo('bionic'),
lto=true,
tests=false,
oxen_repo=true,
@ -420,6 +450,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
docker_base + 'debian-buster/arm32v7',
arch='arm64',
deps=['g++', 'python3-dev', 'automake', 'libtool'],
extra_setup=kitware_repo('bionic'),
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
'-DCMAKE_CXX_FLAGS="-march=armv7-a+fp -Wno-psabi" -DCMAKE_C_FLAGS="-march=armv7-a+fp" ' +
'-DNATIVE_BUILD=OFF -DWITH_SYSTEMD=OFF -DWITH_BOOTSTRAP=OFF',

3
.gitmodules vendored

@ -32,3 +32,6 @@
[submodule "external/oxen-libquic"]
path = external/oxen-libquic
url = https://github.com/oxen-io/oxen-libquic.git
[submodule "external/span-lite"]
path = external/span-lite
url = https://github.com/martinmoene/span-lite.git

@ -96,7 +96,7 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions(-DLOKINET_DEBUG)
endif()
option(WARN_DEPRECATED "show deprecation warnings" ${debug})
option(WARN_DEPRECATED "show deprecation warnings" OFF)
include(CheckCXXSourceCompiles)
include(CheckLibraryExists)
@ -187,11 +187,7 @@ set(warning_flags -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werro
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND warning_flags -Wno-unknown-warning-option)
endif()
if(WARN_DEPRECATED)
list(APPEND warning_flags -Wdeprecated-declarations)
else()
list(APPEND warning_flags -Wno-deprecated-declarations)
endif()
list(APPEND warning_flags -Wno-deprecated-declarations)
# If we blindly add these directly as compile_options then they get passed to swiftc on Apple and
# break, so we use a generate expression to set them only for C++/C/ObjC

@ -55,11 +55,11 @@ set(LIBUV_SOURCE libuv-v${LIBUV_VERSION}.tar.gz)
set(LIBUV_HASH SHA512=91197ff9303112567bbb915bbb88058050e2ad1c048815a3b57c054635d5dc7df458b956089d785475290132236cb0edcfae830f5d749de29a9a3213eeaf0b20
CACHE STRING "libuv source hash")
set(ZLIB_VERSION 1.2.13 CACHE STRING "zlib version")
set(ZLIB_VERSION 1.3 CACHE STRING "zlib version")
set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net
CACHE STRING "zlib mirror(s)")
set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.xz)
set(ZLIB_HASH SHA256=d14c38e313afc35a9a8760dadf26042f51ea0f5d154b0630a31da0540107fb98
set(ZLIB_HASH SHA256=8a9ba2898e1d0d774eca6ba5b4627a11e5588ba85c8851336eb38de4683050a7
CACHE STRING "zlib source hash")
set(CURL_VERSION 7.86.0 CACHE STRING "curl version")

@ -508,7 +508,7 @@ namespace
{
auto deadlock_cat = llarp::log::Cat("deadlock");
for (const auto& wtf :
{"you have been visited by the mascott of the deadlocked router.",
{"you have been visited by the mascot of the deadlocked router.",
"⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠄⠄⠄⠄",
"⠄⠄⠄⠄⠄⢀⣀⣀⡀⠄⠄⠄⡠⢲⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠄⠄",
"⠄⠄⠄⠔⣈⣀⠄⢔⡒⠳⡴⠊⠄⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣧⠄⠄",
@ -674,4 +674,4 @@ main(int argc, char* argv[])
return 1;
}
#endif
}
}

@ -24,16 +24,15 @@ if(SUBMODULE_CHECK)
endfunction ()
message(STATUS "Checking submodules")
check_submodule(CLI11)
check_submodule(cpr)
check_submodule(nlohmann)
# check_submodule(ghc-filesystem)
# check_submodule(oxen-logging fmt spdlog)
check_submodule(oxen-libquic)
check_submodule(oxen-mq)
check_submodule(pybind11)
check_submodule(span-lite)
check_submodule(sqlite_orm)
check_submodule(oxen-mq)
check_submodule(uvw)
check_submodule(cpr)
check_submodule(oxen-libquic)
check_submodule(CLI11)
endif()
endif()
@ -65,12 +64,6 @@ set(JSON_BuildTests OFF CACHE INTERNAL "")
set(JSON_Install OFF CACHE INTERNAL "")
system_or_submodule(NLOHMANN nlohmann_json nlohmann_json>=3.7.0 nlohmann)
#if (STATIC OR FORCE_SPDLOG_SUBMODULE OR FORCE_FMT_SUBMODULE)
# set(OXEN_LOGGING_FORCE_SUBMODULES ON CACHE INTERNAL "")
#endif()
#set(OXEN_LOGGING_SOURCE_ROOT "${PROJECT_SOURCE_DIR}" CACHE INTERNAL "")
#add_subdirectory(oxen-logging)
if(WITH_HIVE)
add_subdirectory(pybind11 EXCLUDE_FROM_ALL)
endif()
@ -92,12 +85,10 @@ add_library(uvw INTERFACE)
target_include_directories(uvw INTERFACE uvw/src)
target_link_libraries(uvw INTERFACE libuv)
# ngtcp2 needs some massaging to build nicely:
#include(ngtcp2_lib)
#add_ngtcp2_lib()
add_subdirectory(oxen-libquic)
add_subdirectory(span-lite)
# 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)

@ -1 +1 @@
Subproject commit 0f2504f7a2bfe4803e62cf7a9161806b0718ee5b
Subproject commit 33982d24a380268933ebea33976ad806e5c4e4bb

2
external/oxen-mq vendored

@ -1 +1 @@
Subproject commit e1b66ced4803e1b0cb05ed554cb5fc82b14c13c0
Subproject commit 68b3420bad5f0384f06d378b89ccdc06aba07465

@ -0,0 +1 @@
Subproject commit bc08bf87258d881aaa83b50c54dea67ea33d0e8e

@ -21,7 +21,7 @@ namespace llarp
struct Config;
struct Crypto;
struct CryptoManager;
struct AbstractRouter;
struct Router;
class NodeDB;
namespace thread
@ -40,7 +40,7 @@ namespace llarp
{
std::shared_ptr<Crypto> crypto = nullptr;
std::shared_ptr<CryptoManager> cryptoManager = nullptr;
std::shared_ptr<AbstractRouter> router = nullptr;
std::shared_ptr<Router> router = nullptr;
std::shared_ptr<EventLoop> loop = nullptr;
std::shared_ptr<NodeDB> nodedb = nullptr;
@ -89,7 +89,7 @@ namespace llarp
/// Creates a router. Can be overridden to allow a different class of router
/// to be created instead. Defaults to llarp::Router.
virtual std::shared_ptr<AbstractRouter>
virtual std::shared_ptr<Router>
makeRouter(const std::shared_ptr<EventLoop>& loop);
/// create the nodedb given our current configs

@ -2,7 +2,7 @@
#include "lokinet_jni_common.hpp"
#include <llarp.hpp>
#include <llarp/config/config.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
extern "C"
{
@ -85,7 +85,7 @@ extern "C"
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self)
{
if (auto ptr = GetImpl<llarp::Context>(env, self); ptr and ptr->router)
return ptr->router->OutboundUDPSocket();
return ptr->router->outbound_socket();
return -1;
}

@ -1,7 +1,6 @@
include(Version)
target_sources(lokinet-cryptography PRIVATE
crypto/crypto_libsodium.cpp
crypto/crypto.cpp
crypto/encrypted_frame.cpp
crypto/types.cpp
@ -103,7 +102,6 @@ endif()
add_library(lokinet-nodedb
STATIC
bootstrap.cpp
net/address_info.cpp
net/exit_info.cpp
net/traffic_policy.cpp
nodedb.cpp
@ -133,14 +131,14 @@ endforeach()
configure_file("bootstrap-fallbacks.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp" @ONLY)
target_sources(lokinet-nodedb PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp")
# lokinet-config is for all configuration types and parsers
add_library(lokinet-config
STATIC
config/config.cpp
config/definition.cpp
config/ini.cpp
config/key_manager.cpp)
config/key_manager.cpp
)
# lokinet-consensus is for deriving and tracking network consensus state for both service nodes and clients
add_library(lokinet-consensus
@ -148,85 +146,27 @@ add_library(lokinet-consensus
consensus/reachability_testing.cpp
)
# lokinet-dht holds all logic related to interacting with and participating in the DHT hashring
add_library(lokinet-dht
STATIC
dht/context.cpp
dht/dht.cpp
dht/explorenetworkjob.cpp
dht/localtaglookup.cpp
dht/localrouterlookup.cpp
dht/localserviceaddresslookup.cpp
dht/message.cpp
dht/messages/findintro.cpp
dht/messages/findrouter.cpp
dht/messages/gotintro.cpp
dht/messages/gotrouter.cpp
dht/messages/pubintro.cpp
dht/messages/findname.cpp
dht/messages/gotname.cpp
dht/publishservicejob.cpp
dht/recursiverouterlookup.cpp
dht/serviceaddresslookup.cpp
dht/taglookup.cpp
)
# lokinet-layer-flow is the flow layer which sits atop the routing layer which manages
# flows between lokinet snapp endpoints be they .loki or .snode
add_library(lokinet-layer-flow
STATIC
layers/flow/stub.cpp # todo: remove me
)
# lokinet-layer-onion is the "dumb" onion routing layer with builds manages and does i/o
# with onion paths. onion paths anonymize routing layer pdu.
add_library(lokinet-layer-onion
STATIC
path/ihophandler.cpp
path/abstracthophandler.cpp
path/path_context.cpp
path/path.cpp
path/pathbuilder.cpp
path/pathset.cpp
path/transit_hop.cpp
messages/relay.cpp
messages/relay_commit.cpp
messages/relay_status.cpp
)
# lokinet-layer-wire is a layer 1 analog which splits up
# layer 2 frames into layer 1 symbols which in the case of iwp are encrypted udp/ip packets
add_library(lokinet-layer-wire
STATIC
iwp/iwp.cpp
iwp/linklayer.cpp
iwp/message_buffer.cpp
iwp/session.cpp
)
# lokinet-layer-link is for our layer 2 analog which splits up layer 2 frames into
# a series of layer 1 symbols which are then transmitted between lokinet instances
add_library(lokinet-layer-link
STATIC
link/connection.cpp
link/contacts.cpp
link/link_manager.cpp
link/session.cpp
link/server.cpp
messages/dht_immediate.cpp
messages/link_intro.cpp
messages/link_message_parser.cpp
)
# lokinet-plainquic is for holding the tunneled plainquic code, not quic wire protocol code
add_library(lokinet-plainquic
STATIC
quic/address.cpp
quic/client.cpp
quic/connection.cpp
quic/endpoint.cpp
quic/null_crypto.cpp
quic/server.cpp
quic/stream.cpp
quic/tunnel.cpp
# link/tunnel.cpp
)
# lokinet-context holds the contextualized god objects for a lokinet instance
@ -235,7 +175,6 @@ add_library(lokinet-context
STATIC
context.cpp
link/link_manager.cpp
router/outbound_message_handler.cpp
router/rc_lookup_handler.cpp
router/rc_gossiper.cpp
router/router.cpp
@ -260,18 +199,6 @@ add_library(lokinet-peerstats
peerstats/types.cpp
)
# lokinet-layer-routing holds logic related to the routing layer
# routing layer is anonymized over the onion layer
add_library(lokinet-layer-routing
STATIC
routing/dht_message.cpp
routing/message_parser.cpp
routing/path_confirm_message.cpp
routing/path_latency_message.cpp
routing/path_transfer_message.cpp
routing/transfer_traffic_message.cpp
)
# kitchen sink to be removed after refactor
add_library(lokinet-service-deprecated-kitchensink
STATIC
@ -292,27 +219,19 @@ add_library(lokinet-service-deprecated-kitchensink
service/endpoint_state.cpp
service/endpoint_util.cpp
service/endpoint.cpp
service/hidden_service_address_lookup.cpp
service/identity.cpp
service/info.cpp
service/intro_set.cpp
service/intro.cpp
service/lns_tracker.cpp
service/lookup.cpp
service/name.cpp
service/outbound_context.cpp
service/protocol.cpp
service/router_lookup_job.cpp
service/sendcontext.cpp
service/session.cpp
service/tag.cpp
)
add_library(lokinet-layer-platform
STATIC
layers/platform/stub.cpp # todo: remove me
)
# interal tooling for pybind
add_library(lokinet-tooling INTERFACE)
@ -326,7 +245,6 @@ if(WITH_HIVE)
target_link_libraries(lokinet-tooling INTERFACE lokinet-hive-tooling)
endif()
# interface library for setting commone includes, linkage and flags.
add_library(lokinet-base INTERFACE)
target_include_directories(lokinet-base
@ -343,7 +261,6 @@ endif()
add_library(lokinet-layers INTERFACE)
add_library(lokinet-amalgum INTERFACE)
# helper function to link a library to lokinet-base, enable lto, add to lokinet-amalgum and then link to other libs
function(lokinet_link_lib libname)
message(DEBUG "created target: ${libname}")
@ -388,12 +305,7 @@ lokinet_link_lib(lokinet-context
lokinet-rpc
)
lokinet_link_lib(lokinet-dht
lokinet-util
lokinet-nodedb
)
lokinet_link_lib(lokinet-plainquic
lokinet_link_lib(
lokinet-platform
lokinet-config
)
@ -447,12 +359,8 @@ function(link_lokinet_layers)
endfunction()
link_lokinet_layers(
lokinet-layer-platform
lokinet-layer-flow
lokinet-layer-routing
lokinet-layer-onion
lokinet-layer-link
lokinet-layer-wire
)
# set me to OFF to disable old codepath
@ -463,10 +371,7 @@ if(use_old_impl)
lokinet-dns
lokinet-nodedb
lokinet-context
lokinet-plainquic
lokinet-layer-routing
lokinet-layer-onion
lokinet-dht
lokinet-platform
lokinet-rpc
)
@ -474,12 +379,8 @@ if(use_old_impl)
endif()
target_link_libraries(lokinet-layers INTERFACE
lokinet-layer-platform
lokinet-layer-flow
lokinet-layer-routing
lokinet-layer-onion
lokinet-layer-link
lokinet-layer-wire
)
@ -512,7 +413,7 @@ target_link_libraries(lokinet-util PUBLIC
oxenc::oxenc
)
target_link_libraries(lokinet-plainquic PUBLIC
target_link_libraries(lokinet-layer-link PUBLIC
quic
uvw
)

@ -19,7 +19,7 @@ namespace llarp::apple
}
std::shared_ptr<llarp::handlers::TunEndpoint> tun;
router->hiddenServiceContext().ForEachService([&tun](const auto& /*name*/, const auto ep) {
router->hidden_service_context().ForEachService([&tun](const auto& /*name*/, const auto ep) {
tun = std::dynamic_pointer_cast<llarp::handlers::TunEndpoint>(ep);
return !tun;
});
@ -33,13 +33,13 @@ namespace llarp::apple
if (enable)
tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, {dns_trampoline_port}}});
else
tun->ReconfigureDNS(router->GetConfig()->dns.m_upstreamDNS);
tun->ReconfigureDNS(router->config()->dns.m_upstreamDNS);
trampoline_active = enable;
}
void
RouteManager::AddDefaultRouteViaInterface(vpn::NetworkInterface&)
RouteManager::add_default_route_via_interface(vpn::NetworkInterface&)
{
check_trampoline(true);
if (callback_context and route_callbacks.add_default_route)
@ -47,7 +47,7 @@ namespace llarp::apple
}
void
RouteManager::DelDefaultRouteViaInterface(vpn::NetworkInterface&)
RouteManager::delete_default_route_via_interface(vpn::NetworkInterface&)
{
check_trampoline(false);
if (callback_context and route_callbacks.del_default_route)
@ -55,7 +55,7 @@ namespace llarp::apple
}
void
RouteManager::AddRouteViaInterface(vpn::NetworkInterface&, IPRange range)
RouteManager::add_route_via_interface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(true);
if (callback_context)
@ -78,7 +78,7 @@ namespace llarp::apple
}
void
RouteManager::DelRouteViaInterface(vpn::NetworkInterface&, IPRange range)
RouteManager::delete_route_via_interface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(false);
if (callback_context)

@ -1,12 +1,12 @@
#pragma once
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/vpn/platform.hpp>
#include "context_wrapper.h"
namespace llarp::apple
{
class RouteManager final : public llarp::vpn::IRouteManager
class RouteManager final : public llarp::vpn::AbstractRouteManager
{
public:
RouteManager(llarp::Context& ctx, llarp_route_callbacks rcs, void* callback_context)
@ -16,33 +16,31 @@ namespace llarp::apple
/// These are called for poking route holes, but we don't have to do that at all on macos
/// because the appex isn't subject to its own rules.
void
AddRoute(net::ipaddr_t /*ip*/, net::ipaddr_t /*gateway*/) override
add_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override
{}
void
DelRoute(net::ipaddr_t /*ip*/, net::ipaddr_t /*gateway*/) override
delete_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override
{}
void
AddDefaultRouteViaInterface(vpn::NetworkInterface& vpn) override;
add_default_route_via_interface(vpn::NetworkInterface& vpn) override;
void
DelDefaultRouteViaInterface(vpn::NetworkInterface& vpn) override;
delete_default_route_via_interface(vpn::NetworkInterface& vpn) override;
void
AddRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override;
add_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override;
void
DelRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override;
delete_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override;
std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(vpn::NetworkInterface& /*vpn*/) override
std::vector<oxen::quic::Address>
get_non_interface_gateways(vpn::NetworkInterface& /*vpn*/) override
{
// We can't get this on mac from our sandbox, but we don't actually need it because we
// ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP.
std::vector<net::ipaddr_t> ret;
ret.emplace_back(net::ipv4addr_t{});
return ret;
return std::vector<oxen::quic::Address>{};
}
private:

@ -1,7 +1,7 @@
#include "vpn_interface.hpp"
#include "context.hpp"
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
namespace llarp::apple
{
@ -9,7 +9,7 @@ namespace llarp::apple
Context& ctx,
packet_write_callback packet_writer,
on_readable_callback on_readable,
AbstractRouter* router)
Router* router)
: vpn::NetworkInterface{{}}
, m_PacketWriter{std::move(packet_writer)}
, m_OnReadable{std::move(on_readable)}

@ -20,7 +20,7 @@ namespace llarp::apple
Context& ctx,
packet_write_callback packet_writer,
on_readable_callback on_readable,
AbstractRouter* router);
Router* router);
// Method to call when a packet has arrived to deliver the packet to lokinet
bool
@ -50,7 +50,7 @@ namespace llarp::apple
thread::Queue<net::IPPacket> m_ReadQueue{PacketQueueSize};
AbstractRouter* const _router;
Router* const _router;
};
} // namespace llarp::apple

@ -9,15 +9,15 @@ namespace llarp::apple
VPNInterface::on_readable_callback on_readable,
llarp_route_callbacks route_callbacks,
void* callback_context)
: m_Context{ctx}
, m_RouteManager{ctx, std::move(route_callbacks), callback_context}
, m_PacketWriter{std::move(packet_writer)}
, m_OnReadable{std::move(on_readable)}
: _context{ctx}
, _route_manager{ctx, std::move(route_callbacks), callback_context}
, _packet_writer{std::move(packet_writer)}
, _read_cb{std::move(on_readable)}
{}
std::shared_ptr<vpn::NetworkInterface>
VPNPlatform::ObtainInterface(vpn::InterfaceInfo, AbstractRouter* router)
VPNPlatform::ObtainInterface(vpn::InterfaceInfo, Router* router)
{
return std::make_shared<VPNInterface>(m_Context, m_PacketWriter, m_OnReadable, router);
return std::make_shared<VPNInterface>(_context, _packet_writer, _read_cb, router);
}
} // namespace llarp::apple

@ -17,19 +17,19 @@ namespace llarp::apple
void* callback_context);
std::shared_ptr<vpn::NetworkInterface>
ObtainInterface(vpn::InterfaceInfo, AbstractRouter*) override;
ObtainInterface(vpn::InterfaceInfo, Router*) override;
vpn::IRouteManager&
vpn::AbstractRouteManager&
RouteManager() override
{
return m_RouteManager;
return _route_manager;
}
private:
Context& m_Context;
apple::RouteManager m_RouteManager;
VPNInterface::packet_write_callback m_PacketWriter;
VPNInterface::on_readable_callback m_OnReadable;
Context& _context;
apple::RouteManager _route_manager;
VPNInterface::packet_write_callback _packet_writer;
VPNInterface::on_readable_callback _read_cb;
};
} // namespace llarp::apple

@ -527,7 +527,7 @@ namespace llarp
arg = arg.substr(0, pos);
}
if (service::NameIsValid(arg))
if (service::is_valid_name(arg))
{
m_LNSExitMap.Insert(range, arg);
return;
@ -567,7 +567,7 @@ namespace llarp
const auto exit_str = arg.substr(0, pos);
auth.token = arg.substr(pos + 1);
if (service::NameIsValid(exit_str))
if (service::is_valid_name(exit_str))
{
m_LNSExitAuths.emplace(exit_str, auth);
return;
@ -1343,13 +1343,10 @@ namespace llarp
std::set<IPRange> seenRanges;
for (const auto& hop : rcs)
{
for (const auto& addr : hop.addrs)
const auto network_addr = net::In6ToHUInt(hop.addr.in6().sin6_addr) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{
const auto network_addr = net::In6ToHUInt(addr.ip) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{
return false;
}
return false;
}
}
return true;

@ -1,6 +1,8 @@
#pragma once
#include "ini.hpp"
#include "definition.hpp"
#include <llarp/net/traffic_policy.hpp>
#include <llarp/net/net.hpp>
#include <chrono>

@ -1,7 +1,7 @@
#include "reachability_testing.hpp"
#include <chrono>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/crypto/crypto.hpp>
@ -72,7 +72,7 @@ namespace llarp::consensus
}
std::optional<RouterID>
reachability_testing::next_random(AbstractRouter* router, const time_point_t& now, bool requeue)
reachability_testing::next_random(Router* router, const time_point_t& now, bool requeue)
{
if (next_general_test > now)
return std::nullopt;
@ -104,7 +104,7 @@ namespace llarp::consensus
// We exhausted the queue so repopulate it and try again
testing_queue.clear();
const auto all = router->GetRouterWhitelist();
const auto all = router->router_whitelist();
testing_queue.insert(testing_queue.begin(), all.begin(), all.end());
std::shuffle(testing_queue.begin(), testing_queue.end(), rng);

@ -12,7 +12,7 @@
namespace llarp
{
struct AbstractRouter;
struct Router;
}
namespace llarp::consensus
@ -117,8 +117,7 @@ namespace llarp::consensus
// `requeue` is mainly for internal use: if false it avoids rebuilding the queue if we run
// out (and instead just return nullopt).
std::optional<RouterID>
next_random(
AbstractRouter* router, const time_point_t& now = clock_t::now(), bool requeue = true);
next_random(Router* router, const time_point_t& now = clock_t::now(), bool requeue = true);
// Removes and returns up to MAX_RETESTS_PER_TICK nodes that are due to be tested (i.e.
// next-testing-time <= now). Returns [snrecord, #previous-failures] for each.

@ -6,41 +6,37 @@
#include <llarp/util/types.hpp>
#include <llarp/util/time.hpp>
namespace llarp
namespace llarp::path
{
namespace path
{
/// maximum path length
constexpr std::size_t max_len = 8;
/// default path length
constexpr std::size_t default_len = 4;
/// pad messages to the nearest this many bytes
constexpr std::size_t pad_size = 128;
/// default path lifetime in ms
constexpr std::chrono::milliseconds default_lifetime = 20min;
/// minimum into lifetime we will advertise
constexpr std::chrono::milliseconds min_intro_lifetime = default_lifetime / 2;
/// number of slices of path lifetime to spread intros out via
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).
constexpr std::size_t min_intro_paths = 4;
/// after this many ms a path build times out
constexpr auto build_timeout = 10s;
/// maximum path length
constexpr std::size_t MAX_LEN = 8;
/// default path length
constexpr std::size_t DEFAULT_LEN = 4;
/// pad messages to the nearest this many bytes
constexpr std::size_t PAD_SIZE = 128;
/// default path lifetime in ms
constexpr std::chrono::milliseconds DEFAULT_LIFETIME = 20min;
/// minimum intro lifetime we will advertise
constexpr std::chrono::milliseconds MIN_INTRO_LIFETIME = DEFAULT_LIFETIME / 2;
/// number of slices of path lifetime to spread intros out via
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).
constexpr std::size_t MIN_INTRO_PATHS = 4;
/// after this many ms a path build times out
constexpr auto BUILD_TIMEOUT = 10s;
/// measure latency every this interval ms
constexpr auto latency_interval = 20s;
/// if a path is inactive for this amount of time it's dead
constexpr auto alive_timeout = latency_interval * 1.5;
/// measure latency every this interval ms
constexpr auto LATENCY_INTERVAL = 20s;
/// if a path is inactive for this amount of time it's dead
constexpr auto ALIVE_TIMEOUT = LATENCY_INTERVAL * 1.5;
/// how big transit hop traffic queues are
constexpr std::size_t transit_hop_queue_size = 256;
/// how big transit hop traffic queues are
constexpr std::size_t TRANSIT_HOP_QUEUE_SIZE = 256;
} // namespace path
} // namespace llarp
} // namespace llarp::path

@ -3,8 +3,7 @@
#include "constants/evloop.hpp"
#include "config/config.hpp"
#include "crypto/crypto_libsodium.hpp"
#include "dht/context.hpp"
#include "crypto/crypto.hpp"
#include "ev/ev.hpp"
#include <memory>
#include "nodedb.hpp"
@ -75,7 +74,7 @@ namespace llarp
loop = EventLoop::create(jobQueueSize);
}
crypto = std::make_shared<sodium::CryptoLibSodium>();
crypto = std::make_shared<Crypto>();
cryptoManager = std::make_shared<CryptoManager>(crypto.get());
router = makeRouter(loop);
@ -90,10 +89,10 @@ namespace llarp
Context::makeNodeDB()
{
return std::make_shared<NodeDB>(
nodedb_dirname, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); });
nodedb_dirname, [r = router.get()](auto call) { r->queue_disk_io(std::move(call)); });
}
std::shared_ptr<AbstractRouter>
std::shared_ptr<Router>
Context::makeRouter(const EventLoop_ptr& loop)
{
return std::make_shared<Router>(loop, makeVPNPlatform());

@ -1,6 +1,569 @@
#include "crypto.hpp"
#include <sodium/core.h>
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_scalarmult.h>
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/crypto_stream_xchacha20.h>
#include <sodium/crypto_core_ed25519.h>
#include <sodium/crypto_aead_xchacha20poly1305.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <oxenc/endian.h>
#include <llarp/util/mem.hpp>
#include <llarp/util/str.hpp>
#include <cassert>
#include <cstring>
#ifdef HAVE_CRYPT
#include <crypt.h>
#endif
#include <llarp/util/str.hpp>
namespace llarp
{
Crypto* CryptoManager::m_crypto = nullptr;
}
static bool
dh(llarp::SharedSecret& out,
const PubKey& client_pk,
const PubKey& server_pk,
const uint8_t* themPub,
const SecretKey& usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if (crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out.data(), shared.size());
return true;
}
static bool
dh(uint8_t* out,
const uint8_t* client_pk,
const uint8_t* server_pk,
const uint8_t* themPub,
const uint8_t* usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if (crypto_scalarmult_curve25519(shared.data(), usSec, themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk, 32);
crypto_generichash_blake2b_update(&h, server_pk, 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out, shared.size());
return true;
}
static bool
dh_client_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, sk.toPublic(), pk, pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_client - dh failed");
return false;
}
static bool
dh_server_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
static bool
dh_server_priv(uint8_t* shared, const uint8_t* pk, const uint8_t* sk, const uint8_t* nonce)
{
llarp::SharedSecret dh_result;
if (dh(dh_result.data(), pk, sk, pk, sk))
{
return crypto_generichash_blake2b(shared, 32, nonce, 32, dh_result.data(), 32) != -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
Crypto::Crypto()
{
if (sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char* avx2 = std::getenv("AVX2_FORCE_DISABLE");
if (avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
randombytes(reinterpret_cast<unsigned char*>(&seed), sizeof(seed));
srand(seed);
}
std::optional<AlignedBuffer<32>>
Crypto::maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name)
{
const auto payloadsize = ciphertext.size() - crypto_aead_xchacha20poly1305_ietf_ABYTES;
if (payloadsize != 32)
return {};
SharedSecret derivedKey{};
ShortHash namehash{};
ustring name_buf{reinterpret_cast<const uint8_t*>(name.data()), name.size()};
if (not shorthash(namehash, name_buf.data(), name_buf.size()))
return {};
if (not hmac(derivedKey.data(), name_buf.data(), derivedKey.size(), namehash))
return {};
AlignedBuffer<32> result{};
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
result.data(),
nullptr,
nullptr,
reinterpret_cast<const byte_t*>(ciphertext.data()),
ciphertext.size(),
nullptr,
0,
nounce.data(),
derivedKey.data())
== -1)
{
return {};
}
return result;
}
bool
Crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const TunnelNonce& n)
{
return crypto_stream_xchacha20_xor(buf, buf, size, n.data(), k.data()) == 0;
}
bool
Crypto::xchacha20(uint8_t* buf, size_t size, const uint8_t* secret, const uint8_t* nonce)
{
return crypto_stream_xchacha20_xor(buf, buf, size, nonce, secret) == 0;
}
bool
Crypto::dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
Crypto::dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
Crypto::dh_server(
uint8_t* shared_secret,
const uint8_t* other_pk,
const uint8_t* local_pk,
const uint8_t* nonce)
{
return dh_server_priv(shared_secret, other_pk, local_pk, nonce);
}
/// transport dh client side
bool
Crypto::transport_dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// transport dh server side
bool
Crypto::transport_dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
Crypto::shorthash(ShortHash& result, uint8_t* buf, size_t size)
{
return crypto_generichash_blake2b(result.data(), ShortHash::SIZE, buf, size, nullptr, 0) != -1;
}
bool
Crypto::hmac(uint8_t* result, uint8_t* buf, size_t size, const SharedSecret& secret)
{
return crypto_generichash_blake2b(result, HMACSIZE, buf, size, secret.data(), HMACSECSIZE)
!= -1;
}
static bool
hash(uint8_t* result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1;
}
bool
Crypto::sign(Signature& sig, const SecretKey& secret, uint8_t* buf, size_t size)
{
return crypto_sign_detached(sig.data(), nullptr, buf, size, secret.data()) != -1;
}
bool
Crypto::sign(uint8_t* sig, uint8_t* sk, uint8_t* buf, size_t size)
{
return crypto_sign_detached(sig, nullptr, buf, size, sk) != -1;
}
bool
Crypto::sign(uint8_t* sig, const SecretKey& sk, ustring_view buf)
{
return crypto_sign_detached(sig, nullptr, buf.data(), buf.size(), sk.data()) != -1;
}
bool
Crypto::sign(Signature& sig, const PrivateKey& privkey, uint8_t* buf, size_t size)
{
PubKey pubkey;
privkey.toPublic(pubkey);
crypto_hash_sha512_state hs;
unsigned char nonce[64];
unsigned char hram[64];
unsigned char mulres[32];
// r = H(s || M) where here s is pseudorandom bytes typically generated as
// part of hashing the seed (i.e. [a,s] = H(k)), but for derived
// PrivateKeys will come from a hash of the root key's s concatenated with
// the derivation hash.
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, privkey.signingHash(), 32);
crypto_hash_sha512_update(&hs, buf, size);
crypto_hash_sha512_final(&hs, nonce);
crypto_core_ed25519_scalar_reduce(nonce, nonce);
// copy pubkey into sig to make (for now) sig = (R || A)
memmove(sig.data() + 32, pubkey.data(), 32);
// R = r * B
crypto_scalarmult_ed25519_base_noclamp(sig.data(), nonce);
// hram = H(R || A || M)
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, sig.data(), 64);
crypto_hash_sha512_update(&hs, buf, size);
crypto_hash_sha512_final(&hs, hram);
// S = r + H(R || A || M) * s, so sig = (R || S)
crypto_core_ed25519_scalar_reduce(hram, hram);
crypto_core_ed25519_scalar_mul(mulres, hram, privkey.data());
crypto_core_ed25519_scalar_add(sig.data() + 32, mulres, nonce);
sodium_memzero(nonce, sizeof nonce);
return true;
}
bool
Crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig)
{
return crypto_sign_verify_detached(sig.data(), buf, size, pub.data()) != -1;
}
bool
Crypto::verify(ustring_view pub, ustring_view buf, ustring_view sig)
{
return (pub.size() == 32 && sig.size() == 64)
? crypto_sign_verify_detached(sig.data(), buf.data(), buf.size(), pub.data()) != -1
: false;
}
bool
Crypto::verify(uint8_t* pub, uint8_t* buf, size_t size, uint8_t* sig)
{
return crypto_sign_verify_detached(sig, buf, size, pub) != -1;
}
/// clamp a 32 byte ec point
static void
clamp_ed25519(byte_t* out)
{
out[0] &= 248;
out[31] &= 127;
out[31] |= 64;
}
template <typename K>
static K
clamp(const K& p)
{
K out = p;
clamp_ed25519(out);
return out;
}
template <typename K>
static bool
is_clamped(const K& key)
{
K other(key);
clamp_ed25519(other.data());
return other == key;
}
constexpr static char derived_key_hash_str[161] =
"just imagine what would happen if we all decided to understand. you "
"can't in the and by be or then before so just face it this text hurts "
"to read? lokinet yolo!";
template <typename K>
static bool
make_scalar(AlignedBuffer<32>& out, const K& k, uint64_t i)
{
// b = BLIND-STRING || k || i
std::array<byte_t, 160 + K::SIZE + sizeof(uint64_t)> buf;
std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin());
std::copy(k.begin(), k.end(), buf.begin() + 160);
oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE);
// n = H(b)
// h = make_point(n)
ShortHash n;
return -1
!= crypto_generichash_blake2b(n.data(), ShortHash::SIZE, buf.data(), buf.size(), nullptr, 0)
&& -1 != crypto_core_ed25519_from_uniform(out.data(), n.data());
}
static AlignedBuffer<32> zero;
bool
Crypto::derive_subkey(
PubKey& out_pubkey, const PubKey& root_pubkey, uint64_t key_n, const AlignedBuffer<32>* hash)
{
// scalar h = H( BLIND-STRING || root_pubkey || key_n )
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data());
}
bool
Crypto::derive_subkey_private(
PrivateKey& out_key, const SecretKey& root_key, uint64_t key_n, const AlignedBuffer<32>* hash)
{
// Derives a private subkey from a root key.
//
// The basic idea is:
//
// h = H( BLIND-STRING || A || key_n )
// a - private key
// A = aB - public key
// s - signing hash
// a' = ah - derived private key
// A' = a'B = (ah)B - derived public key
// s' = H(h || s) - derived signing hash
//
// libsodium throws some wrenches in the mechanics which are a nuisance,
// the biggest of which is that sodium's secret key is *not* `a`; rather
// it is the seed. If you want to get the private key (i.e. "a"), you
// need to SHA-512 hash it and then clamp that.
//
// This also makes signature verification harder: we can't just use
// sodium's sign function because it wants to be given the seed rather
// than the private key, and moreover we can't actually *get* the seed to
// make libsodium happy because we only have `ah` above; thus we
// reimplemented most of sodium's detached signing function but without
// the hash step.
//
// Lastly, for the signing hash s', we need some value that is both
// different from the root s but also unknowable from the public key
// (since otherwise `r` in the signing function would be known), so we
// generate it from a hash of `h` and the root key's (psuedorandom)
// signing hash, `s`.
//
const auto root_pubkey = root_key.toPublic();
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
h[0] &= 248;
h[31] &= 63;
h[31] |= 64;
PrivateKey a;
if (!root_key.toPrivate(a))
return false;
// a' = ha
crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data());
// s' = H(h || s)
std::array<byte_t, 64> buf;
std::copy(h.begin(), h.end(), buf.begin());
std::copy(a.signingHash(), a.signingHash() + 32, buf.begin() + 32);
return -1
!= crypto_generichash_blake2b(
out_key.signingHash(), 32, buf.data(), buf.size(), nullptr, 0);
return true;
}
bool
Crypto::seed_to_secretkey(llarp::SecretKey& secret, const llarp::IdentitySecret& seed)
{
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void
Crypto::randomize(const llarp_buffer_t& buff)
{
randombytes((unsigned char*)buff.base, buff.sz);
}
void
Crypto::randbytes(byte_t* ptr, size_t sz)
{
randombytes((unsigned char*)ptr, sz);
}
void
Crypto::identity_keygen(llarp::SecretKey& keys)
{
PubKey pk;
int result = crypto_sign_keypair(pk.data(), keys.data());
assert(result != -1);
const PubKey sk_pk = keys.toPublic();
assert(pk == sk_pk);
(void)result;
(void)sk_pk;
// encryption_keygen(keys);
}
bool
Crypto::check_identity_privkey(const llarp::SecretKey& keys)
{
AlignedBuffer<crypto_sign_SEEDBYTES> seed;
llarp::PubKey pk;
llarp::SecretKey sk;
if (crypto_sign_ed25519_sk_to_seed(seed.data(), keys.data()) == -1)
return false;
if (crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()) == -1)
return false;
return keys.toPublic() == pk && sk == keys;
}
void
Crypto::encryption_keygen(llarp::SecretKey& keys)
{
auto d = keys.data();
randbytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
bool
Crypto::pqe_encrypt(PQCipherBlock& ciphertext, SharedSecret& sharedkey, const PQPubKey& pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1;
}
bool
Crypto::pqe_decrypt(
const PQCipherBlock& ciphertext, SharedSecret& sharedkey, const byte_t* secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1;
}
void
Crypto::pqe_keygen(PQKeyPair& keypair)
{
auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
}
bool
Crypto::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;
}
const byte_t*
seckey_topublic(const SecretKey& sec)
{
return sec.data() + 32;
}
const byte_t*
pq_keypair_to_public(const PQKeyPair& k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t*
pq_keypair_to_secret(const PQKeyPair& k)
{
return k.data();
}
uint64_t
randint()
{
uint64_t i;
randombytes((byte_t*)&i, sizeof(i));
return i;
}
} // namespace llarp

@ -9,105 +9,122 @@
#include <cstdint>
/**
* crypto.hpp
*
* libsodium abstraction layer
* potentially allow libssl support in the future
*/
namespace llarp
{
/// library crypto configuration
/*
TODO:
- make uint8_t pointers const where needed
-
*/
struct Crypto
{
virtual ~Crypto() = 0;
Crypto();
/// decrypt cipherText name given the key generated from name
virtual std::optional<AlignedBuffer<32>>
maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name) = 0;
~Crypto() = default;
/// xchacha symmetric cipher
virtual bool
xchacha20(const llarp_buffer_t&, const SharedSecret&, const TunnelNonce&) = 0;
/// decrypt cipherText given the key generated from name
std::optional<AlignedBuffer<32>>
maybe_decrypt_name(std::string_view ciphertext, SymmNonce nounce, std::string_view name);
/// xchacha symmetric cipher (multibuffer)
virtual bool
xchacha20_alt(
const llarp_buffer_t&, const llarp_buffer_t&, const SharedSecret&, const byte_t*) = 0;
/// xchacha symmetric cipher
bool
xchacha20(uint8_t*, size_t size, const SharedSecret&, const TunnelNonce&);
bool
xchacha20(uint8_t*, size_t size, const uint8_t*, const uint8_t*);
/// path dh creator's side
virtual bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// path dh relay side
virtual bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
bool
dh_server(
uint8_t* shared_secret,
const uint8_t* other_pk,
const uint8_t* local_pk,
const uint8_t* nonce);
/// transport dh client side
virtual bool
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// transport dh server side
virtual bool
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) = 0;
bool
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
/// blake2b 256 bit
virtual bool
shorthash(ShortHash&, const llarp_buffer_t&) = 0;
/// blake2s 256 bit "hmac" (keyed hash)
virtual bool
hmac(byte_t*, const llarp_buffer_t&, const SharedSecret&) = 0;
bool
shorthash(ShortHash&, uint8_t*, size_t size);
/// blake2s 256 bit hmac
bool
hmac(uint8_t*, uint8_t*, size_t, const SharedSecret&);
/// ed25519 sign
virtual bool
sign(Signature&, const SecretKey&, const llarp_buffer_t&) = 0;
bool
sign(Signature&, const SecretKey&, uint8_t* buf, size_t size);
/// ed25519 sign, using pointers
bool
sign(uint8_t* sig, uint8_t* sk, uint8_t* buf, size_t size);
bool
sign(uint8_t* sig, const SecretKey& sk, ustring_view buf);
/// ed25519 sign (custom with derived keys)
virtual bool
sign(Signature&, const PrivateKey&, const llarp_buffer_t&) = 0;
bool
sign(Signature&, const PrivateKey&, uint8_t* buf, size_t size);
/// ed25519 verify
virtual bool
verify(const PubKey&, const llarp_buffer_t&, const Signature&) = 0;
/// derive sub keys for public keys
virtual bool
derive_subkey(PubKey&, const PubKey&, uint64_t, const AlignedBuffer<32>* = nullptr) = 0;
/// derive sub keys for private keys
virtual bool
bool
verify(const PubKey&, uint8_t*, size_t, const Signature&);
bool verify(ustring_view, ustring_view, ustring_view);
bool
verify(uint8_t*, uint8_t*, size_t, uint8_t*);
/// derive sub keys for public keys. hash is really only intended for
/// testing ands key_n if given.
bool
derive_subkey(
PubKey& derived,
const PubKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr);
/// derive sub keys for private keys. hash is really only intended for
/// testing ands key_n if given.
bool
derive_subkey_private(
PrivateKey&, const SecretKey&, uint64_t, const AlignedBuffer<32>* = nullptr) = 0;
PrivateKey& derived,
const SecretKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr);
/// seed to secretkey
virtual bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&) = 0;
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&);
/// randomize buffer
virtual void
randomize(const llarp_buffer_t&) = 0;
void
randomize(const llarp_buffer_t&);
/// randomizer memory
virtual void
randbytes(byte_t*, size_t) = 0;
void
randbytes(byte_t*, size_t);
/// generate signing keypair
virtual void
identity_keygen(SecretKey&) = 0;
void
identity_keygen(SecretKey&);
/// generate encryption keypair
virtual void
encryption_keygen(SecretKey&) = 0;
void
encryption_keygen(SecretKey&);
/// generate post quantum encrytion key
virtual void
pqe_keygen(PQKeyPair&) = 0;
void
pqe_keygen(PQKeyPair&);
/// post quantum decrypt (buffer, sharedkey_dst, sec)
virtual bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*) = 0;
bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*);
/// post quantum encrypt (buffer, sharedkey_dst, pub)
virtual bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&) = 0;
bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&);
virtual bool
check_identity_privkey(const SecretKey&) = 0;
bool
check_identity_privkey(const SecretKey&);
/// check if a password hash string matches the challenge
virtual bool
check_passwd_hash(std::string pwhash, std::string challenge) = 0;
bool
check_passwd_hash(std::string pwhash, std::string challenge);
};
inline Crypto::~Crypto() = default;
/// return random 64bit unsigned interger
uint64_t
randint();

@ -1,518 +0,0 @@
#include "crypto_libsodium.hpp"
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_scalarmult.h>
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/crypto_stream_xchacha20.h>
#include <sodium/crypto_core_ed25519.h>
#include <sodium/crypto_aead_xchacha20poly1305.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <oxenc/endian.h>
#include <llarp/util/mem.hpp>
#include <llarp/util/str.hpp>
#include <cassert>
#include <cstring>
#ifdef HAVE_CRYPT
#include <crypt.h>
#endif
#include <llarp/util/str.hpp>
extern "C"
{
extern int
sodium_init(void);
}
namespace llarp
{
namespace sodium
{
static bool
dh(llarp::SharedSecret& out,
const PubKey& client_pk,
const PubKey& server_pk,
const uint8_t* themPub,
const SecretKey& usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if (crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out.data(), shared.size());
return true;
}
static bool
dh_client_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, sk.toPublic(), pk, pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_client - dh failed");
return false;
}
static bool
dh_server_priv(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
llarp::SharedSecret dh_result;
if (dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
!= -1;
}
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
CryptoLibSodium::CryptoLibSodium()
{
if (sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char* avx2 = std::getenv("AVX2_FORCE_DISABLE");
if (avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
randombytes(reinterpret_cast<unsigned char*>(&seed), sizeof(seed));
srand(seed);
}
std::optional<AlignedBuffer<32>>
CryptoLibSodium::maybe_decrypt_name(
std::string_view ciphertext, SymmNonce nounce, std::string_view name)
{
const auto payloadsize = ciphertext.size() - crypto_aead_xchacha20poly1305_ietf_ABYTES;
if (payloadsize != 32)
return {};
SharedSecret derivedKey{};
ShortHash namehash{};
const llarp_buffer_t namebuf(reinterpret_cast<const char*>(name.data()), name.size());
if (not shorthash(namehash, namebuf))
return {};
if (not hmac(derivedKey.data(), namebuf, namehash))
return {};
AlignedBuffer<32> result{};
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
result.data(),
nullptr,
nullptr,
reinterpret_cast<const byte_t*>(ciphertext.data()),
ciphertext.size(),
nullptr,
0,
nounce.data(),
derivedKey.data())
== -1)
{
return {};
}
return result;
}
bool
CryptoLibSodium::xchacha20(
const llarp_buffer_t& buff, const SharedSecret& k, const TunnelNonce& n)
{
return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz, n.data(), k.data()) == 0;
}
bool
CryptoLibSodium::xchacha20_alt(
const llarp_buffer_t& out, const llarp_buffer_t& in, const SharedSecret& k, const byte_t* n)
{
if (in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data()) == 0;
}
bool
CryptoLibSodium::dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
CryptoLibSodium::dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
/// transport dh client side
bool
CryptoLibSodium::transport_dh_client(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// transport dh server side
bool
CryptoLibSodium::transport_dh_server(
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
CryptoLibSodium::shorthash(ShortHash& result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(
result.data(), ShortHash::SIZE, buff.base, buff.sz, nullptr, 0)
!= -1;
}
bool
CryptoLibSodium::hmac(byte_t* result, const llarp_buffer_t& buff, const SharedSecret& secret)
{
return crypto_generichash_blake2b(
result, HMACSIZE, buff.base, buff.sz, secret.data(), HMACSECSIZE)
!= -1;
}
static bool
hash(uint8_t* result, const llarp_buffer_t& buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1;
}
bool
CryptoLibSodium::sign(Signature& sig, const SecretKey& secret, const llarp_buffer_t& buf)
{
return crypto_sign_detached(sig.data(), nullptr, buf.base, buf.sz, secret.data()) != -1;
}
bool
CryptoLibSodium::sign(Signature& sig, const PrivateKey& privkey, const llarp_buffer_t& buf)
{
PubKey pubkey;
privkey.toPublic(pubkey);
crypto_hash_sha512_state hs;
unsigned char nonce[64];
unsigned char hram[64];
unsigned char mulres[32];
// r = H(s || M) where here s is pseudorandom bytes typically generated as
// part of hashing the seed (i.e. [a,s] = H(k)), but for derived
// PrivateKeys will come from a hash of the root key's s concatenated with
// the derivation hash.
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, privkey.signingHash(), 32);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, nonce);
crypto_core_ed25519_scalar_reduce(nonce, nonce);
// copy pubkey into sig to make (for now) sig = (R || A)
memmove(sig.data() + 32, pubkey.data(), 32);
// R = r * B
crypto_scalarmult_ed25519_base_noclamp(sig.data(), nonce);
// hram = H(R || A || M)
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, sig.data(), 64);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, hram);
// S = r + H(R || A || M) * s, so sig = (R || S)
crypto_core_ed25519_scalar_reduce(hram, hram);
crypto_core_ed25519_scalar_mul(mulres, hram, privkey.data());
crypto_core_ed25519_scalar_add(sig.data() + 32, mulres, nonce);
sodium_memzero(nonce, sizeof nonce);
return true;
}
bool
CryptoLibSodium::verify(const PubKey& pub, const llarp_buffer_t& buf, const Signature& sig)
{
return crypto_sign_verify_detached(sig.data(), buf.base, buf.sz, pub.data()) != -1;
}
/// clamp a 32 byte ec point
static void
clamp_ed25519(byte_t* out)
{
out[0] &= 248;
out[31] &= 127;
out[31] |= 64;
}
template <typename K>
static K
clamp(const K& p)
{
K out = p;
clamp_ed25519(out);
return out;
}
template <typename K>
static bool
is_clamped(const K& key)
{
K other(key);
clamp_ed25519(other.data());
return other == key;
}
constexpr static char derived_key_hash_str[161] =
"just imagine what would happen if we all decided to understand. you "
"can't in the and by be or then before so just face it this text hurts "
"to read? lokinet yolo!";
template <typename K>
static bool
make_scalar(AlignedBuffer<32>& out, const K& k, uint64_t i)
{
// b = BLIND-STRING || k || i
std::array<byte_t, 160 + K::SIZE + sizeof(uint64_t)> buf;
std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin());
std::copy(k.begin(), k.end(), buf.begin() + 160);
oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE);
// n = H(b)
// h = make_point(n)
ShortHash n;
return -1
!= crypto_generichash_blake2b(
n.data(), ShortHash::SIZE, buf.data(), buf.size(), nullptr, 0)
&& -1 != crypto_core_ed25519_from_uniform(out.data(), n.data());
}
static AlignedBuffer<32> zero;
bool
CryptoLibSodium::derive_subkey(
PubKey& out_pubkey,
const PubKey& root_pubkey,
uint64_t key_n,
const AlignedBuffer<32>* hash)
{
// scalar h = H( BLIND-STRING || root_pubkey || key_n )
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data());
}
bool
CryptoLibSodium::derive_subkey_private(
PrivateKey& out_key,
const SecretKey& root_key,
uint64_t key_n,
const AlignedBuffer<32>* hash)
{
// Derives a private subkey from a root key.
//
// The basic idea is:
//
// h = H( BLIND-STRING || A || key_n )
// a - private key
// A = aB - public key
// s - signing hash
// a' = ah - derived private key
// A' = a'B = (ah)B - derived public key
// s' = H(h || s) - derived signing hash
//
// libsodium throws some wrenches in the mechanics which are a nuisance,
// the biggest of which is that sodium's secret key is *not* `a`; rather
// it is the seed. If you want to get the private key (i.e. "a"), you
// need to SHA-512 hash it and then clamp that.
//
// This also makes signature verification harder: we can't just use
// sodium's sign function because it wants to be given the seed rather
// than the private key, and moreover we can't actually *get* the seed to
// make libsodium happy because we only have `ah` above; thus we
// reimplemented most of sodium's detached signing function but without
// the hash step.
//
// Lastly, for the signing hash s', we need some value that is both
// different from the root s but also unknowable from the public key
// (since otherwise `r` in the signing function would be known), so we
// generate it from a hash of `h` and the root key's (psuedorandom)
// signing hash, `s`.
//
const auto root_pubkey = root_key.toPublic();
AlignedBuffer<32> h;
if (hash)
h = *hash;
else if (not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
h[0] &= 248;
h[31] &= 63;
h[31] |= 64;
PrivateKey a;
if (!root_key.toPrivate(a))
return false;
// a' = ha
crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data());
// s' = H(h || s)
std::array<byte_t, 64> buf;
std::copy(h.begin(), h.end(), buf.begin());
std::copy(a.signingHash(), a.signingHash() + 32, buf.begin() + 32);
return -1
!= crypto_generichash_blake2b(
out_key.signingHash(), 32, buf.data(), buf.size(), nullptr, 0);
return true;
}
bool
CryptoLibSodium::seed_to_secretkey(llarp::SecretKey& secret, const llarp::IdentitySecret& seed)
{
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void
CryptoLibSodium::randomize(const llarp_buffer_t& buff)
{
randombytes((unsigned char*)buff.base, buff.sz);
}
void
CryptoLibSodium::randbytes(byte_t* ptr, size_t sz)
{
randombytes((unsigned char*)ptr, sz);
}
void
CryptoLibSodium::identity_keygen(llarp::SecretKey& keys)
{
PubKey pk;
int result = crypto_sign_keypair(pk.data(), keys.data());
assert(result != -1);
const PubKey sk_pk = keys.toPublic();
assert(pk == sk_pk);
(void)result;
(void)sk_pk;
// encryption_keygen(keys);
}
bool
CryptoLibSodium::check_identity_privkey(const llarp::SecretKey& keys)
{
AlignedBuffer<crypto_sign_SEEDBYTES> seed;
llarp::PubKey pk;
llarp::SecretKey sk;
if (crypto_sign_ed25519_sk_to_seed(seed.data(), keys.data()) == -1)
return false;
if (crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()) == -1)
return false;
return keys.toPublic() == pk && sk == keys;
}
void
CryptoLibSodium::encryption_keygen(llarp::SecretKey& keys)
{
auto d = keys.data();
randbytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
bool
CryptoLibSodium::pqe_encrypt(
PQCipherBlock& ciphertext, SharedSecret& sharedkey, const PQPubKey& pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1;
}
bool
CryptoLibSodium::pqe_decrypt(
const PQCipherBlock& ciphertext, SharedSecret& sharedkey, const byte_t* secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1;
}
void
CryptoLibSodium::pqe_keygen(PQKeyPair& keypair)
{
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*
seckey_topublic(const SecretKey& sec)
{
return sec.data() + 32;
}
const byte_t*
pq_keypair_to_public(const PQKeyPair& k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t*
pq_keypair_to_secret(const PQKeyPair& k)
{
return k.data();
}
uint64_t
randint()
{
uint64_t i;
randombytes((byte_t*)&i, sizeof(i));
return i;
}
} // namespace llarp

@ -1,113 +0,0 @@
#pragma once
#include "crypto.hpp"
namespace llarp
{
namespace sodium
{
struct CryptoLibSodium final : public Crypto
{
CryptoLibSodium();
~CryptoLibSodium() override = default;
/// decrypt cipherText given the key generated from name
std::optional<AlignedBuffer<32>>
maybe_decrypt_name(
std::string_view ciphertext, SymmNonce nounce, std::string_view name) override;
/// xchacha symmetric cipher
bool
xchacha20(const llarp_buffer_t&, const SharedSecret&, const TunnelNonce&) override;
/// xchacha symmetric cipher (multibuffer)
bool
xchacha20_alt(
const llarp_buffer_t&,
const llarp_buffer_t&,
const SharedSecret&,
const byte_t*) override;
/// path dh creator's side
bool
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// path dh relay side
bool
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// transport dh client side
bool
transport_dh_client(
SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// transport dh server side
bool
transport_dh_server(
SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&) override;
/// blake2b 256 bit
bool
shorthash(ShortHash&, const llarp_buffer_t&) override;
/// blake2s 256 bit hmac
bool
hmac(byte_t*, const llarp_buffer_t&, const SharedSecret&) override;
/// ed25519 sign
bool
sign(Signature&, const SecretKey&, const llarp_buffer_t&) override;
/// ed25519 sign (custom with derived keys)
bool
sign(Signature&, const PrivateKey&, const llarp_buffer_t&) override;
/// ed25519 verify
bool
verify(const PubKey&, const llarp_buffer_t&, const Signature&) override;
/// derive sub keys for public keys. hash is really only intended for
/// testing and overrides key_n if given.
bool
derive_subkey(
PubKey& derived,
const PubKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr) override;
/// derive sub keys for private keys. hash is really only intended for
/// testing and overrides key_n if given.
bool
derive_subkey_private(
PrivateKey& derived,
const SecretKey& root,
uint64_t key_n,
const AlignedBuffer<32>* hash = nullptr) override;
/// seed to secretkey
bool
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&) override;
/// randomize buffer
void
randomize(const llarp_buffer_t&) override;
/// randomizer memory
void
randbytes(byte_t*, size_t) override;
/// generate signing keypair
void
identity_keygen(SecretKey&) override;
/// generate encryption keypair
void
encryption_keygen(SecretKey&) override;
/// generate post quantum encrytion key
void
pqe_keygen(PQKeyPair&) override;
/// post quantum decrypt (buffer, sharedkey_dst, sec)
bool
pqe_decrypt(const PQCipherBlock&, SharedSecret&, const byte_t*) override;
/// post quantum encrypt (buffer, sharedkey_dst, pub)
bool
pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&) override;
bool
check_identity_privkey(const SecretKey&) override;
bool
check_passwd_hash(std::string pwhash, std::string challenge) override;
};
} // namespace sodium
} // namespace llarp

@ -9,42 +9,29 @@ namespace llarp
bool
EncryptedFrame::DoEncrypt(const SharedSecret& shared, bool noDH)
{
byte_t* hash = data();
byte_t* noncePtr = hash + SHORTHASHSIZE;
byte_t* pubkey = noncePtr + TUNNONCESIZE;
byte_t* body = pubkey + PUBKEYSIZE;
auto crypto = CryptoManager::instance();
// if noDH flag, means key exchange has already taken place
// in this case, set pubkey to random noise and choose a
// random nonce here
uint8_t* hash_ptr = data();
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
uint8_t* pubkey_ptr = nonce_ptr + TUNNONCESIZE;
uint8_t* body_ptr = pubkey_ptr + PUBKEYSIZE;
if (noDH)
{
crypto->randbytes(noncePtr, TUNNONCESIZE);
crypto->randbytes(pubkey, PUBKEYSIZE);
crypto->randbytes(nonce_ptr, TUNNONCESIZE);
crypto->randbytes(pubkey_ptr, PUBKEYSIZE);
}
TunnelNonce nonce(noncePtr);
llarp_buffer_t buf;
buf.base = body;
buf.cur = buf.base;
buf.sz = size() - EncryptedFrameOverheadSize;
TunnelNonce nonce(nonce_ptr);
// encrypt body
if (!crypto->xchacha20(buf, shared, nonce))
if (!crypto->xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("encrypt failed");
return false;
}
// generate message auth
buf.base = noncePtr;
buf.cur = buf.base;
buf.sz = size() - SHORTHASHSIZE;
if (!crypto->hmac(hash, buf, shared))
if (!crypto->hmac(hash_ptr, nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Failed to generate message auth");
return false;
@ -89,36 +76,28 @@ namespace llarp
bool
EncryptedFrame::DoDecrypt(const SharedSecret& shared)
{
ShortHash hash(data());
byte_t* noncePtr = data() + SHORTHASHSIZE;
byte_t* body = data() + EncryptedFrameOverheadSize;
TunnelNonce nonce(noncePtr);
auto crypto = CryptoManager::instance();
llarp_buffer_t buf;
buf.base = noncePtr;
buf.cur = buf.base;
buf.sz = size() - SHORTHASHSIZE;
uint8_t* hash_ptr = data();
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
uint8_t* body_ptr = hash_ptr + EncryptedFrameOverheadSize;
TunnelNonce nonce(nonce_ptr);
ShortHash digest;
if (!crypto->hmac(digest.data(), buf, shared))
if (!crypto->hmac(digest.data(), nonce_ptr, size() - SHORTHASHSIZE, shared))
{
llarp::LogError("Digest failed");
return false;
}
if (!std::equal(digest.begin(), digest.end(), hash.begin()))
if (!std::equal(digest.begin(), digest.end(), hash_ptr))
{
llarp::LogError("message authentication failed");
return false;
}
buf.base = body;
buf.cur = body;
buf.sz = size() - EncryptedFrameOverheadSize;
if (!crypto->xchacha20(buf, shared, nonce))
if (!crypto->xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
{
llarp::LogError("decrypt failed");
return false;

@ -22,6 +22,14 @@ namespace llarp
return true;
}
PubKey
PubKey::from_string(const std::string& s)
{
PubKey p;
oxenc::from_hex(s.begin(), s.end(), p.begin());
return p;
}
std::string
PubKey::ToString() const
{
@ -91,7 +99,7 @@ namespace llarp
{
std::string tmp(128, 0);
llarp_buffer_t buf(tmp);
if (!BEncode(&buf))
if (!bt_encode(&buf))
return false;
tmp.resize(buf.cur - buf.base);

@ -21,7 +21,7 @@ namespace llarp
explicit PubKey(const byte_t* ptr) : AlignedBuffer<SIZE>(ptr)
{}
explicit PubKey(const Data& data) : AlignedBuffer<SIZE>(data)
explicit PubKey(const std::array<byte_t, SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
explicit PubKey(const AlignedBuffer<SIZE>& other) : AlignedBuffer<SIZE>(other)
@ -33,6 +33,9 @@ namespace llarp
bool
FromString(const std::string& str);
static PubKey
from_string(const std::string& s);
operator RouterID() const
{
return {as_array()};

@ -8,224 +8,221 @@
#include <set>
#include <vector>
namespace llarp
namespace llarp::dht
{
namespace dht
template <typename Val_t>
struct Bucket
{
template <typename Val_t>
struct Bucket
{
using BucketStorage_t = std::map<Key_t, Val_t, XorMetric>;
using Random_t = std::function<uint64_t()>;
using BucketStorage_t = std::map<Key_t, Val_t, XorMetric>;
using Random_t = std::function<uint64_t()>;
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(std::move(r))
{}
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(std::move(r))
{}
util::StatusObject
ExtractStatus() const
util::StatusObject
ExtractStatus() const
{
util::StatusObject obj{};
for (const auto& item : nodes)
{
util::StatusObject obj{};
for (const auto& item : nodes)
{
obj[item.first.ToString()] = item.second.ExtractStatus();
}
return obj;
obj[item.first.ToString()] = item.second.ExtractStatus();
}
return obj;
}
size_t
size() const
{
return nodes.size();
}
size_t
size() const
{
return nodes.size();
}
struct SetIntersector
struct SetIntersector
{
bool
operator()(const typename BucketStorage_t::value_type& lhs, const Key_t& rhs)
{
bool
operator()(const typename BucketStorage_t::value_type& lhs, const Key_t& rhs)
{
return lhs.first < rhs;
}
bool
operator()(const Key_t& lhs, const typename BucketStorage_t::value_type& rhs)
{
return lhs < rhs.first;
}
};
return lhs.first < rhs;
}
bool
GetRandomNodeExcluding(Key_t& result, const std::set<Key_t>& exclude) const
operator()(const Key_t& lhs, const typename BucketStorage_t::value_type& rhs)
{
std::vector<typename BucketStorage_t::value_type> candidates;
std::set_difference(
nodes.begin(),
nodes.end(),
exclude.begin(),
exclude.end(),
std::back_inserter(candidates),
SetIntersector());
if (candidates.empty())
{
return false;
}
result = candidates[random() % candidates.size()].first;
return true;
return lhs < rhs.first;
}
};
bool
FindClosest(const Key_t& target, Key_t& result) const
bool
GetRandomNodeExcluding(Key_t& result, const std::set<Key_t>& exclude) const
{
std::vector<typename BucketStorage_t::value_type> candidates;
std::set_difference(
nodes.begin(),
nodes.end(),
exclude.begin(),
exclude.end(),
std::back_inserter(candidates),
SetIntersector());
if (candidates.empty())
{
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
auto curDist = item.first ^ target;
if (curDist < mindist)
{
mindist = curDist;
result = item.first;
}
}
return nodes.size() > 0;
return false;
}
result = candidates[random() % candidates.size()].first;
return true;
}
bool
GetManyRandom(std::set<Key_t>& result, size_t N) const
bool
FindClosest(const Key_t& target, Key_t& result) const
{
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
if (nodes.size() < N || nodes.empty())
auto curDist = item.first ^ target;
if (curDist < mindist)
{
llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", N);
return false;
mindist = curDist;
result = item.first;
}
if (nodes.size() == N)
{
std::transform(
nodes.begin(), nodes.end(), std::inserter(result, result.end()), [](const auto& a) {
return a.first;
});
}
return nodes.size() > 0;
}
return true;
}
size_t expecting = N;
size_t sz = nodes.size();
while (N)
{
auto itr = nodes.begin();
std::advance(itr, random() % sz);
if (result.insert(itr->first).second)
{
--N;
}
}
return result.size() == expecting;
bool
GetManyRandom(std::set<Key_t>& result, size_t N) const
{
if (nodes.size() < N || nodes.empty())
{
llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", N);
return false;
}
if (nodes.size() == N)
{
std::transform(
nodes.begin(), nodes.end(), std::inserter(result, result.end()), [](const auto& a) {
return a.first;
});
bool
FindCloseExcluding(const Key_t& target, Key_t& result, const std::set<Key_t>& exclude) const
return true;
}
size_t expecting = N;
size_t sz = nodes.size();
while (N)
{
Key_t maxdist;
maxdist.Fill(0xff);
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
auto itr = nodes.begin();
std::advance(itr, random() % sz);
if (result.insert(itr->first).second)
{
if (exclude.count(item.first))
{
continue;
}
auto curDist = item.first ^ target;
if (curDist < mindist)
{
mindist = curDist;
result = item.first;
}
--N;
}
return mindist < maxdist;
}
return result.size() == expecting;
}
bool
GetManyNearExcluding(
const Key_t& target,
std::set<Key_t>& result,
size_t N,
const std::set<Key_t>& exclude) const
bool
FindCloseExcluding(const Key_t& target, Key_t& result, const std::set<Key_t>& exclude) const
{
Key_t maxdist;
maxdist.Fill(0xff);
Key_t mindist;
mindist.Fill(0xff);
for (const auto& item : nodes)
{
std::set<Key_t> s(exclude.begin(), exclude.end());
Key_t peer;
while (N--)
if (exclude.count(item.first))
{
if (!FindCloseExcluding(target, peer, s))
{
return false;
}
s.insert(peer);
result.insert(peer);
continue;
}
return true;
}
void
PutNode(const Val_t& val)
{
auto itr = nodes.find(val.ID);
if (itr == nodes.end() || itr->second < val)
auto curDist = item.first ^ target;
if (curDist < mindist)
{
nodes[val.ID] = val;
mindist = curDist;
result = item.first;
}
}
return mindist < maxdist;
}
bool
GetManyNearExcluding(
const Key_t& target,
std::set<Key_t>& result,
size_t N,
const std::set<Key_t>& exclude) const
{
std::set<Key_t> s(exclude.begin(), exclude.end());
void
DelNode(const Key_t& key)
Key_t peer;
while (N--)
{
auto itr = nodes.find(key);
if (itr != nodes.end())
if (!FindCloseExcluding(target, peer, s))
{
nodes.erase(itr);
return false;
}
s.insert(peer);
result.insert(peer);
}
return true;
}
bool
HasNode(const Key_t& key) const
void
PutNode(const Val_t& val)
{
auto itr = nodes.find(val.ID);
if (itr == nodes.end() || itr->second < val)
{
return nodes.find(key) != nodes.end();
nodes[val.ID] = val;
}
}
// remove all nodes who's key matches a predicate
template <typename Predicate>
void
RemoveIf(Predicate pred)
void
DelNode(const Key_t& key)
{
auto itr = nodes.find(key);
if (itr != nodes.end())
{
auto itr = nodes.begin();
while (itr != nodes.end())
{
if (pred(itr->first))
itr = nodes.erase(itr);
else
++itr;
}
nodes.erase(itr);
}
}
bool
HasNode(const Key_t& key) const
{
return nodes.find(key) != nodes.end();
}
template <typename Visit_t>
void
ForEachNode(Visit_t visit)
// remove all nodes who's key matches a predicate
template <typename Predicate>
void
RemoveIf(Predicate pred)
{
auto itr = nodes.begin();
while (itr != nodes.end())
{
for (const auto& item : nodes)
{
visit(item.second);
}
if (pred(itr->first))
itr = nodes.erase(itr);
else
++itr;
}
}
void
Clear()
template <typename Visit_t>
void
ForEachNode(Visit_t visit)
{
for (const auto& item : nodes)
{
nodes.clear();
visit(item.second);
}
}
BucketStorage_t nodes;
Random_t random;
};
} // namespace dht
} // namespace llarp
void
Clear()
{
nodes.clear();
}
BucketStorage_t nodes;
Random_t random;
};
} // namespace llarp::dht

File diff suppressed because it is too large Load Diff

@ -1,207 +0,0 @@
#ifndef LLARP_DHT_CONTEXT
#define LLARP_DHT_CONTEXT
#include "bucket.hpp"
#include "dht.h"
#include "key.hpp"
#include "message.hpp"
#include <llarp/dht/messages/findintro.hpp>
#include "node.hpp"
#include "tx.hpp"
#include "txholder.hpp"
#include "txowner.hpp"
#include <llarp/service/intro_set.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/status.hpp>
#include <memory>
#include <set>
namespace llarp
{
struct AbstractRouter;
namespace dht
{
/// number of routers to publish to
static constexpr size_t IntroSetRelayRedundancy = 2;
/// number of dht locations handled per relay
static constexpr size_t IntroSetRequestsPerRelay = 2;
static constexpr size_t IntroSetStorageRedundancy =
(IntroSetRelayRedundancy * IntroSetRequestsPerRelay);
struct AbstractContext
{
using PendingIntrosetLookups = TXHolder<TXOwner, service::EncryptedIntroSet>;
using PendingRouterLookups = TXHolder<RouterID, RouterContact>;
using PendingExploreLookups = TXHolder<RouterID, RouterID>;
virtual ~AbstractContext() = 0;
virtual bool
LookupRouter(const RouterID& target, RouterLookupHandler result) = 0;
virtual void
LookupRouterRecursive(
const RouterID& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
RouterLookupHandler result = nullptr) = 0;
/// Ask a Service Node to perform an Introset lookup for us
virtual void
LookupIntroSetRelayed(
const Key_t& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
uint64_t relayOrder,
service::EncryptedIntroSetLookupHandler result =
service::EncryptedIntroSetLookupHandler()) = 0;
/// Directly as a Service Node for an Introset
virtual void
LookupIntroSetDirect(
const Key_t& target,
const Key_t& whoasked,
uint64_t whoaskedTX,
const Key_t& askpeer,
service::EncryptedIntroSetLookupHandler result =
service::EncryptedIntroSetLookupHandler()) = 0;
virtual bool
HasRouterLookup(const RouterID& target) const = 0;
/// issue dht lookup for router via askpeer and send reply to local path
virtual void
LookupRouterForPath(
const RouterID& target, uint64_t txid, const PathID_t& path, const Key_t& askpeer) = 0;
virtual void
LookupIntroSetForPath(
const Key_t& addr,
uint64_t txid,
const PathID_t& path,
const Key_t& askpeer,
uint64_t relayOrder) = 0;
virtual void
DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true) = 0;
/// get routers closest to target excluding requester
virtual bool
HandleExploritoryRouterLookup(
const Key_t& requester,
uint64_t txid,
const RouterID& target,
std::vector<std::unique_ptr<IMessage>>& reply) = 0;
/// handle rc lookup from requester for target
virtual void
LookupRouterRelayed(
const Key_t& requester,
uint64_t txid,
const Key_t& target,
bool recursive,
std::vector<std::unique_ptr<IMessage>>& replies) = 0;
virtual bool
RelayRequestForPath(const PathID_t& localPath, const IMessage& msg) = 0;
/// send introset to peer from source with S counter and excluding peers
virtual void
PropagateLocalIntroSet(
const PathID_t& path,
uint64_t sourceTX,
const service::EncryptedIntroSet& introset,
const Key_t& peer,
uint64_t relayOrder) = 0;
/// send introset to peer from source with S counter and excluding peers
virtual void
PropagateIntroSetTo(
const Key_t& source,
uint64_t sourceTX,
const service::EncryptedIntroSet& introset,
const Key_t& peer,
uint64_t relayOrder) = 0;
virtual void
Init(const Key_t& us, AbstractRouter* router) = 0;
virtual std::optional<llarp::service::EncryptedIntroSet>
GetIntroSetByLocation(const Key_t& location) const = 0;
virtual llarp_time_t
Now() const = 0;
virtual void
ExploreNetworkVia(const Key_t& peer) = 0;
virtual llarp::AbstractRouter*
GetRouter() const = 0;
virtual bool
GetRCFromNodeDB(const Key_t& k, llarp::RouterContact& rc) const = 0;
virtual const Key_t&
OurKey() const = 0;
virtual PendingIntrosetLookups&
pendingIntrosetLookups() = 0;
virtual const PendingIntrosetLookups&
pendingIntrosetLookups() const = 0;
virtual PendingRouterLookups&
pendingRouterLookups() = 0;
virtual const PendingRouterLookups&
pendingRouterLookups() const = 0;
virtual PendingExploreLookups&
pendingExploreLookups() = 0;
virtual const PendingExploreLookups&
pendingExploreLookups() const = 0;
virtual Bucket<ISNode>*
services() = 0;
virtual bool&
AllowTransit() = 0;
virtual const bool&
AllowTransit() const = 0;
virtual Bucket<RCNode>*
Nodes() const = 0;
virtual void
PutRCNodeAsync(const RCNode& val) = 0;
virtual void
DelRCNodeAsync(const Key_t& val) = 0;
virtual util::StatusObject
ExtractStatus() const = 0;
virtual void
StoreRC(const RouterContact rc) const = 0;
};
std::unique_ptr<AbstractContext>
makeContext();
} // namespace dht
} // namespace llarp
struct llarp_dht_context
{
std::unique_ptr<llarp::dht::AbstractContext> impl;
llarp::AbstractRouter* parent;
llarp_dht_context(llarp::AbstractRouter* router);
};
#endif

@ -2,14 +2,14 @@
#include "dht.h"
#include <llarp/router_contact.hpp>
llarp_dht_context::llarp_dht_context(llarp::AbstractRouter* router)
llarp_dht_context::llarp_dht_context(llarp::Router* router)
{
parent = router;
impl = llarp::dht::makeContext();
impl = llarp::dht::make_handler();
}
struct llarp_dht_context*
llarp_dht_context_new(llarp::AbstractRouter* router)
llarp_dht_context_new(llarp::Router* router)
{
return new llarp_dht_context(router);
}

@ -14,12 +14,12 @@ struct llarp_dht_context;
namespace llarp
{
struct AbstractRouter;
struct Router;
}
/// allocator
struct llarp_dht_context*
llarp_dht_context_new(llarp::AbstractRouter* parent);
llarp_dht_context_new(llarp::Router* parent);
/// deallocator
void

@ -1,43 +1,39 @@
#include "explorenetworkjob.hpp"
#include "context.hpp"
#include <llarp/dht/messages/findrouter.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/tooling/dht_event.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
void
ExploreNetworkJob::Start(const TXOwner& peer)
{
void
ExploreNetworkJob::Start(const TXOwner& peer)
auto msg = new FindRouterMessage(peer.txid);
auto router = parent->GetRouter();
if (router)
{
auto msg = new FindRouterMessage(peer.txid);
auto router = parent->GetRouter();
if (router)
{
router->NotifyRouterEvent<tooling::FindRouterSentEvent>(router->pubkey(), *msg);
}
parent->DHTSendTo(peer.node.as_array(), msg);
router->notify_router_event<tooling::FindRouterSentEvent>(router->pubkey(), *msg);
}
parent->DHTSendTo(peer.node.as_array(), msg);
}
void
ExploreNetworkJob::SendReply()
{
llarp::LogDebug("got ", valuesFound.size(), " routers from exploration");
void
ExploreNetworkJob::SendReply()
{
llarp::LogDebug("got ", valuesFound.size(), " routers from exploration");
auto router = parent->GetRouter();
for (const auto& pk : valuesFound)
{
// lookup router
if (router and router->nodedb()->Has(pk))
continue;
parent->LookupRouter(
pk, [router, pk](const auto& res) { router->HandleDHTLookupForExplore(pk, res); });
}
auto router = parent->GetRouter();
for (const auto& pk : valuesFound)
{
// lookup router
if (router and router->node_db()->Has(pk))
continue;
parent->LookupRouter(
pk, [router, pk](const auto& res) { router->HandleDHTLookupForExplore(pk, res); });
}
} // namespace dht
} // namespace llarp
}
} // namespace llarp::dht

@ -4,30 +4,27 @@
#include "tx.hpp"
#include <llarp/router_id.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
struct ExploreNetworkJob : public TX<RouterID, RouterID>
{
struct ExploreNetworkJob : public TX<RouterID, RouterID>
{
ExploreNetworkJob(const RouterID& peer, AbstractContext* ctx)
: TX<RouterID, RouterID>(TXOwner{}, peer, ctx)
{}
ExploreNetworkJob(const RouterID& peer, AbstractDHTMessageHandler* ctx)
: TX<RouterID, RouterID>(TXOwner{}, peer, ctx)
{}
bool
Validate(const RouterID&) const override
{
// TODO: check with lokid
return true;
}
bool
Validate(const RouterID&) const override
{
// TODO: check with lokid
return true;
}
void
Start(const TXOwner& peer) override;
void
Start(const TXOwner& peer) override;
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
void
SendReply() override;
};
} // namespace llarp::dht
#endif

@ -3,28 +3,25 @@
#include "key.hpp"
#include <llarp/router_contact.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
struct XorMetric
{
struct XorMetric
{
const Key_t us;
const Key_t us;
XorMetric(const Key_t& ourKey) : us(ourKey)
{}
XorMetric(const Key_t& ourKey) : us(ourKey)
{}
bool
operator()(const Key_t& left, const Key_t& right) const
{
return (us ^ left) < (us ^ right);
}
bool
operator()(const Key_t& left, const Key_t& right) const
{
return (us ^ left) < (us ^ right);
}
bool
operator()(const RouterContact& left, const RouterContact& right) const
{
return (left.pubkey ^ us) < (right.pubkey ^ us);
}
};
} // namespace dht
} // namespace llarp
bool
operator()(const RouterContact& left, const RouterContact& right) const
{
return (left.pubkey ^ us) < (right.pubkey ^ us);
}
};
} // namespace llarp::dht

@ -6,72 +6,69 @@
#include <array>
namespace llarp
namespace llarp::dht
{
namespace dht
struct Key_t : public AlignedBuffer<32>
{
struct Key_t : public AlignedBuffer<32>
{
explicit Key_t(const byte_t* buf) : AlignedBuffer<SIZE>(buf)
{}
explicit Key_t(const byte_t* buf) : AlignedBuffer<SIZE>(buf)
{}
explicit Key_t(const Data& data) : AlignedBuffer<SIZE>(data)
{}
explicit Key_t(const std::array<byte_t, SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
explicit Key_t(const AlignedBuffer<SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
explicit Key_t(const AlignedBuffer<SIZE>& data) : AlignedBuffer<SIZE>(data)
{}
Key_t() : AlignedBuffer<SIZE>()
{}
Key_t() : AlignedBuffer<SIZE>()
{}
/// get snode address string
std::string
SNode() const
{
const RouterID rid{as_array()};
return rid.ToString();
}
/// get snode address string
std::string
SNode() const
{
const RouterID rid{as_array()};
return rid.ToString();
}
util::StatusObject
ExtractStatus() const;
util::StatusObject
ExtractStatus() const;
std::string
ToString() const
{
return SNode();
}
std::string
ToString() const
{
return SNode();
}
Key_t
operator^(const Key_t& other) const
{
Key_t dist;
std::transform(begin(), end(), other.begin(), dist.begin(), std::bit_xor<byte_t>());
return dist;
}
Key_t
operator^(const Key_t& other) const
{
Key_t dist;
std::transform(begin(), end(), other.begin(), dist.begin(), std::bit_xor<byte_t>());
return dist;
}
bool
operator==(const Key_t& other) const
{
return as_array() == other.as_array();
}
bool
operator==(const Key_t& other) const
{
return as_array() == other.as_array();
}
bool
operator!=(const Key_t& other) const
{
return as_array() != other.as_array();
}
bool
operator!=(const Key_t& other) const
{
return as_array() != other.as_array();
}
bool
operator<(const Key_t& other) const
{
return as_array() < other.as_array();
}
bool
operator<(const Key_t& other) const
{
return as_array() < other.as_array();
}
bool
operator>(const Key_t& other) const
{
return as_array() > other.as_array();
}
};
} // namespace dht
} // namespace llarp
bool
operator>(const Key_t& other) const
{
return as_array() > other.as_array();
}
};
} // namespace llarp::dht

@ -4,61 +4,58 @@
#include <llarp/dht/messages/gotrouter.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/router/router.hpp>
#include <llarp/routing/path_dht_message.hpp>
#include <llarp/util/logging.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
{
LocalRouterLookup::LocalRouterLookup(
const PathID_t& path, uint64_t txid, const RouterID& _target, AbstractContext* ctx)
: RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, _target, ctx, nullptr)
, localPath(path)
{}
LocalRouterLookup::LocalRouterLookup(
const PathID_t& path, uint64_t txid, const RouterID& _target, AbstractDHTMessageHandler* ctx)
: RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, _target, ctx, nullptr), localPath(path)
{}
void
LocalRouterLookup::SendReply()
void
LocalRouterLookup::SendReply()
{
auto path =
parent->GetRouter()->path_context().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
if (valuesFound.size())
{
auto path =
parent->GetRouter()->pathContext().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
RouterContact found;
for (const auto& rc : valuesFound)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
if (rc.OtherIsNewer(found))
found = rc;
}
if (valuesFound.size())
valuesFound.clear();
if (not found.pubkey.IsZero())
{
RouterContact found;
for (const auto& rc : valuesFound)
{
if (rc.OtherIsNewer(found))
found = rc;
}
valuesFound.clear();
if (not found.pubkey.IsZero())
{
valuesFound.resize(1);
valuesFound[0] = found;
}
else
{
llarp::LogWarn("We found a null RC for dht request, dropping it");
}
valuesFound.resize(1);
valuesFound[0] = found;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotRouterMessage(parent->OurKey(), whoasked.txid, valuesFound, true));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
else
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
llarp::LogWarn("We found a null RC for dht request, dropping it");
}
}
} // namespace dht
} // namespace llarp
routing::PathDHTMessage msg;
msg.dht_msgs.emplace_back(
new GotRouterMessage(parent->OurKey(), whoasked.txid, valuesFound, true));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
} // namespace llarp::dht

@ -7,21 +7,21 @@
#include <llarp/router_contact.hpp>
#include <llarp/router_id.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
struct LocalRouterLookup : public RecursiveRouterLookup
{
struct LocalRouterLookup : public RecursiveRouterLookup
{
PathID_t localPath;
PathID_t localPath;
LocalRouterLookup(
const PathID_t& path, uint64_t txid, const RouterID& target, AbstractContext* ctx);
LocalRouterLookup(
const PathID_t& path,
uint64_t txid,
const RouterID& target,
AbstractDHTMessageHandler* ctx);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
void
SendReply() override;
};
} // namespace llarp::dht
#endif

@ -3,59 +3,56 @@
#include "context.hpp"
#include <llarp/dht/messages/gotintro.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/router/router.hpp>
#include <llarp/routing/path_dht_message.hpp>
#include <llarp/util/logging.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
{
LocalServiceAddressLookup::LocalServiceAddressLookup(
const PathID_t& pathid,
uint64_t txid,
uint64_t relayOrder,
const Key_t& addr,
AbstractContext* ctx,
[[maybe_unused]] const Key_t& askpeer)
: ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, relayOrder, nullptr)
, localPath(pathid)
{}
LocalServiceAddressLookup::LocalServiceAddressLookup(
const PathID_t& pathid,
uint64_t txid,
uint64_t relayOrder,
const Key_t& addr,
AbstractDHTMessageHandler* ctx,
[[maybe_unused]] const Key_t& askpeer)
: ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, relayOrder, nullptr)
, localPath(pathid)
{}
void
LocalServiceAddressLookup::SendReply()
void
LocalServiceAddressLookup::SendReply()
{
auto path =
parent->GetRouter()->path_context().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
auto path =
parent->GetRouter()->pathContext().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
// pick newest if we have more than 1 result
if (valuesFound.size())
{
service::EncryptedIntroSet found;
for (const auto& introset : valuesFound)
{
if (found.OtherIsNewer(introset))
found = introset;
}
valuesFound.clear();
valuesFound.emplace_back(found);
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
// pick newest if we have more than 1 result
if (valuesFound.size())
{
service::EncryptedIntroSet found;
for (const auto& introset : valuesFound)
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
if (found.OtherIsNewer(introset))
found = introset;
}
valuesFound.clear();
valuesFound.emplace_back(found);
}
routing::PathDHTMessage msg;
msg.dht_msgs.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
} // namespace dht
} // namespace llarp
}
} // namespace llarp::dht

@ -5,27 +5,24 @@
#include <llarp/path/path_types.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
struct LocalServiceAddressLookup : public ServiceAddressLookup
{
struct LocalServiceAddressLookup : public ServiceAddressLookup
{
PathID_t localPath;
PathID_t localPath;
LocalServiceAddressLookup(
const PathID_t& pathid,
uint64_t txid,
uint64_t relayOrder,
const Key_t& addr,
AbstractContext* ctx,
[[maybe_unused]] const Key_t& askpeer);
LocalServiceAddressLookup(
const PathID_t& pathid,
uint64_t txid,
uint64_t relayOrder,
const Key_t& addr,
AbstractDHTMessageHandler* ctx,
[[maybe_unused]] const Key_t& askpeer);
void
SendReply() override;
};
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
} // namespace llarp::dht
#endif

@ -3,40 +3,40 @@
#include "context.hpp"
#include <llarp/dht/messages/gotintro.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/router/router.hpp>
#include <llarp/routing/path_dht_message.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
{
LocalTagLookup::LocalTagLookup(
const PathID_t& path, uint64_t txid, const service::Tag& _target, AbstractContext* ctx)
: TagLookup(TXOwner{ctx->OurKey(), txid}, _target, ctx, 0), localPath(path)
{}
LocalTagLookup::LocalTagLookup(
const PathID_t& path,
uint64_t txid,
const service::Tag& _target,
AbstractDHTMessageHandler* ctx)
: TagLookup(TXOwner{ctx->OurKey(), txid}, _target, ctx, 0), localPath(path)
{}
void
LocalTagLookup::SendReply()
void
LocalTagLookup::SendReply()
{
auto path =
parent->GetRouter()->path_context().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::PathDHTMessage msg;
msg.dht_msgs.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
auto path =
parent->GetRouter()->pathContext().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
} // namespace dht
} // namespace llarp
}
} // namespace llarp::dht

@ -3,21 +3,21 @@
#include "taglookup.hpp"
namespace llarp
namespace llarp::dht
{
namespace dht
struct LocalTagLookup : public TagLookup
{
struct LocalTagLookup : public TagLookup
{
PathID_t localPath;
PathID_t localPath;
LocalTagLookup(
const PathID_t& path, uint64_t txid, const service::Tag& target, AbstractContext* ctx);
LocalTagLookup(
const PathID_t& path,
uint64_t txid,
const service::Tag& target,
AbstractDHTMessageHandler* ctx);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
void
SendReply() override;
};
} // namespace llarp::dht
#endif

@ -1,4 +1,5 @@
#include "context.hpp"
#include "oxenc/bt_serialize.h"
#include <memory>
#include <llarp/util/bencode.hpp>
@ -10,128 +11,128 @@
#include <llarp/dht/messages/findname.hpp>
#include <llarp/dht/messages/gotname.hpp>
namespace llarp
namespace llarp::dht
{
namespace dht
struct MessageDecoder
{
struct MessageDecoder
{
const Key_t& From;
IMessage::Ptr_t msg;
bool firstKey = true;
bool relayed = false;
const Key_t& From;
std::unique_ptr<AbstractDHTMessage> msg;
bool firstKey = true;
bool relayed = false;
MessageDecoder(const Key_t& from, bool wasRelayed) : From(from), relayed(wasRelayed)
{}
MessageDecoder(const Key_t& from, bool wasRelayed) : From(from), relayed(wasRelayed)
{}
bool
operator()(llarp_buffer_t* buffer, llarp_buffer_t* key)
bool
operator()(llarp_buffer_t* buffer, llarp_buffer_t* key)
{
llarp_buffer_t strbuf;
// check for empty dict
if (!key)
return !firstKey;
// first key
if (firstKey)
{
llarp_buffer_t strbuf;
// check for empty dict
if (!key)
return !firstKey;
// first key
if (firstKey)
if (!(key->startswith("A")))
return false;
if (!bencode_read_string(buffer, &strbuf))
return false;
// bad msg size?
if (strbuf.sz != 1)
return false;
llarp::LogDebug("Handle DHT message ", *strbuf.base, " relayed=", relayed);
switch (*strbuf.base)
{
if (!(key->startswith("A")))
return false;
if (!bencode_read_string(buffer, &strbuf))
return false;
// bad msg size?
if (strbuf.sz != 1)
return false;
llarp::LogDebug("Handle DHT message ", *strbuf.base, " relayed=", relayed);
switch (*strbuf.base)
{
case 'N':
msg = std::make_unique<FindNameMessage>(From, Key_t{}, 0);
case 'N':
msg = std::make_unique<FindNameMessage>(From, Key_t{}, 0);
break;
case 'M':
msg = std::make_unique<GotNameMessage>(From, 0, service::EncryptedName{});
break;
case 'F':
msg = std::make_unique<FindIntroMessage>(From, relayed, 0);
break;
case 'R':
if (relayed)
msg = std::make_unique<RelayedFindRouterMessage>(From);
else
msg = std::make_unique<FindRouterMessage>(From);
break;
case 'S':
msg = std::make_unique<GotRouterMessage>(From, relayed);
break;
case 'I':
msg = std::make_unique<PublishIntroMessage>(From, relayed);
break;
case 'G':
if (relayed)
{
msg = std::make_unique<RelayedGotIntroMessage>();
break;
case 'M':
msg = std::make_unique<GotNameMessage>(From, 0, service::EncryptedName{});
}
else
{
msg = std::make_unique<GotIntroMessage>(From);
break;
case 'F':
msg = std::make_unique<FindIntroMessage>(From, relayed, 0);
break;
case 'R':
if (relayed)
msg = std::make_unique<RelayedFindRouterMessage>(From);
else
msg = std::make_unique<FindRouterMessage>(From);
break;
case 'S':
msg = std::make_unique<GotRouterMessage>(From, relayed);
break;
case 'I':
msg = std::make_unique<PublishIntroMessage>(From, relayed);
break;
case 'G':
if (relayed)
{
msg = std::make_unique<RelayedGotIntroMessage>();
break;
}
else
{
msg = std::make_unique<GotIntroMessage>(From);
break;
}
default:
llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base);
// bad msg type
return false;
}
firstKey = false;
return msg != nullptr;
}
default:
llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base);
// bad msg type
return false;
}
return msg->DecodeKey(*key, buffer);
firstKey = false;
return msg != nullptr;
}
};
IMessage::Ptr_t
DecodeMesssage(const Key_t& from, llarp_buffer_t* buf, bool relayed)
{
MessageDecoder dec(from, relayed);
if (!bencode_read_dict(dec, buf))
return nullptr;
return std::move(dec.msg);
return msg->decode_key(*key, buffer);
}
};
struct ListDecoder
{
ListDecoder(bool hasRelayed, const Key_t& from, std::vector<IMessage::Ptr_t>& list)
: relayed(hasRelayed), From(from), l(list)
{}
std::unique_ptr<AbstractDHTMessage>
DecodeMessage(const Key_t& from, llarp_buffer_t* buf, bool relayed)
{
MessageDecoder dec(from, relayed);
if (!bencode_read_dict(dec, buf))
return nullptr;
bool relayed;
const Key_t& From;
std::vector<IMessage::Ptr_t>& l;
return std::move(dec.msg);
}
bool
operator()(llarp_buffer_t* buffer, bool has)
{
if (!has)
return true;
auto msg = DecodeMesssage(From, buffer, relayed);
if (msg)
{
l.emplace_back(std::move(msg));
return true;
}
struct ListDecoder
{
ListDecoder(
bool hasRelayed, const Key_t& from, std::vector<std::unique_ptr<AbstractDHTMessage>>& list)
: relayed(hasRelayed), From(from), l(list)
{}
return false;
}
};
bool relayed;
const Key_t& From;
std::vector<std::unique_ptr<AbstractDHTMessage>>& l;
bool
DecodeMesssageList(
Key_t from, llarp_buffer_t* buf, std::vector<IMessage::Ptr_t>& list, bool relayed)
operator()(llarp_buffer_t* buffer, bool has)
{
ListDecoder dec(relayed, from, list);
return bencode_read_list(dec, buf);
if (!has)
return true;
auto msg = DecodeMessage(From, buffer, relayed);
if (msg)
{
l.emplace_back(std::move(msg));
return true;
}
return false;
}
} // namespace dht
} // namespace llarp
};
bool
DecodeMessageList(
Key_t from,
llarp_buffer_t* buf,
std::vector<std::unique_ptr<AbstractDHTMessage>>& list,
bool relayed)
{
ListDecoder dec(relayed, from, list);
return bencode_read_list(dec, buf);
}
} // namespace llarp::dht

@ -1,47 +0,0 @@
#pragma once
#include "dht.h"
#include "key.hpp"
#include <llarp/path/path_types.hpp>
#include <llarp/util/bencode.hpp>
#include <vector>
namespace llarp
{
namespace dht
{
constexpr size_t MAX_MSG_SIZE = 2048;
struct IMessage
{
virtual ~IMessage() = default;
/// construct
IMessage(const Key_t& from) : From(from)
{}
using Ptr_t = std::unique_ptr<IMessage>;
virtual bool
HandleMessage(struct llarp_dht_context* dht, std::vector<Ptr_t>& replies) const = 0;
virtual bool
BEncode(llarp_buffer_t* buf) const = 0;
virtual bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) = 0;
Key_t From;
PathID_t pathID;
uint64_t version = llarp::constants::proto_version;
};
IMessage::Ptr_t
DecodeMessage(const Key_t& from, llarp_buffer_t* buf, bool relayed = false);
bool
DecodeMesssageList(
Key_t from, llarp_buffer_t* buf, std::vector<IMessage::Ptr_t>& dst, bool relayed = false);
} // namespace dht
} // namespace llarp

@ -1,27 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/router_version.hpp>
namespace llarp
{
namespace dht
{
struct ConsensusMessage
{
/// H
ShortHash m_Hash;
/// K
std::vector<RouterID> m_Keys;
/// N
uint64_t m_NumberOfEntries;
/// O
uint64_t m_EntryOffset;
/// T
uint64_t m_TxID;
/// U
llarp_time_t m_NextUpdateRequired;
/// V
RouterVersion m_RotuerVersion;
};
} // namespace dht
} // namespace llarp

@ -1,139 +0,0 @@
#include <llarp/dht/context.hpp>
#include "findintro.hpp"
#include "gotintro.hpp"
#include <llarp/routing/message.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/nodedb.hpp>
namespace llarp
{
namespace dht
{
FindIntroMessage::~FindIntroMessage() = default;
bool
FindIntroMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* val)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("N", tagName, read, k, val))
return false;
if (!BEncodeMaybeReadDictInt("O", relayOrder, read, k, val))
return false;
if (!BEncodeMaybeReadDictEntry("S", location, read, k, val))
return false;
if (!BEncodeMaybeReadDictInt("T", txID, read, k, val))
return false;
if (!BEncodeMaybeVerifyVersion("V", version, llarp::constants::proto_version, read, k, val))
return false;
return read;
}
bool
FindIntroMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
// message id
if (!BEncodeWriteDictMsgType(buf, "A", "F"))
return false;
if (tagName.Empty())
{
// relay order
if (!BEncodeWriteDictInt("O", relayOrder, buf))
return false;
// service address
if (!BEncodeWriteDictEntry("S", location, buf))
return false;
}
else
{
if (!BEncodeWriteDictEntry("N", tagName, buf))
return false;
// relay order
if (!BEncodeWriteDictInt("O", relayOrder, buf))
return false;
}
// txid
if (!BEncodeWriteDictInt("T", txID, buf))
return false;
// protocol version
if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf))
return false;
return bencode_end(buf);
}
bool
FindIntroMessage::HandleMessage(
llarp_dht_context* ctx, std::vector<IMessage::Ptr_t>& replies) const
{
auto& dht = *ctx->impl;
if (dht.pendingIntrosetLookups().HasPendingLookupFrom(TXOwner{From, txID}))
{
llarp::LogWarn("duplicate FIM from ", From, " txid=", txID);
return false;
}
if (not tagName.Empty())
{
return false;
}
// bad request (request for zero-key)
if (location.IsZero())
{
// we dont got it
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
// we are relaying this message for e.g. a client
if (relayed)
{
if (relayOrder >= IntroSetStorageRedundancy)
{
llarp::LogWarn("Invalid relayOrder received: ", relayOrder);
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
auto closestRCs =
dht.GetRouter()->nodedb()->FindManyClosestTo(location, IntroSetStorageRedundancy);
if (closestRCs.size() <= relayOrder)
{
llarp::LogWarn("Can't fulfill FindIntro for relayOrder: ", relayOrder);
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
const auto& entry = closestRCs[relayOrder];
Key_t peer = Key_t(entry.pubkey);
dht.LookupIntroSetForPath(location, txID, pathID, peer, 0);
}
else
{
// we should have this value if introset was propagated properly
const auto maybe = dht.GetIntroSetByLocation(location);
if (maybe)
{
replies.emplace_back(new GotIntroMessage({*maybe}, txID));
}
else
{
LogWarn("Got FIM with relayed == false and we don't have entry");
replies.emplace_back(new GotIntroMessage({}, txID));
}
}
return true;
}
} // namespace dht
} // namespace llarp

@ -1,48 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/routing/message.hpp>
#include <llarp/service/address.hpp>
#include <llarp/service/tag.hpp>
namespace llarp
{
namespace dht
{
struct FindIntroMessage final : public IMessage
{
Key_t location;
llarp::service::Tag tagName;
uint64_t txID = 0;
bool relayed = false;
uint64_t relayOrder = 0;
FindIntroMessage(const Key_t& from, bool relay, uint64_t order) : IMessage(from)
{
relayed = relay;
relayOrder = order;
}
FindIntroMessage(const llarp::service::Tag& tag, uint64_t txid)
: IMessage({}), tagName(tag), txID(txid)
{}
explicit FindIntroMessage(uint64_t txid, const Key_t& addr, uint64_t order)
: IMessage({}), location(addr), txID(txid), relayOrder(order)
{
tagName.Zero();
}
~FindIntroMessage() override;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(llarp_dht_context* ctx, std::vector<IMessage::Ptr_t>& replies) const override;
};
} // namespace dht
} // namespace llarp

@ -1,65 +0,0 @@
#include "findname.hpp"
#include <oxenc/bt_serialize.h>
#include <llarp/dht/context.hpp>
#include "gotname.hpp"
#include <llarp/router/abstractrouter.hpp>
#include <llarp/rpc/lokid_rpc_client.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/routing/dht_message.hpp>
namespace llarp::dht
{
FindNameMessage::FindNameMessage(const Key_t& from, Key_t namehash, uint64_t txid)
: IMessage(from), NameHash(std::move(namehash)), TxID(txid)
{}
bool
FindNameMessage::BEncode(llarp_buffer_t* buf) const
{
const auto data = oxenc::bt_serialize(oxenc::bt_dict{
{"A", "N"sv},
{"H", std::string_view{(char*)NameHash.data(), NameHash.size()}},
{"T", TxID}});
return buf->write(data.begin(), data.end());
}
bool
FindNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
{
if (key.startswith("H"))
{
return NameHash.BDecode(val);
}
if (key.startswith("T"))
{
return bencode_read_integer(val, &TxID);
}
return bencode_discard(val);
}
bool
FindNameMessage::HandleMessage(struct llarp_dht_context* dht, std::vector<Ptr_t>& replies) const
{
(void)replies;
auto r = dht->impl->GetRouter();
if (pathID.IsZero() or not r->IsServiceNode())
return false;
r->RpcClient()->LookupLNSNameHash(NameHash, [r, pathID = pathID, TxID = TxID](auto maybe) {
auto path = r->pathContext().GetPathForTransfer(pathID);
if (path == nullptr)
return;
routing::DHTMessage msg;
if (maybe.has_value())
{
msg.M.emplace_back(new GotNameMessage(dht::Key_t{}, TxID, *maybe));
}
else
{
msg.M.emplace_back(new GotNameMessage(dht::Key_t{}, TxID, service::EncryptedName{}));
}
path->SendRoutingMessage(msg, r);
});
return true;
}
} // namespace llarp::dht

@ -1,24 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
namespace llarp::dht
{
struct FindNameMessage : public IMessage
{
explicit FindNameMessage(const Key_t& from, Key_t namehash, uint64_t txid);
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(struct llarp_dht_context* dht, std::vector<Ptr_t>& replies) const override;
Key_t NameHash;
uint64_t TxID;
};
} // namespace llarp::dht

@ -1,180 +0,0 @@
#include "findrouter.hpp"
#include <llarp/dht/context.hpp>
#include "gotrouter.hpp"
#include <llarp/nodedb.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/tooling/dht_event.hpp>
namespace llarp
{
namespace dht
{
bool
RelayedFindRouterMessage::HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const
{
auto& dht = *ctx->impl;
/// lookup for us, send an immeidate reply
const Key_t us = dht.OurKey();
const Key_t k{targetKey};
if (k == us)
{
auto path = dht.GetRouter()->pathContext().GetByUpstream(targetKey, pathID);
if (path)
{
replies.emplace_back(new GotRouterMessage(k, txid, {dht.GetRouter()->rc()}, false));
return true;
}
return false;
}
Key_t peer;
// check if we know this in our nodedb first
if (not dht.GetRouter()->SessionToRouterAllowed(targetKey))
{
// explicitly disallowed by network
replies.emplace_back(new GotRouterMessage(k, txid, {}, false));
return true;
}
// check netdb
const auto rc = dht.GetRouter()->nodedb()->FindClosestTo(k);
if (rc.pubkey == targetKey)
{
replies.emplace_back(new GotRouterMessage(k, txid, {rc}, false));
return true;
}
peer = Key_t(rc.pubkey);
// lookup if we don't have it in our nodedb
dht.LookupRouterForPath(targetKey, txid, pathID, peer);
return true;
}
FindRouterMessage::~FindRouterMessage() = default;
bool
FindRouterMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
// message type
if (!bencode_write_bytestring(buf, "A", 1))
return false;
if (!bencode_write_bytestring(buf, "R", 1))
return false;
// exploritory or not?
if (!bencode_write_bytestring(buf, "E", 1))
return false;
if (!bencode_write_uint64(buf, exploritory ? 1 : 0))
return false;
// iterative or not?
if (!bencode_write_bytestring(buf, "I", 1))
return false;
if (!bencode_write_uint64(buf, iterative ? 1 : 0))
return false;
// key
if (!bencode_write_bytestring(buf, "K", 1))
return false;
if (!bencode_write_bytestring(buf, targetKey.data(), targetKey.size()))
return false;
// txid
if (!bencode_write_bytestring(buf, "T", 1))
return false;
if (!bencode_write_uint64(buf, txid))
return false;
// version
if (!bencode_write_bytestring(buf, "V", 1))
return false;
if (!bencode_write_uint64(buf, version))
return false;
return bencode_end(buf);
}
bool
FindRouterMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
{
llarp_buffer_t strbuf;
if (key.startswith("E"))
{
uint64_t result;
if (!bencode_read_integer(val, &result))
return false;
exploritory = result != 0;
return true;
}
if (key.startswith("I"))
{
uint64_t result;
if (!bencode_read_integer(val, &result))
return false;
iterative = result != 0;
return true;
}
if (key.startswith("K"))
{
if (!bencode_read_string(val, &strbuf))
return false;
if (strbuf.sz != targetKey.size())
return false;
std::copy(strbuf.base, strbuf.base + targetKey.SIZE, targetKey.begin());
return true;
}
if (key.startswith("T"))
{
return bencode_read_integer(val, &txid);
}
if (key.startswith("V"))
{
return bencode_read_integer(val, &version);
}
return false;
}
bool
FindRouterMessage::HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const
{
auto& dht = *ctx->impl;
auto router = dht.GetRouter();
router->NotifyRouterEvent<tooling::FindRouterReceivedEvent>(router->pubkey(), *this);
if (!dht.AllowTransit())
{
llarp::LogWarn("Got DHT lookup from ", From, " when we are not allowing dht transit");
return false;
}
if (dht.pendingRouterLookups().HasPendingLookupFrom({From, txid}))
{
llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid);
return false;
}
RouterContact found;
if (targetKey.IsZero())
{
llarp::LogError("invalid FRM from ", From, " key is zero");
return false;
}
const Key_t k(targetKey);
if (exploritory)
return dht.HandleExploritoryRouterLookup(From, txid, targetKey, replies);
dht.LookupRouterRelayed(From, txid, k, !iterative, replies);
return true;
}
} // namespace dht
} // namespace llarp

@ -1,57 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
namespace llarp
{
namespace dht
{
struct FindRouterMessage : public IMessage
{
// inbound parsing
FindRouterMessage(const Key_t& from) : IMessage(from)
{}
// find by routerid
FindRouterMessage(uint64_t id, const RouterID& target)
: IMessage({}), targetKey(target), txid(id)
{}
// exploritory
FindRouterMessage(uint64_t id) : IMessage({}), exploritory(true), txid(id)
{
targetKey.Randomize();
}
~FindRouterMessage() override;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const override;
RouterID targetKey;
bool iterative = false;
bool exploritory = false;
uint64_t txid = 0;
uint64_t version = 0;
};
/// variant of FindRouterMessage relayed via path
struct RelayedFindRouterMessage final : public FindRouterMessage
{
RelayedFindRouterMessage(const Key_t& from) : FindRouterMessage(from)
{}
/// handle a relayed FindRouterMessage, do a lookup on the dht and inform
/// the path of the result
/// TODO: smart path expiration logic needs to be implemented
bool
HandleMessage(llarp_dht_context* ctx, std::vector<IMessage::Ptr_t>& replies) const override;
};
} // namespace dht
} // namespace llarp

@ -1,125 +0,0 @@
#include "gotintro.hpp"
#include <llarp/service/intro.hpp>
#include <llarp/dht/context.hpp>
#include <memory>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/tooling/dht_event.hpp>
#include <utility>
namespace llarp
{
namespace dht
{
GotIntroMessage::GotIntroMessage(std::vector<service::EncryptedIntroSet> results, uint64_t tx)
: IMessage({}), found(std::move(results)), txid(tx)
{}
bool
GotIntroMessage::HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& /*replies*/) const
{
auto& dht = *ctx->impl;
auto* router = dht.GetRouter();
router->NotifyRouterEvent<tooling::GotIntroReceivedEvent>(
router->pubkey(),
Key_t(From.data()),
(found.size() > 0 ? found[0] : llarp::service::EncryptedIntroSet{}),
txid);
for (const auto& introset : found)
{
if (!introset.Verify(dht.Now()))
{
LogWarn(
"Invalid introset while handling direct GotIntro "
"from ",
From);
return false;
}
}
TXOwner owner(From, txid);
auto serviceLookup = dht.pendingIntrosetLookups().GetPendingLookupFrom(owner);
if (serviceLookup)
{
if (not found.empty())
{
dht.pendingIntrosetLookups().Found(owner, serviceLookup->target, found);
}
else
{
dht.pendingIntrosetLookups().NotFound(owner, nullptr);
}
return true;
}
LogError("no pending TX for GIM from ", From, " txid=", txid);
return false;
}
bool
RelayedGotIntroMessage::HandleMessage(
llarp_dht_context* ctx,
[[maybe_unused]] std::vector<std::unique_ptr<IMessage>>& replies) const
{
// TODO: implement me better?
auto pathset = ctx->impl->GetRouter()->pathContext().GetLocalPathSet(pathID);
if (pathset)
{
auto copy = std::make_shared<const RelayedGotIntroMessage>(*this);
return pathset->HandleGotIntroMessage(copy);
}
LogWarn("No path for got intro message pathid=", pathID);
return false;
}
bool
GotIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
if (key.startswith("I"))
{
return BEncodeReadList(found, buf);
}
if (key.startswith("K"))
{
if (closer) // duplicate key?
return false;
dht::Key_t K;
if (not K.BDecode(buf))
return false;
closer = K;
return true;
}
bool read = false;
if (!BEncodeMaybeReadDictInt("T", txid, read, key, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, key, buf))
return false;
return read;
}
bool
GotIntroMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "G"))
return false;
if (!BEncodeWriteDictList("I", found, buf))
return false;
if (closer)
{
if (!BEncodeWriteDictEntry("K", *closer, buf))
return false;
}
if (!BEncodeWriteDictInt("T", txid, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -1,64 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/service/intro_set.hpp>
#include <llarp/util/copy_or_nullptr.hpp>
#include <vector>
#include <optional>
namespace llarp
{
namespace dht
{
/// acknowledgement to PublishIntroMessage or reply to FindIntroMessage
struct GotIntroMessage : public IMessage
{
/// the found introsets
std::vector<service::EncryptedIntroSet> found;
/// txid
uint64_t txid = 0;
/// the key of a router closer in keyspace if iterative lookup
std::optional<Key_t> closer;
GotIntroMessage(const Key_t& from) : IMessage(from)
{}
GotIntroMessage(const GotIntroMessage& other)
: IMessage(other.From), found(other.found), txid(other.txid), closer(other.closer)
{
version = other.version;
}
/// for iterative reply
GotIntroMessage(const Key_t& from, const Key_t& _closer, uint64_t _txid)
: IMessage(from), txid(_txid), closer(_closer)
{}
/// for recursive reply
GotIntroMessage(std::vector<service::EncryptedIntroSet> results, uint64_t txid);
~GotIntroMessage() override = default;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(llarp_dht_context* ctx, std::vector<IMessage::Ptr_t>& replies) const override;
};
struct RelayedGotIntroMessage final : public GotIntroMessage
{
RelayedGotIntroMessage() : GotIntroMessage({})
{}
bool
HandleMessage(llarp_dht_context* ctx, std::vector<IMessage::Ptr_t>& replies) const override;
};
using GotIntroMessage_constptr = std::shared_ptr<const GotIntroMessage>;
} // namespace dht
} // namespace llarp

@ -1,62 +0,0 @@
#include "gotname.hpp"
#include <oxenc/bt_serialize.h>
#include <llarp/dht/context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/path/path_context.hpp>
namespace llarp::dht
{
constexpr size_t NameSizeLimit = 128;
GotNameMessage::GotNameMessage(const Key_t& from, uint64_t txid, service::EncryptedName data)
: IMessage(from), result(std::move(data)), TxID(txid)
{
if (result.ciphertext.size() > NameSizeLimit)
throw std::invalid_argument("name data too big");
}
bool
GotNameMessage::BEncode(llarp_buffer_t* buf) const
{
const std::string nonce((const char*)result.nonce.data(), result.nonce.size());
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());
}
bool
GotNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
{
if (key.startswith("D"))
{
llarp_buffer_t str{};
if (not bencode_read_string(val, &str))
return false;
if (str.sz > NameSizeLimit)
return false;
result.ciphertext.resize(str.sz);
std::copy_n(str.cur, str.sz, result.ciphertext.data());
return true;
}
if (key.startswith("N"))
{
return result.nonce.BDecode(val);
}
if (key.startswith("T"))
{
return bencode_read_integer(val, &TxID);
}
return bencode_discard(val);
}
bool
GotNameMessage::HandleMessage(struct llarp_dht_context* ctx, std::vector<Ptr_t>&) const
{
auto pathset = ctx->impl->GetRouter()->pathContext().GetLocalPathSet(pathID);
if (pathset == nullptr)
return false;
auto copy = std::make_shared<const GotNameMessage>(*this);
return pathset->HandleGotNameMessage(copy);
}
} // namespace llarp::dht

@ -1,25 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/service/name.hpp>
namespace llarp::dht
{
struct GotNameMessage : public IMessage
{
explicit GotNameMessage(const Key_t& from, uint64_t txid, service::EncryptedName data);
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(struct llarp_dht_context* dht, std::vector<Ptr_t>& replies) const override;
service::EncryptedName result;
uint64_t TxID;
};
} // namespace llarp::dht

@ -1,139 +0,0 @@
#include <llarp/dht/context.hpp>
#include "gotrouter.hpp"
#include <memory>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/i_rc_lookup_handler.hpp>
#include <llarp/tooling/rc_event.hpp>
namespace llarp
{
namespace dht
{
GotRouterMessage::~GotRouterMessage() = default;
bool
GotRouterMessage::BEncode(llarp_buffer_t* buf) const
{
if (not bencode_start_dict(buf))
return false;
// message type
if (not BEncodeWriteDictMsgType(buf, "A", "S"))
return false;
if (closerTarget)
{
if (not BEncodeWriteDictEntry("K", *closerTarget, buf))
return false;
}
// near
if (not nearKeys.empty())
{
if (not BEncodeWriteDictList("N", nearKeys, buf))
return false;
}
if (not BEncodeWriteDictList("R", foundRCs, buf))
return false;
// txid
if (not BEncodeWriteDictInt("T", txid, buf))
return false;
// version
if (not BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
bool
GotRouterMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
{
if (key.startswith("K"))
{
if (closerTarget) // duplicate key?
return false;
closerTarget = std::make_unique<dht::Key_t>();
return closerTarget->BDecode(val);
}
if (key.startswith("N"))
{
return BEncodeReadList(nearKeys, val);
}
if (key.startswith("R"))
{
return BEncodeReadList(foundRCs, val);
}
if (key.startswith("T"))
{
return bencode_read_integer(val, &txid);
}
bool read = false;
if (!BEncodeMaybeVerifyVersion("V", version, llarp::constants::proto_version, read, key, val))
return false;
return read;
}
bool
GotRouterMessage::HandleMessage(
llarp_dht_context* ctx,
[[maybe_unused]] std::vector<std::unique_ptr<IMessage>>& replies) const
{
auto& dht = *ctx->impl;
if (relayed)
{
auto pathset = ctx->impl->GetRouter()->pathContext().GetLocalPathSet(pathID);
auto copy = std::make_shared<const GotRouterMessage>(*this);
return pathset && pathset->HandleGotRouterMessage(copy);
}
// not relayed
const TXOwner owner(From, txid);
if (dht.pendingExploreLookups().HasPendingLookupFrom(owner))
{
LogDebug("got ", nearKeys.size(), " results in GRM for explore");
if (nearKeys.empty())
dht.pendingExploreLookups().NotFound(owner, closerTarget);
else
{
dht.pendingExploreLookups().Found(owner, From.as_array(), nearKeys);
}
return true;
}
// not explore lookup
if (dht.pendingRouterLookups().HasPendingLookupFrom(owner))
{
LogDebug("got ", foundRCs.size(), " results in GRM for lookup");
if (foundRCs.empty())
dht.pendingRouterLookups().NotFound(owner, closerTarget);
else if (foundRCs[0].pubkey.IsZero())
return false;
else
dht.pendingRouterLookups().Found(owner, foundRCs[0].pubkey, foundRCs);
return true;
}
// store if valid
for (const auto& rc : foundRCs)
{
if (not dht.GetRouter()->rcLookupHandler().CheckRC(rc))
return false;
if (txid == 0) // txid == 0 on gossip
{
auto* router = dht.GetRouter();
router->NotifyRouterEvent<tooling::RCGossipReceivedEvent>(router->pubkey(), rc);
router->GossipRCIfNeeded(rc);
auto peerDb = router->peerDb();
if (peerDb)
peerDb->handleGossipedRC(rc);
}
}
return true;
}
} // namespace dht
} // namespace llarp

@ -1,68 +0,0 @@
#pragma once
#include <llarp/constants/proto.hpp>
#include <llarp/dht/message.hpp>
#include <llarp/router_contact.hpp>
#include <llarp/util/copy_or_nullptr.hpp>
#include <utility>
#include <vector>
namespace llarp
{
namespace dht
{
struct GotRouterMessage final : public IMessage
{
GotRouterMessage(const Key_t& from, bool tunneled) : IMessage(from), relayed(tunneled)
{}
GotRouterMessage(
const Key_t& from, uint64_t id, const std::vector<RouterContact>& results, bool tunneled)
: IMessage(from), foundRCs(results), txid(id), relayed(tunneled)
{}
GotRouterMessage(const Key_t& from, const Key_t& closer, uint64_t id, bool tunneled)
: IMessage(from), closerTarget(new Key_t(closer)), txid(id), relayed(tunneled)
{}
GotRouterMessage(uint64_t id, std::vector<RouterID> _near, bool tunneled)
: IMessage({}), nearKeys(std::move(_near)), txid(id), relayed(tunneled)
{}
/// gossip message
GotRouterMessage(const RouterContact rc) : IMessage({}), foundRCs({rc}), txid(0)
{
version = llarp::constants::proto_version;
}
GotRouterMessage(const GotRouterMessage& other)
: IMessage(other.From)
, foundRCs(other.foundRCs)
, nearKeys(other.nearKeys)
, closerTarget(copy_or_nullptr(other.closerTarget))
, txid(other.txid)
, relayed(other.relayed)
{
version = other.version;
}
~GotRouterMessage() override;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const override;
std::vector<RouterContact> foundRCs;
std::vector<RouterID> nearKeys;
std::unique_ptr<Key_t> closerTarget;
uint64_t txid = 0;
bool relayed = false;
};
using GotRouterMessage_constptr = std::shared_ptr<const GotRouterMessage>;
} // namespace dht
} // namespace llarp

@ -1,202 +0,0 @@
#include "pubintro.hpp"
#include <llarp/dht/context.hpp>
#include "gotintro.hpp"
#include <llarp/messages/dht_immediate.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/tooling/dht_event.hpp>
namespace llarp
{
namespace dht
{
const uint64_t PublishIntroMessage::MaxPropagationDepth = 5;
PublishIntroMessage::~PublishIntroMessage() = default;
bool
PublishIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
{
bool read = false;
if (!BEncodeMaybeReadDictEntry("I", introset, read, key, val))
return false;
if (read)
return true;
if (!BEncodeMaybeReadDictInt("O", relayOrder, read, key, val))
return false;
if (read)
return true;
uint64_t relayedInt = (relayed ? 1 : 0);
if (!BEncodeMaybeReadDictInt("R", relayedInt, read, key, val))
return false;
if (read)
{
relayed = relayedInt;
return true;
}
if (!BEncodeMaybeReadDictInt("T", txID, read, key, val))
return false;
if (read)
return true;
if (!BEncodeMaybeReadDictInt("V", version, read, key, val))
return false;
if (read)
return true;
return false;
}
bool
PublishIntroMessage::HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const
{
const auto now = ctx->impl->Now();
const llarp::dht::Key_t addr{introset.derivedSigningKey.data()};
auto router = ctx->impl->GetRouter();
router->NotifyRouterEvent<tooling::PubIntroReceivedEvent>(
router->pubkey(),
Key_t(relayed ? router->pubkey() : From.data()),
addr,
txID,
relayOrder);
auto& dht = *ctx->impl;
if (!introset.Verify(now))
{
llarp::LogWarn("Received PublishIntroMessage with invalid introset: ", introset);
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
if (introset.IsExpired(now + llarp::service::MAX_INTROSET_TIME_DELTA))
{
// don't propogate or store
llarp::LogWarn("Received PublishIntroMessage with expired Introset: ", introset);
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
// identify closest 4 routers
auto closestRCs =
dht.GetRouter()->nodedb()->FindManyClosestTo(addr, IntroSetStorageRedundancy);
if (closestRCs.size() != IntroSetStorageRedundancy)
{
llarp::LogWarn("Received PublishIntroMessage but only know ", closestRCs.size(), " nodes");
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
const auto& us = dht.OurKey();
// function to identify the closest 4 routers we know of for this introset
auto propagateIfNotUs = [&](size_t index) {
assert(index < IntroSetStorageRedundancy);
const auto& rc = closestRCs[index];
const Key_t peer{rc.pubkey};
if (peer == us)
{
llarp::LogInfo("we are peer ", index, " so storing instead of propagating");
dht.services()->PutNode(introset);
replies.emplace_back(new GotIntroMessage({introset}, txID));
}
else
{
llarp::LogInfo("propagating to peer ", index);
if (relayed)
{
dht.PropagateLocalIntroSet(pathID, txID, introset, peer, 0);
}
else
{
dht.PropagateIntroSetTo(From, txID, introset, peer, 0);
}
}
};
if (relayed)
{
if (relayOrder >= IntroSetStorageRedundancy)
{
llarp::LogWarn("Received PublishIntroMessage with invalid relayOrder: ", relayOrder);
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
llarp::LogInfo("Relaying PublishIntroMessage for ", addr, ", txid=", txID);
propagateIfNotUs(relayOrder);
}
else
{
int candidateNumber = -1;
int index = 0;
for (const auto& rc : closestRCs)
{
if (rc.pubkey == dht.OurKey())
{
candidateNumber = index;
break;
}
++index;
}
if (candidateNumber >= 0)
{
LogInfo(
"Received PubIntro for ",
addr,
", txid=",
txID,
" and we are candidate ",
candidateNumber);
dht.services()->PutNode(introset);
replies.emplace_back(new GotIntroMessage({introset}, txID));
}
else
{
LogWarn(
"!!! Received PubIntro with relayed==false but we aren't"
" candidate, intro derived key: ",
addr,
", txid=",
txID,
", message from: ",
From);
}
}
return true;
}
bool
PublishIntroMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "I"))
return false;
if (!BEncodeWriteDictEntry("I", introset, buf))
return false;
if (!BEncodeWriteDictInt("O", relayOrder, buf))
return false;
if (!BEncodeWriteDictInt("R", relayed, buf))
return false;
if (!BEncodeWriteDictInt("T", txID, buf))
return false;
if (!BEncodeWriteDictInt("V", llarp::constants::proto_version, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -1,43 +0,0 @@
#pragma once
#include <llarp/dht/message.hpp>
#include <llarp/service/intro_set.hpp>
#include <utility>
#include <vector>
namespace llarp
{
namespace dht
{
struct PublishIntroMessage final : public IMessage
{
static const uint64_t MaxPropagationDepth;
llarp::service::EncryptedIntroSet introset;
bool relayed = false;
uint64_t relayOrder = 0;
uint64_t txID = 0;
PublishIntroMessage(const Key_t& from, bool relayed_) : IMessage(from), relayed(relayed_)
{}
PublishIntroMessage(
const llarp::service::EncryptedIntroSet& introset_,
uint64_t tx,
bool relayed_,
uint64_t relayOrder_)
: IMessage({}), introset(introset_), relayed(relayed_), relayOrder(relayOrder_), txID(tx)
{}
~PublishIntroMessage() override;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val) override;
bool
HandleMessage(
llarp_dht_context* ctx, std::vector<std::unique_ptr<IMessage>>& replies) const override;
};
} // namespace dht
} // namespace llarp

@ -5,63 +5,60 @@
#include <llarp/service/intro_set.hpp>
#include <utility>
namespace llarp
namespace llarp::dht
{
namespace dht
struct RCNode
{
struct RCNode
{
RouterContact rc;
Key_t ID;
RCNode()
{
ID.Zero();
}
RouterContact rc;
Key_t ID;
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey)
{}
RCNode()
{
ID.Zero();
}
util::StatusObject
ExtractStatus() const
{
return rc.ExtractStatus();
}
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey)
{}
bool
operator<(const RCNode& other) const
{
return rc.last_updated < other.rc.last_updated;
}
};
util::StatusObject
ExtractStatus() const
{
return rc.ExtractStatus();
}
struct ISNode
bool
operator<(const RCNode& other) const
{
service::EncryptedIntroSet introset;
return rc.last_updated < other.rc.last_updated;
}
};
struct ISNode
{
service::EncryptedIntroSet introset;
Key_t ID;
Key_t ID;
ISNode()
{
ID.Zero();
}
ISNode()
{
ID.Zero();
}
ISNode(service::EncryptedIntroSet other) : introset(std::move(other))
{
ID = Key_t(introset.derivedSigningKey.as_array());
}
ISNode(service::EncryptedIntroSet other) : introset(std::move(other))
{
ID = Key_t(introset.derivedSigningKey.as_array());
}
util::StatusObject
ExtractStatus() const
{
return introset.ExtractStatus();
}
util::StatusObject
ExtractStatus() const
{
return introset.ExtractStatus();
}
bool
operator<(const ISNode& other) const
{
return introset.signedAt < other.introset.signedAt;
}
};
} // namespace dht
} // namespace llarp
bool
operator<(const ISNode& other) const
{
return introset.signedAt < other.introset.signedAt;
}
};
} // namespace llarp::dht

@ -4,81 +4,78 @@
#include <llarp/dht/messages/pubintro.hpp>
#include <llarp/dht/messages/gotintro.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/routing/dht_message.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/path_dht_message.hpp>
#include <llarp/router/router.hpp>
#include <utility>
namespace llarp
namespace llarp::dht
{
namespace dht
{
PublishServiceJob::PublishServiceJob(
const TXOwner& asker,
const service::EncryptedIntroSet& introset_,
AbstractContext* ctx,
uint64_t relayOrder_)
: TX<TXOwner, service::EncryptedIntroSet>(asker, asker, ctx)
, relayOrder(relayOrder_)
, introset(introset_)
{}
PublishServiceJob::PublishServiceJob(
const TXOwner& asker,
const service::EncryptedIntroSet& introset_,
AbstractDHTMessageHandler* ctx,
uint64_t relayOrder_)
: TX<TXOwner, service::EncryptedIntroSet>(asker, asker, ctx)
, relayOrder(relayOrder_)
, introset(introset_)
{}
bool
PublishServiceJob::Validate(const service::EncryptedIntroSet& value) const
bool
PublishServiceJob::Validate(const service::EncryptedIntroSet& value) const
{
if (value.derivedSigningKey != introset.derivedSigningKey)
{
if (value.derivedSigningKey != introset.derivedSigningKey)
{
llarp::LogWarn("publish introset acknowledgement acked a different service");
return false;
}
const llarp_time_t now = llarp::time_now_ms();
return value.Verify(now);
llarp::LogWarn("publish introset acknowledgement acked a different service");
return false;
}
const llarp_time_t now = llarp::time_now_ms();
return value.verify(now);
}
void
PublishServiceJob::Start(const TXOwner& peer)
{
parent->DHTSendTo(
peer.node.as_array(), new PublishIntroMessage(introset, peer.txid, false, relayOrder));
}
void
PublishServiceJob::Start(const TXOwner& peer)
{
parent->DHTSendTo(
peer.node.as_array(), new PublishIntroMessage(introset, peer.txid, false, relayOrder));
}
void
PublishServiceJob::SendReply()
{
parent->DHTSendTo(whoasked.node.as_array(), new GotIntroMessage({introset}, whoasked.txid));
}
void
PublishServiceJob::SendReply()
{
parent->DHTSendTo(whoasked.node.as_array(), new GotIntroMessage({introset}, whoasked.txid));
}
LocalPublishServiceJob::LocalPublishServiceJob(
const TXOwner& peer,
const PathID_t& fromID,
uint64_t _txid,
const service::EncryptedIntroSet& introset,
AbstractContext* ctx,
uint64_t relayOrder)
: PublishServiceJob(peer, introset, ctx, relayOrder), localPath(fromID), txid(_txid)
{}
LocalPublishServiceJob::LocalPublishServiceJob(
const TXOwner& peer,
const PathID_t& fromID,
uint64_t _txid,
const service::EncryptedIntroSet& introset,
AbstractDHTMessageHandler* ctx,
uint64_t relayOrder)
: PublishServiceJob(peer, introset, ctx, relayOrder), localPath(fromID), txid(_txid)
{}
void
LocalPublishServiceJob::SendReply()
void
LocalPublishServiceJob::SendReply()
{
auto path =
parent->GetRouter()->path_context().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::PathDHTMessage msg;
msg.dht_msgs.emplace_back(new GotIntroMessage({introset}, txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
auto path =
parent->GetRouter()->pathContext().GetByUpstream(parent->OurKey().as_array(), localPath);
if (!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage({introset}, txid));
if (!path->SendRoutingMessage(msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
} // namespace dht
} // namespace llarp
}
} // namespace llarp::dht

@ -8,47 +8,44 @@
#include <set>
namespace llarp
namespace llarp::dht
{
namespace dht
struct PublishServiceJob : public TX<TXOwner, service::EncryptedIntroSet>
{
struct PublishServiceJob : public TX<TXOwner, service::EncryptedIntroSet>
{
uint64_t relayOrder;
service::EncryptedIntroSet introset;
PublishServiceJob(
const TXOwner& asker,
const service::EncryptedIntroSet& introset,
AbstractContext* ctx,
uint64_t relayOrder);
bool
Validate(const service::EncryptedIntroSet& introset) const override;
void
Start(const TXOwner& peer) override;
virtual void
SendReply() override;
};
struct LocalPublishServiceJob : public PublishServiceJob
{
PathID_t localPath;
uint64_t txid;
LocalPublishServiceJob(
const TXOwner& peer,
const PathID_t& fromID,
uint64_t txid,
const service::EncryptedIntroSet& introset,
AbstractContext* ctx,
uint64_t relayOrder);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
uint64_t relayOrder;
service::EncryptedIntroSet introset;
PublishServiceJob(
const TXOwner& asker,
const service::EncryptedIntroSet& introset,
AbstractDHTMessageHandler* ctx,
uint64_t relayOrder);
bool
Validate(const service::EncryptedIntroSet& introset) const override;
void
Start(const TXOwner& peer) override;
void
SendReply() override;
};
struct LocalPublishServiceJob : public PublishServiceJob
{
PathID_t localPath;
uint64_t txid;
LocalPublishServiceJob(
const TXOwner& peer,
const PathID_t& fromID,
uint64_t txid,
const service::EncryptedIntroSet& introset,
AbstractDHTMessageHandler* ctx,
uint64_t relayOrder);
void
SendReply() override;
};
} // namespace llarp::dht
#endif

@ -4,8 +4,8 @@
#include <llarp/dht/messages/findrouter.hpp>
#include <llarp/dht/messages/gotrouter.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/i_rc_lookup_handler.hpp>
#include <llarp/router/router.hpp>
#include <llarp/router/rc_lookup_handler.hpp>
#include <utility>
@ -16,7 +16,7 @@ namespace llarp
RecursiveRouterLookup::RecursiveRouterLookup(
const TXOwner& _whoasked,
const RouterID& _target,
AbstractContext* ctx,
AbstractDHTMessageHandler* ctx,
RouterLookupHandler result)
: TX<RouterID, RouterContact>(_whoasked, _target, ctx), resultHandler(std::move(result))
@ -49,7 +49,7 @@ namespace llarp
RouterContact found;
for (const auto& rc : valuesFound)
{
if (found.OtherIsNewer(rc) && parent->GetRouter()->rcLookupHandler().CheckRC(rc))
if (found.OtherIsNewer(rc) && parent->GetRouter()->rc_lookup_handler().check_rc(rc))
found = rc;
}
valuesFound.clear();

@ -16,7 +16,7 @@ namespace llarp
RecursiveRouterLookup(
const TXOwner& whoasked,
const RouterID& target,
AbstractContext* ctx,
AbstractDHTMessageHandler* ctx,
RouterLookupHandler result);
bool

@ -1,6 +1,5 @@
#include "serviceaddresslookup.hpp"
#include "context.hpp"
#include <llarp/dht/messages/findintro.hpp>
#include <llarp/dht/messages/gotintro.hpp>
#include <utility>
@ -12,7 +11,7 @@ namespace llarp
ServiceAddressLookup::ServiceAddressLookup(
const TXOwner& asker,
const Key_t& addr,
AbstractContext* ctx,
AbstractDHTMessageHandler* ctx,
uint32_t order,
service::EncryptedIntroSetLookupHandler handler)
: TX<TXOwner, service::EncryptedIntroSet>(asker, asker, ctx)
@ -26,7 +25,7 @@ namespace llarp
bool
ServiceAddressLookup::Validate(const service::EncryptedIntroSet& value) const
{
if (!value.Verify(parent->Now()))
if (!value.verify(parent->Now()))
{
llarp::LogWarn("Got invalid introset from service lookup");
return false;

@ -21,7 +21,7 @@ namespace llarp
ServiceAddressLookup(
const TXOwner& asker,
const Key_t& addr,
AbstractContext* ctx,
AbstractDHTMessageHandler* ctx,
uint32_t relayOrder,
service::EncryptedIntroSetLookupHandler handler);

@ -10,7 +10,7 @@ namespace llarp
bool
TagLookup::Validate(const service::EncryptedIntroSet& introset) const
{
if (!introset.Verify(parent->Now()))
if (!introset.verify(parent->Now()))
{
llarp::LogWarn("got invalid introset from tag lookup");
return false;

@ -13,7 +13,10 @@ namespace llarp
{
uint64_t recursionDepth;
TagLookup(
const TXOwner& asker, const service::Tag& tag, AbstractContext* ctx, uint64_t recursion)
const TXOwner& asker,
const service::Tag& tag,
AbstractDHTMessageHandler* ctx,
uint64_t recursion)
: TX<service::Tag, service::EncryptedIntroSet>(asker, tag, ctx), recursionDepth(recursion)
{}

@ -9,74 +9,71 @@
#include <set>
#include <vector>
namespace llarp
namespace llarp::dht
{
namespace dht
{
struct AbstractContext;
struct AbstractDHTMessageHandler;
template <typename K, typename V>
struct TX
{
K target;
AbstractContext* parent;
std::set<Key_t> peersAsked;
std::vector<V> valuesFound;
TXOwner whoasked;
template <typename K, typename V>
struct TX
{
K target;
AbstractDHTMessageHandler* parent;
std::set<Key_t> peersAsked;
std::vector<V> valuesFound;
TXOwner whoasked;
TX(const TXOwner& asker, const K& k, AbstractContext* p)
: target(k), parent(p), whoasked(asker)
{}
TX(const TXOwner& asker, const K& k, AbstractDHTMessageHandler* p)
: target(k), parent(p), whoasked(asker)
{}
virtual ~TX() = default;
virtual ~TX() = default;
void
OnFound(const Key_t& askedPeer, const V& value);
void
OnFound(const Key_t& askedPeer, const V& value);
util::StatusObject
ExtractStatus() const
{
util::StatusObject obj{
{"whoasked", whoasked.ExtractStatus()}, {"target", target.ExtractStatus()}};
std::vector<util::StatusObject> foundObjs;
std::transform(
valuesFound.begin(),
valuesFound.end(),
std::back_inserter(foundObjs),
[](const auto& item) -> util::StatusObject { return item.ExtractStatus(); });
util::StatusObject
ExtractStatus() const
{
util::StatusObject obj{
{"whoasked", whoasked.ExtractStatus()}, {"target", target.ExtractStatus()}};
std::vector<util::StatusObject> foundObjs;
std::transform(
valuesFound.begin(),
valuesFound.end(),
std::back_inserter(foundObjs),
[](const auto& item) -> util::StatusObject { return item.ExtractStatus(); });
obj["found"] = foundObjs;
std::vector<std::string> asked;
std::transform(
peersAsked.begin(),
peersAsked.end(),
std::back_inserter(asked),
[](const auto& item) -> std::string { return item.ToString(); });
obj["asked"] = asked;
return obj;
}
obj["found"] = foundObjs;
std::vector<std::string> asked;
std::transform(
peersAsked.begin(),
peersAsked.end(),
std::back_inserter(asked),
[](const auto& item) -> std::string { return item.ToString(); });
obj["asked"] = asked;
return obj;
}
virtual bool
Validate(const V& value) const = 0;
virtual bool
Validate(const V& value) const = 0;
virtual void
Start(const TXOwner& peer) = 0;
virtual void
Start(const TXOwner& peer) = 0;
virtual void
SendReply() = 0;
};
virtual void
SendReply() = 0;
};
template <typename K, typename V>
inline void
TX<K, V>::OnFound(const Key_t& askedPeer, const V& value)
template <typename K, typename V>
inline void
TX<K, V>::OnFound(const Key_t& askedPeer, const V& value)
{
peersAsked.insert(askedPeer);
if (Validate(value))
{
peersAsked.insert(askedPeer);
if (Validate(value))
{
valuesFound.push_back(value);
}
valuesFound.push_back(value);
}
} // namespace dht
} // namespace llarp
}
} // namespace llarp::dht
#endif

@ -6,6 +6,7 @@
#include <llarp/ev/ev.hpp>
#include <llarp/net/net.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/compare_ptr.hpp>
#include <set>
namespace llarp::dns

@ -105,11 +105,10 @@ namespace llarp::dns
return IsValid();
}
bool
SRVData::BEncode(llarp_buffer_t* buf) const
std::string
SRVData::bt_encode() const
{
const std::string data = oxenc::bt_serialize(toTuple());
return buf->write(data.begin(), data.end());
return oxenc::bt_serialize(toTuple());
}
bool

@ -57,8 +57,8 @@ namespace llarp::dns
return toTupleRef() == other.toTupleRef();
}
bool
BEncode(llarp_buffer_t*) const;
std::string
bt_encode() const;
bool
BDecode(llarp_buffer_t*);

@ -15,6 +15,7 @@
#include <unordered_set>
#include <set>
#include "oxenc/variant.h"
#include <quic.hpp>
namespace llarp
{
@ -126,15 +127,13 @@ namespace llarp
llarp_time_t timeout) = 0;
virtual void
LookupNameAsync(
std::string name, std::function<void(std::optional<AddressVariant_t>)> resultHandler) = 0;
lookup_name(std::string name, std::function<void(oxen::quic::message)> func) = 0;
virtual const EventLoop_ptr&
Loop() = 0;
virtual bool
SendToOrQueue(
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) = 0;
send_to(service::ConvoTag tag, std::string payload) = 0;
/// lookup srv records async
virtual void
@ -144,7 +143,7 @@ namespace llarp
std::function<void(std::vector<dns::SRVData>)> resultHandler) = 0;
virtual void
MarkAddressOutbound(AddressVariant_t remote) = 0;
MarkAddressOutbound(service::Address remote) = 0;
};
} // namespace llarp

@ -11,6 +11,8 @@
#include <future>
#include <utility>
using oxen::log::slns::source_location;
namespace uvw
{
class Loop;
@ -21,6 +23,21 @@ namespace llarp
struct SockAddr;
struct UDPHandle;
static auto loop_cat = llarp::log::Cat("ev-loop");
template <typename... T>
void
loop_trace_log(
const log::logger_ptr& cat_logger,
[[maybe_unused]] const source_location& location,
[[maybe_unused]] fmt::format_string<T...> fmt,
[[maybe_unused]] T&&... args)
{
if (cat_logger)
cat_logger->log(
log::detail::spdlog_sloc(location), log::Level::trace, fmt, std::forward<T>(args)...);
}
namespace vpn
{
class NetworkInterface;
@ -143,6 +160,35 @@ namespace llarp
});
}
/// Calls a function and synchronously obtains its return value. If called from within the
/// event loop, the function is called and returned immediately, otherwise a promise/future
/// is used with `call_soon` to block until the event loop comes around and calls the
/// function.
template <typename Callable, typename Ret = decltype(std::declval<Callable>()())>
Ret
call_get(Callable&& f, source_location src = source_location::current())
{
if (inEventLoop())
{
loop_trace_log(loop_cat, src, "Event loop calling `{}`", src.function_name());
return f();
}
std::promise<Ret> prom;
auto fut = prom.get_future();
call_soon([&f, &prom] {
try
{
prom.set_value(f());
}
catch (...)
{
prom.set_exception(std::current_exception());
}
});
return fut.get();
}
// Wraps a lambda with a lambda that triggers it to be called via loop->call()
// when invoked. E.g.:
//

@ -2,126 +2,123 @@
#include <memory>
#include <stdexcept>
namespace llarp
namespace llarp::exit
{
namespace exit
{
Context::Context(AbstractRouter* r) : m_Router(r)
{}
Context::~Context() = default;
Context::Context(Router* r) : router(r)
{}
Context::~Context() = default;
void
Context::Tick(llarp_time_t now)
void
Context::Tick(llarp_time_t now)
{
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
itr->second->Tick(now);
++itr;
}
}
{
auto itr = m_Closed.begin();
while (itr != m_Closed.end())
{
if ((*itr)->ShouldRemove())
itr = m_Closed.erase(itr);
else
++itr;
}
itr->second->Tick(now);
++itr;
}
}
void
Context::Stop()
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = m_Closed.begin();
while (itr != m_Closed.end())
{
itr->second->Stop();
m_Closed.emplace_back(std::move(itr->second));
itr = m_Exits.erase(itr);
if ((*itr)->ShouldRemove())
itr = m_Closed.erase(itr);
else
++itr;
}
}
}
util::StatusObject
Context::ExtractStatus() const
void
Context::Stop()
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
util::StatusObject obj{};
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
obj[itr->first] = itr->second->ExtractStatus();
++itr;
}
return obj;
itr->second->Stop();
m_Closed.emplace_back(std::move(itr->second));
itr = m_Exits.erase(itr);
}
}
void
Context::CalculateExitTraffic(TrafficStats& stats)
util::StatusObject
Context::ExtractStatus() const
{
util::StatusObject obj{};
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
itr->second->CalculateTrafficStats(stats);
++itr;
}
obj[itr->first] = itr->second->ExtractStatus();
++itr;
}
return obj;
}
exit::Endpoint*
Context::FindEndpointForPath(const PathID_t& path) const
void
Context::CalculateExitTraffic(TrafficStats& stats)
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
auto ep = itr->second->FindEndpointByPath(path);
if (ep)
return ep;
++itr;
}
return nullptr;
itr->second->CalculateTrafficStats(stats);
++itr;
}
}
bool
Context::ObtainNewExit(const PubKey& pk, const PathID_t& path, bool permitInternet)
exit::Endpoint*
Context::FindEndpointForPath(const PathID_t& path) const
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
if (itr->second->AllocateNewExit(pk, path, permitInternet))
return true;
++itr;
}
return false;
auto ep = itr->second->FindEndpointByPath(path);
if (ep)
return ep;
++itr;
}
return nullptr;
}
std::shared_ptr<handlers::ExitEndpoint>
Context::GetExitEndpoint(std::string name) const
bool
Context::ObtainNewExit(const PubKey& pk, const PathID_t& path, bool permitInternet)
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
{
if (auto itr = m_Exits.find(name); itr != m_Exits.end())
{
return itr->second;
}
return nullptr;
if (itr->second->AllocateNewExit(pk, path, permitInternet))
return true;
++itr;
}
return false;
}
void
Context::AddExitEndpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig)
std::shared_ptr<handlers::ExitEndpoint>
Context::GetExitEndpoint(std::string name) const
{
if (auto itr = m_Exits.find(name); itr != m_Exits.end())
{
if (m_Exits.find(name) != m_Exits.end())
throw std::invalid_argument{fmt::format("An exit with name {} already exists", name)};
return itr->second;
}
return nullptr;
}
auto endpoint = std::make_unique<handlers::ExitEndpoint>(name, m_Router);
endpoint->Configure(networkConfig, dnsConfig);
void
Context::AddExitEndpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig)
{
if (m_Exits.find(name) != m_Exits.end())
throw std::invalid_argument{fmt::format("An exit with name {} already exists", name)};
// add endpoint
if (!endpoint->Start())
throw std::runtime_error{fmt::format("Failed to start endpoint {}", name)};
auto endpoint = std::make_unique<handlers::ExitEndpoint>(name, router);
endpoint->Configure(networkConfig, dnsConfig);
m_Exits.emplace(name, std::move(endpoint));
}
// add endpoint
if (!endpoint->Start())
throw std::runtime_error{fmt::format("Failed to start endpoint {}", name)};
m_Exits.emplace(name, std::move(endpoint));
}
} // namespace exit
} // namespace llarp
} // namespace llarp::exit

@ -5,52 +5,49 @@
#include <string>
#include <unordered_map>
namespace llarp
namespace llarp::exit
{
namespace exit
/// owner of all the exit endpoints
struct Context
{
/// owner of all the exit endpoints
struct Context
{
Context(AbstractRouter* r);
~Context();
Context(Router* r);
~Context();
void
Tick(llarp_time_t now);
void
Tick(llarp_time_t now);
void
ClearAllEndpoints();
void
ClearAllEndpoints();
util::StatusObject
ExtractStatus() const;
util::StatusObject
ExtractStatus() const;
/// send close to all exit sessions and remove all sessions
void
Stop();
/// send close to all exit sessions and remove all sessions
void
Stop();
void
AddExitEndpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig);
void
AddExitEndpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig);
bool
ObtainNewExit(const PubKey& remote, const PathID_t& path, bool permitInternet);
bool
ObtainNewExit(const PubKey& remote, const PathID_t& path, bool permitInternet);
exit::Endpoint*
FindEndpointForPath(const PathID_t& path) const;
exit::Endpoint*
FindEndpointForPath(const PathID_t& path) const;
/// calculate (pk, tx, rx) for all exit traffic
using TrafficStats = std::unordered_map<PubKey, std::pair<uint64_t, uint64_t>>;
/// calculate (pk, tx, rx) for all exit traffic
using TrafficStats = std::unordered_map<PubKey, std::pair<uint64_t, uint64_t>>;
void
CalculateExitTraffic(TrafficStats& stats);
void
CalculateExitTraffic(TrafficStats& stats);
std::shared_ptr<handlers::ExitEndpoint>
GetExitEndpoint(std::string name) const;
std::shared_ptr<handlers::ExitEndpoint>
GetExitEndpoint(std::string name) const;
private:
AbstractRouter* m_Router;
std::unordered_map<std::string, std::shared_ptr<handlers::ExitEndpoint>> m_Exits;
std::list<std::shared_ptr<handlers::ExitEndpoint>> m_Closed;
};
} // namespace exit
} // namespace llarp
private:
Router* router;
std::unordered_map<std::string, std::shared_ptr<handlers::ExitEndpoint>> m_Exits;
std::list<std::shared_ptr<handlers::ExitEndpoint>> m_Closed;
};
} // namespace llarp::exit

@ -2,240 +2,236 @@
#include <llarp/handlers/exit.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/quic/tunnel.hpp>
namespace llarp
namespace llarp::exit
{
namespace exit
Endpoint::Endpoint(
const llarp::PubKey& remoteIdent,
const llarp::path::HopHandler_ptr& beginPath,
bool rewriteIP,
huint128_t ip,
llarp::handlers::ExitEndpoint* parent)
: createdAt{parent->Now()}
, m_Parent{parent}
, m_remoteSignKey{remoteIdent}
, m_CurrentPath{beginPath}
, m_IP{ip}
, m_RewriteSource{rewriteIP}
{
Endpoint::Endpoint(
const llarp::PubKey& remoteIdent,
const llarp::path::HopHandler_ptr& beginPath,
bool rewriteIP,
huint128_t ip,
llarp::handlers::ExitEndpoint* parent)
: createdAt{parent->Now()}
, m_Parent{parent}
, m_remoteSignKey{remoteIdent}
, m_CurrentPath{beginPath}
, m_IP{ip}
, m_RewriteSource{rewriteIP}
{
m_LastActive = parent->Now();
}
m_LastActive = parent->Now();
}
Endpoint::~Endpoint()
{
if (m_CurrentPath)
m_Parent->DelEndpointInfo(m_CurrentPath->RXID());
}
Endpoint::~Endpoint()
{
if (m_CurrentPath)
m_Parent->DelEndpointInfo(m_CurrentPath->RXID());
}
void
Endpoint::Close()
{
m_Parent->RemoveExit(this);
}
void
Endpoint::Close()
{
m_Parent->RemoveExit(this);
}
util::StatusObject
Endpoint::ExtractStatus() const
util::StatusObject
Endpoint::ExtractStatus() const
{
auto now = m_Parent->Now();
util::StatusObject obj{
{"identity", m_remoteSignKey.ToString()},
{"ip", m_IP.ToString()},
{"txRate", m_TxRate},
{"rxRate", m_RxRate},
{"createdAt", to_json(createdAt)},
{"exiting", !m_RewriteSource},
{"looksDead", LooksDead(now)},
{"expiresSoon", ExpiresSoon(now)},
{"expired", IsExpired(now)}};
return obj;
}
bool
Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath)
{
if (!m_Parent->UpdateEndpointPath(m_remoteSignKey, nextPath))
return false;
const RouterID us{m_Parent->GetRouter()->pubkey()};
m_CurrentPath = m_Parent->GetRouter()->path_context().GetByUpstream(us, nextPath);
return true;
}
void
Endpoint::Tick(llarp_time_t now)
{
(void)now;
m_RxRate = 0;
m_TxRate = 0;
}
bool
Endpoint::IsExpired(llarp_time_t now) const
{
auto path = GetCurrentPath();
if (path)
{
auto now = m_Parent->Now();
util::StatusObject obj{
{"identity", m_remoteSignKey.ToString()},
{"ip", m_IP.ToString()},
{"txRate", m_TxRate},
{"rxRate", m_RxRate},
{"createdAt", to_json(createdAt)},
{"exiting", !m_RewriteSource},
{"looksDead", LooksDead(now)},
{"expiresSoon", ExpiresSoon(now)},
{"expired", IsExpired(now)}};
return obj;
return path->Expired(now);
}
// if we don't have an underlying path we are considered expired
return true;
}
bool
Endpoint::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
{
if (m_CurrentPath)
return m_CurrentPath->ExpiresSoon(now, dlt);
return true;
}
bool
Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath)
bool
Endpoint::LooksDead(llarp_time_t now, llarp_time_t timeout) const
{
if (ExpiresSoon(now, timeout))
return true;
auto path = GetCurrentPath();
if (not path)
return true;
auto lastPing = path->LastRemoteActivityAt();
if (lastPing == 0s || (now > lastPing && now - lastPing > timeout))
return now > m_LastActive && now - m_LastActive > timeout;
else if (lastPing > 0s) // NOLINT
return now > lastPing && now - lastPing > timeout;
return lastPing > 0s;
}
bool
Endpoint::QueueOutboundTraffic(
PathID_t path, std::vector<byte_t> buf, uint64_t counter, service::ProtocolType t)
{
const service::ConvoTag tag{path.as_array()};
if (t == service::ProtocolType::QUIC)
{
if (!m_Parent->UpdateEndpointPath(m_remoteSignKey, nextPath))
auto quic = m_Parent->GetQUICTunnel();
if (not quic)
return false;
const RouterID us{m_Parent->GetRouter()->pubkey()};
m_CurrentPath = m_Parent->GetRouter()->pathContext().GetByUpstream(us, nextPath);
m_TxRate += buf.size();
quic->receive_packet(tag, std::move(buf));
m_LastActive = m_Parent->Now();
return true;
}
// queue overflow
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
return false;
void
Endpoint::Tick(llarp_time_t now)
{
(void)now;
m_RxRate = 0;
m_TxRate = 0;
}
llarp::net::IPPacket pkt{std::move(buf)};
if (pkt.empty())
return false;
bool
Endpoint::IsExpired(llarp_time_t now) const
if (pkt.IsV6() && m_Parent->SupportsV6())
{
auto path = GetCurrentPath();
if (path)
{
return path->Expired(now);
}
// if we don't have an underlying path we are considered expired
return true;
huint128_t dst;
if (m_RewriteSource)
dst = m_Parent->GetIfAddr();
else
dst = pkt.dstv6();
pkt.UpdateIPv6Address(m_IP, dst);
}
bool
Endpoint::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
else if (pkt.IsV4() && !m_Parent->SupportsV6())
{
if (m_CurrentPath)
return m_CurrentPath->ExpiresSoon(now, dlt);
return true;
huint32_t dst;
if (m_RewriteSource)
dst = net::TruncateV6(m_Parent->GetIfAddr());
else
dst = pkt.dstv4();
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(m_IP)), xhtonl(dst));
}
bool
Endpoint::LooksDead(llarp_time_t now, llarp_time_t timeout) const
else
{
if (ExpiresSoon(now, timeout))
return true;
auto path = GetCurrentPath();
if (not path)
return true;
auto lastPing = path->LastRemoteActivityAt();
if (lastPing == 0s || (now > lastPing && now - lastPing > timeout))
return now > m_LastActive && now - m_LastActive > timeout;
else if (lastPing > 0s) // NOLINT
return now > lastPing && now - lastPing > timeout;
return lastPing > 0s;
return false;
}
bool
Endpoint::QueueOutboundTraffic(
PathID_t path, std::vector<byte_t> buf, uint64_t counter, service::ProtocolType t)
m_TxRate += pkt.size();
m_UpstreamQueue.emplace(std::move(pkt), counter);
m_LastActive = m_Parent->Now();
return true;
}
bool
Endpoint::QueueInboundTraffic(std::vector<byte_t> buf, service::ProtocolType type)
{
if (type != service::ProtocolType::QUIC)
{
const service::ConvoTag tag{path.as_array()};
if (t == service::ProtocolType::QUIC)
{
auto quic = m_Parent->GetQUICTunnel();
if (not quic)
return false;
m_TxRate += buf.size();
quic->receive_packet(tag, std::move(buf));
m_LastActive = m_Parent->Now();
return true;
}
// queue overflow
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
return false;
llarp::net::IPPacket pkt{std::move(buf)};
if (pkt.empty())
return false;
if (pkt.IsV6() && m_Parent->SupportsV6())
{
huint128_t dst;
if (m_RewriteSource)
dst = m_Parent->GetIfAddr();
else
dst = pkt.dstv6();
pkt.UpdateIPv6Address(m_IP, dst);
}
else if (pkt.IsV4() && !m_Parent->SupportsV6())
{
huint32_t dst;
if (m_RewriteSource)
dst = net::TruncateV6(m_Parent->GetIfAddr());
else
dst = pkt.dstv4();
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(m_IP)), xhtonl(dst));
}
huint128_t src;
if (m_RewriteSource)
src = m_Parent->GetIfAddr();
else
{
return false;
}
m_TxRate += pkt.size();
m_UpstreamQueue.emplace(std::move(pkt), counter);
m_LastActive = m_Parent->Now();
return true;
src = pkt.srcv6();
if (pkt.IsV6())
pkt.UpdateIPv6Address(src, m_IP);
else
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
buf = pkt.steal();
}
bool
Endpoint::QueueInboundTraffic(std::vector<byte_t> buf, service::ProtocolType type)
const uint8_t queue_idx = buf.size() / llarp::routing::EXIT_PAD_SIZE;
if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end())
m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{});
auto& queue = m_DownstreamQueues.at(queue_idx);
if (queue.size() == 0)
{
if (type != service::ProtocolType::QUIC)
{
llarp::net::IPPacket pkt{std::move(buf)};
if (pkt.empty())
return false;
huint128_t src;
if (m_RewriteSource)
src = m_Parent->GetIfAddr();
else
src = pkt.srcv6();
if (pkt.IsV6())
pkt.UpdateIPv6Address(src, m_IP);
else
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
buf = pkt.steal();
}
const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize;
if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end())
m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{});
auto& queue = m_DownstreamQueues.at(queue_idx);
if (queue.size() == 0)
{
queue.emplace_back();
queue.back().protocol = type;
return queue.back().PutBuffer(std::move(buf), m_Counter++);
}
auto& msg = queue.back();
if (msg.Size() + buf.size() > llarp::routing::ExitPadSize)
{
queue.emplace_back();
queue.back().protocol = type;
return queue.back().PutBuffer(std::move(buf), m_Counter++);
}
msg.protocol = type;
return msg.PutBuffer(std::move(buf), m_Counter++);
queue.emplace_back();
queue.back().protocol = type;
return queue.back().PutBuffer(std::move(buf), m_Counter++);
}
auto& msg = queue.back();
if (msg.Size() + buf.size() > llarp::routing::EXIT_PAD_SIZE)
{
queue.emplace_back();
queue.back().protocol = type;
return queue.back().PutBuffer(std::move(buf), m_Counter++);
}
msg.protocol = type;
return msg.PutBuffer(std::move(buf), m_Counter++);
}
bool
Endpoint::Flush()
bool
Endpoint::Flush()
{
// flush upstream queue
while (m_UpstreamQueue.size())
{
// flush upstream queue
while (m_UpstreamQueue.size())
{
m_Parent->QueueOutboundTraffic(
const_cast<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
m_UpstreamQueue.pop();
}
// flush downstream queue
auto path = GetCurrentPath();
bool sent = path != nullptr;
if (path)
m_Parent->QueueOutboundTraffic(const_cast<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
m_UpstreamQueue.pop();
}
// flush downstream queue
auto path = GetCurrentPath();
bool sent = path != nullptr;
if (path)
{
for (auto& item : m_DownstreamQueues)
{
for (auto& item : m_DownstreamQueues)
auto& queue = item.second;
while (queue.size())
{
auto& queue = item.second;
while (queue.size())
auto& msg = queue.front();
msg.sequence_number = path->NextSeqNo();
if (path->SendRoutingMessage(msg, m_Parent->GetRouter()))
{
auto& msg = queue.front();
msg.S = path->NextSeqNo();
if (path->SendRoutingMessage(msg, m_Parent->GetRouter()))
{
m_RxRate += msg.Size();
sent = true;
}
queue.pop_front();
m_RxRate += msg.Size();
sent = true;
}
queue.pop_front();
}
}
for (auto& item : m_DownstreamQueues)
item.second.clear();
return sent;
}
} // namespace exit
} // namespace llarp
for (auto& item : m_DownstreamQueues)
item.second.clear();
return sent;
}
} // namespace llarp::exit

@ -2,8 +2,7 @@
#include <llarp/crypto/types.hpp>
#include <llarp/net/ip_packet.hpp>
#include <llarp/path/ihophandler.hpp>
#include <llarp/routing/transfer_traffic_message.hpp>
#include <llarp/path/abstracthophandler.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/util/time.hpp>

@ -3,418 +3,362 @@
#include <llarp/crypto/crypto.hpp>
#include <llarp/routing/handler.hpp>
namespace llarp
namespace llarp::routing
{
namespace routing
bool
ObtainExitMessage::Sign(const llarp::SecretKey& sk)
{
bool
ObtainExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 1024> tmp;
llarp_buffer_t buf(tmp);
I = seckey_topublic(sk);
Z.Zero();
if (!BEncode(&buf))
{
return false;
}
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(Z, sk, buf);
}
pubkey = seckey_topublic(sk);
sig.Zero();
bool
ObtainExitMessage::Verify() const
{
std::array<byte_t, 1024> tmp;
llarp_buffer_t buf(tmp);
ObtainExitMessage copy;
copy = *this;
copy.Z.Zero();
if (!copy.BEncode(&buf))
{
return false;
}
// rewind buffer
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(I, buf, Z);
}
auto bte = bt_encode();
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
ObtainExitMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "O"))
return false;
if (!BEncodeWriteDictArray("B", B, buf))
return false;
if (!BEncodeWriteDictInt("E", E, buf))
return false;
if (!BEncodeWriteDictEntry("I", I, buf))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("T", T, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
if (!BEncodeWriteDictArray("W", W, buf))
return false;
if (!BEncodeWriteDictInt("X", X, buf))
return false;
if (!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
}
bool
ObtainExitMessage::Verify() const
{
ObtainExitMessage copy;
copy = *this;
copy.sig.Zero();
bool
ObtainExitMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictList("B", B, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("E", E, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("I", I, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", T, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictList("W", W, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("X", X, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf))
return false;
return read;
}
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
pubkey, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
ObtainExitMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
{
return h->HandleObtainExitMessage(*this, r);
}
std::string
ObtainExitMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bool
GrantExitMessage::BEncode(llarp_buffer_t* buf) const
try
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "G"))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("T", T, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
if (!BEncodeWriteDictEntry("Y", Y, buf))
return false;
if (!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
btdp.append("E", flag);
btdp.append("I", pubkey.ToView());
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("Z", sig.ToView());
}
bool
GrantExitMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
catch (...)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", T, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf))
return false;
return read;
log::critical(route_cat, "Error: ObtainExitMessage failed to bt encode contents!");
}
bool
GrantExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
GrantExitMessage copy;
copy = *this;
copy.Z.Zero();
if (!copy.BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, Z);
}
return std::move(btdp).str();
}
bool
GrantExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
Z.Zero();
Y.Randomize();
if (!BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(Z, sk, buf);
}
bool
ObtainExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("E", flag, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("I", pubkey, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
return false;
return read;
}
bool
GrantExitMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
{
return h->HandleGrantExitMessage(*this, r);
}
bool
ObtainExitMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleObtainExitMessage(*this, r);
}
bool
RejectExitMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "J"))
return false;
if (!BEncodeWriteDictInt("B", B, buf))
return false;
if (!BEncodeWriteDictList("R", R, buf))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("T", T, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
if (!BEncodeWriteDictEntry("Y", Y, buf))
return false;
if (!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
}
std::string
GrantExitMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bool
RejectExitMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
try
{
bool read = false;
if (!BEncodeMaybeReadDictInt("B", B, read, k, buf))
return false;
if (!BEncodeMaybeReadDictList("R", R, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", T, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf))
return false;
return read;
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("Y", nonce.ToView());
btdp.append("Z", sig.ToView());
}
bool
RejectExitMessage::Sign(const llarp::SecretKey& sk)
catch (...)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
Z.Zero();
Y.Randomize();
if (!BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(Z, sk, buf);
log::critical(route_cat, "Error: GrantExitMessage failed to bt encode contents!");
}
bool
RejectExitMessage::Verify(const llarp::PubKey& pk) const
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
RejectExitMessage copy;
copy = *this;
copy.Z.Zero();
if (!copy.BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, Z);
}
return std::move(btdp).str();
}
bool
RejectExitMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
{
return h->HandleRejectExitMessage(*this, r);
}
bool
GrantExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", nonce, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
return false;
return read;
}
bool
UpdateExitMessage::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "V"))
return false;
if (!BEncodeWriteDictEntry("P", P, buf))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("T", T, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
if (!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
}
bool
GrantExitMessage::Verify(const llarp::PubKey& pk) const
{
GrantExitMessage copy;
copy = *this;
copy.sig.Zero();
bool
UpdateExitMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", T, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("P", P, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf))
return false;
return read;
}
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
UpdateExitMessage::Verify(const llarp::PubKey& pk) const
bool
GrantExitMessage::Sign(const llarp::SecretKey& sk)
{
sig.Zero();
nonce.Randomize();
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
UpdateExitMessage copy;
copy = *this;
copy.Z.Zero();
if (!copy.BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, Z);
}
auto bte = bt_encode();
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
UpdateExitMessage::Sign(const llarp::SecretKey& sk)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
Y.Randomize();
if (!BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(Z, sk, buf);
}
bool
GrantExitMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleGrantExitMessage(*this, r);
}
bool
UpdateExitMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
{
return h->HandleUpdateExitMessage(*this, r);
}
std::string
RejectExitMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bool
UpdateExitVerifyMessage::BEncode(llarp_buffer_t* buf) const
try
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "V"))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("T", T, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
btdp.append("B", backoff_time);
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("Y", nonce.ToView());
btdp.append("Z", sig.ToView());
}
bool
UpdateExitVerifyMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
catch (...)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", T, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
return read;
log::critical(route_cat, "Error: RejectExitMessage failed to bt encode contents!");
}
bool
UpdateExitVerifyMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
return std::move(btdp).str();
}
bool
RejectExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("B", backoff_time, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", nonce, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
return false;
return read;
}
bool
RejectExitMessage::Sign(const llarp::SecretKey& sk)
{
sig.Zero();
nonce.Randomize();
auto bte = bt_encode();
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
RejectExitMessage::Verify(const llarp::PubKey& pk) const
{
RejectExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
RejectExitMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleRejectExitMessage(*this, r);
}
std::string
UpdateExitMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
return h->HandleUpdateExitVerifyMessage(*this, r);
btdp.append("P", path_id.ToView());
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("Z", sig.ToView());
}
bool
CloseExitMessage::BEncode(llarp_buffer_t* buf) const
catch (...)
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictMsgType(buf, "A", "C"))
return false;
if (!BEncodeWriteDictInt("S", S, buf))
return false;
if (!BEncodeWriteDictInt("V", version, buf))
return false;
if (!BEncodeWriteDictEntry("Y", Y, buf))
return false;
if (!BEncodeWriteDictEntry("Z", Z, buf))
return false;
return bencode_end(buf);
log::critical(route_cat, "Error: UpdateExitMessage failed to bt encode contents!");
}
bool
CloseExitMessage::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
return std::move(btdp).str();
}
bool
UpdateExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("P", path_id, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
return false;
return read;
}
bool
UpdateExitMessage::Verify(const llarp::PubKey& pk) const
{
UpdateExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
UpdateExitMessage::Sign(const llarp::SecretKey& sk)
{
nonce.Randomize();
auto bte = bt_encode();
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
UpdateExitMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleUpdateExitMessage(*this, r);
}
std::string
UpdateExitVerifyMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf))
return false;
return read;
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
}
bool
CloseExitMessage::Verify(const llarp::PubKey& pk) const
catch (...)
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
CloseExitMessage copy;
copy = *this;
copy.Z.Zero();
if (!copy.BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->verify(pk, buf, Z);
log::critical(route_cat, "Error: UpdateExitVerifyMessage failed to bt encode contents!");
}
bool
CloseExitMessage::Sign(const llarp::SecretKey& sk)
return std::move(btdp).str();
}
bool
UpdateExitVerifyMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
return false;
return read;
}
bool
UpdateExitVerifyMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleUpdateExitVerifyMessage(*this, r);
}
std::string
CloseExitMessage::bt_encode() const
{
oxenc::bt_dict_producer btdp;
try
{
std::array<byte_t, 512> tmp;
llarp_buffer_t buf(tmp);
Z.Zero();
Y.Randomize();
if (!BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
return CryptoManager::instance()->sign(Z, sk, buf);
btdp.append("S", sequence_number);
btdp.append("Y", nonce.ToView());
btdp.append("Z", sig.ToView());
}
bool
CloseExitMessage::HandleMessage(IMessageHandler* h, AbstractRouter* r) const
catch (...)
{
return h->HandleCloseExitMessage(*this, r);
log::critical(route_cat, "Error: CloseExitMessage failed to bt encode contents!");
}
} // namespace routing
} // namespace llarp
return std::move(btdp).str();
}
bool
CloseExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Y", nonce, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
return false;
return read;
}
bool
CloseExitMessage::Verify(const llarp::PubKey& pk) const
{
CloseExitMessage copy;
copy = *this;
copy.sig.Zero();
auto bte = copy.bt_encode();
return CryptoManager::instance()->verify(
pk, reinterpret_cast<uint8_t*>(bte.data()), bte.size(), sig);
}
bool
CloseExitMessage::Sign(const llarp::SecretKey& sk)
{
sig.Zero();
nonce.Randomize();
auto bte = bt_encode();
return CryptoManager::instance()->sign(
sig, sk, reinterpret_cast<uint8_t*>(bte.data()), bte.size());
}
bool
CloseExitMessage::handle_message(AbstractRoutingMessageHandler* h, Router* r) const
{
return h->HandleCloseExitMessage(*this, r);
}
} // namespace llarp::routing

@ -2,214 +2,194 @@
#include <llarp/crypto/types.hpp>
#include "policy.hpp"
#include <llarp/routing/message.hpp>
#include <vector>
namespace llarp
namespace llarp::routing
{
namespace routing
struct ObtainExitMessage final : public AbstractRoutingMessage
{
struct ObtainExitMessage final : public IMessage
{
std::vector<llarp::exit::Policy> B;
uint64_t E{0};
llarp::PubKey I;
uint64_t T{0};
std::vector<llarp::exit::Policy> W;
uint64_t X{0};
llarp::Signature Z;
ObtainExitMessage() : IMessage()
{}
~ObtainExitMessage() override = default;
void
Clear() override
{
B.clear();
E = 0;
I.Zero();
T = 0;
W.clear();
X = 0;
Z.Zero();
}
/// populates I and signs
bool
Sign(const llarp::SecretKey& sk);
bool
Verify() const;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
};
struct GrantExitMessage final : public IMessage
uint64_t flag{0}; // 0 for snode, 1 for internet access
llarp::PubKey pubkey;
uint64_t tx_id{0};
llarp::Signature sig;
ObtainExitMessage() : AbstractRoutingMessage()
{}
~ObtainExitMessage() override = default;
void
clear() override
{
using Nonce_t = llarp::AlignedBuffer<16>;
flag = 0;
pubkey.Zero();
tx_id = 0;
sig.Zero();
}
/// populates I and signs
bool
Sign(const llarp::SecretKey& sk);
bool
Verify() const;
std::string
bt_encode() const override;
uint64_t T;
Nonce_t Y;
llarp::Signature Z;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
};
bool
Sign(const llarp::SecretKey& sk);
struct GrantExitMessage final : public AbstractRoutingMessage
{
uint64_t tx_id;
llarp::AlignedBuffer<16> nonce;
llarp::Signature sig;
std::string
bt_encode() const override;
bool
Verify(const llarp::PubKey& pk) const;
bool
Sign(const llarp::SecretKey& sk);
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
Verify(const llarp::PubKey& pk) const;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
void
Clear() override
{
T = 0;
Y.Zero();
Z.Zero();
}
};
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
struct RejectExitMessage final : public IMessage
void
clear() override
{
using Nonce_t = llarp::AlignedBuffer<16>;
uint64_t B;
std::vector<llarp::exit::Policy> R;
uint64_t T;
Nonce_t Y;
llarp::Signature Z;
void
Clear() override
{
B = 0;
R.clear();
T = 0;
Y.Zero();
Z.Zero();
}
bool
Sign(const llarp::SecretKey& sk);
bool
Verify(const llarp::PubKey& pk) const;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
};
struct UpdateExitVerifyMessage final : public IMessage
tx_id = 0;
nonce.Zero();
sig.Zero();
}
};
struct RejectExitMessage final : public AbstractRoutingMessage
{
uint64_t backoff_time;
uint64_t tx_id;
llarp::AlignedBuffer<16> nonce;
llarp::Signature sig;
void
clear() override
{
using Nonce_t = llarp::AlignedBuffer<16>;
uint64_t T;
Nonce_t Y;
llarp::Signature Z;
backoff_time = 0;
tx_id = 0;
nonce.Zero();
sig.Zero();
}
~UpdateExitVerifyMessage() override = default;
bool
Sign(const llarp::SecretKey& sk);
void
Clear() override
{
T = 0;
Y.Zero();
Z.Zero();
}
bool
Verify(const llarp::PubKey& pk) const;
bool
BEncode(llarp_buffer_t* buf) const override;
std::string
bt_encode() const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
};
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
};
struct UpdateExitMessage final : public IMessage
{
using Nonce_t = llarp::AlignedBuffer<16>;
llarp::PathID_t P;
uint64_t T;
Nonce_t Y;
llarp::Signature Z;
bool
Sign(const llarp::SecretKey& sk);
bool
Verify(const llarp::PubKey& pk) const;
bool
BEncode(llarp_buffer_t* buf) const override;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
void
Clear() override
{
P.Zero();
T = 0;
Y.Zero();
Z.Zero();
}
};
struct CloseExitMessage final : public IMessage
struct UpdateExitVerifyMessage final : public AbstractRoutingMessage
{
uint64_t tx_id;
llarp::AlignedBuffer<16> nonce;
llarp::Signature sig;
~UpdateExitVerifyMessage() override = default;
void
clear() override
{
using Nonce_t = llarp::AlignedBuffer<16>;
tx_id = 0;
nonce.Zero();
sig.Zero();
}
std::string
bt_encode() const override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
};
struct UpdateExitMessage final : public AbstractRoutingMessage
{
llarp::PathID_t path_id;
uint64_t tx_id;
llarp::AlignedBuffer<16> nonce;
llarp::Signature sig;
Nonce_t Y;
llarp::Signature Z;
bool
Sign(const llarp::SecretKey& sk);
bool
BEncode(llarp_buffer_t* buf) const override;
bool
Verify(const llarp::PubKey& pk) const;
bool
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
std::string
bt_encode() const override;
bool
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
bool
Sign(const llarp::SecretKey& sk);
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
bool
Verify(const llarp::PubKey& pk) const;
void
clear() override
{
path_id.Zero();
tx_id = 0;
nonce.Zero();
sig.Zero();
}
};
struct CloseExitMessage final : public AbstractRoutingMessage
{
llarp::AlignedBuffer<16> nonce;
llarp::Signature sig;
std::string
bt_encode() const override;
bool
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
void
Clear() override
{
Y.Zero();
Z.Zero();
}
};
bool
handle_message(AbstractRoutingMessageHandler* h, Router* r) const override;
} // namespace routing
} // namespace llarp
bool
Sign(const llarp::SecretKey& sk);
bool
Verify(const llarp::PubKey& pk) const;
void
clear() override
{
nonce.Zero();
sig.Zero();
}
};
} // namespace llarp::routing

@ -1,39 +1,36 @@
#include "policy.hpp"
namespace llarp
namespace llarp::exit
{
namespace exit
void
Policy::bt_encode(oxenc::bt_dict_producer& btdp) const
{
bool
Policy::BEncode(llarp_buffer_t* buf) const
try
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictInt("a", proto, buf))
return false;
if (!BEncodeWriteDictInt("b", port, buf))
return false;
if (!BEncodeWriteDictInt("d", drop, buf))
return false;
if (!BEncodeWriteDictInt("v", version, buf))
return false;
return bencode_end(buf);
btdp.append("a", proto);
btdp.append("b", port);
btdp.append("d", drop);
btdp.append("v", version);
}
bool
Policy::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
catch (...)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("a", proto, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("b", port, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("d", drop, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("v", version, read, k, buf))
return false;
return read;
log::critical(policy_cat, "Error: exit Policy failed to bt encode contents!");
}
}
bool
Policy::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictInt("a", proto, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("b", port, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("d", drop, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("v", version, read, k, buf))
return false;
return read;
}
} // namespace exit
} // namespace llarp
} // namespace llarp::exit

@ -1,29 +1,32 @@
#pragma once
#include <oxenc/bt.h>
#include <llarp/util/bencode.hpp>
namespace llarp
namespace
{
namespace exit
static auto policy_cat = llarp::log::Cat("lokinet.policy");
} // namespace
namespace llarp::exit
{
struct Policy
{
struct Policy
{
uint64_t proto = 0;
uint64_t port = 0;
uint64_t drop = 0;
uint64_t version = llarp::constants::proto_version;
uint64_t proto = 0;
uint64_t port = 0;
uint64_t drop = 0;
uint64_t version = llarp::constants::proto_version;
bool
BDecode(llarp_buffer_t* buf)
{
return bencode_decode_dict(*this, buf);
}
bool
BDecode(llarp_buffer_t* buf)
{
return bencode_decode_dict(*this, buf);
}
bool
DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* val);
bool
decode_key(const llarp_buffer_t& k, llarp_buffer_t* val);
bool
BEncode(llarp_buffer_t* buf) const;
};
} // namespace exit
} // namespace llarp
void
bt_encode(oxenc::bt_dict_producer& btdp) const;
};
} // namespace llarp::exit

@ -5,384 +5,392 @@
#include <llarp/path/path_context.hpp>
#include <llarp/path/path.hpp>
#include <llarp/quic/tunnel.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/util/meta/memfn.hpp>
#include <utility>
namespace llarp
namespace llarp::exit
{
namespace exit
BaseSession::BaseSession(
const llarp::RouterID& routerId,
std::function<bool(const llarp_buffer_t&)> writepkt,
Router* r,
size_t numpaths,
size_t hoplen,
EndpointBase* parent)
: llarp::path::Builder{r, numpaths, hoplen}
, exit_router{routerId}
, packet_write_func{std::move(writepkt)}
, m_Counter{0}
, m_LastUse{r->now()}
, m_BundleRC{false}
, m_Parent{parent}
{
BaseSession::BaseSession(
const llarp::RouterID& routerId,
std::function<bool(const llarp_buffer_t&)> writepkt,
AbstractRouter* r,
size_t numpaths,
size_t hoplen,
EndpointBase* parent)
: llarp::path::Builder{r, numpaths, hoplen}
, m_ExitRouter{routerId}
, m_WritePacket{std::move(writepkt)}
, m_Counter{0}
, m_LastUse{r->Now()}
, m_BundleRC{false}
, m_Parent{parent}
{
CryptoManager::instance()->identity_keygen(m_ExitIdentity);
}
CryptoManager::instance()->identity_keygen(exit_key);
}
BaseSession::~BaseSession() = default;
BaseSession::~BaseSession() = default;
void
BaseSession::HandlePathDied(path::Path_ptr p)
{
p->Rebuild();
}
void
BaseSession::HandlePathDied(path::Path_ptr p)
{
p->Rebuild();
}
util::StatusObject
BaseSession::ExtractStatus() const
{
auto obj = path::Builder::ExtractStatus();
obj["lastExitUse"] = to_json(m_LastUse);
auto pub = m_ExitIdentity.toPublic();
obj["exitIdentity"] = pub.ToString();
obj["endpoint"] = m_ExitRouter.ToString();
return obj;
}
util::StatusObject
BaseSession::ExtractStatus() const
{
auto obj = path::Builder::ExtractStatus();
obj["lastExitUse"] = to_json(m_LastUse);
auto pub = exit_key.toPublic();
obj["exitIdentity"] = pub.ToString();
obj["endpoint"] = exit_router.ToString();
return obj;
}
bool
BaseSession::LoadIdentityFromFile(const char* fname)
{
return exit_key.LoadFromFile(fname);
}
bool
BaseSession::LoadIdentityFromFile(const char* fname)
{
return m_ExitIdentity.LoadFromFile(fname);
}
bool
BaseSession::ShouldBuildMore(llarp_time_t now) const
{
if (BuildCooldownHit(now))
return false;
const size_t expect = (1 + (numDesiredPaths / 2));
// check 30 seconds into the future and see if we need more paths
const llarp_time_t future = now + 30s + buildIntervalLimit;
return NumPathsExistingAt(future) < expect;
}
void
BaseSession::BlacklistSNode(const RouterID snode)
{
snode_blacklist.insert(std::move(snode));
}
bool
BaseSession::ShouldBuildMore(llarp_time_t now) const
std::optional<std::vector<RouterContact>>
BaseSession::GetHopsForBuild()
{
if (numHops == 1)
{
if (BuildCooldownHit(now))
return false;
const size_t expect = (1 + (numDesiredPaths / 2));
// check 30 seconds into the future and see if we need more paths
const llarp_time_t future = now + 30s + buildIntervalLimit;
return NumPathsExistingAt(future) < expect;
if (auto maybe = router->node_db()->get_rc(exit_router))
return std::vector<RouterContact>{*maybe};
return std::nullopt;
}
else
return GetHopsAlignedToForBuild(exit_router);
}
void
BaseSession::BlacklistSNode(const RouterID snode)
{
m_SnodeBlacklist.insert(std::move(snode));
}
bool
BaseSession::CheckPathDead(path::Path_ptr, llarp_time_t dlt)
{
return dlt >= path::ALIVE_TIMEOUT;
}
std::optional<std::vector<RouterContact>>
BaseSession::GetHopsForBuild()
{
if (numHops == 1)
{
if (auto maybe = m_router->nodedb()->Get(m_ExitRouter))
return std::vector<RouterContact>{*maybe};
return std::nullopt;
}
else
return GetHopsAlignedToForBuild(m_ExitRouter);
}
void
BaseSession::HandlePathBuilt(llarp::path::Path_ptr p)
{
path::Builder::HandlePathBuilt(p);
// p->SetDropHandler(util::memFn(&BaseSession::HandleTrafficDrop, this));
// p->SetDeadChecker(util::memFn(&BaseSession::CheckPathDead, this));
// p->SetExitTrafficHandler(util::memFn(&BaseSession::HandleTraffic, this));
// p->AddObtainExitHandler(util::memFn(&BaseSession::HandleGotExit, this));
if (p->obtain_exit(
exit_key, std::is_same_v<decltype(p), ExitSession> ? 1 : 0, p->TXID().bt_encode()))
log::info(link_cat, "Asking {} for exit", exit_router);
else
log::warning(link_cat, "Failed to send exit request");
}
void
BaseSession::AddReadyHook(SessionReadyFunc func)
{
m_PendingCallbacks.emplace_back(func);
}
bool
BaseSession::CheckPathDead(path::Path_ptr, llarp_time_t dlt)
bool
BaseSession::HandleGotExit(llarp::path::Path_ptr p, llarp_time_t b)
{
if (b == 0s)
{
return dlt >= path::alive_timeout;
llarp::LogInfo("obtained an exit via ", p->Endpoint());
m_CurrentPath = p->RXID();
CallPendingCallbacks(true);
}
return true;
}
void
BaseSession::HandlePathBuilt(llarp::path::Path_ptr p)
{
path::Builder::HandlePathBuilt(p);
p->SetDropHandler(util::memFn(&BaseSession::HandleTrafficDrop, this));
p->SetDeadChecker(util::memFn(&BaseSession::CheckPathDead, this));
p->SetExitTrafficHandler(util::memFn(&BaseSession::HandleTraffic, this));
p->AddObtainExitHandler(util::memFn(&BaseSession::HandleGotExit, this));
routing::ObtainExitMessage obtain;
obtain.S = p->NextSeqNo();
obtain.T = llarp::randint();
PopulateRequest(obtain);
if (!obtain.Sign(m_ExitIdentity))
{
llarp::LogError("Failed to sign exit request");
return;
}
if (p->SendExitRequest(obtain, m_router))
llarp::LogInfo("asking ", m_ExitRouter, " for exit");
else
llarp::LogError("failed to send exit request");
}
void
BaseSession::CallPendingCallbacks(bool success)
{
if (m_PendingCallbacks.empty())
return;
void
BaseSession::AddReadyHook(SessionReadyFunc func)
if (success)
{
m_PendingCallbacks.emplace_back(func);
auto self = shared_from_this();
for (auto& f : m_PendingCallbacks)
f(self);
}
bool
BaseSession::HandleGotExit(llarp::path::Path_ptr p, llarp_time_t b)
else
{
if (b == 0s)
{
llarp::LogInfo("obtained an exit via ", p->Endpoint());
m_CurrentPath = p->RXID();
CallPendingCallbacks(true);
}
return true;
for (auto& f : m_PendingCallbacks)
f(nullptr);
}
m_PendingCallbacks.clear();
}
void
BaseSession::CallPendingCallbacks(bool success)
{
if (m_PendingCallbacks.empty())
return;
if (success)
{
auto self = shared_from_this();
for (auto& f : m_PendingCallbacks)
f(self);
}
else
void
BaseSession::ResetInternalState()
{
auto sendExitClose = [&](const llarp::path::Path_ptr p) {
const static auto roles = llarp::path::ePathRoleExit | llarp::path::ePathRoleSVC;
if (p->SupportsAnyRoles(roles))
{
for (auto& f : m_PendingCallbacks)
f(nullptr);
log::info(link_cat, "{} closing exit path", p->name());
if (p->close_exit(exit_key, p->TXID().bt_encode()))
p->ClearRoles(roles);
else
llarp::LogWarn(p->name(), " failed to send exit close message");
}
m_PendingCallbacks.clear();
}
};
void
BaseSession::ResetInternalState()
{
auto sendExitClose = [&](const llarp::path::Path_ptr p) {
const static auto roles = llarp::path::ePathRoleExit | llarp::path::ePathRoleSVC;
if (p->SupportsAnyRoles(roles))
{
llarp::LogInfo(p->Name(), " closing exit path");
routing::CloseExitMessage msg;
if (msg.Sign(m_ExitIdentity) && p->SendExitClose(msg, m_router))
{
p->ClearRoles(roles);
}
else
llarp::LogWarn(p->Name(), " failed to send exit close message");
}
};
ForEachPath(sendExitClose);
path::Builder::ResetInternalState();
}
ForEachPath(sendExitClose);
path::Builder::ResetInternalState();
}
bool
BaseSession::Stop()
{
CallPendingCallbacks(false);
auto sendExitClose = [&](const path::Path_ptr p) {
if (p->SupportsAnyRoles(path::ePathRoleExit))
{
LogInfo(p->Name(), " closing exit path");
routing::CloseExitMessage msg;
if (!(msg.Sign(m_ExitIdentity) && p->SendExitClose(msg, m_router)))
LogWarn(p->Name(), " failed to send exit close message");
}
};
ForEachPath(sendExitClose);
m_router->pathContext().RemovePathSet(shared_from_this());
return path::Builder::Stop();
}
bool
BaseSession::HandleTraffic(
llarp::path::Path_ptr path,
const llarp_buffer_t& buf,
uint64_t counter,
service::ProtocolType t)
{
const service::ConvoTag tag{path->RXID().as_array()};
if (t == service::ProtocolType::QUIC)
{
auto quic = m_Parent->GetQUICTunnel();
if (not quic)
return false;
quic->receive_packet(tag, buf);
return true;
}
if (m_WritePacket)
bool
BaseSession::Stop()
{
CallPendingCallbacks(false);
auto sendExitClose = [&](const path::Path_ptr p) {
if (p->SupportsAnyRoles(path::ePathRoleExit))
{
llarp::net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return false;
m_LastUse = m_router->Now();
m_Downstream.emplace(counter, pkt);
return true;
LogInfo(p->name(), " closing exit path");
routing::CloseExitMessage msg;
if (!(msg.Sign(exit_key) && p->SendExitClose(msg, router)))
LogWarn(p->name(), " failed to send exit close message");
}
return false;
}
};
ForEachPath(sendExitClose);
router->path_context().RemovePathSet(shared_from_this());
return path::Builder::Stop();
}
bool
BaseSession::HandleTraffic(
llarp::path::Path_ptr path,
const llarp_buffer_t& buf,
uint64_t counter,
service::ProtocolType t)
{
const service::ConvoTag tag{path->RXID().as_array()};
bool
BaseSession::HandleTrafficDrop(llarp::path::Path_ptr p, const PathID_t& path, uint64_t s)
if (t == service::ProtocolType::QUIC)
{
llarp::LogError("dropped traffic on exit ", m_ExitRouter, " S=", s, " P=", path);
p->EnterState(path::ePathIgnore, m_router->Now());
auto quic = m_Parent->GetQUICTunnel();
if (not quic)
return false;
quic->receive_packet(tag, buf);
return true;
}
bool
BaseSession::QueueUpstreamTraffic(
llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t)
if (packet_write_func)
{
auto& queue = m_Upstream[pkt.size() / N];
// queue overflow
if (queue.size() >= MaxUpstreamQueueLength)
llarp::net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return false;
if (queue.size() == 0)
{
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
}
auto& back = queue.back();
// pack to nearest N
if (back.Size() + pkt.size() > N)
{
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
}
back.protocol = t;
return back.PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
m_LastUse = router->now();
m_Downstream.emplace(counter, pkt);
return true;
}
return false;
}
bool
BaseSession::IsReady() const
bool
BaseSession::HandleTrafficDrop(llarp::path::Path_ptr p, const PathID_t& path, uint64_t s)
{
llarp::LogError("dropped traffic on exit ", exit_router, " S=", s, " P=", path);
p->EnterState(path::ePathIgnore, router->now());
return true;
}
bool
BaseSession::QueueUpstreamTraffic(
llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t)
{
auto& queue = m_Upstream[pkt.size() / N];
// queue overflow
if (queue.size() >= MaxUpstreamQueueLength)
return false;
if (queue.size() == 0)
{
if (m_CurrentPath.IsZero())
return false;
const size_t expect = (1 + (numDesiredPaths / 2));
return AvailablePaths(llarp::path::ePathRoleExit) >= expect;
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
}
bool
BaseSession::IsExpired(llarp_time_t now) const
auto& back = queue.back();
// pack to nearest N
if (back.Size() + pkt.size() > N)
{
return now > m_LastUse && now - m_LastUse > LifeSpan;
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
}
back.protocol = t;
return back.PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
}
bool
BaseSession::UrgentBuild(llarp_time_t now) const
{
if (BuildCooldownHit(now))
return false;
if (IsReady() and NumInStatus(path::ePathBuilding) < numDesiredPaths)
return path::Builder::UrgentBuild(now);
bool
BaseSession::IsReady() const
{
if (m_CurrentPath.IsZero())
return false;
}
const size_t expect = (1 + (numDesiredPaths / 2));
return AvailablePaths(llarp::path::ePathRoleExit) >= expect;
}
bool
BaseSession::IsExpired(llarp_time_t now) const
{
return now > m_LastUse && now - m_LastUse > LifeSpan;
}
bool
BaseSession::UrgentBuild(llarp_time_t now) const
{
if (BuildCooldownHit(now))
return false;
if (IsReady() and NumInStatus(path::ePathBuilding) < numDesiredPaths)
return path::Builder::UrgentBuild(now);
return false;
}
bool
BaseSession::FlushUpstream()
bool
BaseSession::FlushUpstream()
{
auto now = router->now();
auto path = PickEstablishedPath(llarp::path::ePathRoleExit);
if (path)
{
auto now = m_router->Now();
auto path = PickEstablishedPath(llarp::path::ePathRoleExit);
if (path)
for (auto& [i, queue] : m_Upstream)
{
for (auto& [i, queue] : m_Upstream)
while (queue.size())
{
while (queue.size())
{
auto& msg = queue.front();
msg.S = path->NextSeqNo();
path->SendRoutingMessage(msg, m_router);
queue.pop_front();
}
auto& msg = queue.front();
msg.sequence_number = path->NextSeqNo();
path->SendRoutingMessage(msg, router);
queue.pop_front();
}
}
else
{
if (m_Upstream.size())
llarp::LogWarn("no path for exit session");
// discard upstream
for (auto& [i, queue] : m_Upstream)
queue.clear();
m_Upstream.clear();
if (numHops == 1)
{
auto r = m_router;
if (const auto maybe = r->nodedb()->Get(m_ExitRouter); maybe.has_value())
r->TryConnectAsync(*maybe, 5);
else
r->LookupRouter(m_ExitRouter, [r](const std::vector<RouterContact>& results) {
if (results.size())
r->TryConnectAsync(results[0], 5);
});
}
else if (UrgentBuild(now))
BuildOneAlignedTo(m_ExitRouter);
}
return true;
}
void
BaseSession::FlushDownstream()
else
{
while (m_Downstream.size())
{
if (m_WritePacket)
m_WritePacket(const_cast<net::IPPacket&>(m_Downstream.top().second).steal());
m_Downstream.pop();
}
}
SNodeSession::SNodeSession(
const llarp::RouterID& snodeRouter,
std::function<bool(const llarp_buffer_t&)> writepkt,
AbstractRouter* r,
size_t numpaths,
size_t hoplen,
bool useRouterSNodeKey,
EndpointBase* parent)
: BaseSession{snodeRouter, writepkt, r, numpaths, hoplen, parent}
{
if (useRouterSNodeKey)
if (m_Upstream.size())
llarp::LogWarn("no path for exit session");
// discard upstream
for (auto& [i, queue] : m_Upstream)
queue.clear();
m_Upstream.clear();
if (numHops == 1)
{
m_ExitIdentity = r->identity();
auto r = router;
if (const auto maybe = r->node_db()->get_rc(exit_router); maybe.has_value())
r->connect_to(*maybe);
else
r->lookup_router(exit_router, [r](oxen::quic::message m) mutable {
if (m)
{
std::string payload;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
payload = btdc.require<std::string>("RC");
}
catch (...)
{
log::warning(link_cat, "Failed to parse Find Router response!");
throw;
}
RouterContact result{std::move(payload)};
r->node_db()->put_rc_if_newer(result);
r->connect_to(result);
}
else
{
r->link_manager().handle_find_router_error(std::move(m));
}
});
}
else if (UrgentBuild(now))
BuildOneAlignedTo(exit_router);
}
return true;
}
std::string
SNodeSession::Name() const
void
BaseSession::FlushDownstream()
{
while (m_Downstream.size())
{
return "SNode::" + m_ExitRouter.ToString();
if (packet_write_func)
packet_write_func(const_cast<net::IPPacket&>(m_Downstream.top().second).steal());
m_Downstream.pop();
}
std::string
ExitSession::Name() const
}
SNodeSession::SNodeSession(
const llarp::RouterID& snodeRouter,
std::function<bool(const llarp_buffer_t&)> writepkt,
Router* r,
size_t numpaths,
size_t hoplen,
bool useRouterSNodeKey,
EndpointBase* parent)
: BaseSession{snodeRouter, writepkt, r, numpaths, hoplen, parent}
{
if (useRouterSNodeKey)
{
return "Exit::" + m_ExitRouter.ToString();
exit_key = r->identity();
}
}
void
SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
pkt.ZeroAddresses();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
}
std::string
SNodeSession::Name() const
{
return "SNode::" + exit_router.ToString();
}
void
ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
std::string
ExitSession::Name() const
{
return "Exit::" + exit_router.ToString();
}
pkt.ZeroSourceAddress();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
}
} // namespace exit
} // namespace llarp
void
SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
pkt.ZeroAddresses();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
}
void
ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
pkt.ZeroSourceAddress();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
}
} // namespace llarp::exit

@ -1,10 +1,8 @@
#pragma once
#include "exit_messages.hpp"
#include <llarp/service/protocol_type.hpp>
#include <llarp/net/ip_packet.hpp>
#include <llarp/path/pathbuilder.hpp>
#include <llarp/routing/transfer_traffic_message.hpp>
#include <llarp/constants/path.hpp>
#include <deque>
@ -27,7 +25,7 @@ namespace llarp
using SessionReadyFunc = std::function<void(BaseSession_ptr)>;
static constexpr auto LifeSpan = path::default_lifetime;
static constexpr auto LifeSpan = path::DEFAULT_LIFETIME;
/// a persisting exit session with an exit router
struct BaseSession : public llarp::path::Builder,
@ -38,7 +36,7 @@ namespace llarp
BaseSession(
const llarp::RouterID& exitRouter,
std::function<bool(const llarp_buffer_t&)> writepkt,
AbstractRouter* r,
Router* r,
size_t numpaths,
size_t hoplen,
EndpointBase* parent);
@ -117,7 +115,7 @@ namespace llarp
const llarp::RouterID
Endpoint() const
{
return m_ExitRouter;
return exit_router;
}
std::optional<PathID_t>
@ -138,9 +136,9 @@ namespace llarp
AddReadyHook(SessionReadyFunc func);
protected:
llarp::RouterID m_ExitRouter;
llarp::SecretKey m_ExitIdentity;
std::function<bool(const llarp_buffer_t&)> m_WritePacket;
llarp::RouterID exit_router;
llarp::SecretKey exit_key;
std::function<bool(const llarp_buffer_t&)> packet_write_func;
virtual void
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const = 0;
@ -159,11 +157,9 @@ namespace llarp
service::ProtocolType t);
private:
std::set<RouterID> m_SnodeBlacklist;
std::set<RouterID> snode_blacklist;
using UpstreamTrafficQueue_t = std::deque<llarp::routing::TransferTrafficMessage>;
using TieredQueue_t = std::map<uint8_t, UpstreamTrafficQueue_t>;
TieredQueue_t m_Upstream;
std::map<uint8_t, std::deque<routing::TransferTrafficMessage>> m_Upstream;
PathID_t m_CurrentPath;
@ -198,7 +194,7 @@ namespace llarp
ExitSession(
const llarp::RouterID& snodeRouter,
std::function<bool(const llarp_buffer_t&)> writepkt,
AbstractRouter* r,
Router* r,
size_t numpaths,
size_t hoplen,
EndpointBase* parent)
@ -210,7 +206,7 @@ namespace llarp
std::string
Name() const override;
virtual void
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:
@ -218,8 +214,8 @@ namespace llarp
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
{
// TODO: set expiration time
msg.X = 0;
msg.E = 1;
// msg.address_lifetime = 0;
msg.flag = 1;
}
};
@ -228,7 +224,7 @@ namespace llarp
SNodeSession(
const llarp::RouterID& snodeRouter,
std::function<bool(const llarp_buffer_t&)> writepkt,
AbstractRouter* r,
Router* r,
size_t numpaths,
size_t hoplen,
bool useRouterSNodeKey,
@ -239,7 +235,7 @@ namespace llarp
std::string
Name() const override;
virtual void
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:
@ -247,8 +243,8 @@ namespace llarp
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
{
// TODO: set expiration time
msg.X = 0;
msg.E = 0;
// msg.address_lifetime = 0;
msg.flag = 0;
}
};

File diff suppressed because it is too large Load Diff

@ -7,7 +7,7 @@
namespace llarp
{
struct AbstractRouter;
struct Router;
namespace handlers
{
struct ExitEndpoint : public dns::Resolver_Base, public EndpointBase
@ -31,7 +31,7 @@ namespace llarp
const SockAddr& to,
const SockAddr& from) override;
ExitEndpoint(std::string name, AbstractRouter* r);
ExitEndpoint(std::string name, Router* r);
~ExitEndpoint() override;
std::optional<AddressVariant_t>
@ -47,9 +47,7 @@ namespace llarp
llarp_time_t timeout) override;
void
LookupNameAsync(
std::string name,
std::function<void(std::optional<AddressVariant_t>)> resultHandler) override;
lookup_name(std::string name, std::function<void(oxen::quic::message)> func) override;
const EventLoop_ptr&
Loop() override;
@ -60,11 +58,10 @@ namespace llarp
void
SRVRecordsChanged() override;
void MarkAddressOutbound(AddressVariant_t) override{};
void MarkAddressOutbound(service::Address) override{};
bool
SendToOrQueue(
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) override;
send_to(service::ConvoTag tag, std::string payload) override;
void
Tick(llarp_time_t now);
@ -112,7 +109,7 @@ namespace llarp
void
OnInetPacket(net::IPPacket buf);
AbstractRouter*
Router*
GetRouter();
llarp_time_t
@ -122,7 +119,7 @@ namespace llarp
void
CalculateTrafficStats(Stats& stats)
{
for (auto& [pubkey, endpoint] : m_ActiveExits)
for (auto& [pubkey, endpoint] : active_exits)
{
stats[pubkey].first += endpoint->TxRate();
stats[pubkey].second += endpoint->RxRate();
@ -172,7 +169,7 @@ namespace llarp
GetIPForIdent(const PubKey pk);
/// async obtain snode session and call callback when it's ready to send
void
ObtainSNodeSession(const RouterID& router, exit::SessionReadyFunc obtainCb);
ObtainSNodeSession(const RouterID& rid, exit::SessionReadyFunc obtain_cb);
private:
huint128_t
@ -192,54 +189,52 @@ namespace llarp
void
KickIdentOffExit(const PubKey& pk);
AbstractRouter* m_Router;
std::shared_ptr<dns::Server> m_Resolver;
bool m_ShouldInitTun;
std::string m_Name;
bool m_PermitExit;
std::unordered_map<PathID_t, PubKey> m_Paths;
std::unordered_map<PubKey, exit::Endpoint*> m_ChosenExits;
Router* router;
std::shared_ptr<dns::Server> resolver;
bool should_init_tun;
std::string name;
bool permit_exit;
std::unordered_map<PathID_t, PubKey> paths;
std::unordered_multimap<PubKey, std::unique_ptr<exit::Endpoint>> m_ActiveExits;
std::unordered_map<PubKey, exit::Endpoint*> chosen_exits;
using KeyMap_t = std::unordered_map<PubKey, huint128_t>;
std::unordered_multimap<PubKey, std::unique_ptr<exit::Endpoint>> active_exits;
KeyMap_t m_KeyToIP;
std::unordered_map<PubKey, huint128_t> key_to_IP;
using SNodes_t = std::set<PubKey>;
/// set of pubkeys we treat as snodes
SNodes_t m_SNodeKeys;
SNodes_t snode_keys;
using SNodeSessions_t = std::unordered_map<RouterID, std::shared_ptr<exit::SNodeSession>>;
/// snode sessions we are talking to directly
SNodeSessions_t m_SNodeSessions;
SNodeSessions_t snode_sessions;
std::unordered_map<huint128_t, PubKey> m_IPToKey;
std::unordered_map<huint128_t, PubKey> ip_to_key;
huint128_t m_IfAddr;
huint128_t m_HigestAddr;
huint128_t if_addr;
huint128_t highest_addr;
huint128_t m_NextAddr;
IPRange m_OurRange;
std::string m_ifname;
huint128_t next_addr;
IPRange ip_range;
std::string if_name;
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
std::unordered_map<huint128_t, llarp_time_t> ip_activity;
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
std::shared_ptr<vpn::NetworkInterface> if_net;
SockAddr m_LocalResolverAddr;
std::vector<SockAddr> m_UpstreamResolvers;
SockAddr resolver_addr;
std::vector<SockAddr> upstream_resolvers;
std::shared_ptr<quic::TunnelManager> m_QUIC;
std::shared_ptr<quic::TunnelManager> tunnel_manager;
using PacketQueue_t = std::
priority_queue<net::IPPacket, std::vector<net::IPPacket>, net::IPPacket::CompareOrder>;
/// internet to llarp packet queue
PacketQueue_t m_InetToNetwork;
bool m_UseV6;
DnsConfig m_DNSConf;
PacketQueue_t inet_to_network;
bool use_ipv6;
DnsConfig dns_conf;
};
} // namespace handlers
} // namespace llarp

@ -3,7 +3,7 @@
#include <llarp/service/endpoint.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/quic/tunnel.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/ev/ev.hpp>
#include <llarp/vpn/egres_packet_router.hpp>
@ -12,7 +12,7 @@ namespace llarp::handlers
struct NullEndpoint final : public llarp::service::Endpoint,
public std::enable_shared_from_this<NullEndpoint>
{
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
NullEndpoint(Router* r, llarp::service::Context* parent)
: llarp::service::Endpoint{r, parent}
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
var::visit(

File diff suppressed because it is too large Load Diff

@ -16,313 +16,303 @@
#include <type_traits>
#include <variant>
namespace llarp
namespace llarp::handlers
{
namespace handlers
struct TunEndpoint : public service::Endpoint,
public dns::Resolver_Base,
public std::enable_shared_from_this<TunEndpoint>
{
struct TunEndpoint : public service::Endpoint,
public dns::Resolver_Base,
public std::enable_shared_from_this<TunEndpoint>
TunEndpoint(Router* r, llarp::service::Context* parent);
~TunEndpoint() override;
vpn::NetworkInterface*
GetVPNInterface() override
{
TunEndpoint(AbstractRouter* r, llarp::service::Context* parent);
~TunEndpoint() override;
return m_NetIf.get();
}
vpn::NetworkInterface*
GetVPNInterface() override
{
return m_NetIf.get();
}
int
Rank() const override
{
return 0;
}
int
Rank() const override
{
return 0;
}
std::string_view
ResolverName() const override
{
return "lokinet";
}
bool
MaybeHookDNS(
std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query,
const SockAddr& to,
const SockAddr& from) override;
path::PathSet_ptr
GetSelf() override
{
return shared_from_this();
}
std::string_view
ResolverName() const override
{
return "lokinet";
}
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
bool
MaybeHookDNS(
std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query,
const SockAddr& to,
const SockAddr& from) override;
path::PathSet_ptr
GetSelf() override
{
return shared_from_this();
}
void
Thaw() override;
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
// Reconfigures DNS servers and restarts libunbound with the new servers.
void
ReconfigureDNS(std::vector<SockAddr> servers);
void
Thaw() override;
bool
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf) override;
// Reconfigures DNS servers and restarts libunbound with the new servers.
void
ReconfigureDNS(std::vector<SockAddr> servers);
void
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
bool
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf) override;
std::string
GetIfName() const override;
void
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
void
Tick(llarp_time_t now) override;
std::string
GetIfName() const override;
util::StatusObject
ExtractStatus() const override;
void
Tick(llarp_time_t now) override;
std::unordered_map<std::string, std::string>
NotifyParams() const override;
util::StatusObject
ExtractStatus() const override;
bool
SupportsV6() const override;
std::unordered_map<std::string, std::string>
NotifyParams() const override;
bool
ShouldHookDNSMessage(const dns::Message& msg) const;
bool
SupportsV6() const override;
bool
HandleHookedDNSMessage(dns::Message query, std::function<void(dns::Message)> sendreply);
bool
ShouldHookDNSMessage(const dns::Message& msg) const;
void
TickTun(llarp_time_t now);
bool
HandleHookedDNSMessage(dns::Message query, std::function<void(dns::Message)> sendreply);
bool
MapAddress(const service::Address& remote, huint128_t ip, bool SNode);
void
TickTun(llarp_time_t now);
bool
Start() override;
bool
MapAddress(const service::Address& remote, huint128_t ip, bool SNode);
bool
Stop() override;
bool
Start() override;
bool
Stop() override;
bool
IsSNode() const;
/// set up tun interface, blocking
bool
SetupTun();
bool
IsSNode() const;
void
SetupDNS();
/// set up tun interface, blocking
bool
SetupTun();
/// overrides Endpoint
std::shared_ptr<dns::Server>
DNS() const override
{
return m_DNS;
};
void
SetupDNS();
/// overrides Endpoint
bool
SetupNetworking() override;
/// overrides Endpoint
std::shared_ptr<dns::Server>
DNS() const override
{
return m_DNS;
};
/// overrides Endpoint
bool
HandleInboundPacket(
const service::ConvoTag tag,
const llarp_buffer_t& pkt,
service::ProtocolType t,
uint64_t seqno) override;
/// overrides Endpoint
bool
SetupNetworking() override;
/// overrides Endpoint
bool
HandleInboundPacket(
const service::ConvoTag tag,
const llarp_buffer_t& pkt,
service::ProtocolType t,
uint64_t seqno) override;
/// handle inbound traffic
bool
HandleWriteIPPacket(const llarp_buffer_t& buf, huint128_t src, huint128_t dst, uint64_t seqno);
/// we got a packet from the user
void
HandleGotUserPacket(llarp::net::IPPacket pkt);
/// get the local interface's address
huint128_t
GetIfAddr() const override;
/// we have an interface addr
bool
HasIfAddr() const override
{
return true;
}
/// handle inbound traffic
bool
HandleWriteIPPacket(
const llarp_buffer_t& buf, huint128_t src, huint128_t dst, uint64_t seqno);
bool
HasLocalIP(const huint128_t& ip) const;
/// we got a packet from the user
void
HandleGotUserPacket(llarp::net::IPPacket pkt);
std::optional<net::TrafficPolicy>
GetExitPolicy() const override
{
return m_TrafficPolicy;
}
/// get the local interface's address
huint128_t
GetIfAddr() const override;
std::set<IPRange>
GetOwnedRanges() const override
{
return m_OwnedRanges;
}
/// we have an interface addr
bool
HasIfAddr() const override
{
return true;
}
llarp_time_t
PathAlignmentTimeout() const override
{
return m_PathAlignmentTimeout;
}
bool
HasLocalIP(const huint128_t& ip) const;
/// ip packet against any exit policies we have
/// returns false if this traffic is disallowed by any of those policies
/// returns true otherwise
bool
ShouldAllowTraffic(const net::IPPacket& pkt) const;
std::optional<net::TrafficPolicy>
GetExitPolicy() const override
{
return m_TrafficPolicy;
}
/// get a key for ip address
std::optional<std::variant<service::Address, RouterID>>
ObtainAddrForIP(huint128_t ip) const override;
std::set<IPRange>
GetOwnedRanges() const override
{
return m_OwnedRanges;
}
bool
HasAddress(const AlignedBuffer<32>& addr) const
{
return m_AddrToIP.find(addr) != m_AddrToIP.end();
}
llarp_time_t
PathAlignmentTimeout() const override
{
return m_PathAlignmentTimeout;
}
/// get ip address for key unconditionally
huint128_t
ObtainIPForAddr(std::variant<service::Address, RouterID> addr) override;
/// ip packet against any exit policies we have
/// returns false if this traffic is disallowed by any of those policies
/// returns true otherwise
bool
ShouldAllowTraffic(const net::IPPacket& pkt) const;
void
ResetInternalState() override;
/// get a key for ip address
std::optional<std::variant<service::Address, RouterID>>
ObtainAddrForIP(huint128_t ip) const override;
protected:
struct WritePacket
{
uint64_t seqno;
net::IPPacket pkt;
bool
HasAddress(const AlignedBuffer<32>& addr) const
operator>(const WritePacket& other) const
{
return m_AddrToIP.find(addr) != m_AddrToIP.end();
return seqno > other.seqno;
}
};
/// get ip address for key unconditionally
huint128_t
ObtainIPForAddr(std::variant<service::Address, RouterID> addr) override;
void
ResetInternalState() override;
protected:
struct WritePacket
{
uint64_t seqno;
net::IPPacket pkt;
bool
operator>(const WritePacket& other) const
{
return seqno > other.seqno;
}
};
/// queue for sending packets to user from network
util::ascending_priority_queue<WritePacket> m_NetworkToUserPktQueue;
void
Pump(llarp_time_t now) override;
/// return true if we have a remote loki address for this ip address
bool
HasRemoteForIP(huint128_t ipv4) const;
/// mark this address as active
void
MarkIPActive(huint128_t ip);
/// mark this address as active forever
void
MarkIPActiveForever(huint128_t ip);
/// flush writing ip packets to interface
void
FlushWrite();
/// maps ip to key (host byte order)
std::unordered_map<huint128_t, AlignedBuffer<32>> m_IPToAddr;
/// maps key to ip (host byte order)
std::unordered_map<AlignedBuffer<32>, huint128_t> m_AddrToIP;
/// maps key to true if key is a service node, maps key to false if key is
/// a hidden service
std::unordered_map<AlignedBuffer<32>, bool> m_SNodes;
/// maps ip address to an exit endpoint, useful when we have multiple exits on a range
std::unordered_map<huint128_t, service::Address> 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<service::Address>
ObtainExitAddressFor(
huint128_t ip,
std::function<service::Address(std::unordered_set<service::Address>)> exitSelectionStrat =
nullptr);
template <typename Addr_t, typename Endpoint_t>
void
SendDNSReply(
Addr_t addr,
Endpoint_t ctx,
std::shared_ptr<dns::Message> query,
std::function<void(dns::Message)> reply,
bool sendIPv6)
/// return true if we have a remote loki address for this ip address
bool
HasRemoteForIP(huint128_t ipv4) const;
/// mark this address as active
void
MarkIPActive(huint128_t ip);
/// mark this address as active forever
void
MarkIPActiveForever(huint128_t ip);
/// flush writing ip packets to interface
void
FlushWrite();
/// maps ip to key (host byte order)
std::unordered_map<huint128_t, AlignedBuffer<32>> m_IPToAddr;
/// maps key to ip (host byte order)
std::unordered_map<AlignedBuffer<32>, huint128_t> m_AddrToIP;
/// maps key to true if key is a service node, maps key to false if key is
/// a hidden service
std::unordered_map<AlignedBuffer<32>, bool> m_SNodes;
/// maps ip address to an exit endpoint, useful when we have multiple exits on a range
std::unordered_map<huint128_t, service::Address> 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<service::Address>
ObtainExitAddressFor(
huint128_t ip,
std::function<service::Address(std::unordered_set<service::Address>)> exitSelectionStrat =
nullptr);
template <typename Addr_t, typename Endpoint_t>
void
SendDNSReply(
Addr_t addr,
Endpoint_t ctx,
std::shared_ptr<dns::Message> query,
std::function<void(dns::Message)> reply,
bool sendIPv6)
{
if (ctx)
{
if (ctx)
{
huint128_t ip = ObtainIPForAddr(addr);
query->answers.clear();
query->AddINReply(ip, sendIPv6);
}
else
query->AddNXReply();
reply(*query);
huint128_t ip = ObtainIPForAddr(addr);
query->answers.clear();
query->AddINReply(ip, sendIPv6);
}
/// dns subsystem for this endpoint
std::shared_ptr<dns::Server> m_DNS;
DnsConfig m_DnsConfig;
/// maps ip address to timestamp last active
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
/// our ip address (host byte order)
huint128_t m_OurIP;
/// our network interface's ipv6 address
huint128_t m_OurIPv6;
/// next ip address to allocate (host byte order)
huint128_t m_NextIP;
/// highest ip address to allocate (host byte order)
huint128_t m_MaxIP;
/// our ip range we are using
llarp::IPRange m_OurRange;
/// list of strict connect addresses for hooks
std::vector<IpAddress> m_StrictConnectAddrs;
/// use v6?
bool m_UseV6;
std::string m_IfName;
std::optional<huint128_t> m_BaseV6Address;
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
std::shared_ptr<vpn::PacketRouter> m_PacketRouter;
std::optional<net::TrafficPolicy> m_TrafficPolicy;
/// ranges we advetise as reachable
std::set<IPRange> m_OwnedRanges;
/// how long to wait for path alignment
llarp_time_t m_PathAlignmentTimeout;
/// a file to load / store the ephemeral address map to
std::optional<fs::path> m_PersistAddrMapFile;
/// for raw packet dns
std::shared_ptr<vpn::I_Packet_IO> m_RawDNS;
};
} // namespace handlers
} // namespace llarp
else
query->AddNXReply();
reply(*query);
}
/// dns subsystem for this endpoint
std::shared_ptr<dns::Server> m_DNS;
DnsConfig m_DnsConfig;
/// maps ip address to timestamp last active
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
/// our ip address (host byte order)
huint128_t m_OurIP;
/// our network interface's ipv6 address
huint128_t m_OurIPv6;
/// next ip address to allocate (host byte order)
huint128_t m_NextIP;
/// highest ip address to allocate (host byte order)
huint128_t m_MaxIP;
/// our ip range we are using
llarp::IPRange m_OurRange;
/// list of strict connect addresses for hooks
std::vector<IpAddress> m_StrictConnectAddrs;
/// use v6?
bool m_UseV6;
std::string m_IfName;
std::optional<huint128_t> m_BaseV6Address;
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
std::shared_ptr<vpn::PacketRouter> m_PacketRouter;
std::optional<net::TrafficPolicy> m_TrafficPolicy;
/// ranges we advetise as reachable
std::set<IPRange> m_OwnedRanges;
/// how long to wait for path alignment
llarp_time_t m_PathAlignmentTimeout;
/// a file to load / store the ephemeral address map to
std::optional<fs::path> m_PersistAddrMapFile;
/// for raw packet dns
std::shared_ptr<vpn::I_Packet_IO> m_RawDNS;
};
} // namespace llarp::handlers

@ -1,72 +0,0 @@
#include "iwp.hpp"
#include "linklayer.hpp"
#include <memory>
#include <llarp/router/abstractrouter.hpp>
namespace llarp
{
namespace iwp
{
LinkLayer_ptr
NewInboundLink(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> loop,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t work)
{
return std::make_shared<LinkLayer>(
keyManager,
loop,
getrc,
h,
sign,
before,
est,
reneg,
timeout,
closed,
pumpDone,
work,
true);
}
LinkLayer_ptr
NewOutboundLink(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> loop,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t work)
{
return std::make_shared<LinkLayer>(
keyManager,
loop,
getrc,
h,
sign,
before,
est,
reneg,
timeout,
closed,
pumpDone,
work,
false);
}
} // namespace iwp
} // namespace llarp

@ -1,40 +0,0 @@
#pragma once
#include <llarp/link/server.hpp>
#include "linklayer.hpp"
#include <memory>
#include <llarp/config/key_manager.hpp>
namespace llarp::iwp
{
LinkLayer_ptr
NewInboundLink(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> loop,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t work);
LinkLayer_ptr
NewOutboundLink(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> loop,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t work);
} // namespace llarp::iwp

@ -1,115 +0,0 @@
#include "linklayer.hpp"
#include "session.hpp"
#include <llarp/config/key_manager.hpp>
#include <memory>
#include <unordered_set>
namespace llarp::iwp
{
LinkLayer::LinkLayer(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> ev,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t worker,
bool allowInbound)
: ILinkLayer(
keyManager, getrc, h, sign, before, est, reneg, timeout, closed, pumpDone, worker)
, m_Wakeup{ev->make_waker([this]() { HandleWakeupPlaintext(); })}
, m_Inbound{allowInbound}
{}
std::string_view
LinkLayer::Name() const
{
return "iwp";
}
std::string
LinkLayer::PrintableName() const
{
if (m_Inbound)
return "inbound iwp link";
else
return "outbound iwp link";
}
uint16_t
LinkLayer::Rank() const
{
return 2;
}
void
LinkLayer::RecvFrom(const SockAddr& from, ILinkSession::Packet_t pkt)
{
std::shared_ptr<ILinkSession> session;
auto itr = m_AuthedAddrs.find(from);
bool isNewSession = false;
if (itr == m_AuthedAddrs.end())
{
Lock_t lock{m_PendingMutex};
auto it = m_Pending.find(from);
if (it == m_Pending.end())
{
if (not m_Inbound)
return;
isNewSession = true;
it = m_Pending.emplace(from, std::make_shared<Session>(this, from)).first;
}
session = it->second;
}
else
{
if (auto s_itr = m_AuthedLinks.find(itr->second); s_itr != m_AuthedLinks.end())
session = s_itr->second;
}
if (session)
{
bool success = session->Recv_LL(std::move(pkt));
if (not success and isNewSession)
{
LogDebug("Brand new session failed; removing from pending sessions list");
m_Pending.erase(from);
}
WakeupPlaintext();
}
}
std::shared_ptr<ILinkSession>
LinkLayer::NewOutboundSession(const RouterContact& rc, const AddressInfo& ai)
{
if (m_Inbound)
throw std::logic_error{"inbound link cannot make outbound sessions"};
return std::make_shared<Session>(this, rc, ai);
}
void
LinkLayer::WakeupPlaintext()
{
m_Wakeup->Trigger();
}
void
LinkLayer::HandleWakeupPlaintext()
{
// Copy bare pointers out first because HandlePlaintext can end up removing themselves from the
// structures.
m_WakingUp.clear(); // Reused to minimize allocations.
for (const auto& [router_id, session] : m_AuthedLinks)
m_WakingUp.push_back(session.get());
for (const auto& [addr, session] : m_Pending)
m_WakingUp.push_back(session.get());
for (auto* session : m_WakingUp)
session->HandlePlaintext();
PumpDone();
}
} // namespace llarp::iwp

@ -1,63 +0,0 @@
#pragma once
#include <llarp/constants/link_layer.hpp>
#include <llarp/crypto/crypto.hpp>
#include <llarp/crypto/encrypted.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/link/server.hpp>
#include <llarp/config/key_manager.hpp>
#include <memory>
#include <llarp/ev/ev.hpp>
namespace llarp::iwp
{
struct Session;
struct LinkLayer final : public ILinkLayer
{
LinkLayer(
std::shared_ptr<KeyManager> keyManager,
std::shared_ptr<EventLoop> ev,
GetRCFunc getrc,
LinkMessageHandler h,
SignBufferFunc sign,
BeforeConnectFunc_t before,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t dowork,
bool permitInbound);
std::shared_ptr<ILinkSession>
NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) override;
std::string_view
Name() const override;
uint16_t
Rank() const override;
void
RecvFrom(const SockAddr& from, ILinkSession::Packet_t pkt) override;
void
WakeupPlaintext();
std::string
PrintableName() const;
private:
void
HandleWakeupPlaintext();
const std::shared_ptr<EventLoopWakeup> m_Wakeup;
std::vector<ILinkSession*> m_WakingUp;
const bool m_Inbound;
};
using LinkLayer_ptr = std::shared_ptr<LinkLayer>;
} // namespace llarp::iwp

@ -1,193 +0,0 @@
#include "message_buffer.hpp"
#include "session.hpp"
#include <llarp/crypto/crypto.hpp>
namespace llarp
{
namespace iwp
{
OutboundMessage::OutboundMessage(
uint64_t msgid,
ILinkSession::Message_t msg,
llarp_time_t now,
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);
m_Acks.set(0);
}
ILinkSession::Packet_t
OutboundMessage::XMIT() const
{
size_t extra = std::min(m_Data.size(), FragmentSize);
auto xmit = CreatePacket(Command::eXMIT, 10 + 32 + extra, 0, 0);
oxenc::write_host_as_big(
static_cast<uint16_t>(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);
return xmit;
}
void
OutboundMessage::Completed()
{
if (m_Completed)
{
m_Completed(ILinkSession::DeliveryStatus::eDeliverySuccess);
}
m_Completed = nullptr;
}
bool
OutboundMessage::ShouldFlush(llarp_time_t now) const
{
return now - m_LastFlush >= TXFlushInterval;
}
void
OutboundMessage::Ack(byte_t bitmask)
{
m_Acks = std::bitset<8>(bitmask);
}
void
OutboundMessage::FlushUnAcked(
std::function<void(ILinkSession::Packet_t)> sendpkt, llarp_time_t now)
{
/// overhead for a data packet in plaintext
static constexpr size_t Overhead = 10;
uint16_t idx = 0;
const auto datasz = m_Data.size();
while (idx < datasz)
{
if (not m_Acks[idx / FragmentSize])
{
const size_t fragsz = idx + FragmentSize < datasz ? FragmentSize : datasz - idx;
auto frag = CreatePacket(Command::eDATA, fragsz + Overhead, 0, 0);
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,
frag.data() + PacketOverhead + Overhead + 2);
sendpkt(std::move(frag));
}
idx += FragmentSize;
}
m_LastFlush = now;
}
bool
OutboundMessage::IsTransmitted() const
{
const auto sz = m_Data.size();
for (uint16_t idx = 0; idx < sz; idx += FragmentSize)
{
if (not m_Acks.test(idx / FragmentSize))
return false;
}
return true;
}
bool
OutboundMessage::IsTimedOut(const llarp_time_t now) const
{
// TODO: make configurable by outbound message deliverer
return now > m_StartedAt && now - m_StartedAt > DeliveryTimeout;
}
void
OutboundMessage::InformTimeout()
{
if (m_Completed)
{
m_Completed(ILinkSession::DeliveryStatus::eDeliveryDropped);
}
m_Completed = nullptr;
}
InboundMessage::InboundMessage(uint64_t msgid, uint16_t sz, ShortHash h, llarp_time_t now)
: m_Data(size_t{sz}), m_Digset{std::move(h)}, m_MsgID(msgid), m_LastActiveAt{now}
{}
void
InboundMessage::HandleData(uint16_t idx, const llarp_buffer_t& buf, llarp_time_t now)
{
if (idx + buf.sz > m_Data.size())
{
LogWarn("invalid fragment offset ", idx);
return;
}
byte_t* dst = m_Data.data() + idx;
std::copy_n(buf.base, buf.sz, dst);
m_Acks.set(idx / FragmentSize);
LogTrace("got fragment ", idx / FragmentSize);
m_LastActiveAt = now;
}
ILinkSession::Packet_t
InboundMessage::ACKS() const
{
auto acks = CreatePacket(Command::eACKS, 9);
oxenc::write_host_as_big(m_MsgID, acks.data() + CommandOverhead + PacketOverhead);
acks[PacketOverhead + 10] = AcksBitmask();
return acks;
}
byte_t
InboundMessage::AcksBitmask() const
{
return byte_t{(byte_t)m_Acks.to_ulong()};
}
bool
InboundMessage::IsCompleted() const
{
const auto sz = m_Data.size();
for (size_t idx = 0; idx < sz; idx += FragmentSize)
{
if (not m_Acks.test(idx / FragmentSize))
return false;
}
return true;
}
bool
InboundMessage::ShouldSendACKS(llarp_time_t now) const
{
return now > m_LastACKSent + ACKResendInterval;
}
bool
InboundMessage::IsTimedOut(const llarp_time_t now) const
{
return now > m_LastActiveAt && now - m_LastActiveAt > DeliveryTimeout;
}
void
InboundMessage::SendACKS(std::function<void(ILinkSession::Packet_t)> sendpkt, llarp_time_t now)
{
sendpkt(ACKS());
m_LastACKSent = now;
}
bool
InboundMessage::Verify() const
{
ShortHash gotten;
const llarp_buffer_t buf(m_Data);
CryptoManager::instance()->shorthash(gotten, buf);
return gotten == m_Digset;
}
} // namespace iwp
} // namespace llarp

@ -1,127 +0,0 @@
#pragma once
#include <vector>
#include <llarp/constants/link_layer.hpp>
#include <llarp/link/session.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/buffer.hpp>
#include <llarp/util/types.hpp>
namespace llarp
{
namespace iwp
{
enum Command
{
/// keep alive message
ePING = 0,
/// begin transission
eXMIT = 1,
/// fragment data
eDATA = 2,
/// acknolege fragments
eACKS = 3,
/// negative ack
eNACK = 4,
/// multiack
eMACK = 5,
/// close session
eCLOS = 0xff,
};
/// max size of data fragments
static constexpr size_t FragmentSize = 1024;
/// plaintext header overhead size
static constexpr size_t CommandOverhead = 2;
struct OutboundMessage
{
OutboundMessage() = default;
OutboundMessage(
uint64_t msgid,
ILinkSession::Message_t data,
llarp_time_t now,
ILinkSession::CompletionHandler handler,
uint16_t priority);
ILinkSession::Message_t m_Data;
uint64_t m_MsgID = 0;
std::bitset<MAX_LINK_MSG_SIZE / FragmentSize> m_Acks;
ILinkSession::CompletionHandler m_Completed;
llarp_time_t m_LastFlush = 0s;
ShortHash m_Digest;
llarp_time_t m_StartedAt = 0s;
uint16_t m_ResendPriority;
bool
operator<(const OutboundMessage& other) const
{
// yes, the first order is reversed as higher means more important
// second part is for queue order
int prioA = -m_ResendPriority, prioB = -other.m_ResendPriority;
return std::tie(prioA, m_MsgID) < std::tie(prioB, other.m_MsgID);
}
ILinkSession::Packet_t
XMIT() const;
void
Ack(byte_t bitmask);
void
FlushUnAcked(std::function<void(ILinkSession::Packet_t)> sendpkt, llarp_time_t now);
bool
ShouldFlush(llarp_time_t now) const;
void
Completed();
bool
IsTransmitted() const;
bool
IsTimedOut(llarp_time_t now) const;
void
InformTimeout();
};
struct InboundMessage
{
InboundMessage() = default;
InboundMessage(uint64_t msgid, uint16_t sz, ShortHash h, llarp_time_t now);
ILinkSession::Message_t m_Data;
ShortHash m_Digset;
uint64_t m_MsgID = 0;
llarp_time_t m_LastACKSent = 0s;
llarp_time_t m_LastActiveAt = 0s;
std::bitset<MAX_LINK_MSG_SIZE / FragmentSize> m_Acks;
void
HandleData(uint16_t idx, const llarp_buffer_t& buf, llarp_time_t now);
bool
IsCompleted() const;
bool
IsTimedOut(llarp_time_t now) const;
bool
Verify() const;
byte_t
AcksBitmask() const;
bool
ShouldSendACKS(llarp_time_t now) const;
void
SendACKS(std::function<void(ILinkSession::Packet_t)> sendpkt, llarp_time_t now);
ILinkSession::Packet_t
ACKS() const;
};
} // namespace iwp
} // namespace llarp

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save