Merge pull request #2228 from dr7ana/trust-model

Trust model
pull/2212/head
dr7ana 3 months ago committed by GitHub
commit a4bd7806b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

2
.gitignore vendored

@ -43,7 +43,7 @@ testnet_tmp
vsproject/
.vs
daemon.ini
*.ini
.gradle/

@ -41,8 +41,6 @@ if(APPLE)
set(LOKINET_APPLE_BUILD 5)
endif()
set(LOKINET_RELEASE_MOTTO "Anonymous, decentralized, IP-based overlay network" CACHE STRING "Release motto")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
set(DEFAULT_WITH_BOOTSTRAP ON)

Binary file not shown.

@ -83,7 +83,7 @@ endforeach()
target_link_libraries(lokinet PRIVATE CLI11)
target_link_libraries(lokinet-vpn PRIVATE CLI11)
if(SETCAP)
if(WITH_SETCAP)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif()

@ -46,10 +46,8 @@ int
main(int argc, char* argv[])
{
const std::unordered_map<std::string, std::string> bootstrap_urls = {
{"mainnet", "https://seed.lokinet.org/lokinet.signed"},
{"lokinet", "https://seed.lokinet.org/lokinet.signed"},
{"testnet", "https://seed.lokinet.org/testnet.signed"},
{"gamma", "https://seed.lokinet.org/testnet.signed"}};
{"testnet", "https://seed.lokinet.org/testnet.signed"}};
std::string bootstrap_url = bootstrap_urls.at("lokinet");
fs::path outputfile{llarp::GetDefaultBootstrap()};

@ -125,7 +125,7 @@ namespace
if (!GetModuleFileName(nullptr, szPath.data(), MAX_PATH))
{
llarp::LogError("Cannot install service ", GetLastError());
llarp::log::error(logcat, "Cannot install service {}", GetLastError());
return;
}
@ -137,7 +137,7 @@ namespace
if (nullptr == schSCManager)
{
llarp::LogError("OpenSCManager failed ", GetLastError());
llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError());
return;
}
@ -159,12 +159,12 @@ namespace
if (schService == nullptr)
{
llarp::LogError("CreateService failed ", GetLastError());
llarp::log::error(logcat, "CreateService failed {}", GetLastError());
CloseServiceHandle(schSCManager);
return;
}
else
llarp::LogInfo("Service installed successfully");
llarp::log::info(logcat, "Service installed successfully");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
@ -189,7 +189,7 @@ namespace
if (nullptr == schSCManager)
{
llarp::LogError("OpenSCManager failed ", GetLastError());
llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError());
return;
}
@ -201,7 +201,7 @@ namespace
if (schService == nullptr)
{
llarp::LogError("OpenService failed ", GetLastError());
llarp::log::error(logcat, "OpenService failed {}", GetLastError());
CloseServiceHandle(schSCManager);
return;
}
@ -214,10 +214,10 @@ namespace
SERVICE_CONFIG_DESCRIPTION, // change: description
&sd)) // new description
{
llarp::LogError("ChangeServiceConfig2 failed");
llarp::log::error(logcat, "ChangeServiceConfig2 failed");
}
else
llarp::LogInfo("Service description updated successfully.");
llarp::log::info(log_cat, "Service description updated successfully.");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
@ -237,7 +237,7 @@ namespace
if (nullptr == schSCManager)
{
llarp::LogError("OpenSCManager failed ", GetLastError());
llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError());
return;
}
@ -249,7 +249,7 @@ namespace
if (schService == nullptr)
{
llarp::LogError("OpenService failed ", GetLastError());
llarp::log::error(logcat, "OpenService failed {}", GetLastError());
CloseServiceHandle(schSCManager);
return;
}
@ -257,10 +257,10 @@ namespace
// Delete the service.
if (!DeleteService(schService))
{
llarp::LogError("DeleteService failed ", GetLastError());
llarp::log::error(logcat, "DeleteService failed {}", GetLastError());
}
else
llarp::LogInfo("Service deleted successfully\n");
llarp::log::info(logcat, "Service deleted successfully");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
@ -337,7 +337,7 @@ namespace
if (svc->handle == nullptr)
{
llarp::LogError("failed to register daemon control handler");
llarp::log::error(logcat, "failed to register daemon control handler");
return;
}
@ -442,6 +442,9 @@ namespace
cli.exit(e);
};
// TESTNET:
oxen::log::set_level("quic", oxen::log::Level::critical);
if (configFile.has_value())
{
// when we have an explicit filepath
@ -454,7 +457,7 @@ namespace
}
catch (std::exception& ex)
{
llarp::LogError("cannot generate config at ", *configFile, ": ", ex.what());
llarp::log::error(logcat, "cannot generate config at {}: {}", *configFile, ex.what());
return 1;
}
}
@ -464,13 +467,13 @@ namespace
{
if (!fs::exists(*configFile))
{
llarp::LogError("Config file not found ", *configFile);
llarp::log::error(logcat, "Config file not found {}", *configFile);
return 1;
}
}
catch (std::exception& ex)
{
llarp::LogError("cannot check if ", *configFile, " exists: ", ex.what());
llarp::log::error(logcat, "cannot check if ", *configFile, " exists: ", ex.what());
return 1;
}
}
@ -487,7 +490,7 @@ namespace
}
catch (std::exception& ex)
{
llarp::LogError("cannot ensure config: ", ex.what());
llarp::log::error(logcat, "cannot ensure config: {}", ex.what());
return 1;
}
configFile = llarp::GetDefaultConfigPath();
@ -548,14 +551,13 @@ namespace
static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{
llarp::LogInfo(fmt::format(
"starting up {} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
llarp::log::info(logcat, "starting up {}", llarp::LOKINET_VERSION_FULL);
try
{
std::shared_ptr<llarp::Config> conf;
if (confFile)
{
llarp::LogInfo("Using config file: ", *confFile);
llarp::log::info(logcat, "Using config file: {}", *confFile);
conf = std::make_shared<llarp::Config>(confFile->parent_path());
}
else
@ -564,7 +566,7 @@ namespace
}
if (not conf->load(confFile, opts.isSNode))
{
llarp::LogError("failed to parse configuration");
llarp::log::error(logcat, "failed to parse configuration");
exit_code.set_value(1);
return;
}
@ -589,13 +591,13 @@ namespace
}
catch (llarp::util::bind_socket_error& ex)
{
llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what()));
llarp::log::error(logcat, "{}; is lokinet already running?", ex.what());
exit_code.set_value(1);
return;
}
catch (std::exception& ex)
catch (const std::exception& ex)
{
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
llarp::log::error(logcat, "failed to start up lokinet: {}", ex.what());
exit_code.set_value(1);
return;
}
@ -604,14 +606,14 @@ namespace
auto result = ctx->Run(opts);
exit_code.set_value(result);
}
catch (std::exception& e)
catch (const std::exception& e)
{
llarp::LogError("Fatal: caught exception while running: ", e.what());
llarp::log::error(logcat, "Fatal: caught exception while running: {}", e.what());
exit_code.set_exception(std::current_exception());
}
catch (...)
{
llarp::LogError("Fatal: caught non-standard exception while running");
llarp::log::error(logcat, "Fatal: caught non-standard exception while running");
exit_code.set_exception(std::current_exception());
}
}

@ -1 +1 @@
Subproject commit f6172d58d3358473a4c98d96270058a32e166d5f
Subproject commit 24fbdb794ef26bf5324d1b56d48d1da8de8a140c

@ -1 +1 @@
Subproject commit 3ced484e8cc543b90c5fc554ccc0ea2e54ec8d37
Subproject commit 73d4b6f940c0790e6f7c3ec46e03c014f51be1fa

@ -178,10 +178,10 @@ lokinet_add_library(lokinet-nodedb
set(BOOTSTRAP_FALLBACKS)
foreach(bs IN ITEMS MAINNET TESTNET)
if(BOOTSTRAP_FALLBACK_${bs})
message(STATUS "Building with ${bs} fallback boostrap path \"${BOOTSTRAP_FALLBACK_${bs}}\"")
message(STATUS "Building with ${bs} fallback bootstrap path \"${BOOTSTRAP_FALLBACK_${bs}}\"")
file(READ "${BOOTSTRAP_FALLBACK_${bs}}" bs_data HEX)
if(bs STREQUAL TESTNET)
set(network "gamma")
set(network "testnet")
elseif(bs STREQUAL MAINNET)
set(network "lokinet")
else()

@ -9,12 +9,27 @@ namespace llarp
bool
BootstrapList::bt_decode(std::string_view buf)
{
try
const auto& f = buf.front();
switch (f)
{
oxenc::bt_list_consumer btlc{buf};
case 'l':
return bt_decode(oxenc::bt_list_consumer{buf});
case 'd':
return bt_decode(oxenc::bt_dict_consumer{buf});
default:
log::critical(logcat, "Unable to parse bootstrap as bt list or dict!");
return false;
}
}
bool
BootstrapList::bt_decode(oxenc::bt_list_consumer btlc)
{
try
{
while (not btlc.is_finished())
emplace(btlc.consume_dict_consumer());
emplace(btlc.consume_dict_data());
}
catch (...)
{
@ -22,9 +37,45 @@ namespace llarp
return false;
}
_curr = begin();
return true;
}
bool
BootstrapList::bt_decode(oxenc::bt_dict_consumer btdc)
{
try
{
emplace(btdc);
}
catch (const std::exception& e)
{
log::warning(logcat, "Unable to decode bootstrap RemoteRC: {}", e.what());
return false;
}
_curr = begin();
return true;
}
bool
BootstrapList::contains(const RouterID& rid) const
{
for (const auto& it : *this)
{
if (it.router_id() == rid)
return true;
}
return false;
}
bool
BootstrapList::contains(const RemoteRC& rc) const
{
return count(rc);
}
std::string_view
BootstrapList::bt_encode() const
{
@ -37,34 +88,82 @@ namespace llarp
}
void
BootstrapList::read_from_file(const fs::path& fpath)
BootstrapList::populate_bootstraps(
std::vector<fs::path> paths, const fs::path& def, bool load_fallbacks)
{
bool isListFile = false;
if (std::ifstream inf(fpath.c_str(), std::ios::binary); inf.is_open())
for (const auto& f : paths)
{
const char ch = inf.get();
isListFile = ch == 'l';
// TESTNET: TODO: revise fucked config
log::debug(logcat, "Loading BootstrapRC from file at path:{}", f);
if (not read_from_file(f))
throw std::invalid_argument{"User-provided BootstrapRC is invalid!"};
}
if (isListFile)
if (empty())
{
auto content = util::file_to_string(fpath);
log::debug(
logcat,
"BootstrapRC list empty; looking for default BootstrapRC from file at path:{}",
def);
read_from_file(def);
}
if (not bt_decode(content))
for (auto itr = begin(); itr != end(); ++itr)
{
if (RouterContact::is_obsolete(*itr)) // can move this into ::read_from_file
{
throw std::runtime_error{fmt::format("failed to read bootstrap list file '{}'", fpath)};
log::critical(logcat, "Deleting obsolete BootstrapRC (rid:{})", itr->router_id());
itr = erase(itr);
continue;
}
}
else
if (empty() and load_fallbacks)
{
RemoteRC rc;
if (not rc.read(fpath))
log::critical(logcat, "BootstrapRC list empty; loading fallbacks...");
auto fallbacks = llarp::load_bootstrap_fallbacks();
if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end())
{
throw std::runtime_error{
fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc.to_string())};
log::critical(
logcat, "Loading {} default fallback bootstrap router(s)!", itr->second.size());
merge(itr->second);
}
if (empty())
{
log::error(
logcat,
"No Bootstrap routers were loaded. The default Bootstrap file {} does not exist, and "
"loading fallback Bootstrap RCs failed.",
def);
throw std::runtime_error("No Bootstrap nodes available.");
}
insert(rc);
}
log::critical(logcat, "We have {} Bootstrap router(s)!", size());
_curr = begin();
}
bool
BootstrapList::read_from_file(const fs::path& fpath)
{
bool result = false;
if (not fs::exists(fpath))
{
log::critical(logcat, "Bootstrap RC file non-existant at path:{}", fpath);
return result;
}
auto content = util::file_to_string(fpath);
result = bt_decode(content);
log::critical(
logcat, "{}uccessfully loaded BootstrapRC file at path:{}", result ? "S" : "Un", fpath);
_curr = begin();
return result;
}
} // namespace llarp

@ -2,6 +2,7 @@
#include "router_contact.hpp"
#include <llarp/crypto/crypto.hpp>
#include <llarp/util/fs.hpp>
#include <set>
@ -11,15 +12,61 @@ namespace llarp
{
struct BootstrapList final : public std::set<RemoteRC>
{
std::set<RemoteRC>::iterator _curr = begin();
const RemoteRC&
current()
{
return *_curr;
}
bool
bt_decode(std::string_view buf);
bool
bt_decode(oxenc::bt_list_consumer btlc);
bool
bt_decode(oxenc::bt_dict_consumer btdc);
std::string_view
bt_encode() const;
void
populate_bootstraps(std::vector<fs::path> paths, const fs::path& def, bool load_fallbacks);
bool
read_from_file(const fs::path& fpath);
bool
contains(const RouterID& rid) const;
// returns a reference to the next index and a boolean that equals true if
// this is the front of the set
const RemoteRC&
next()
{
if (size() < 2)
return *_curr;
++_curr;
if (_curr == this->end())
_curr = this->begin();
return *_curr;
}
bool
contains(const RemoteRC& rc) const;
void
randomize()
{
if (size() > 1)
_curr = std::next(begin(), std::uniform_int_distribution<size_t>{0, size() - 1}(csrng));
}
void
clear_list()
{

@ -12,6 +12,7 @@
#include <llarp/router_contact.hpp>
#include <llarp/service/name.hpp>
#include <llarp/util/file.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/util/str.hpp>
@ -19,15 +20,6 @@
namespace llarp
{
// constants for config file default values
constexpr int DefaultMinConnectionsForRouter = 6;
constexpr int DefaultMaxConnectionsForRouter = 60;
constexpr int DefaultMinConnectionsForClient = 4;
constexpr int DefaultMaxConnectionsForClient = 6;
constexpr int DefaultPublicPort = 1090;
using namespace config;
namespace
{
@ -61,8 +53,8 @@ namespace llarp
"netid",
Default{llarp::LOKINET_DEFAULT_NETID},
Comment{
"Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID
+ "' for mainnet, 'gamma' for testnet.",
"Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID + "' for mainnet, '"s
+ llarp::LOKINET_TESTNET_NETID + "' for testnet."s,
},
[this](std::string arg) {
if (arg.size() > NETID_SIZE)
@ -72,38 +64,44 @@ namespace llarp
net_id = std::move(arg);
});
int minConnections =
(params.is_relay ? DefaultMinConnectionsForRouter : DefaultMinConnectionsForClient);
conf.define_option<int>(
"router",
"min-connections",
Default{minConnections},
"relay-connections",
Default{CLIENT_ROUTER_CONNECTIONS},
ClientOnly,
Comment{
"Minimum number of routers lokinet will attempt to maintain connections to.",
"Minimum number of routers lokinet client will attempt to maintain connections to.",
},
[=](int arg) {
if (arg < minConnections)
if (arg < CLIENT_ROUTER_CONNECTIONS)
throw std::invalid_argument{
fmt::format("min-connections must be >= {}", minConnections)};
fmt::format("Client relay connections must be >= {}", CLIENT_ROUTER_CONNECTIONS)};
min_connected_routers = arg;
client_router_connections = arg;
});
conf.define_option<int>(
"router",
"min-connections",
Deprecated,
Comment{
"Minimum number of routers lokinet will attempt to maintain connections to.",
},
[=](int) {
log::warning(
logcat, "Router min-connections is deprecated; use relay-connections for clients");
});
int maxConnections =
(params.is_relay ? DefaultMaxConnectionsForRouter : DefaultMaxConnectionsForClient);
conf.define_option<int>(
"router",
"max-connections",
Default{maxConnections},
Deprecated,
Comment{
"Maximum number (hard limit) of routers lokinet will be connected to at any time.",
},
[=](int arg) {
if (arg < maxConnections)
throw std::invalid_argument{
fmt::format("max-connections must be >= {}", maxConnections)};
max_connected_routers = arg;
[=](int) {
log::warning(
logcat, "Router max-connections is deprecated; use relay-connections for clients");
});
conf.define_option<std::string>("router", "nickname", Deprecated);
@ -135,19 +133,7 @@ namespace llarp
"this setting specifies the public IP at which this router is reachable. When",
"provided the public-port option must also be specified.",
},
[this, net = params.Net_ptr()](std::string arg) {
if (arg.empty())
return;
nuint32_t addr{};
if (not addr.FromString(arg))
throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", arg)};
if (net->IsBogonIP(addr))
throw std::invalid_argument{
fmt::format("{} is not a publicly routable ip address", addr)};
public_ip = addr;
});
[this](std::string arg) { public_ip = std::move(arg); });
conf.define_option<std::string>("router", "public-address", Hidden, [](std::string) {
throw std::invalid_argument{
@ -155,19 +141,18 @@ namespace llarp
"[router]:public-port instead"};
});
conf.define_option<int>(
conf.define_option<uint16_t>(
"router",
"public-port",
RelayOnly,
Default{DefaultPublicPort},
Comment{
"When specifying public-ip=, this specifies the public UDP port at which this lokinet",
"router is reachable. Required when public-ip is used.",
},
[this](int arg) {
[this](uint16_t arg) {
if (arg <= 0 || arg > std::numeric_limits<uint16_t>::max())
throw std::invalid_argument("public-port must be >= 0 and <= 65536");
public_port = ToNet(huint16_t{static_cast<uint16_t>(arg)});
public_port = arg;
});
conf.define_option<int>(
@ -290,7 +275,7 @@ namespace llarp
MultiValue,
[this](std::string value) {
RouterID router;
if (not router.FromString(value))
if (not router.from_string(value))
throw std::invalid_argument{"bad snode value: " + value};
if (not strict_connect.insert(router).second)
throw std::invalid_argument{"duplicate strict connect snode: " + value};
@ -710,7 +695,7 @@ namespace llarp
},
[this](std::string arg) {
RouterID id;
if (not id.FromString(arg))
if (not id.from_string(arg))
throw std::invalid_argument{fmt::format("Invalid RouterID: {}", arg)};
auto itr = snode_blacklist.emplace(std::move(id));
@ -913,66 +898,59 @@ namespace llarp
conf.define_option<std::string>(
"bind",
"public-ip",
Hidden,
RelayOnly,
Comment{
"The IP address to advertise to the network instead of the incoming= or auto-detected",
"IP. This is typically required only when incoming= is used to listen on an internal",
"private range IP address that received traffic forwarded from the public IP.",
},
[this](std::string_view arg) {
SockAddr pubaddr{arg};
public_addr = pubaddr.getIP();
[this](std::string arg) {
public_addr = std::move(arg);
log::warning(
logcat,
"Using deprecated option; pass this value to [Router]:public-ip instead PLEASE");
});
conf.define_option<uint16_t>(
"bind",
"public-port",
Hidden,
RelayOnly,
Comment{
"The port to advertise to the network instead of the incoming= (or default) port.",
"This is typically required only when incoming= is used to listen on an internal",
"private range IP address/port that received traffic forwarded from the public IP.",
},
[this](uint16_t arg) { public_port = net::port_t::from_host(arg); });
[this](uint16_t arg) {
if (arg <= 0 || arg > std::numeric_limits<uint16_t>::max())
throw std::invalid_argument("public-port must be >= 0 and <= 65536");
public_port = arg;
log::warning(
logcat,
"Using deprecated option; pass this value to [Router]:public-port instead PLEASE");
});
auto parse_addr_for_link = [net_ptr](const std::string& arg) {
std::optional<oxen::quic::Address> maybe = std::nullopt;
std::string_view arg_v;
std::string_view arg_v{arg}, host;
uint16_t p{DEFAULT_LISTEN_PORT};
// explicitly provided value
if (not arg.empty())
if (auto pos = arg_v.find(':'); pos != arg_v.npos)
{
arg_v = std::string_view{arg};
}
if (arg_v[0] == ':')
{
uint16_t res;
if (auto rv = llarp::parse_int<uint16_t>(arg_v.substr(1), res); not rv)
res = DEFAULT_LISTEN_PORT;
host = arg_v.substr(0, pos);
maybe = oxen::quic::Address{""s, res};
if (not llarp::parse_int<uint16_t>(arg_v.substr(pos + 1), p))
throw std::invalid_argument{"Failed to parse port in arg:{}"_format(arg)};
}
else if (auto pos = arg_v.find(':'); pos != arg_v.npos)
{
auto h = arg_v.substr(0, pos);
uint16_t p;
if (auto rv = llarp::parse_int<uint16_t>(arg_v.substr(pos + 1), p); not rv)
p = DEFAULT_LISTEN_PORT;
maybe = oxen::quic::Address{std::string{h}, p};
if (host.empty())
maybe = net_ptr->get_best_public_address(true, p);
else
maybe = oxen::quic::Address{std::string{host}, p};
if (maybe->is_loopback())
throw std::invalid_argument{fmt::format("{} is a loopback address", arg)};
}
if (not maybe)
{
// infer public address
if (auto maybe_ifname = net_ptr->GetBestNetIF())
maybe = oxen::quic::Address{*maybe_ifname};
}
if (maybe && maybe->port() == 0)
maybe = oxen::quic::Address{maybe->host(), DEFAULT_LISTEN_PORT};
if (maybe and maybe->is_loopback())
throw std::invalid_argument{"{} is a loopback address"_format(arg)};
return maybe;
};
@ -980,10 +958,7 @@ namespace llarp
conf.define_option<std::string>(
"bind",
"listen",
Required,
Comment{
"********** NEW API OPTION (see note) **********",
"",
"IP and/or port for lokinet to bind to for inbound/outbound connections.",
"",
"If IP is omitted then lokinet will search for a local network interface with a",
@ -1007,11 +982,13 @@ namespace llarp
},
[this, parse_addr_for_link](const std::string& arg) {
if (auto a = parse_addr_for_link(arg); a and a->is_addressable())
addr = a;
{
listen_addr = *a;
using_user_value = true;
using_new_api = true;
}
else
addr = oxen::quic::Address{""s, DEFAULT_LISTEN_PORT};
using_new_api = true;
throw std::invalid_argument{"Could not parse listen address!"};
});
conf.define_option<std::string>(
@ -1020,86 +997,22 @@ namespace llarp
RelayOnly,
MultiValue,
Hidden,
Comment{
"********** DEPRECATED **********",
"Note: the new API dictates the lokinet bind address through the 'listen' config",
"parameter. Only ONE address will be read (no more lists of inbounds). Any address",
"passed to `listen` will supersede the",
"",
"IP and/or port to listen on for incoming connections.",
"",
"If IP is omitted then lokinet will search for a local network interface with a",
"public IP address and use that IP (and will exit with an error if no such IP is found",
"on the system). If port is omitted then lokinet defaults to 1090.",
"",
"Examples:",
" inbound=15.5.29.5:443",
" inbound=10.0.2.2",
" inbound=:1234",
"",
"Using a private range IP address (like the second example entry) will require using",
"the public-ip= and public-port= to specify the public IP address at which this",
"router can be reached.",
},
[this, parse_addr_for_link](const std::string& arg) {
if (using_new_api)
throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"};
if (auto a = parse_addr_for_link(arg); a and a->is_addressable())
addr = a;
else
addr = oxen::quic::Address{""s, DEFAULT_LISTEN_PORT};
{
log::warning(
logcat,
"Loaded address from deprecated [inbound] options; update your config to use "
"[bind]:listen instead PLEASE");
listen_addr = *a;
using_user_value = true;
}
});
conf.define_option<std::string>(
"bind",
"outbound",
MultiValue,
params.is_relay ? Comment{
"********** THIS PARAMETER IS DEPRECATED -- USE 'LISTEN' INSTEAD **********",
"",
"IP and/or port to use for outbound socket connections to other lokinet routers.",
"",
"If no outbound bind IP is configured, or the 0.0.0.0 wildcard IP is given, then",
"lokinet will bind to the same IP being used for inbound connections (either an",
"explicit inbound= provided IP, or the default). If no port is given, or port is",
"given as 0, then a random high port will be used.",
"",
"If using multiple inbound= addresses then you *must* provide an explicit oubound= IP.",
"",
"Examples:",
" outbound=1.2.3.4:5678",
" outbound=:9000",
" outbound=8.9.10.11",
"",
"The second example binds on the default incoming IP using port 9000; the third",
"example binds on the given IP address using a random high port.",
} : Comment{
"********** DEPRECATED **********",
"",
"IP and/or port to use for outbound socket connections to lokinet routers.",
"",
"If no outbound bind IP is configured then lokinet will use a wildcard IP address",
"(equivalent to specifying 0.0.0.0). If no port is given then a random high port",
"will be used.",
"",
"Examples:",
" outbound=1.2.3.4:5678",
" outbound=:9000",
" outbound=8.9.10.11",
"",
"The second example binds on the wildcard address using port 9000; the third example",
"binds on the given IP address using a random high port.",
},
[this, parse_addr_for_link](const std::string& arg) {
if (using_new_api)
throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"};
if (auto a = parse_addr_for_link(arg); a and a->is_addressable())
addr = a;
else
addr = oxen::quic::Address{""s, DEFAULT_LISTEN_PORT};
});
conf.define_option<std::string>("bind", "outbound", MultiValue, Deprecated, Hidden);
conf.add_undeclared_handler(
"bind", [this](std::string_view, std::string_view key, std::string_view val) {
@ -1118,7 +1031,11 @@ namespace llarp
// special case: wildcard for outbound
if (key == "*")
{
addr = oxen::quic::Address{port};
log::warning(
logcat,
"Wildcat address referencing port {} is referencing deprecated outbound config "
"options; use [bind]:listen instead",
port);
return;
}
@ -1137,36 +1054,17 @@ namespace llarp
e.what())};
}
if (temp.is_addressable())
if (not temp.is_addressable())
{
addr = std::move(temp);
return;
}
throw std::runtime_error{fmt::format(
"Invalid address: {}; stop using this deprecated handler, update your config to use "
"[bind]:listen instead PLEASE",
temp)};
});
}
void
ConnectConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
{
(void)params;
conf.add_undeclared_handler(
"connect", [this](std::string_view section, std::string_view name, std::string_view value) {
fs::path file{value.begin(), value.end()};
if (not fs::exists(file))
throw std::runtime_error{fmt::format(
"Specified bootstrap file {} specified in [{}]:{} does not exist",
value,
section,
name)};
"Invalid address: {}; stop using this deprecated handler, update your config to "
"use "
"[bind]:listen instead PLEASE",
temp)};
}
routers.emplace_back(std::move(file));
return true;
listen_addr = std::move(temp);
using_user_value = true;
});
}
@ -1221,17 +1119,16 @@ namespace llarp
LokidConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
{
(void)params;
conf.define_option<bool>("lokid", "enabled", RelayOnly, Deprecated);
conf.define_option<std::string>("lokid", "jsonrpc", RelayOnly, [](std::string arg) {
if (arg.empty())
return;
throw std::invalid_argument(
"the [lokid]:jsonrpc option is no longer supported; please use the [lokid]:rpc config "
"option instead with oxend's lmq-local-control address -- typically a value such as "
"rpc=ipc:///var/lib/oxen/oxend.sock or rpc=ipc:///home/snode/.oxen/oxend.sock");
});
conf.define_option<bool>(
"lokid",
"disable-testing",
Default{false},
Hidden,
RelayOnly,
Comment{
"Development option: set to true to disable reachability testing when using ",
"testnet"},
assignment_acceptor(disable_testing));
conf.define_option<std::string>(
"lokid",
@ -1250,6 +1147,15 @@ namespace llarp
[this](std::string arg) { rpc_addr = oxenmq::address(arg); });
// Deprecated options:
conf.define_option<std::string>("lokid", "jsonrpc", RelayOnly, Hidden, [](std::string arg) {
if (arg.empty())
return;
throw std::invalid_argument(
"the [lokid]:jsonrpc option is no longer supported; please use the [lokid]:rpc config "
"option instead with oxend's lmq-local-control address -- typically a value such as "
"rpc=ipc:///var/lib/oxen/oxend.sock or rpc=ipc:///home/snode/.oxen/oxend.sock");
});
conf.define_option<bool>("lokid", "enabled", RelayOnly, Deprecated);
conf.define_option<std::string>("lokid", "username", Deprecated);
conf.define_option<std::string>("lokid", "password", Deprecated);
conf.define_option<std::string>("lokid", "service-node-seed", Deprecated);
@ -1278,14 +1184,12 @@ namespace llarp
},
[this](std::string arg) {
if (arg.empty())
{
throw std::invalid_argument("cannot use empty filename as bootstrap");
}
files.emplace_back(std::move(arg));
if (not fs::exists(files.back()))
{
throw std::invalid_argument("file does not exist: " + arg);
}
});
}
@ -1437,22 +1341,19 @@ namespace llarp
const auto overridesDir = GetOverridesDir(data_dir);
if (fs::exists(overridesDir))
{
util::IterDir(overridesDir, [&](const fs::path& overrideFile) {
if (overrideFile.extension() == ".ini")
{
ConfigParser parser;
if (not parser.load_file(overrideFile))
throw std::runtime_error{"cannot load '" + overrideFile.u8string() + "'"};
parser.iter_all_sections([&](std::string_view section, const SectionValues& values) {
for (const auto& pair : values)
{
conf.add_config_value(section, pair.first, pair.second);
}
});
}
return true;
});
for (const auto& f : fs::directory_iterator{overridesDir})
{
if (not f.is_regular_file() or f.path().extension() != ".ini")
continue;
ConfigParser parser;
if (not parser.load_file(f.path()))
throw std::runtime_error{"cannot load '" + f.path().u8string() + "'"};
parser.iter_all_sections([&](std::string_view section, const SectionValues& values) {
for (const auto& [k, v] : values)
conf.add_config_value(section, k, v);
});
}
}
}
@ -1537,7 +1438,6 @@ namespace llarp
router.define_config_options(conf, params);
network.define_config_options(conf, params);
paths.define_config_options(conf, params);
connect.define_config_options(conf, params);
dns.define_config_options(conf, params);
links.define_config_options(conf, params);
api.define_config_options(conf, params);

@ -34,7 +34,9 @@ namespace llarp
using SectionValues = llarp::ConfigParser::SectionValues;
using ConfigMap = llarp::ConfigParser::ConfigMap;
inline static constexpr uint16_t DEFAULT_LISTEN_PORT{1090};
inline const std::string QUAD_ZERO{"0.0.0.0"};
inline constexpr uint16_t DEFAULT_LISTEN_PORT{1090};
inline constexpr int CLIENT_ROUTER_CONNECTIONS = 4;
// TODO: don't use these maps. they're sloppy and difficult to follow
/// Small struct to gather all parameters needed for config generation to reduce the number of
@ -57,8 +59,7 @@ namespace llarp
struct RouterConfig
{
size_t min_connected_routers = 0;
size_t max_connected_routers = 0;
int client_router_connections{CLIENT_ROUTER_CONNECTIONS};
std::string net_id;
@ -77,10 +78,9 @@ namespace llarp
std::string transkey_file;
bool is_relay = false;
/// deprecated
std::optional<net::ipaddr_t> public_ip;
/// deprecated
std::optional<net::port_t> public_port;
std::optional<std::string> public_ip;
std::optional<uint16_t> public_port;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
@ -170,19 +170,15 @@ namespace llarp
struct LinksConfig
{
std::optional<net::ipaddr_t> public_addr;
std::optional<net::port_t> public_port;
std::optional<oxen::quic::Address> addr;
bool using_new_api = false;
// DEPRECATED -- use [Router]:public_addr
std::optional<std::string> public_addr;
// DEPRECATED -- use [Router]:public_port
std::optional<uint16_t> public_port;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
};
std::optional<oxen::quic::Address> listen_addr;
struct ConnectConfig
{
std::vector<fs::path> routers;
bool using_user_value = false;
bool using_new_api = false;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
@ -202,6 +198,7 @@ namespace llarp
{
fs::path id_keyfile;
oxenmq::address rpc_addr;
bool disable_testing = true;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
@ -210,7 +207,6 @@ namespace llarp
struct BootstrapConfig
{
std::vector<fs::path> files;
BootstrapList routers;
bool seednode;
void
@ -240,7 +236,6 @@ namespace llarp
RouterConfig router;
NetworkConfig network;
PeerSelectionConfig paths;
ConnectConfig connect;
DnsConfig dns;
LinksConfig links;
ApiConfig api;

@ -10,20 +10,20 @@
namespace llarp
{
KeyManager::KeyManager() : m_initialized(false), m_needBackup(false)
KeyManager::KeyManager() : is_initialized(false), backup_keys(false)
{}
bool
KeyManager::initialize(const llarp::Config& config, bool genIfAbsent, bool isSNode)
KeyManager::initialize(const llarp::Config& config, bool gen_if_absent, bool is_snode)
{
if (m_initialized)
if (is_initialized)
return false;
if (not isSNode)
if (not is_snode)
{
crypto::identity_keygen(identityKey);
crypto::encryption_keygen(encryptionKey);
crypto::encryption_keygen(transportKey);
crypto::identity_keygen(identity_key);
crypto::encryption_keygen(encryption_key);
crypto::encryption_keygen(transport_key);
return true;
}
@ -46,80 +46,87 @@ namespace llarp
}
};
m_rcPath = deriveFile(our_rc_filename, config.router.rc_file);
m_idKeyPath = deriveFile(our_identity_filename, config.router.idkey_file);
m_encKeyPath = deriveFile(our_enc_key_filename, config.router.enckey_file);
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.transkey_file);
rc_path = deriveFile(our_rc_filename, config.router.rc_file);
idkey_path = deriveFile(our_identity_filename, config.router.idkey_file);
enckey_path = deriveFile(our_enc_key_filename, config.router.enckey_file);
transkey_path = deriveFile(our_transport_key_filename, config.router.transkey_file);
RemoteRC rc;
bool exists = rc.read(m_rcPath);
if (not exists and not genIfAbsent)
{
LogError("Could not read RouterContact at path ", m_rcPath);
return false;
}
// we need to back up keys if our self.signed doesn't appear to have a
// valid signature
m_needBackup = (isSNode and not rc.verify());
// if our RC file can't be verified, assume it is out of date (e.g. uses
// older encryption) and needs to be regenerated. before doing so, backup
// files that will be overwritten
if (exists and m_needBackup)
if (auto exists = rc.read(rc_path); not exists)
{
if (!genIfAbsent)
if (not gen_if_absent)
{
LogError("Our RouterContact ", m_rcPath, " is invalid or out of date");
log::error(logcat, "Could not read RC at path {}", rc_path);
return false;
}
else
}
else
{
if (backup_keys = (is_snode and not rc.verify()); backup_keys)
{
LogWarn(
"Our RouterContact ",
m_rcPath,
" seems out of date, backing up and regenerating private keys");
auto err = "RC (path:{}) is invalid or out of date"_format(rc_path);
if (!backupKeyFilesByMoving())
if (not gen_if_absent)
{
LogError(
"Could not mv some key files, please ensure key files"
" are backed up if needed and remove");
log::error(logcat, err);
return false;
}
}
}
if (not config.router.is_relay)
{
// load identity key or create if needed
auto identityKeygen = [](llarp::SecretKey& key) {
// TODO: handle generating from service node seed
llarp::crypto::identity_keygen(key);
};
if (not loadOrCreateKey(m_idKeyPath, identityKey, identityKeygen))
return false;
log::warning(logcat, "{}; backing up and regenerating private keys...", err);
if (not copy_backup_keyfiles())
{
log::error(logcat, "Failed to copy-backup key files");
return false;
}
}
}
// load encryption key
auto encryptionKeygen = [](llarp::SecretKey& key) { llarp::crypto::encryption_keygen(key); };
if (not loadOrCreateKey(m_encKeyPath, encryptionKey, encryptionKeygen))
auto enckey_gen = [](llarp::SecretKey& key) { llarp::crypto::encryption_keygen(key); };
if (not keygen(enckey_path, encryption_key, enckey_gen))
{
log::critical(
logcat, "KeyManager::keygen failed to generate encryption key line:{}", __LINE__);
return false;
}
// TODO: transport key (currently done in LinkLayer)
auto transportKeygen = [](llarp::SecretKey& key) {
auto transkey_gen = [](llarp::SecretKey& key) {
key.Zero();
crypto::encryption_keygen(key);
};
if (not loadOrCreateKey(m_transportKeyPath, transportKey, transportKeygen))
if (not keygen(transkey_path, transport_key, transkey_gen))
{
log::critical(
logcat, "KeyManager::keygen failed to generate transport key line:{}", __LINE__);
return false;
}
m_initialized = true;
if (not config.router.is_relay)
{
// load identity key or create if needed
auto idkey_gen = [](llarp::SecretKey& key) {
// TODO: handle generating from service node seed
llarp::crypto::identity_keygen(key);
};
if (not keygen(idkey_path, identity_key, idkey_gen))
{
log::critical(
logcat, "KeyManager::keygen failed to generate identity key line:{}", __LINE__);
return false;
}
}
is_initialized = true;
return true;
}
bool
KeyManager::backupFileByMoving(const fs::path& filepath)
KeyManager::copy_backup_keyfile(const fs::path& filepath)
{
auto findFreeBackupFilename = [](const fs::path& filepath) {
for (int i = 0; i < 9; i++)
@ -136,6 +143,7 @@ namespace llarp
std::error_code ec;
bool exists = fs::exists(filepath, ec);
if (ec)
{
LogError("Could not determine status of file ", filepath, ": ", ec.message());
@ -168,13 +176,13 @@ namespace llarp
}
bool
KeyManager::backupKeyFilesByMoving() const
KeyManager::copy_backup_keyfiles() const
{
std::vector<fs::path> files = {m_rcPath, m_idKeyPath, m_encKeyPath, m_transportKeyPath};
std::vector<fs::path> files = {rc_path, idkey_path, enckey_path, transkey_path};
for (auto& filepath : files)
{
if (not backupFileByMoving(filepath))
if (not copy_backup_keyfile(filepath))
return false;
}
@ -182,7 +190,7 @@ namespace llarp
}
bool
KeyManager::loadOrCreateKey(
KeyManager::keygen(
fs::path path, llarp::SecretKey& key, std::function<void(llarp::SecretKey& key)> keygen)
{
if (not fs::exists(path))

@ -29,15 +29,13 @@ namespace llarp
/// @param filepath is the name of the original file to backup.
/// @return true if the file could be moved or didn't exist, false otherwise
static bool
backupFileByMoving(const fs::path& filepath);
copy_backup_keyfile(const fs::path& filepath);
/// Constructor
KeyManager();
/// Initializes keys using the provided config, loading from disk
///
/// NOTE: Must be called prior to obtaining any keys.
/// NOTE: blocks on I/O
/// Initializes keys using the provided config, loading from disk. Must be called
/// prior to obtaining any keys and blocks on I/O
///
/// @param config should be a prepared config object
/// @param genIfAbsent determines whether or not we will create files if they
@ -52,37 +50,37 @@ namespace llarp
/// @param rc (out) will be modified to contian the RouterContact
/// @return true on success, false otherwise
bool
getRouterContact(llarp::RouterContact& rc) const;
gen_rc(llarp::RouterContact& rc) const;
/// Return whether or not we need to backup keys as we load them
bool
needBackup() const
needs_backup() const
{
return m_needBackup;
return backup_keys;
}
llarp::SecretKey identityKey;
llarp::SecretKey encryptionKey;
llarp::SecretKey transportKey;
llarp::SecretKey identity_key;
llarp::SecretKey encryption_key;
llarp::SecretKey transport_key;
fs::path m_rcPath;
fs::path m_idKeyPath;
fs::path m_encKeyPath;
fs::path m_transportKeyPath;
fs::path rc_path;
fs::path idkey_path;
fs::path enckey_path;
fs::path transkey_path;
private:
std::atomic_bool m_initialized;
std::atomic_bool m_needBackup;
std::atomic_bool is_initialized;
std::atomic_bool backup_keys;
/// Backup each key file (by copying, e.g. foo -> foo.bak)
bool
backupKeyFilesByMoving() const;
copy_backup_keyfiles() const;
/// Load the key at a given filepath or create it
///
/// @param keygen is a function that will generate the key if needed
static bool
loadOrCreateKey(
keygen(
fs::path filepath,
llarp::SecretKey& key,
std::function<void(llarp::SecretKey& key)> keygen);

@ -82,29 +82,34 @@ namespace llarp::consensus
// Pull the next element off the queue, but skip ourself, any that are no longer registered, and
// any that are currently known to be failing (those are queued for testing separately).
RouterID my_pk{router->pubkey()};
auto local_pk = router->local_rid();
while (!testing_queue.empty())
{
auto& pk = testing_queue.back();
std::optional<RouterID> sn;
if (pk != my_pk && !failing.count(pk))
if (pk != local_pk && !failing.count(pk))
sn = pk;
testing_queue.pop_back();
if (sn)
return sn;
}
if (!requeue)
return std::nullopt;
// FIXME: when a *new* node comes online we need to inject it into a random position in the SN
// list with probability (L/N) [L = current list size, N = potential list size]
//
// (FIXME: put this FIXME in a better place ;-) )
// We exhausted the queue so repopulate it and try again
testing_queue.clear();
const auto all = router->get_whitelist();
const auto& all = router->get_whitelist();
testing_queue.insert(testing_queue.begin(), all.begin(), all.end());
std::shuffle(testing_queue.begin(), testing_queue.end(), llarp::csrng);

@ -5,6 +5,5 @@
namespace llarp
{
using namespace std::literals;
/// how big of a time skip before we reset network state
constexpr auto TimeskipDetectedDuration = 1min;
} // namespace llarp

@ -4,11 +4,11 @@
namespace llarp
{
// clang-format off
const std::array<uint16_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const std::array<uint8_t, 3> LOKINET_VERSION{{@lokinet_VERSION_MAJOR@, @lokinet_VERSION_MINOR@, @lokinet_VERSION_PATCH@}};
const char* const LOKINET_VERSION_TAG = "@VERSIONTAG@";
const char* const LOKINET_VERSION_FULL = "lokinet-@lokinet_VERSION_MAJOR@.@lokinet_VERSION_MINOR@.@lokinet_VERSION_PATCH@-@LOKINET_VERSION_TAG@";
const char* const LOKINET_RELEASE_MOTTO = "@RELEASE_MOTTO@";
const char* const LOKINET_DEFAULT_NETID = "lokinet";
const char* const LOKINET_TESTNET_NETID = "testnet";
// clang-format on
} // namespace llarp

@ -6,10 +6,10 @@
namespace llarp
{
// Given a full lokinet version of: lokinet-1.2.3-abc these are:
extern const std::array<uint16_t, 3> LOKINET_VERSION;
extern const std::array<uint8_t, 3> LOKINET_VERSION;
extern const char* const LOKINET_VERSION_TAG;
extern const char* const LOKINET_VERSION_FULL;
extern const char* const LOKINET_RELEASE_MOTTO;
extern const char* const LOKINET_DEFAULT_NETID;
extern const char* const LOKINET_TESTNET_NETID;
} // namespace llarp

@ -57,8 +57,7 @@ namespace llarp
throw std::runtime_error("Cannot call Setup() on context without a Config");
if (opts.showBanner)
llarp::LogInfo(
fmt::format("{} {}", llarp::LOKINET_VERSION_FULL, llarp::LOKINET_RELEASE_MOTTO));
llarp::LogInfo(fmt::format("{}", llarp::LOKINET_VERSION_FULL));
if (!loop)
{
@ -67,7 +66,6 @@ namespace llarp
}
router = makeRouter(loop);
nodedb = makeNodeDB();
if (!router->Configure(config, opts.isSNode, nodedb))
@ -183,7 +181,7 @@ namespace llarp
{
if (router)
{
llarp::log::debug(logcat, "Handling SIGINT");
llarp::log::error(logcat, "Handling SIGINT");
/// async stop router on sigint
router->Stop();
}

@ -32,9 +32,11 @@ namespace llarp
std::string
ToString() const;
// FIXME: this is deceptively named: it should be "from_hex" since that's what it does.
bool
FromString(const std::string& str);
// FIXME: this is deceptively named: it should be "from_hex" since that's what it does.
static PubKey
from_string(const std::string& s);

@ -13,19 +13,19 @@ namespace llarp::exit
Context::Tick(llarp_time_t now)
{
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
itr->second->Tick(now);
++itr;
}
}
{
auto itr = m_Closed.begin();
while (itr != m_Closed.end())
auto itr = _closed.begin();
while (itr != _closed.end())
{
if ((*itr)->ShouldRemove())
itr = m_Closed.erase(itr);
itr = _closed.erase(itr);
else
++itr;
}
@ -33,14 +33,14 @@ namespace llarp::exit
}
void
Context::Stop()
Context::stop()
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
itr->second->Stop();
m_Closed.emplace_back(std::move(itr->second));
itr = m_Exits.erase(itr);
_closed.emplace_back(std::move(itr->second));
itr = _exits.erase(itr);
}
}
@ -48,8 +48,8 @@ namespace llarp::exit
Context::ExtractStatus() const
{
util::StatusObject obj{};
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
obj[itr->first] = itr->second->ExtractStatus();
++itr;
@ -58,10 +58,10 @@ namespace llarp::exit
}
void
Context::CalculateExitTraffic(TrafficStats& stats)
Context::calculate_exit_traffic(TrafficStats& stats)
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
itr->second->CalculateTrafficStats(stats);
++itr;
@ -69,10 +69,10 @@ namespace llarp::exit
}
exit::Endpoint*
Context::FindEndpointForPath(const PathID_t& path) const
Context::find_endpoint_for_path(const PathID_t& path) const
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
auto ep = itr->second->FindEndpointByPath(path);
if (ep)
@ -83,10 +83,10 @@ namespace llarp::exit
}
bool
Context::ObtainNewExit(const PubKey& pk, const PathID_t& path, bool permitInternet)
Context::obtain_new_exit(const PubKey& pk, const PathID_t& path, bool permitInternet)
{
auto itr = m_Exits.begin();
while (itr != m_Exits.end())
auto itr = _exits.begin();
while (itr != _exits.end())
{
if (itr->second->AllocateNewExit(pk, path, permitInternet))
return true;
@ -96,9 +96,9 @@ namespace llarp::exit
}
std::shared_ptr<handlers::ExitEndpoint>
Context::GetExitEndpoint(std::string name) const
Context::get_exit_endpoint(std::string name) const
{
if (auto itr = m_Exits.find(name); itr != m_Exits.end())
if (auto itr = _exits.find(name); itr != _exits.end())
{
return itr->second;
}
@ -106,10 +106,10 @@ namespace llarp::exit
}
void
Context::AddExitEndpoint(
Context::add_exit_endpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig)
{
if (m_Exits.find(name) != m_Exits.end())
if (_exits.find(name) != _exits.end())
throw std::invalid_argument{fmt::format("An exit with name {} already exists", name)};
auto endpoint = std::make_unique<handlers::ExitEndpoint>(name, router);
@ -119,7 +119,7 @@ namespace llarp::exit
if (!endpoint->Start())
throw std::runtime_error{fmt::format("Failed to start endpoint {}", name)};
m_Exits.emplace(name, std::move(endpoint));
_exits.emplace(name, std::move(endpoint));
}
} // namespace llarp::exit

@ -18,37 +18,37 @@ namespace llarp::exit
Tick(llarp_time_t now);
void
ClearAllEndpoints();
clear_all_endpoints();
util::StatusObject
ExtractStatus() const;
/// send close to all exit sessions and remove all sessions
void
Stop();
stop();
void
AddExitEndpoint(
add_exit_endpoint(
const std::string& name, const NetworkConfig& networkConfig, const DnsConfig& dnsConfig);
bool
ObtainNewExit(const PubKey& remote, const PathID_t& path, bool permitInternet);
obtain_new_exit(const PubKey& remote, const PathID_t& path, bool permitInternet);
exit::Endpoint*
FindEndpointForPath(const PathID_t& path) const;
find_endpoint_for_path(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>>;
void
CalculateExitTraffic(TrafficStats& stats);
calculate_exit_traffic(TrafficStats& stats);
std::shared_ptr<handlers::ExitEndpoint>
GetExitEndpoint(std::string name) const;
get_exit_endpoint(std::string name) const;
private:
Router* router;
std::unordered_map<std::string, std::shared_ptr<handlers::ExitEndpoint>> m_Exits;
std::list<std::shared_ptr<handlers::ExitEndpoint>> m_Closed;
std::unordered_map<std::string, std::shared_ptr<handlers::ExitEndpoint>> _exits;
std::list<std::shared_ptr<handlers::ExitEndpoint>> _closed;
};
} // namespace llarp::exit

@ -214,7 +214,7 @@ namespace llarp::exit
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());
p->EnterState(path::IGNORE, router->now());
return true;
}
@ -238,7 +238,7 @@ namespace llarp::exit
{
if (BuildCooldownHit(now))
return false;
if (IsReady() and NumInStatus(path::ePathBuilding) < numDesiredPaths)
if (IsReady() and NumInStatus(path::BUILDING) < numDesiredPaths)
return path::Builder::UrgentBuild(now);
return false;
}
@ -246,38 +246,38 @@ namespace llarp::exit
bool
BaseSession::FlushUpstream()
{
auto now = router->now();
auto path = PickEstablishedPath(llarp::path::ePathRoleExit);
if (path)
{
// for (auto& [i, queue] : m_Upstream)
// {
// while (queue.size())
// {
// 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)
{
if (const auto maybe = router->node_db()->get_rc(exit_router); maybe.has_value())
router->connect_to(*maybe);
}
else if (UrgentBuild(now))
BuildOneAlignedTo(exit_router);
}
// auto now = router->now();
// auto path = PickEstablishedPath(llarp::path::ePathRoleExit);
// if (path)
// {
// for (auto& [i, queue] : m_Upstream)
// {
// while (queue.size())
// {
// 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)
// {
// if (const auto maybe = router->node_db()->get_rc(exit_router); maybe.has_value())
// router->connect_to(*maybe);
// }
// else if (UrgentBuild(now))
// BuildOneAlignedTo(exit_router);
// }
return true;
}

@ -281,7 +281,7 @@ namespace llarp::handlers
}
// forward dns for snode
RouterID r;
if (r.FromString(msg.questions[0].Name()))
if (r.from_string(msg.questions[0].Name()))
{
huint128_t ip;
PubKey pubKey(r);

@ -535,7 +535,7 @@ namespace llarp::handlers
if (auto saddr = service::Address(); saddr.FromString(name))
ReplyToLokiDNSWhenReady(saddr, msg, isV6);
if (auto rid = RouterID(); rid.FromString(name))
if (auto rid = RouterID(); rid.from_string(name))
ReplyToSNodeDNSWhenReady(rid, msg, isV6);
};
@ -568,7 +568,7 @@ namespace llarp::handlers
if (not qname)
return false;
RouterID addr;
if (not addr.FromString(*qname))
if (not addr.from_string(*qname))
return false;
auto replyMsg = std::make_shared<dns::Message>(clear_dns_message(msg));
return ReplyToSNodeDNSWhenReady(addr, std::move(replyMsg), false);
@ -604,7 +604,7 @@ namespace llarp::handlers
if (msg.questions[0].qtype == dns::qTypeTXT)
{
RouterID snode;
if (snode.FromString(qname))
if (snode.from_string(qname))
{
if (auto rc = router()->node_db()->get_rc(snode))
msg.AddTXTReply(std::string{rc->view()});

@ -3,10 +3,10 @@
namespace llarp::link
{
Connection::Connection(
std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RemoteRC& rc)
: conn{c}, control_stream{s}, remote_rc{std::move(rc)}
std::shared_ptr<oxen::quic::connection_interface> c,
std::shared_ptr<oxen::quic::BTRequestStream> s,
bool is_relay)
: conn{std::move(c)}, control_stream{std::move(s)}, remote_is_relay{is_relay}
{}
} // namespace llarp::link

@ -11,16 +11,19 @@ namespace llarp::link
{
std::shared_ptr<oxen::quic::connection_interface> conn;
std::shared_ptr<oxen::quic::BTRequestStream> control_stream;
RemoteRC remote_rc;
// one side of a connection will be responsible for some things, e.g. heartbeat
bool inbound{false};
bool remote_is_relay{true};
bool
is_inbound() const
{
return conn->is_inbound();
}
Connection(
std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RemoteRC& rc);
std::shared_ptr<oxen::quic::connection_interface> c,
std::shared_ptr<oxen::quic::BTRequestStream> s,
bool is_relay = true);
};
} // namespace llarp::link

@ -5,82 +5,38 @@
namespace llarp
{
Contacts::Contacts(const dht::Key_t& k, Router& r) : _local_key{k}, _router{r}
Contacts::Contacts(Router& r) : _router{r}, _local_key{r.pubkey()}
{
timer_keepalive = std::make_shared<int>(0);
_router.loop()->call_every(1s, timer_keepalive, [this]() { on_clean_contacts(); });
_rc_nodes = std::make_unique<dht::Bucket<dht::RCNode>>(_local_key, llarp::randint);
_introset_nodes = std::make_unique<dht::Bucket<dht::ISNode>>(_local_key, llarp::randint);
}
std::optional<service::EncryptedIntroSet>
Contacts::get_introset_by_location(const dht::Key_t& key) const
{
return _router.loop()->call_get([this, key]() -> std::optional<service::EncryptedIntroSet> {
auto& introsets = _introset_nodes->nodes;
std::optional<service::EncryptedIntroSet> enc = std::nullopt;
if (auto itr = introsets.find(key); itr != introsets.end())
return itr->second.introset;
auto& introsets = _introset_nodes->nodes;
return std::nullopt;
});
}
void
Contacts::on_clean_contacts()
{
const auto now = llarp::time_now_ms();
if (_rc_nodes)
{
auto& nodes = _rc_nodes->nodes;
auto itr = nodes.begin();
while (itr != nodes.end())
{
if (itr->second.rc.is_expired(now))
itr = nodes.erase(itr);
else
++itr;
}
}
if (auto itr = introsets.find(key); itr != introsets.end())
enc = itr->second.introset;
if (_introset_nodes)
{
auto& svcs = _introset_nodes->nodes;
auto itr = svcs.begin();
while (itr != svcs.end())
{
if (itr->second.introset.IsExpired(now))
itr = svcs.erase(itr);
else
++itr;
}
}
return enc;
}
util::StatusObject
Contacts::ExtractStatus() const
{
util::StatusObject obj{
{"nodes", _rc_nodes->ExtractStatus()},
{"services", _introset_nodes->ExtractStatus()},
{"local_key", _local_key.ToHex()}};
{"services", _introset_nodes->ExtractStatus()}, {"local_key", _local_key.ToHex()}};
return obj;
}
void
Contacts::put_rc_node_async(const dht::RCNode& val)
{
_router.loop()->call([this, val]() { _rc_nodes->PutNode(val); });
}
void
Contacts::delete_rc_node_async(const dht::Key_t& val)
Contacts::put_intro(service::EncryptedIntroSet enc)
{
_router.loop()->call([this, val]() { _rc_nodes->DelNode(val); });
_introset_nodes->PutNode(std::move(enc));
}
} // namespace llarp

@ -15,28 +15,14 @@ namespace llarp
private:
// TODO: why was this a shared ptr in the original implementation? revisit this
std::shared_ptr<int> timer_keepalive;
const dht::Key_t& _local_key;
Router& _router;
std::atomic<bool> transit_allowed{false};
const dht::Key_t _local_key;
// holds router contacts
std::unique_ptr<dht::Bucket<dht::RCNode>> _rc_nodes;
// holds introsets for remote services
std::unique_ptr<dht::Bucket<dht::ISNode>> _introset_nodes;
public:
Contacts(const dht::Key_t& local, Router& r);
/// Sets the value of transit_allowed to the value of `b`. Returns false if the
/// value was already b, true otherwise
bool
set_transit_allowed(bool b)
{
return not transit_allowed.exchange(b) == b;
}
void
on_clean_contacts();
Contacts(Router& r);
std::optional<service::EncryptedIntroSet>
get_introset_by_location(const dht::Key_t& key) const;
@ -46,16 +32,7 @@ namespace llarp
ExtractStatus() const;
void
put_rc_node_async(const dht::RCNode& val);
void
delete_rc_node_async(const dht::Key_t& val);
dht::Bucket<dht::RCNode>*
rc_nodes() const
{
return _rc_nodes.get();
}
put_intro(service::EncryptedIntroSet enc);
dht::Bucket<dht::ISNode>*
services() const

File diff suppressed because it is too large Load Diff

@ -28,48 +28,94 @@ namespace llarp
struct LinkManager;
class NodeDB;
using conn_open_hook = oxen::quic::connection_established_callback;
using conn_closed_hook = oxen::quic::connection_closed_callback;
using stream_open_hook = oxen::quic::stream_open_callback;
using stream_closed_hook = oxen::quic::stream_close_callback;
using keep_alive = oxen::quic::opt::keep_alive;
using inbound_alpns = oxen::quic::opt::inbound_alpns;
using outbound_alpns = oxen::quic::opt::outbound_alpns;
inline const keep_alive ROUTER_KEEP_ALIVE{10s};
inline const keep_alive CLIENT_KEEP_ALIVE{10s};
inline constexpr int MIN_CLIENT_ROUTER_CONNS{4};
inline constexpr int MAX_CLIENT_ROUTER_CONNS{6};
namespace alpns
{
inline const auto SN_ALPNS = "SERVICE_NODE"_us;
inline const auto C_ALPNS = "CLIENT"_us;
inline const inbound_alpns SERVICE_INBOUND{{SN_ALPNS, C_ALPNS}};
inline const outbound_alpns SERVICE_OUTBOUND{{SN_ALPNS}};
inline const inbound_alpns CLIENT_INBOUND{};
inline const outbound_alpns CLIENT_OUTBOUND{{C_ALPNS}};
} // namespace alpns
namespace link
{
struct Connection;
struct Endpoint
{
Endpoint(std::shared_ptr<oxen::quic::Endpoint> ep, LinkManager& lm)
: endpoint{std::move(ep)}, link_manager{lm}
{}
Endpoint(std::shared_ptr<oxen::quic::Endpoint> ep, LinkManager& lm);
std::shared_ptr<oxen::quic::Endpoint> endpoint;
LinkManager& link_manager;
// for outgoing packets, we route via RouterID; map RouterID->Connection
// for incoming packets, we get a ConnectionID; map ConnectionID->RouterID
std::unordered_map<RouterID, std::shared_ptr<link::Connection>> conns;
std::unordered_map<oxen::quic::ConnectionID, RouterID> connid_map;
/** Connection containers:
- service_conns: holds all connections where the remote (from the perspective
of the local lokinet instance) is a service node. This means all relay to
relay connections are held here; clients will also hold their connections to
relays here as well
- client_conns: holds all connections wehre the remote is a client. This is only
used by service nodes to store their client connections
*/
std::unordered_map<RouterID, std::shared_ptr<link::Connection>> service_conns;
std::unordered_map<RouterID, std::shared_ptr<link::Connection>> client_conns;
// TODO: see which of these is actually useful and delete the other
std::shared_ptr<link::Connection>
get_conn(const RemoteRC&) const;
get_conn(const RouterID&) const;
std::shared_ptr<link::Connection>
get_conn(const RouterID&) const;
get_service_conn(const RouterID&) const;
bool
have_conn(const RouterID& remote) const;
bool
have_conn(const RouterID& remote, bool client_only) const;
have_client_conn(const RouterID& remote) const;
bool
deregister_peer(RouterID remote);
have_service_conn(const RouterID& remote) const;
std::pair<size_t, size_t>
num_in_out() const;
size_t
num_connected(bool clients_only) const;
num_client_conns() const;
bool
get_random_connection(RemoteRC& router) const;
size_t
num_router_conns() const;
template <typename... Opt>
bool
establish_connection(
const oxen::quic::RemoteAddress& remote, const RemoteRC& rc, Opt&&... opts);
template <typename... Opt>
bool
establish_and_send(
const oxen::quic::RemoteAddress& remote,
const RemoteRC& rc,
std::optional<std::string> endpoint,
std::string body,
std::function<void(oxen::quic::message m)> func = nullptr,
Opt&&... opts);
void
for_each_connection(std::function<void(link::Connection&)> func);
@ -77,6 +123,7 @@ namespace llarp
close_connection(RouterID rid);
private:
const bool _is_service_node;
};
} // namespace link
@ -107,27 +154,18 @@ namespace llarp
struct PendingMessage
{
std::string body;
std::optional<std::string> endpoint = std::nullopt;
std::function<void(oxen::quic::message)> func = nullptr;
RouterID rid;
bool is_control{false};
bool is_control = false;
PendingMessage(std::string b, bool control = false) : body{std::move(b)}, is_control{control}
PendingMessage(std::string b) : body{std::move(b)}
{}
};
struct PendingDataMessage : PendingMessage
{
PendingDataMessage(std::string b) : PendingMessage(b)
{}
};
struct PendingControlMessage : PendingMessage
{
std::string endpoint;
std::function<void(oxen::quic::message)> func;
PendingControlMessage(
std::string b, std::string e, std::function<void(oxen::quic::message)> f = nullptr)
: PendingMessage(b, true), endpoint{std::move(e)}, func{std::move(f)}
PendingMessage(
std::string b, std::string ep, std::function<void(oxen::quic::message)> f = nullptr)
: body{std::move(b)}, endpoint{std::move(ep)}, func{std::move(f)}, is_control{true}
{}
};
@ -138,7 +176,8 @@ namespace llarp
struct LinkManager
{
public:
explicit LinkManager(Router& r);
static std::unique_ptr<LinkManager>
make(Router& r);
bool
send_control_message(
@ -157,12 +196,7 @@ namespace llarp
}
private:
bool
send_control_message_impl(
const RouterID& remote,
std::string endpoint,
std::string body,
std::function<void(oxen::quic::message)> = nullptr);
explicit LinkManager(Router& r);
friend struct link::Endpoint;
@ -171,9 +205,6 @@ namespace llarp
// sessions to persist -> timestamp to end persist at
std::unordered_map<RouterID, llarp_time_t> persisting_conns;
// holds any messages we attempt to send while connections are establishing
std::unordered_map<RouterID, MessageQueue> pending_conn_msg_queue;
util::DecayingHashSet<RouterID> clients{path::DEFAULT_LIFETIME};
std::shared_ptr<NodeDB> node_db;
@ -182,6 +213,8 @@ namespace llarp
Router& _router;
const bool _is_service_node;
// FIXME: Lokinet currently expects to be able to kill all network functionality before
// finishing other shutdown things, including destroying this class, and that is all in
// Network's destructor, so we need to be able to destroy it before this class.
@ -192,8 +225,14 @@ namespace llarp
void
recv_data_message(oxen::quic::dgram_interface& dgi, bstring dgram);
std::shared_ptr<oxen::quic::BTRequestStream>
make_control(oxen::quic::connection_interface& ci, const RouterID& rid);
void
recv_control_message(oxen::quic::message msg);
on_inbound_conn(oxen::quic::connection_interface& ci);
void
on_outbound_conn(oxen::quic::connection_interface& ci);
void
on_conn_open(oxen::quic::connection_interface& ci);
@ -205,11 +244,14 @@ namespace llarp
startup_endpoint();
void
register_commands(std::shared_ptr<oxen::quic::BTRequestStream>& s);
register_commands(
std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RouterID& rid,
bool client_only = false);
public:
const link::Endpoint&
endpoint()
endpoint() const
{
return ep;
}
@ -221,37 +263,57 @@ namespace llarp
}
void
gossip_rc(const RouterID& rc_rid, std::string serialized_rc);
gossip_rc(const RouterID& last_sender, const RemoteRC& rc);
void
handle_gossip_rc(oxen::quic::message m);
void
fetch_rcs(const RouterID& source, rc_time since, const std::vector<RouterID>& explicit_ids);
fetch_rcs(
const RouterID& source,
std::string payload,
std::function<void(oxen::quic::message m)> func);
void
handle_fetch_rcs(oxen::quic::message m);
void
fetch_router_ids(const RouterID& source);
fetch_router_ids(
const RouterID& via, std::string payload, std::function<void(oxen::quic::message m)> func);
void
handle_fetch_router_ids(oxen::quic::message m);
void
fetch_bootstrap_rcs(
const RemoteRC& source,
std::string payload,
std::function<void(oxen::quic::message m)> func);
void
handle_fetch_bootstrap_rcs(oxen::quic::message m);
bool
have_connection_to(const RouterID& remote, bool client_only = false) const;
have_connection_to(const RouterID& remote) const;
bool
have_service_connection_to(const RouterID& remote) const;
bool
have_client_connection_to(const RouterID& remote) const;
void
deregister_peer(RouterID remote);
test_reachability(const RouterID& rid, conn_open_hook, conn_closed_hook);
void
connect_to(const RouterID& router);
connect_to(const RemoteRC& rc, conn_open_hook = nullptr, conn_closed_hook = nullptr);
void
connect_to(const RemoteRC& rc);
connect_and_send(
const RouterID& router,
std::optional<std::string> endpoint,
std::string body,
std::function<void(oxen::quic::message m)> func = nullptr);
void
close_connection(RouterID rid);
@ -262,14 +324,17 @@ namespace llarp
void
set_conn_persist(const RouterID& remote, llarp_time_t until);
std::pair<size_t, size_t>
num_in_out() const;
size_t
get_num_connected(bool clients_only = false) const;
get_num_connected_routers() const;
size_t
get_num_connected_clients() const;
bool
get_random_connected(RemoteRC& router) const;
is_service_node() const;
void
check_persisting_conns(llarp_time_t now);
@ -289,13 +354,10 @@ namespace llarp
// check if we already have a connection to any of the random set, as making
// that thread safe would be slow...I think.
void
connect_to_random(int num_conns);
connect_to_random(int num_conns, bool client_only = false);
// TODO: tune these (maybe even remove max?) now that we're switching to quic
/// always maintain this many connections to other routers
size_t min_connected_routers = 4;
/// hard upperbound limit on the number of router to router connections
size_t max_connected_routers = 6;
/// always maintain this many client connections to other routers
int client_router_connections = 4;
private:
// DHT messages
@ -330,18 +392,9 @@ namespace llarp
{"find_name"sv, &LinkManager::handle_find_name},
{"publish_intro"sv, &LinkManager::handle_publish_intro},
{"find_intro"sv, &LinkManager::handle_find_intro}};
/*
{"path_confirm", &LinkManager::handle_path_confirm},
{"path_latency", &LinkManager::handle_path_latency},
{"update_exit", &LinkManager::handle_update_exit},
{"obtain_exit", &LinkManager::handle_obtain_exit},
{"close_exit", &LinkManager::handle_close_exit},
{"convo_intro", &LinkManager::handle_convo_intro}};
*/
// these requests are direct, i.e. not over a path;
// the rest are relay->relay
// TODO: new RC fetch endpoint (which will be both client->relay and relay->relay)
std::unordered_map<
std::string_view,
void (LinkManager::*)(std::string_view body, std::function<void(std::string)> respond)>
@ -370,18 +423,75 @@ namespace llarp
void handle_obtain_exit_response(oxen::quic::message);
void handle_update_exit_response(oxen::quic::message);
void handle_close_exit_response(oxen::quic::message);
std::unordered_map<std::string, void (LinkManager::*)(oxen::quic::message)> rpc_responses = {
{"find_name", &LinkManager::handle_find_name_response},
{"publish_intro", &LinkManager::handle_publish_intro_response},
{"find_intro", &LinkManager::handle_find_intro_response},
{"update_exit", &LinkManager::handle_update_exit_response},
{"obtain_exit", &LinkManager::handle_obtain_exit_response},
{"close_exit", &LinkManager::handle_close_exit_response}};
};
namespace link
{
template <typename... Opt>
bool
Endpoint::establish_and_send(
const oxen::quic::RemoteAddress& remote,
const RemoteRC& rc,
std::optional<std::string> ep,
std::string body,
std::function<void(oxen::quic::message m)> func,
Opt&&... opts)
{
try
{
const auto& rid = rc.router_id();
const auto& is_snode = _is_service_node;
const auto& is_control = ep.has_value();
const auto us = is_snode ? "Relay"s : "Client"s;
log::critical(logcat, "Establishing connection to RID:{}", rid);
// add to service conns
auto [itr, b] = service_conns.emplace(rid, nullptr);
auto conn_interface = endpoint->connect(
remote,
link_manager.tls_creds,
is_snode ? ROUTER_KEEP_ALIVE : CLIENT_KEEP_ALIVE,
std::forward<Opt>(opts)...);
// auto
std::shared_ptr<oxen::quic::BTRequestStream> control_stream =
conn_interface->template open_stream<oxen::quic::BTRequestStream>(
[this, rid = rid](oxen::quic::Stream&, uint64_t error_code) {
log::warning(
logcat,
"BTRequestStream closed unexpectedly (ec:{}); closing outbound connection...",
error_code);
close_connection(rid);
});
if (is_snode)
link_manager.register_commands(control_stream, rid);
else
log::critical(logcat, "Client NOT registering BTStream commands!");
log::critical(
logcat,
"{} dispatching {} on outbound connection to remote (rid:{})",
us,
is_control ? "control message (ep:{})"_format(ep) : "data message",
rid);
(is_control) ? control_stream->command(std::move(*ep), std::move(body), std::move(func))
: conn_interface->send_datagram(std::move(body));
itr->second = std::make_shared<link::Connection>(conn_interface, control_stream, true);
log::critical(logcat, "Outbound connection to RID:{} added to service conns...", rid);
return true;
}
catch (...)
{
log::error(quic_cat, "Error: failed to establish connection to {}", remote);
return false;
}
}
template <typename... Opt>
bool
Endpoint::establish_connection(
@ -389,17 +499,35 @@ namespace llarp
{
try
{
auto conn_interface =
endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...);
// emplace immediately for connection open callback to find scid
connid_map.emplace(conn_interface->scid(), rc.router_id());
auto [itr, b] = conns.emplace(rc.router_id(), nullptr);
auto control_stream =
conn_interface->template get_new_stream<oxen::quic::BTRequestStream>();
itr->second = std::make_shared<link::Connection>(conn_interface, control_stream, rc);
const auto& rid = rc.router_id();
const auto& is_snode = _is_service_node;
log::critical(logcat, "Establishing connection to RID:{}", rid);
// add to service conns
auto [itr, b] = service_conns.emplace(rid, nullptr);
auto conn_interface = endpoint->connect(
remote,
link_manager.tls_creds,
is_snode ? ROUTER_KEEP_ALIVE : CLIENT_KEEP_ALIVE,
std::forward<Opt>(opts)...);
auto control_stream = conn_interface->template open_stream<oxen::quic::BTRequestStream>(
[this, rid = rid](oxen::quic::Stream&, uint64_t error_code) {
log::warning(
logcat,
"BTRequestStream closed unexpectedly (ec:{}); closing outbound connection...",
error_code);
close_connection(rid);
});
if (is_snode)
link_manager.register_commands(control_stream, rid);
else
log::critical(logcat, "Client NOT registering BTStream commands!");
itr->second = std::make_shared<link::Connection>(conn_interface, control_stream, true);
log::critical(logcat, "Outbound connection to RID:{} added to service conns...", rid);
return true;
}
catch (...)
@ -409,50 +537,4 @@ namespace llarp
}
}
} // namespace link
} // namespace llarp
/*
- Refactor RouterID to use gnutls info and maybe ConnectionID
- Combine routerID and connectionID to simplify mapping in llarp/link/endpoint.hpp
- Combine llarp/link/session.hpp into llarp/link/connection.hpp::Connection
- Combine llarp/link/server.hpp::ILinkLayer into llarp/link/endpoint.hpp::Endpoint
- must maintain metadata storage, callbacks, etc
- If: one endpoint for ipv4 and ipv6
- Then: can potentially combine:
- llarp/link/endpoint.hpp
- llarp/link/link_manager.hpp
- llarp/link/outbound_message_handler.hpp
- llarp/link/outbound_session_maker.hpp
-> Yields mega-combo endpoint managing object?
- Can avoid "kitchen sink" by greatly reducing complexity of implementation
llarp/router/outbound_message_handler.hpp
- pendingsessionmessagequeue
- establish queue of messages to be sent on a connection we are creating
- upon creation, send these messages in the connection established callback
- if connection times out, flush queue
- TOCHECK: is priority used at all??
std::unordered_map<std::string, void (llarp::link::LinkManager::*)(oxen::quic::message)>
rpc_commands = {
{"find_name", &handle_find_name},
// ...
};
for (const auto& [name, mfn] : rpc_commands)
bparser.add_command(name, [this, mfn] (oxen::quic::message m) {
router->call([this, mfn, m=std::move(m)] mutable {
try {
std::invoke(mfn, this, std::move(m));
} catch (const std::exception& e) {
m.respond("Error: "s + e.what(), true);
}
});
});
*/

@ -20,7 +20,6 @@ namespace llarp
{
namespace messages
{
inline std::string
serialize_response(oxenc::bt_dict supplement = {})
{

@ -34,6 +34,7 @@ namespace llarp
{
inline auto NOT_FOUND = "NOT FOUND"sv;
// NOT USED
inline static std::string
serialize(dht::Key_t name_hash)
{

@ -0,0 +1,123 @@
#pragma once
#include "common.hpp"
#include <llarp/util/logging/buffer.hpp>
namespace llarp
{
namespace GossipRCMessage
{
inline static std::string
serialize(const RouterID& last_sender, const RemoteRC& rc)
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append_encoded("rc", rc.view());
btdp.append("sender", last_sender.ToView());
}
catch (...)
{
log::error(link_cat, "Error: GossipRCMessage failed to bt encode contents");
}
return std::move(btdp).str();
}
} // namespace GossipRCMessage
namespace FetchRCMessage
{
inline const auto INVALID_REQUEST =
messages::serialize_response({{messages::STATUS_KEY, "Invalid relay ID requested"}});
inline static std::string
serialize(
std::chrono::system_clock::time_point since, const std::vector<RouterID>& explicit_ids)
{
oxenc::bt_dict_producer btdp;
try
{
{
auto sublist = btdp.append_list("explicit_ids");
for (const auto& rid : explicit_ids)
sublist.append(rid.ToView());
}
btdp.append("since", since.time_since_epoch() / 1s);
}
catch (...)
{
log::error(link_cat, "Error: RCFetchMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
} // namespace FetchRCMessage
namespace BootstrapFetchMessage
{
// the LocalRC is converted to a RemoteRC type to send to the bootstrap seed
inline static std::string
serialize(std::optional<LocalRC> local_rc, size_t quantity)
{
oxenc::bt_dict_producer btdp;
if (local_rc)
{
log::critical(logcat, "Serializing localRC: {}", oxenc::to_hex(local_rc->view()));
btdp.append_encoded("local", oxen::quic::to_sv(local_rc->view()));
}
btdp.append("quantity", quantity);
return std::move(btdp).str();
}
inline static std::string
serialize_response(const std::vector<RouterID>& explicit_ids)
{
oxenc::bt_dict_producer btdp;
try
{
auto sublist = btdp.append_list("explicit_ids");
for (const auto& rid : explicit_ids)
sublist.append(rid.ToView());
}
catch (...)
{
log::error(link_cat, "Error: BootstrapFetchMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
} // namespace BootstrapFetchMessage
namespace FetchRIDMessage
{
inline constexpr auto INVALID_REQUEST = "Invalid relay ID requested to relay response from."sv;
inline static std::string
serialize(const RouterID& source)
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append("source", source.ToView());
}
catch (...)
{
log::error(link_cat, "Error: FetchRIDMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
} // namespace FetchRIDMessage
} // namespace llarp

@ -1,30 +0,0 @@
#pragma once
#include "common.hpp"
namespace llarp::RCFetchMessage
{
inline constexpr auto INVALID_REQUEST = "Invalid relay ID requested."sv;
inline static std::string
serialize(std::chrono::system_clock::time_point since, const std::vector<RouterID>& explicit_ids)
{
oxenc::bt_dict_producer btdp;
try
{
btdp.append("since", since.time_since_epoch() / 1s);
{
auto id_list = btdp.append_list("explicit_ids");
for (const auto& rid : explicit_ids)
id_list.append(rid.ToView());
}
}
catch (...)
{
log::error(link_cat, "Error: RCFetchMessage failed to bt encode contents!");
}
return std::move(btdp).str();
}
} // namespace llarp::RCFetchMessage

@ -1,17 +0,0 @@
#pragma once
#include "common.hpp"
namespace llarp::RouterIDFetch
{
inline constexpr auto INVALID_REQUEST = "Invalid relay ID requested to relay response from."sv;
inline static std::string
serialize(const RouterID& source)
{
// serialize_response is a bit weird here, and perhaps could have a sister function
// with the same purpose but as a request, but...it works.
return messages::serialize_response({{"source", source.ToView()}});
}
} // namespace llarp::RouterIDFetch

@ -10,6 +10,8 @@
#include <llarp/util/bits.hpp>
#include <llarp/util/mem.hpp>
#include <quic/address.hpp>
#include <cstdlib> // for itoa
#include <functional>
#include <vector>
@ -121,8 +123,10 @@ namespace llarp
return var::visit([](auto&& ip) { return not ip.n; }, ip);
}
virtual std::optional<sockaddr*>
GetBestNetIF(int af = AF_INET) const = 0;
// Attempts to guess a good default public network address from the system's public IP
// addresses; the returned Address (if set) will have its port set to the given value.
virtual std::optional<oxen::quic::Address>
get_best_public_address(bool ipv4, uint16_t port) const = 0;
virtual std::optional<IPRange>
FindFreeRange() const = 0;

@ -10,6 +10,8 @@
#include <ifaddrs.h>
#endif
#include <quic/address.hpp>
#include <list>
namespace llarp::net
@ -50,19 +52,21 @@ namespace llarp::net
return ifname;
}
std::optional<sockaddr*>
GetBestNetIF(int af) const override
std::optional<oxen::quic::Address>
get_best_public_address(bool ipv4, uint16_t port) const override
{
std::optional<sockaddr*> found;
std::optional<oxen::quic::Address> found;
iter_all([this, &found, af](auto i) {
iter_all([&found, ipv4, port](ifaddrs* i) {
if (found)
return;
if (i and i->ifa_addr and i->ifa_addr->sa_family == af)
if (i and i->ifa_addr and i->ifa_addr->sa_family == (ipv4 ? AF_INET : AF_INET6))
{
if (not IsBogon(*i->ifa_addr))
oxen::quic::Address a{i->ifa_addr};
if (a.is_public_ip())
{
found = i->ifa_addr;
a.set_port(port);
found = std::move(a);
}
}
});

@ -232,8 +232,8 @@ namespace llarp
return;
}
// NOTE: this potentially involves multiple memory allocations,
// reimplement without split() if it is performance bottleneck
// TOFIX: This potentially involves multiple memory allocations,
// reimplement without split() if it is performance bottleneck
auto splits = split(str, ":");
// TODO: having ":port" at the end makes this ambiguous with IPv6

@ -129,8 +129,8 @@ namespace llarp::net
return "lokitun0";
}
std::optional<sockaddr*>
GetBestNetIF(int) const override
std::optional<oxen::quic::Address>
get_best_public_address(bool, uint16_t) const override
{
// TODO: implement me ?
return std::nullopt;

File diff suppressed because it is too large Load Diff

@ -12,9 +12,9 @@
#include <algorithm>
#include <atomic>
#include <map>
#include <optional>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
@ -22,15 +22,164 @@ namespace llarp
{
struct Router;
class NodeDB
// TESTNET: the following constants have been shortened for testing purposes
/* RC Fetch Constants */
// fallback to bootstrap if we have less than this many RCs
inline constexpr size_t MIN_ACTIVE_RCS{6};
// max number of attempts we make in non-bootstrap fetch requests
inline constexpr int MAX_FETCH_ATTEMPTS{10};
// the total number of returned rcs that are held locally should be at least this
inline constexpr size_t MIN_GOOD_RC_FETCH_TOTAL{};
// the ratio of returned rcs found locally to to total returned should be above this ratio
inline constexpr double MIN_GOOD_RC_FETCH_THRESHOLD{};
/* RID Fetch Constants */
// the number of rid sources that we make rid fetch requests to
inline constexpr size_t RID_SOURCE_COUNT{8};
// upper limit on how many rid fetch requests to rid sources can fail
inline constexpr size_t MAX_RID_ERRORS{2};
// each returned rid must appear this number of times across all responses
inline constexpr int MIN_RID_FETCH_FREQ{RID_SOURCE_COUNT - MAX_RID_ERRORS - 1};
// the total number of accepted returned rids should be above this number
inline constexpr size_t MIN_GOOD_RID_FETCH_TOTAL{};
// the ratio of accepted:rejected rids must be above this ratio
inline constexpr double GOOD_RID_FETCH_THRESHOLD{};
/* Bootstrap Constants */
// the number of rc's we query the bootstrap for; service nodes pass 0, which means
// gimme all dat RCs
inline constexpr size_t SERVICE_NODE_BOOTSTRAP_SOURCE_COUNT{0};
inline constexpr size_t CLIENT_BOOTSTRAP_SOURCE_COUNT{10};
// the maximum number of fetch requests we make across all bootstraps
inline constexpr int MAX_BOOTSTRAP_FETCH_ATTEMPTS{5};
// if all bootstraps fail, router will trigger re-bootstrapping after this cooldown
inline constexpr auto BOOTSTRAP_COOLDOWN{1min};
/* Other Constants */
// the maximum number of RC/RID fetches that can pass w/o an unconfirmed rc/rid appearing
inline constexpr int MAX_CONFIRMATION_ATTEMPTS{5};
// threshold amount of verifications to promote an unconfirmed rc/rid
inline constexpr int CONFIRMATION_THRESHOLD{3};
inline constexpr auto FLUSH_INTERVAL{5min};
template <
typename ID_t,
std::enable_if_t<std::is_same_v<ID_t, RouterID> || std::is_same_v<ID_t, RemoteRC>, int> = 0>
struct Unconfirmed
{
std::unordered_map<RouterID, RemoteRC> known_rcs;
const ID_t id;
int attempts = 0;
int verifications = 0;
Unconfirmed() = delete;
Unconfirmed(const ID_t& obj) : id{obj}
{}
Unconfirmed(ID_t&& obj) : id{std::move(obj)}
{}
int
strikes() const
{
return attempts;
}
Router& router;
const fs::path m_Root;
const std::function<void(std::function<void()>)> disk;
operator bool() const
{
return verifications == CONFIRMATION_THRESHOLD;
}
llarp_time_t m_NextFlushAt;
bool
operator==(const Unconfirmed& other) const
{
return id == other.id;
}
bool
operator<(const Unconfirmed& other) const
{
return id < other.id;
}
};
class NodeDB
{
Router& _router;
const fs::path _root;
const std::function<void(std::function<void()>)> _disk;
llarp_time_t _next_flush_time;
/******** RouterID/RouterContacts ********/
/** RouterID mappings
Both the following are populated in NodeDB startup with RouterID's stored on disk.
- known_rids: meant to persist between lokinet sessions, and is only
populated during startup and RouterID fetching. This is meant to represent the
client instance's most recent perspective of the network, and record which RouterID's
were recently "active" and connected to
- unconfirmed_rids: holds new rids returned in fetch requests to be verified by subsequent
fetch requests
- known_rcs: populated during startup and when RC's are updated both during gossip
and periodic RC fetching
- unconfirmed_rcs: holds new rcs to be verified by subsequent fetch requests, similar to
the unknown_rids container
- rc_lookup: holds all the same rc's as known_rcs, but can be used to look them up by
their rid
- bootstrap_seeds: if we are the seed node, we insert the rc's of bootstrap fetch requests
senders into this container to "introduce" them to each other
- _bootstraps: the standard container for bootstrap RemoteRCs
*/
std::set<RouterID> known_rids;
std::set<Unconfirmed<RouterID>> unconfirmed_rids;
std::set<RemoteRC> known_rcs;
std::set<Unconfirmed<RemoteRC>> unconfirmed_rcs;
std::map<RouterID, const RemoteRC&> rc_lookup;
BootstrapList _bootstraps{};
/** RouterID lists // TODO: get rid of all these, replace with better decom/not staked sets
- white: active routers
- gray: fully funded, but decommissioned routers
- green: registered, but not fully-staked routers
*/
std::set<RouterID> _router_whitelist{};
std::set<RouterID> _router_greylist{};
std::set<RouterID> _router_greenlist{};
// All registered relays (service nodes)
std::set<RouterID> _registered_routers;
// timing (note: Router holds the variables for last rc and rid request times)
std::unordered_map<RouterID, rc_time> last_rc_update_times;
// if populated from a config file, lists specific exclusively used as path first-hops
std::set<RouterID> _pinned_edges;
// source of "truth" for RC updating. This relay will also mediate requests to the
// 12 selected active RID's for RID fetching
RouterID fetch_source;
// set of 12 randomly selected RID's from the client's set of routers
std::set<RouterID> rid_sources{};
// logs the RID's that resulted in an error during RID fetching
std::set<RouterID> fail_sources{};
// tracks the number of times each rid appears in the above responses
std::unordered_map<RouterID, int> fetch_counters{};
/** Failure counters:
- fetch_failures: tracks errors fetching RC's from the RC node and requesting RID's
from the 12 RID sources. Errors in the individual RID sets are NOT counted towards
this, their performance as a group is evaluated wholistically
- bootstrap_failures: tracks errors fetching both RC's from bootstrasps and RID requests
they mediate. This is a different counter as we only bootstrap in problematic cases
*/
std::atomic<int> fetch_failures{0}, bootstrap_attempts{0};
std::atomic<bool> _using_bootstrap_fallback{false}, _needs_rebootstrap{false},
_needs_initial_fetch{true}, _fetching_initial{false}, _initial_completed{false};
bool
want_rc(const RouterID& rid) const;
/// asynchronously remove the files for a set of rcs on disk given their public ident key
void
@ -38,99 +187,101 @@ namespace llarp
/// get filename of an RC file given its public ident key
fs::path
get_path_by_pubkey(RouterID pk) const;
std::unordered_map<RouterID, RemoteRC> bootstraps;
// Router lists for snodes
// whitelist = active routers
std::unordered_set<RouterID> router_whitelist;
// greylist = fully funded, but decommissioned routers
std::unordered_set<RouterID> router_greylist;
// greenlist = registered but not fully-staked routers
std::unordered_set<RouterID> router_greenlist;
// all registered relays (snodes)
std::unordered_set<RouterID> registered_routers;
std::unordered_map<RouterID, rc_time> last_rc_update_times;
// Router list for clients
std::unordered_set<RouterID> client_known_routers;
get_path_by_pubkey(const RouterID& pk) const;
// only ever use to specific edges as path first-hops
std::unordered_set<RouterID> pinned_edges;
// rc update info
RouterID rc_fetch_source;
rc_time last_rc_update_relay_timestamp;
static constexpr auto ROUTER_ID_SOURCE_COUNT = 12;
std::unordered_set<RouterID> router_id_fetch_sources;
std::unordered_map<RouterID, std::vector<RouterID>> router_id_fetch_responses;
// process responses once all are received (or failed/timed out)
size_t router_id_response_count{0};
bool router_id_fetch_in_progress{false};
public:
explicit NodeDB(
fs::path rootdir, std::function<void(std::function<void()>)> diskCaller, Router* r);
bool
want_rc(const RouterID& rid) const;
/// in memory nodedb
NodeDB();
public:
void
set_bootstrap_routers(const std::set<RemoteRC>& rcs);
const std::set<RouterID>&
get_known_rids() const
{
return known_rids;
}
const std::unordered_set<RouterID>&
whitelist() const
const std::set<RemoteRC>&
get_known_rcs() const
{
return router_whitelist;
return known_rcs;
}
const std::unordered_set<RouterID>&
greylist() const
std::optional<RemoteRC>
get_rc_by_rid(const RouterID& rid);
bool
is_initial_fetching() const
{
return router_greylist;
return _fetching_initial;
}
const std::unordered_set<RouterID>&
get_registered_routers() const
bool
initial_fetch_completed() const
{
return registered_routers;
return _initial_completed;
}
const std::unordered_map<RouterID, RemoteRC>&
get_rcs() const
bool
needs_initial_fetch() const
{
return known_rcs;
return _needs_initial_fetch;
}
const std::unordered_map<RouterID, rc_time>&
get_last_rc_update_times() const
bool
needs_rebootstrap() const
{
return last_rc_update_times;
return _needs_rebootstrap;
}
// If we receive a bad set of RCs from our current RC source relay, we consider
// that relay to be a bad source of RCs and we randomly choose a new one.
//
// When using a new RC fetch relay, we first re-fetch the full RC list and, if
// that aligns with our RouterID list, we go back to periodic updates from that relay.
//
// This will respect edge-pinning and attempt to use a relay we already have
// a connection with.
void
rotate_rc_source();
ingest_bootstrap_seed();
bool
ingest_fetched_rcs(std::set<RemoteRC> rcs);
bool
process_fetched_rcs(std::set<RemoteRC>& rcs);
void
ingest_rcs(RouterID source, std::vector<RemoteRC> rcs, rc_time timestamp);
ingest_rid_fetch_responses(const RouterID& source, std::set<RouterID> ids = {});
bool
process_fetched_rids();
void
ingest_router_ids(RouterID source, std::vector<RouterID> ids);
fetch_initial(bool is_snode = false);
// RouterContact fetching
void
fetch_rcs();
fetch_rcs(bool initial = false);
void
post_fetch_rcs(bool initial = false);
void
fetch_rcs_result(bool initial = false, bool error = false);
// RouterID fetching
void
fetch_rids(bool initial = false);
void
fetch_router_ids();
post_fetch_rids(bool initial = false);
void
fetch_rids_result(bool initial = false);
// Bootstrap fallback fetching
void
fallback_to_bootstrap();
void
select_router_id_sources(std::unordered_set<RouterID> excluded = {});
post_snode_bootstrap();
void
bootstrap_cooldown();
// Populate rid_sources with random sample from known_rids. A set of rids is passed
// if only specific RID's need to be re-selected; to re-select all, pass the member
// variable ::known_rids
void
reselect_router_id_sources(std::set<RouterID> specific);
void
set_router_whitelist(
@ -160,7 +311,7 @@ namespace llarp
bool
is_path_allowed(const RouterID& remote) const
{
return router_whitelist.count(remote);
return known_rids.count(remote);
}
// if pinned edges were specified, the remote must be in that set, else any remote
@ -168,17 +319,83 @@ namespace llarp
bool
is_first_hop_allowed(const RouterID& remote) const;
const std::unordered_set<RouterID>&
get_pinned_edges() const
std::set<RouterID>&
pinned_edges()
{
return pinned_edges;
return _pinned_edges;
}
explicit NodeDB(
fs::path rootdir, std::function<void(std::function<void()>)> diskCaller, Router* r);
void
store_bootstraps();
/// in memory nodedb
NodeDB();
size_t
num_bootstraps() const
{
return _bootstraps.size();
}
bool
has_bootstraps() const
{
return _bootstraps.empty();
}
const BootstrapList&
bootstrap_list() const
{
return _bootstraps;
}
BootstrapList&
bootstrap_list()
{
return _bootstraps;
}
void
set_bootstrap_routers(BootstrapList& from_router);
const std::set<RouterID>&
whitelist() const
{
return _router_whitelist;
}
const std::set<RouterID>&
greylist() const
{
return _router_greylist;
}
std::set<RouterID>&
registered_routers()
{
return _registered_routers;
}
const std::set<RouterID>&
registered_routers() const
{
return _registered_routers;
}
const std::set<RemoteRC>&
get_rcs() const
{
return known_rcs;
}
// const std::unordered_map<RouterID, RemoteRC>&
// get_rcs() const
// {
// return known_rcs;
// }
const std::unordered_map<RouterID, rc_time>&
get_last_rc_update_times() const
{
return last_rc_update_times;
}
/// load all known_rcs from disk syncrhonously
void
@ -190,7 +407,10 @@ namespace llarp
/// the number of RCs that are loaded from disk
size_t
num_loaded() const;
num_rcs() const;
size_t
num_rids() const;
/// do periodic tasks like flush to disk and expiration
void
@ -206,41 +426,78 @@ namespace llarp
/// return true if we have an rc by its ident pubkey
bool
has_rc(RouterID pk) const;
has_rc(const RouterID& pk) const;
bool
has_rc(const RemoteRC& rc) const;
/// maybe get an rc by its ident pubkey
std::optional<RemoteRC>
get_rc(RouterID pk) const;
get_rc(const RouterID& pk) const;
template <typename Filter>
std::optional<RemoteRC>
GetRandom(Filter visit) const
{
return router.loop()->call_get([visit]() -> std::optional<RemoteRC> {
std::vector<const decltype(known_rcs)::value_type*> known_rcs;
for (const auto& entry : known_rcs)
known_rcs.push_back(entry);
get_random_rc() const;
std::shuffle(known_rcs.begin(), known_rcs.end(), llarp::csrng);
// Get `n` random RCs from all RCs we know about. If `exact` is true then we require n matches
// (and otherwise return nullopt); otherwise we return whatever we found, or nullopt if we find
// nothing at all.
std::optional<std::vector<RemoteRC>>
get_n_random_rcs(size_t n, bool exact = false) const;
for (const auto entry : known_rcs)
{
if (visit(entry->second))
return entry->second;
}
/** The following random conditional functions utilize a simple implementation of reservoir
sampling to return either 1 or n random RC's using only one pass through the set of RC's.
return std::nullopt;
});
Pseudocode:
- begin iterating through the set
- load the first n (or 1) that pass hook(n) into a list Selected[]
- for all that pass the hook, increment i, tracking the number seen thus far
- generate a random integer x from 0 to i
- x < n ? Selected[x] = current : continue;
*/
std::optional<RemoteRC>
get_random_rc_conditional(std::function<bool(RemoteRC)> hook) const;
std::optional<std::vector<RemoteRC>>
get_n_random_rcs_conditional(
size_t n, std::function<bool(RemoteRC)> hook, bool exact = false) const;
// Updates `current` to not contain any of the elements of `replace` and resamples (up to
// `target_size`) from population to refill it.
template <typename T, typename RNG>
void
replace_subset(
std::set<T>& current,
const std::set<T>& replace,
std::set<T> population,
size_t target_size,
RNG&& rng)
{
// Remove the ones we are replacing from current:
current.erase(replace.begin(), replace.end());
// Remove ones we are replacing, and ones we already have, from the population so that we
// won't reselect them:
population.erase(replace.begin(), replace.end());
population.erase(current.begin(), current.end());
if (current.size() < target_size)
std::sample(
population.begin(),
population.end(),
std::inserter(current, current.end()),
target_size - current.size(),
rng);
}
/// visit all known_rcs
template <typename Visit>
void
VisitAll(Visit visit) const
visit_all(Visit visit) const
{
router.loop()->call([this, visit]() {
_router.loop()->call([this, visit]() {
for (const auto& item : known_rcs)
visit(item.second);
visit(item);
});
}
@ -251,26 +508,74 @@ namespace llarp
/// remove an entry given a filter that inspects the rc
template <typename Filter>
void
RemoveIf(Filter visit)
remove_if(Filter visit)
{
router.loop()->call([this, visit]() {
_router.loop()->call([this, visit]() {
std::unordered_set<RouterID> removed;
auto itr = known_rcs.begin();
while (itr != known_rcs.end())
for (auto itr = rc_lookup.begin(); itr != rc_lookup.end();)
{
if (visit(itr->second))
{
removed.insert(itr->second.router_id());
itr = known_rcs.erase(itr);
removed.insert(itr->first);
known_rcs.erase(itr->second);
itr = rc_lookup.erase(itr);
}
else
++itr;
}
if (not removed.empty())
remove_many_from_disk_async(std::move(removed));
});
}
template <
typename ID_t,
std::enable_if_t<std::is_same_v<ID_t, RouterID> || std::is_same_v<ID_t, RemoteRC>, int> = 0>
void
process_results(
std::set<ID_t> unconfirmed, std::set<Unconfirmed<ID_t>>& container, std::set<ID_t>& known)
{
// before we add the unconfirmed set, we check to see if our local set of unconfirmed
// rcs/rids appeared in the latest unconfirmed set; if so, we will increment their number
// of verifications and reset the attempts counter. Once appearing in 3 different requests,
// the rc/rid will be "verified" and promoted to the known_{rcs,rids} container
for (auto itr = container.begin(); itr != container.end();)
{
auto& id = itr->id;
auto& count = const_cast<int&>(itr->attempts);
auto& verifications = const_cast<int&>(itr->verifications);
if (auto found = unconfirmed.find(id); found != unconfirmed.end())
{
if (++verifications >= CONFIRMATION_THRESHOLD)
{
if constexpr (std::is_same_v<ID_t, RemoteRC>)
put_rc_if_newer(id);
else
known.emplace(id);
itr = container.erase(itr);
}
else
{
// reset attempt counter and continue
count = 0;
++itr;
}
unconfirmed.erase(found);
}
itr = (++count >= MAX_CONFIRMATION_ATTEMPTS) ? container.erase(itr) : ++itr;
}
for (auto& id : unconfirmed)
{
container.emplace(std::move(id));
}
}
/// remove rcs that are older than we want to keep. For relays, this is when
/// they become "outdated" (i.e. 12hrs). Clients will hang on to them until
/// they are fully "expired" (i.e. 30 days), as the client may go offline for
@ -280,18 +585,29 @@ namespace llarp
/// put (or replace) the RC if we consider it valid (want_rc). returns true if put.
bool
put_rc(
RemoteRC rc,
rc_time now =
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
put_rc(RemoteRC rc, rc_time now = time_point_now());
/// if we consider it valid (want_rc),
/// put this rc into the cache if it is not there or is newer than the one there already
/// returns true if the rc was inserted
bool
put_rc_if_newer(
RemoteRC rc,
rc_time now =
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
put_rc_if_newer(RemoteRC rc);
void
verify_gossip_bfetch_rc(const RemoteRC& rc);
bool
verify_store_gossip_rc(const RemoteRC& rc);
};
} // namespace llarp
namespace std
{
template <>
struct hash<llarp::Unconfirmed<llarp::RemoteRC>> : public hash<llarp::RemoteRC>
{};
template <>
struct hash<llarp::Unconfirmed<llarp::RouterID>> : hash<llarp::RouterID>
{};
} // namespace std

@ -44,7 +44,7 @@ namespace llarp::path
intro.router = hops[hsz - 1].rc.router_id();
intro.path_id = hops[hsz - 1].txID;
if (auto parent = m_PathSet.lock())
EnterState(ePathBuilding, parent->Now());
EnterState(BUILDING, parent->Now());
}
bool
@ -186,7 +186,7 @@ namespace llarp::path
{
if (Expired(llarp::time_now_ms()))
return false;
return intro.latency > 0s && _status == ePathEstablished;
return intro.latency > 0s && _status == ESTABLISHED;
}
bool
@ -227,12 +227,12 @@ namespace llarp::path
if (now == 0s)
now = router.now();
if (st == ePathFailed)
if (st == FAILED)
{
_status = st;
return;
}
if (st == ePathExpired && _status == ePathBuilding)
if (st == EXPIRED && _status == BUILDING)
{
_status = st;
if (auto parent = m_PathSet.lock())
@ -240,16 +240,16 @@ namespace llarp::path
parent->HandlePathBuildTimeout(shared_from_this());
}
}
else if (st == ePathBuilding)
else if (st == BUILDING)
{
LogInfo("path ", name(), " is building");
buildStarted = now;
}
else if (st == ePathEstablished && _status == ePathBuilding)
else if (st == ESTABLISHED && _status == BUILDING)
{
LogInfo("path ", name(), " is built, took ", ToString(now - buildStarted));
}
else if (st == ePathTimeout && _status == ePathEstablished)
else if (st == TIMEOUT && _status == ESTABLISHED)
{
LogInfo("path ", name(), " died");
_status = st;
@ -258,11 +258,11 @@ namespace llarp::path
parent->HandlePathDied(shared_from_this());
}
}
else if (st == ePathEstablished && _status == ePathTimeout)
else if (st == ESTABLISHED && _status == TIMEOUT)
{
LogInfo("path ", name(), " reanimated");
}
else if (st == ePathIgnore)
else if (st == IGNORE)
{
LogInfo("path ", name(), " ignored");
}
@ -309,22 +309,22 @@ namespace llarp::path
switch (_status)
{
case ePathBuilding:
case BUILDING:
obj["status"] = "building";
break;
case ePathEstablished:
case ESTABLISHED:
obj["status"] = "established";
break;
case ePathTimeout:
case TIMEOUT:
obj["status"] = "timeout";
break;
case ePathExpired:
case EXPIRED:
obj["status"] = "expired";
break;
case ePathFailed:
case FAILED:
obj["status"] = "failed";
break;
case ePathIgnore:
case IGNORE:
obj["status"] = "ignored";
break;
default:
@ -385,7 +385,7 @@ namespace llarp::path
m_RXRate = 0;
m_TXRate = 0;
if (_status == ePathBuilding)
if (_status == BUILDING)
{
if (buildStarted == 0s)
return;
@ -396,13 +396,13 @@ namespace llarp::path
{
LogWarn(name(), " waited for ", ToString(dlt), " and no path was built");
r->router_profiling().MarkPathFail(this);
EnterState(ePathExpired, now);
EnterState(EXPIRED, now);
return;
}
}
}
// check to see if this path is dead
if (_status == ePathEstablished)
if (_status == ESTABLISHED)
{
auto dlt = now - m_LastLatencyTestTime;
if (dlt > path::LATENCY_INTERVAL && m_LastLatencyTestID == 0)
@ -420,13 +420,13 @@ namespace llarp::path
{
LogWarn(name(), " waited for ", ToString(dlt), " and path looks dead");
r->router_profiling().MarkPathFail(this);
EnterState(ePathTimeout, now);
EnterState(TIMEOUT, now);
}
}
if (_status == ePathIgnore and now - m_LastRecvMessage >= path::ALIVE_TIMEOUT)
if (_status == IGNORE and now - m_LastRecvMessage >= path::ALIVE_TIMEOUT)
{
// clean up this path as we dont use it anymore
EnterState(ePathExpired, now);
EnterState(EXPIRED, now);
}
}
@ -436,15 +436,15 @@ namespace llarp::path
bool
Path::Expired(llarp_time_t now) const
{
if (_status == ePathFailed)
if (_status == FAILED)
return true;
if (_status == ePathBuilding)
if (_status == BUILDING)
return false;
if (_status == ePathTimeout)
if (_status == TIMEOUT)
{
return now >= m_LastRecvMessage + PathReanimationTimeout;
}
if (_status == ePathEstablished or _status == ePathIgnore)
if (_status == ESTABLISHED or _status == IGNORE)
{
return now >= ExpireTime();
}

@ -13,19 +13,19 @@ namespace llarp::path
{}
void
PathContext::AllowTransit()
PathContext::allow_transit()
{
m_AllowTransit = true;
}
bool
PathContext::AllowingTransit() const
PathContext::is_transit_allowed() const
{
return m_AllowTransit;
}
bool
PathContext::CheckPathLimitHitByIP(const IpAddress& ip)
PathContext::check_path_limit_hit_by_ip(const IpAddress& ip)
{
#ifdef TESTNET
return false;
@ -39,21 +39,6 @@ namespace llarp::path
#endif
}
bool
PathContext::CheckPathLimitHitByIP(const std::string& ip)
{
#ifdef TESTNET
return false;
#else
IpAddress remote{ip};
// null out the port -- we don't care about it for path limiting purposes
remote.setPort(0);
// try inserting remote address by ip into decaying hash set
// if it cannot insert it has hit a limit
return not path_limits.Insert(remote);
#endif
}
const EventLoop_ptr&
PathContext::loop()
{
@ -102,7 +87,7 @@ namespace llarp::path
}
bool
PathContext::HasTransitHop(const TransitHopInfo& info)
PathContext::has_transit_hop(const TransitHopInfo& info)
{
TransitHopID downstream{info.downstream, info.rxID};
if (transit_hops.count(downstream))
@ -125,7 +110,7 @@ namespace llarp::path
}
Path_ptr
PathContext::GetPath(const PathID_t& path_id)
PathContext::get_path(const PathID_t& path_id)
{
if (auto itr = own_paths.find(path_id); itr != own_paths.end())
return itr->second;
@ -186,7 +171,7 @@ namespace llarp::path
}
void
PathContext::PutTransitHop(std::shared_ptr<TransitHop> hop)
PathContext::put_transit_hop(std::shared_ptr<TransitHop> hop)
{
TransitHopID downstream{hop->info.downstream, hop->info.rxID};
TransitHopID upstream{hop->info.upstream, hop->info.txID};

@ -62,28 +62,25 @@ namespace llarp::path
ExpirePaths(llarp_time_t now);
void
AllowTransit();
allow_transit();
void
RejectTransit();
reject_transit();
bool
CheckPathLimitHitByIP(const IpAddress& ip);
check_path_limit_hit_by_ip(const IpAddress& ip);
bool
CheckPathLimitHitByIP(const std::string& ip);
is_transit_allowed() const;
bool
AllowingTransit() const;
bool
HasTransitHop(const TransitHopInfo& info);
has_transit_hop(const TransitHopInfo& info);
void
PutTransitHop(std::shared_ptr<TransitHop> hop);
put_transit_hop(std::shared_ptr<TransitHop> hop);
Path_ptr
GetPath(const PathID_t& path_id);
get_path(const PathID_t& path_id);
bool
TransitHopPreviousIsRouter(const PathID_t& path, const RouterID& r);
@ -125,7 +122,7 @@ namespace llarp::path
/// current number of paths we created in status
uint64_t
CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ePathEstablished);
CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ESTABLISHED);
Router*
router() const

@ -220,11 +220,10 @@ namespace llarp
{
std::optional<RemoteRC> found = std::nullopt;
router->for_each_connection([&](link::Connection& conn) {
const auto& rc = conn.remote_rc;
const auto& rid = rc.router_id();
RouterID rid{conn.conn->remote_key()};
#ifndef TESTNET
if (router->IsBootstrapNode(rid))
if (router->is_bootstrap_node(rid))
return;
#endif
if (exclude.count(rid))
@ -236,7 +235,7 @@ namespace llarp
if (router->router_profiling().IsBadForPath(rid))
return;
found = rc;
found = router->node_db()->get_rc(rid);
});
return found;
}
@ -244,13 +243,13 @@ namespace llarp
std::optional<std::vector<RemoteRC>>
Builder::GetHopsForBuild()
{
auto filter = [r = router](const auto& rc) -> bool {
auto filter = [r = router](const RemoteRC& rc) -> bool {
return not r->router_profiling().IsBadForPath(rc.router_id(), 1);
};
if (const auto maybe = router->node_db()->GetRandom(filter))
{
if (auto maybe = router->node_db()->get_random_rc_conditional(filter))
return GetHopsAlignedToForBuild(maybe->router_id());
}
return std::nullopt;
}
@ -262,7 +261,7 @@ namespace llarp
const auto now = Now();
for (auto& item : m_Paths)
{
item.second->EnterState(ePathIgnore, now);
item.second->EnterState(IGNORE, now);
}
return true;
}
@ -276,7 +275,7 @@ namespace llarp
bool
Builder::ShouldRemove() const
{
return IsStopped() and NumInStatus(ePathEstablished) == 0;
return IsStopped() and NumInStatus(ESTABLISHED) == 0;
}
bool
@ -359,6 +358,7 @@ namespace llarp
if (r->router_profiling().IsBadForPath(rid, 1))
return false;
for (const auto& hop : hopsSet)
{
if (hop.router_id() == rid)
@ -373,7 +373,7 @@ namespace llarp
return rc.router_id() != endpointRC.router_id();
};
if (const auto maybe = router->node_db()->GetRandom(filter))
if (auto maybe = router->node_db()->get_random_rc_conditional(filter))
hops.emplace_back(*maybe);
else
return std::nullopt;
@ -505,40 +505,40 @@ namespace llarp
// be worth doing sooner rather than later. Leaving some TODOs below where fail
// and success live.
auto response_cb = [path](oxen::quic::message m) {
if (m)
{
// TODO: inform success (what this means needs revisiting, badly)
path->EnterState(path::ESTABLISHED);
return;
}
try
{
if (m)
{
// TODO: inform success (what this means needs revisiting, badly)
path->EnterState(path::ePathEstablished);
return;
}
// TODO: inform failure (what this means needs revisiting, badly)
if (m.timed_out)
{
log::warning(path_cat, "Path build timed out");
log::warning(path_cat, "Path build request timed out!");
path->EnterState(path::TIMEOUT);
}
else
{
oxenc::bt_dict_consumer d{m.body()};
auto status = d.require<std::string_view>(messages::STATUS_KEY);
log::warning(path_cat, "Path build returned failure status: {}", status);
path->EnterState(path::FAILED);
}
}
catch (const std::exception& e)
{
log::warning(path_cat, "Failed parsing path build response.");
log::warning(path_cat, "Exception caught parsing path build response: {}", e.what());
}
// TODO: inform failure (what this means needs revisiting, badly)
path->EnterState(path::ePathFailed);
};
if (not router->send_control_message(
path->upstream(), "path_build", std::move(frames).str(), std::move(response_cb)))
{
log::warning(path_cat, "Error sending path_build control message");
// TODO: inform failure (what this means needs revisiting, badly)
path->EnterState(path::ePathFailed, router->now());
path->EnterState(path::FAILED, router->now());
}
}

@ -13,10 +13,10 @@ namespace llarp::path
PathSet::ShouldBuildMore(llarp_time_t now) const
{
(void)now;
const auto building = NumInStatus(ePathBuilding);
const auto building = NumInStatus(BUILDING);
if (building >= numDesiredPaths)
return false;
const auto established = NumInStatus(ePathEstablished);
const auto established = NumInStatus(ESTABLISHED);
return established < numDesiredPaths;
}
@ -255,7 +255,7 @@ namespace llarp::path
auto itr = m_Paths.begin();
while (itr != m_Paths.end())
{
if (itr->second->Status() == ePathEstablished && itr->second->SupportsAnyRoles(roles))
if (itr->second->Status() == ESTABLISHED && itr->second->SupportsAnyRoles(roles))
++count;
++itr;
}

@ -47,12 +47,12 @@ namespace llarp
/// status of a path
enum PathStatus
{
ePathBuilding,
ePathEstablished,
ePathTimeout,
ePathFailed,
ePathIgnore,
ePathExpired
BUILDING,
ESTABLISHED,
TIMEOUT,
FAILED,
IGNORE,
EXPIRED
};
/// Stats about all our path builds

@ -219,7 +219,7 @@ namespace llarp
// explicit route pokes for first hops
router.for_each_connection(
[this](link::Connection conn) { add_route(conn.remote_rc.addr()); });
[this](link::Connection conn) { add_route(conn.conn->remote()); });
add_route(router.link_manager().local());
// add default route
@ -238,7 +238,7 @@ namespace llarp
{
// unpoke routes for first hops
router.for_each_connection(
[this](link::Connection conn) { delete_route(conn.remote_rc.addr()); });
[this](link::Connection conn) { delete_route(conn.conn->remote()); });
if (is_enabled() and is_up)
{
vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager();

File diff suppressed because it is too large Load Diff

@ -37,41 +37,45 @@
#include <unordered_map>
#include <vector>
/*
TONUKE:
- hidden_service_context
TODO:
- router should hold DHT nodes container? in either a class or a map
-
*/
namespace llarp
{
/// number of routers to publish to
static constexpr size_t INTROSET_RELAY_REDUNDANCY = 2;
inline constexpr size_t INTROSET_RELAY_REDUNDANCY{2};
/// number of dht locations handled per relay
static constexpr size_t INTROSET_REQS_PER_RELAY = 2;
inline constexpr size_t INTROSET_REQS_PER_RELAY{2};
inline constexpr size_t INTROSET_STORAGE_REDUNDANCY{
(INTROSET_RELAY_REDUNDANCY * INTROSET_REQS_PER_RELAY)};
// TESTNET: these constants are shortened for testing purposes
inline constexpr std::chrono::milliseconds TESTNET_GOSSIP_INTERVAL{10min};
inline constexpr std::chrono::milliseconds RC_UPDATE_INTERVAL{5min};
inline constexpr std::chrono::milliseconds INITIAL_ATTEMPT_INTERVAL{30s};
// as we advance towards full mesh, we try to connect to this number per tick
inline constexpr int FULL_MESH_ITERATION{1};
inline constexpr std::chrono::milliseconds ROUTERID_UPDATE_INTERVAL{1h};
static constexpr size_t INTROSET_STORAGE_REDUNDANCY =
(INTROSET_RELAY_REDUNDANCY * INTROSET_REQS_PER_RELAY);
// DISCUSS: ask tom and jason about this
// how big of a time skip before we reset network state
inline constexpr std::chrono::milliseconds NETWORK_RESET_SKIP_INTERVAL{1min};
static const std::chrono::seconds RC_UPDATE_INTERVAL = 5min;
static const std::chrono::seconds ROUTERID_UPDATE_INTERVAL = 1h;
inline constexpr std::chrono::milliseconds REPORT_STATS_INTERVAL{10s};
inline constexpr std::chrono::milliseconds DECOMM_WARNING_INTERVAL{5min};
struct Contacts;
struct Router : std::enable_shared_from_this<Router>
{
friend class NodeDB;
explicit Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform);
~Router();
~Router() = default;
private:
std::shared_ptr<RoutePoker> _route_poker;
/// bootstrap RCs
BootstrapList bootstrap_rc_list;
std::chrono::steady_clock::time_point _next_explore_at;
llarp_time_t last_pump = 0s;
// transient iwp encryption key
@ -95,8 +99,14 @@ namespace llarp
int _outbound_udp_socket = -1;
bool _is_service_node = false;
std::optional<SockAddr> _ourAddress;
oxen::quic::Address _local_addr;
bool _testnet = false;
bool _testing_disabled = false;
bool _bootstrap_seed = false;
consensus::reachability_testing router_testing;
std::optional<oxen::quic::Address> _public_address; // public addr for relays
oxen::quic::Address _listen_address;
EventLoop_ptr _loop;
std::shared_ptr<vpn::Platform> _vpn;
@ -108,7 +118,6 @@ namespace llarp
std::shared_ptr<NodeDB> _node_db;
llarp_time_t _started_at;
const oxenmq::TaggedThreadID _disk_thread;
oxen::quic::Network _net;
llarp_time_t _last_stats_report = 0s;
llarp_time_t _next_decomm_warning = time_now_ms() + 15s;
@ -126,24 +135,15 @@ namespace llarp
oxenmq::address rpc_addr;
Profiling _router_profiling;
fs::path _profile_file;
LinkManager _link_manager{*this};
std::chrono::system_clock::time_point last_rc_gossip{
std::chrono::system_clock::time_point::min()};
std::chrono::system_clock::time_point next_rc_gossip{
std::chrono::system_clock::time_point::min()};
std::chrono::system_clock::time_point last_rc_fetch{
std::chrono::system_clock::time_point::min()};
std::chrono::system_clock::time_point last_routerid_fetch{
std::chrono::system_clock::time_point::min()};
std::unique_ptr<LinkManager> _link_manager;
int client_router_connections;
// should we be sending padded messages every interval?
bool send_padding = false;
service::Context _hidden_service_context;
consensus::reachability_testing router_testing;
bool
should_report_stats(llarp_time_t now) const;
@ -159,20 +159,59 @@ namespace llarp
bool
insufficient_peers() const;
protected:
std::chrono::system_clock::time_point last_rc_gossip{
std::chrono::system_clock::time_point::min()};
std::chrono::system_clock::time_point next_rc_gossip{last_rc_gossip};
std::chrono::system_clock::time_point next_initial_fetch_attempt{last_rc_gossip};
std::chrono::system_clock::time_point last_rc_fetch{last_rc_gossip};
std::chrono::system_clock::time_point last_rid_fetch{last_rc_gossip};
std::chrono::system_clock::time_point next_bootstrap_attempt{last_rc_gossip};
public:
void
for_each_connection(std::function<void(link::Connection&)> func);
bool
testnet() const
{
return _testnet;
}
void
connect_to(const RouterID& rid);
bool
is_bootstrap_seed() const
{
return _bootstrap_seed;
}
int
required_num_client_conns() const
{
return client_router_connections;
}
const RouterID&
local_rid() const
{
return router_contact.router_id();
}
bool
needs_initial_fetch() const;
bool
needs_rebootstrap() const;
void
connect_to(const RemoteRC& rc);
for_each_connection(std::function<void(link::Connection&)> func);
Contacts*
const Contacts&
contacts() const
{
return _contacts.get();
return *_contacts;
}
Contacts&
contacts()
{
return *_contacts;
}
std::shared_ptr<Config>
@ -205,10 +244,16 @@ namespace llarp
LinkManager&
link_manager()
{
return _link_manager;
return *_link_manager;
}
const LinkManager&
link_manager() const
{
return *_link_manager;
}
inline int
int
outbound_udp_socket() const
{
return _outbound_udp_socket;
@ -275,7 +320,7 @@ namespace llarp
}
oxen::quic::Address
public_ip() const;
listen_addr() const;
util::StatusObject
ExtractStatus() const;
@ -283,7 +328,7 @@ namespace llarp
util::StatusObject
ExtractSummaryStatus() const;
const std::unordered_set<RouterID>&
const std::set<RouterID>&
get_whitelist() const;
void
@ -369,10 +414,10 @@ namespace llarp
status_line();
void
InitInboundLinks();
init_inbounds();
void
InitOutboundLinks();
init_outbounds();
std::optional<RouterID>
GetRandomGoodRouter();
@ -466,7 +511,7 @@ namespace llarp
std::string body,
std::function<void(oxen::quic::message m)> func = nullptr);
bool IsBootstrapNode(RouterID) const;
bool is_bootstrap_node(RouterID) const;
/// call internal router ticker
void
@ -478,29 +523,16 @@ namespace llarp
return llarp::time_now_ms();
}
/// parse a routing message in a buffer and handle it with a handler if
/// successful parsing return true on parse and handle success otherwise
/// return false
bool
ParseRoutingMessageBuffer(
const llarp_buffer_t& buf, path::AbstractHopHandler& p, const PathID_t& rxid);
void
ConnectToRandomRouters(int N);
/// count the number of unique service nodes connected via pubkey
size_t
NumberOfConnectedRouters() const;
num_router_connections() const;
/// count the number of unique clients connected by pubkey
size_t
NumberOfConnectedClients() const;
bool
GetRandomConnectedRouter(RemoteRC& result) const;
bool
HasSessionTo(const RouterID& remote) const;
num_client_connections() const;
std::string
ShortName() const;

@ -6,19 +6,48 @@
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
namespace llarp
{
void
RouterContact::bt_load(oxenc::bt_dict_consumer& data)
RouterContact::bt_verify(oxenc::bt_dict_consumer& btdc, bool reject_expired) const
{
if (int rc_ver = data.require<uint8_t>(""); rc_ver != RC_VERSION)
btdc.require_signature("~", [this, reject_expired](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (reject_expired and is_expired(time_now_ms()))
throw std::runtime_error{"Rejecting expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC address!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC signature"};
});
if (not btdc.is_finished())
throw std::runtime_error{"RemoteRC has some fucked up shit at the end"};
btdc.finish();
}
void
RouterContact::bt_load(oxenc::bt_dict_consumer& btdc)
{
if (int rc_ver = btdc.require<uint8_t>(""); rc_ver != RC_VERSION)
throw std::runtime_error{"Invalid RC: do not know how to parse v{} RCs"_format(rc_ver)};
auto ipv4_port = data.require<std::string_view>("4");
auto ipv4_port = btdc.require<std::string_view>("4");
if (ipv4_port.size() != 6)
throw std::runtime_error{
@ -35,7 +64,7 @@ namespace llarp
if (!_addr.is_public())
throw std::runtime_error{"Invalid RC: IPv4 address is not a publicly routable IP"};
if (auto ipv6_port = data.maybe<std::string_view>("6"))
if (auto ipv6_port = btdc.maybe<std::string_view>("6"))
{
if (ipv6_port->size() != 18)
throw std::runtime_error{
@ -56,22 +85,22 @@ namespace llarp
_addr6.reset();
}
auto netid = data.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
auto netid = btdc.maybe<std::string_view>("i").value_or(llarp::LOKINET_DEFAULT_NETID);
if (netid != ACTIVE_NETID)
throw std::runtime_error{
"Invalid RC netid: expected {}, got {}; this is an RC for a different network!"_format(
ACTIVE_NETID, netid)};
auto pk = data.require<std::string_view>("p");
if (pk.size() != RouterID::SIZE)
throw std::runtime_error{"Invalid RC: router id has invalid size {}"_format(pk.size())};
std::memcpy(_router_id.data(), pk.data(), RouterID::SIZE);
auto pubkey = btdc.require<std::string_view>("p");
if (pubkey.size() != 32)
throw std::runtime_error{
"Invalid RC pubkey: expected 32 bytes, got {}"_format(pubkey.size())};
std::memcpy(_router_id.data(), pubkey.data(), 32);
_timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
_timestamp = rc_time{std::chrono::seconds{btdc.require<uint64_t>("t")}};
auto ver = data.require<ustring_view>("v");
auto ver = btdc.require<ustring_view>("v");
if (ver.size() != 3)
throw std::runtime_error{
@ -103,7 +132,7 @@ namespace llarp
{
util::StatusObject obj{
{"lastUpdated", _timestamp.time_since_epoch().count()},
{"publicRouter", is_public_router()},
{"publicRouter", is_public_addressable()},
{"identity", _router_id.ToString()},
{"address", _addr.to_string()}};
@ -122,110 +151,11 @@ namespace llarp
}
bool
RouterContact::BDecode(llarp_buffer_t* buf)
{
// TODO: unfuck all of this
(void)buf;
// clear();
// if (*buf->cur == 'd') // old format
// {
// return DecodeVersion_0(buf);
// }
// else if (*buf->cur != 'l') // if not dict, should be new format and start with list
// {
// return false;
// }
// try
// {
// std::string_view buf_view(reinterpret_cast<char*>(buf->cur), buf->size_left());
// oxenc::bt_list_consumer btlist(buf_view);
// uint64_t outer_version = btlist.consume_integer<uint64_t>();
// if (outer_version == 1)
// {
// bool decode_result = DecodeVersion_1(btlist);
// // advance the llarp_buffer_t since lokimq serialization is unaware of it.
// // FIXME: this is broken (current_buffer got dropped), but the whole thing is getting
// // replaced.
// // buf->cur += btlist.
// // current_buffer().data() - buf_view.data() + 1;
// return decode_result;
// }
// else
// {
// log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
// return false;
// }
// }
// catch (const std::exception& e)
// {
// log::debug(logcat, "RouterContact::BDecode failed: {}", e.what());
// }
return false;
}
bool
RouterContact::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
{
bool read = false;
(void)key;
// TOFIX: fuck everything about llarp_buffer_t
// if (!BEncodeMaybeReadDictEntry("a", addr, read, key, buf))
// return false;
// if (!BEncodeMaybeReadDictEntry("i", netID, read, key, buf))
// return false;
// if (!BEncodeMaybeReadDictEntry("k", _router_id, read, key, buf))
// return false;
// if (key.startswith("r"))
// {
// RouterVersion r;
// if (not r.BDecode(buf))
// return false;
// routerVersion = r;
// return true;
// }
// if (not BEncodeMaybeReadDictList("s", srvRecords, read, key, buf))
// return false;
// if (!BEncodeMaybeReadDictEntry("p", enckey, read, key, buf))
// return false;
// if (!BEncodeMaybeReadDictInt("u", _timestamp, read, key, buf))
// return false;
// if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
// return false;
// if (key.startswith("x") and serializeExit)
// {
// return bencode_discard(buf);
// }
// if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
// return false;
return read or bencode_discard(buf);
}
bool
RouterContact::is_public_router() const
RouterContact::is_public_addressable() const
{
if (_router_version.empty())
return false;
return _addr.is_addressable();
}
@ -255,7 +185,7 @@ namespace llarp
return time_to_expiry(now) <= dlt;
}
static constexpr std::array obsolete_bootstraps = {
static const std::set<std::string_view> obsolete_bootstraps{
"7a16ac0b85290bcf69b2f3b52456d7e989ac8913b4afbb980614e249a3723218"sv,
"e6b3a6fe5e32c379b64212c72232d65b0b88ddf9bbaed4997409d329f8519e0b"sv,
};
@ -270,4 +200,12 @@ namespace llarp
}
return false;
}
bool
RouterContact::is_obsolete(const RouterContact& rc)
{
const auto& hex = rc._router_id.ToHex();
return obsolete_bootstraps.count(hex);
}
} // namespace llarp

@ -9,6 +9,7 @@
#include <llarp/util/aligned.hpp>
#include <llarp/util/bencode.hpp>
#include <llarp/util/status.hpp>
#include <llarp/util/time.hpp>
#include <nlohmann/json.hpp>
#include <oxenc/bt_producer.h>
@ -21,8 +22,6 @@ namespace llarp
{
static auto logcat = log::Cat("RC");
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
static inline constexpr size_t NETID_SIZE{8};
/// On the wire we encode the data as a dict containing:
@ -34,7 +33,7 @@ namespace llarp
/// "6" -- optional 18 byte IPv6 address & port: 16 byte raw IPv6 address followed by 2 bytes
/// of port in network order.
/// "i" -- optional network ID string of up to 8 bytes; this is omitted for the default network
/// ID ("lokinet") but included for others (such as "gamma" for testnet).
/// ID ("lokinet") but included for others (such as "testnet" for testnet).
/// "p" -- 32-byte router pubkey
/// "t" -- timestamp when this RC record was created (which also implicitly determines when it
/// goes stale and when it expires).
@ -54,8 +53,6 @@ namespace llarp
static inline constexpr size_t MAX_RC_SIZE = 1024;
/// Timespans for RCs:
/// How long (from its signing time) before an RC is considered "stale". Relays republish
/// their RCs slightly more frequently than this so that ideally this won't happen.
static constexpr auto STALE_AGE = 6h;
@ -171,13 +168,7 @@ namespace llarp
{}
bool
BDecode(llarp_buffer_t* buf);
bool
decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf);
bool
is_public_router() const;
is_public_addressable() const;
/// does this RC expire soon? default delta is 1 minute
bool
@ -204,10 +195,18 @@ namespace llarp
bool
is_obsolete_bootstrap() const;
static bool
is_obsolete(const RouterContact& rc);
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
void
bt_load(oxenc::bt_dict_consumer& data);
};
struct RemoteRC;
/// Extension of RouterContact used to store a local "RC," and inserts a RouterContact by
/// re-parsing and sending it out. This sub-class contains a pubkey and all the other attributes
/// required for signing and serialization
@ -234,9 +233,11 @@ namespace llarp
public:
LocalRC() = default;
explicit LocalRC(std::string payload, const SecretKey sk);
~LocalRC() = default;
RemoteRC
to_remote();
void
resign();
@ -298,8 +299,7 @@ namespace llarp
void
set_systime_timestamp()
{
set_timestamp(
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
set_timestamp(time_point_now());
}
};
@ -307,15 +307,13 @@ namespace llarp
/// the data in the constructor, eliminating the need for a ::verify method/
struct RemoteRC final : public RouterContact
{
private:
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
public:
RemoteRC() = default;
RemoteRC(std::string_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
{}
RemoteRC(ustring_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
explicit RemoteRC(std::string_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
{
_payload = {reinterpret_cast<const unsigned char*>(data.data()), data.size()};
}
explicit RemoteRC(ustring_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
{
_payload = data;
}
@ -360,10 +358,18 @@ namespace std
template <>
struct hash<llarp::RouterContact>
{
size_t
virtual size_t
operator()(const llarp::RouterContact& r) const
{
return std::hash<llarp::PubKey>{}(r.router_id());
}
};
template <>
struct hash<llarp::RemoteRC> : public hash<llarp::RouterContact>
{};
template <>
struct hash<llarp::LocalRC> final : public hash<llarp::RouterContact>
{};
} // namespace std

@ -22,45 +22,16 @@ namespace llarp
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
_addr = std::move(local);
_addr6.emplace(&_addr.in6());
if (_addr.is_ipv6())
_addr6.emplace(&_addr.in6());
resign();
}
LocalRC::LocalRC(std::string payload, const SecretKey sk) : _secret_key{std::move(sk)}
RemoteRC
LocalRC::to_remote()
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
try
{
oxenc::bt_dict_consumer btdc{payload};
bt_load(btdc);
btdc.require_signature("~", [this](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()))
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse LocalRC: {}", e.what());
throw;
}
resign();
return RemoteRC{view()};
}
void
@ -78,7 +49,7 @@ namespace llarp
return sig;
});
_payload = btdp.view<unsigned char>();
_payload = ustring{btdp.view<unsigned char>()};
}
void
@ -123,8 +94,6 @@ namespace llarp
static_assert(llarp::LOKINET_VERSION.size() == 3);
btdp.append(
"v", std::string_view{reinterpret_cast<const char*>(llarp::LOKINET_VERSION.data()), 3});
bt_sign(btdp);
}
void

@ -16,79 +16,33 @@ namespace llarp
try
{
bt_load(btdc);
btdc.require_signature("~", [this](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (is_expired(time_now_ms()))
throw std::runtime_error{"Unable to verify expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
bt_verify(btdc, /*reject_expired=*/true);
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to parse RemoteRC: {}", e.what());
throw;
auto err = "Exception caught parsing RemoteRC: {}"_format(e.what());
log::warning(logcat, err);
throw std::runtime_error{err};
}
}
void
RemoteRC::bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired) const
{
data.require_signature("~", [this, reject_expired](ustring_view msg, ustring_view sig) {
if (sig.size() != 64)
throw std::runtime_error{"Invalid signature: not 64 bytes"};
if (reject_expired and is_expired(time_now_ms()))
throw std::runtime_error{"Rejecting expired RemoteRC!"};
// TODO: revisit if this is needed; detail from previous implementation
const auto* net = net::Platform::Default_ptr();
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
log::info(logcat, err);
throw std::runtime_error{err};
}
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
});
}
bool
RemoteRC::read(const fs::path& fname)
{
ustring buf;
buf.reserve(MAX_RC_SIZE);
_payload.resize(MAX_RC_SIZE);
try
{
util::file_to_buffer(fname, buf.data(), MAX_RC_SIZE);
auto nread = util::file_to_buffer(fname, _payload.data(), _payload.size());
_payload.resize(nread);
oxenc::bt_dict_consumer btdc{buf};
oxenc::bt_dict_consumer btdc{_payload};
bt_load(btdc);
bt_verify(btdc);
_payload = buf;
}
catch (const std::exception& e)
{
log::error(logcat, "Failed to read or validate RC from {}: {}", fname, e.what());
log::warning(logcat, "Failed to read or validate RC from {}: {}", fname, e.what());
return false;
}

@ -29,7 +29,7 @@ namespace llarp
}
bool
RouterID::FromString(std::string_view str)
RouterID::from_string(std::string_view str)
{
auto pos = str.find(SNODE_TLD);
if (pos != str.size() - SNODE_TLD.size())

@ -21,6 +21,12 @@ namespace llarp
RouterID(const Data& data) : PubKey(data)
{}
RouterID(ustring_view data) : PubKey(data.data())
{}
RouterID(std::string_view data) : RouterID(to_usv(data))
{}
util::StatusObject
ExtractStatus() const;
@ -30,8 +36,11 @@ namespace llarp
std::string
ShortString() const;
// FIXME: this is deceptively named: it parses something base32z formatted with .snode on the
// end, so should probably be called "from_snode_address" or "from_base32z" or something that
// doesn't sound exactly like the other (different) from_strings of its base classes.
bool
FromString(std::string_view str);
from_string(std::string_view str);
RouterID&
operator=(const byte_t* ptr)
@ -49,7 +58,6 @@ namespace llarp
template <>
constexpr inline bool IsToStringFormattable<RouterID> = true;
} // namespace llarp
namespace std

@ -97,7 +97,7 @@ namespace llarp::rpc
return; // bail
}
LogDebug("new block at height ", m_BlockHeight);
log::trace(logcat, "new block at height {}", m_BlockHeight);
// don't upadate on block notification if an update is pending
if (not m_UpdatingList)
UpdateServiceNodeList();
@ -138,7 +138,7 @@ namespace llarp::rpc
throw std::runtime_error{"get_service_nodes did not return 'OK' status"};
if (auto it = json.find("unchanged");
it != json.end() and it->is_boolean() and it->get<bool>())
LogDebug("service node list unchanged");
log::trace(logcat, "service node list unchanged");
else
{
self->HandleNewServiceNodeList(json.at("service_node_states"));
@ -269,6 +269,7 @@ namespace llarp::rpc
keymap = std::move(keymap),
router = std::move(router)]() mutable {
m_KeyMap = std::move(keymap);
router->set_router_whitelist(active, decomm, unfunded);
});
}

@ -76,7 +76,7 @@ namespace llarp::rpc
{
if (r.is_service_node())
{
return r.exitContext().GetExitEndpoint(name);
return r.exitContext().get_exit_endpoint(name);
}
return r.hidden_service_context().GetEndpointByName(name);
@ -322,14 +322,14 @@ namespace llarp::rpc
return;
}
if (not routerID.FromString(lookupsnode.request.routerID))
if (not routerID.from_string(lookupsnode.request.routerID))
{
SetJSONError("Invalid remote: " + lookupsnode.request.routerID, lookupsnode.response);
return;
}
m_Router.loop()->call([&]() {
auto endpoint = m_Router.exitContext().GetExitEndpoint("default");
auto endpoint = m_Router.exitContext().get_exit_endpoint("default");
if (endpoint == nullptr)
{

@ -79,7 +79,7 @@ namespace llarp::service
{
RouterID router{};
service::Address addr{};
if (router.FromString(lokinet_addr))
if (router.from_string(lokinet_addr))
return router;
if (addr.FromString(lokinet_addr))
return addr;

@ -299,7 +299,7 @@ namespace llarp::service
// expire convotags
EndpointUtil::ExpireConvoSessions(now, Sessions());
if (NumInStatus(path::ePathEstablished) > 1)
if (NumInStatus(path::ESTABLISHED) > 1)
{
for (const auto& item : _startup_ons_mappings)
{
@ -516,7 +516,7 @@ namespace llarp::service
const auto& keyfile = _state->key_file;
if (!keyfile.empty())
{
_identity.EnsureKeys(keyfile, router()->key_manager()->needBackup());
_identity.EnsureKeys(keyfile, router()->key_manager()->needs_backup());
}
else
{
@ -697,14 +697,16 @@ namespace llarp::service
{
std::unordered_set<RouterID> exclude;
ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); });
const auto maybe =
router()->node_db()->GetRandom([exclude, r = router()](const RemoteRC& rc) -> bool {
const auto& rid = rc.router_id();
return exclude.count(rid) == 0 and not r->router_profiling().IsBadForPath(rid);
});
if (not maybe.has_value())
return std::nullopt;
return GetHopsForBuildWithEndpoint(maybe->router_id());
auto hook = [exclude, r = router()](const RemoteRC& rc) -> bool {
const auto& rid = rc.router_id();
return not(exclude.count(rid) || r->router_profiling().IsBadForPath(rid));
};
if (auto maybe = router()->node_db()->get_random_rc_conditional(hook))
return GetHopsForBuildWithEndpoint(maybe->router_id());
return std::nullopt;
}
std::optional<std::vector<RemoteRC>>
@ -1308,7 +1310,7 @@ namespace llarp::service
// TODO: if all requests fail, call callback with failure?
for (const auto& path : paths)
{
path->find_intro(location, false, 0, [this, hook, got_it](std::string resp) mutable {
path->find_intro(location, false, 0, [hook, got_it, this](std::string resp) mutable {
// asking many, use only first successful
if (*got_it)
return;
@ -1333,7 +1335,7 @@ namespace llarp::service
}
service::EncryptedIntroSet enc{introset};
router()->contacts()->services()->PutNode(std::move(enc));
router()->contacts().put_intro(std::move(enc));
// TODO: finish this
/*
@ -1396,7 +1398,7 @@ namespace llarp::service
while (not _inbound_queue.empty())
{
// succ it out
// suck it out
queue.emplace(std::move(*_inbound_queue.popFront()));
}
@ -1537,7 +1539,7 @@ namespace llarp::service
if (BuildCooldownHit(now))
return false;
const auto requiredPaths = std::max(numDesiredPaths, path::MIN_INTRO_PATHS);
if (NumInStatus(path::ePathBuilding) >= requiredPaths)
if (NumInStatus(path::BUILDING) >= requiredPaths)
return false;
return NumPathsExistingAt(now + (path::DEFAULT_LIFETIME - path::INTRO_PATH_SPREAD))
< requiredPaths;

@ -178,7 +178,7 @@ namespace llarp
EgresPacketRouter()
{
return nullptr;
};
}
virtual vpn::NetworkInterface*
GetVPNInterface()

@ -97,7 +97,7 @@ namespace llarp::service
if (exists and needBackup)
{
KeyManager::backupFileByMoving(fname);
KeyManager::copy_backup_keyfile(fname);
exists = false;
}

@ -39,7 +39,7 @@ namespace llarp::service
{
oxenc::bt_dict_consumer btdc{std::move(buf)};
router.FromString(btdc.require<std::string>("k"));
router.from_string(btdc.require<std::string>("k"));
latency = std::chrono::milliseconds{btdc.require<uint64_t>("l")};
path_id.from_string(btdc.require<std::string>("p"));
expiry = std::chrono::milliseconds{btdc.require<uint64_t>("x")};

@ -143,7 +143,7 @@ namespace llarp::service
{
// ignore new path if we are marked dead
LogInfo(Name(), " marked bad, ignoring new path");
p->EnterState(path::ePathIgnore, Now());
p->EnterState(path::IGNORE, Now());
}
else if (p->Endpoint() == next_intro.router)
{
@ -375,7 +375,7 @@ namespace llarp::service
if (marked_bad or path::Builder::BuildCooldownHit(now))
return false;
if (NumInStatus(path::ePathBuilding) >= std::max(numDesiredPaths / size_t{2}, size_t{1}))
if (NumInStatus(path::BUILDING) >= std::max(numDesiredPaths / size_t{2}, size_t{1}))
return false;
size_t numValidPaths = 0;

@ -24,6 +24,11 @@ namespace llarp
using bstring = std::basic_string<std::byte>;
using bstring_view = std::basic_string_view<std::byte>;
inline ustring operator""_us(const char* str, size_t len) noexcept
{
return {reinterpret_cast<const unsigned char*>(str), len};
}
// Helper function to switch between string_view and ustring_view
inline ustring_view
to_usv(std::string_view v)

@ -25,7 +25,7 @@ namespace llarp::util
template <
typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
inline size_t
size_t
file_to_buffer(const fs::path& filename, Char* buffer, size_t buffer_size)
{
return file_to_buffer(filename, reinterpret_cast<char*>(buffer), buffer_size);
@ -38,7 +38,7 @@ namespace llarp::util
/// Same as above, but works via char-like buffer
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
inline void
void
buffer_to_file(const fs::path& filename, const Char* buffer, size_t buffer_size)
{
return buffer_to_file(
@ -73,31 +73,4 @@ namespace llarp::util
return std::make_optional<T>(pathname, mode);
}
template <typename PathVisitor>
static void
IterDir(const fs::path& path, PathVisitor visit)
{
DIR* d = opendir(path.string().c_str());
if (d == nullptr)
return;
struct dirent* ent = nullptr;
std::set<fs::path> entries;
do
{
ent = readdir(d);
if (not ent)
break;
if (ent->d_name[0] == '.')
continue;
entries.emplace(path / fs::path{ent->d_name});
} while (ent);
closedir(d);
for (const auto& p : entries)
{
if (not visit(p))
return;
}
}
} // namespace llarp::util

@ -34,6 +34,12 @@ namespace llarp
std::chrono::steady_clock::now() - started_at_steady);
}
rc_time
time_point_now()
{
return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
}
Duration_t
time_now_ms()
{

@ -12,6 +12,11 @@ using namespace std::chrono_literals;
namespace llarp
{
using rc_time = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
rc_time
time_point_now();
/// get time right now as milliseconds, this is monotonic
Duration_t
time_now_ms();

Loading…
Cancel
Save