This commit is contained in:
dr7ana 2023-12-12 19:57:35 +00:00 committed by GitHub
commit 4e28bca984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 2742 additions and 1714 deletions

2
.gitignore vendored
View File

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

View File

@ -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)

View File

@ -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()};

View File

@ -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;
}
@ -454,7 +454,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 +464,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 +487,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 +548,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 +563,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 +588,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 +603,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 3ced484e8cc543b90c5fc554ccc0ea2e54ec8d37
Subproject commit 3ace46701449c4c01f74aae2e0be3b4164768911

View File

@ -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()

View File

@ -14,7 +14,7 @@ namespace llarp
oxenc::bt_list_consumer btlc{buf};
while (not btlc.is_finished())
emplace(btlc.consume_dict_consumer());
emplace(btlc.consume_dict_data());
}
catch (...)
{
@ -25,6 +25,24 @@ namespace llarp
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
{
@ -59,6 +77,7 @@ namespace llarp
else
{
RemoteRC rc;
if (not rc.read(fpath))
{
throw std::runtime_error{
@ -66,5 +85,7 @@ namespace llarp
}
insert(rc);
}
_curr = begin();
}
} // namespace llarp

View File

@ -2,6 +2,7 @@
#include "router_contact.hpp"
#include <llarp/crypto/crypto.hpp>
#include <llarp/util/fs.hpp>
#include <set>
@ -11,6 +12,14 @@ 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);
@ -20,6 +29,35 @@ namespace llarp
void
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()
{

View File

@ -19,15 +19,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 +52,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 +63,44 @@ namespace llarp
net_id = std::move(arg);
});
int minConnections =
(params.is_relay ? DefaultMinConnectionsForRouter : DefaultMinConnectionsForClient);
conf.define_option<int>(
"router",
"relay-connections",
Default{CLIENT_ROUTER_CONNECTIONS},
ClientOnly,
Comment{
"Minimum number of routers lokinet client will attempt to maintain connections to.",
},
[=](int arg) {
if (arg < CLIENT_ROUTER_CONNECTIONS)
throw std::invalid_argument{
fmt::format("Client relay connections must be >= {}", CLIENT_ROUTER_CONNECTIONS)};
client_router_connections = arg;
});
conf.define_option<int>(
"router",
"min-connections",
Default{minConnections},
Deprecated,
Comment{
"Minimum number of routers lokinet will attempt to maintain connections to.",
},
[=](int arg) {
if (arg < minConnections)
throw std::invalid_argument{
fmt::format("min-connections must be >= {}", minConnections)};
min_connected_routers = arg;
[=](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 +132,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 +140,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 +274,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 +694,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,26 +897,38 @@ 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;
@ -965,14 +961,10 @@ namespace llarp
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};
maybe = net_ptr->get_best_public_address(true, DEFAULT_LISTEN_PORT);
else if (maybe && maybe->port() == 0)
maybe->set_port(DEFAULT_LISTEN_PORT);
return maybe;
};
@ -980,10 +972,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 +996,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 +1011,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 +1045,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,16 +1068,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)};
}
throw std::runtime_error{fmt::format(
"Invalid address: {}; stop using this deprecated handler, update your config to use "
"[bind]:listen instead PLEASE",
temp)};
listen_addr = std::move(temp);
using_user_value = true;
});
}
@ -1221,17 +1153,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 +1181,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);

View File

@ -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,10 +170,14 @@ namespace llarp
struct LinksConfig
{
std::optional<net::ipaddr_t> public_addr;
std::optional<net::port_t> public_port;
// DEPRECATED -- use [Router]:public_addr
std::optional<std::string> public_addr;
// DEPRECATED -- use [Router]:public_port
std::optional<uint16_t> public_port;
std::optional<oxen::quic::Address> addr;
std::optional<oxen::quic::Address> listen_addr;
bool using_user_value = false;
bool using_new_api = false;
void
@ -202,6 +206,7 @@ namespace llarp
{
fs::path id_keyfile;
oxenmq::address rpc_addr;
bool disable_testing = true;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);

View File

@ -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;
}
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;
}
}
}
if (not config.router.is_relay)
// load encryption key
auto enckey_gen = [](llarp::SecretKey& key) { llarp::crypto::encryption_keygen(key); };
if (not keygen(enckey_path, encryption_key, enckey_gen))
{
// 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::critical(
logcat, "KeyManager::keygen failed to generate encryption key line:{}", __LINE__);
return false;
}
// load encryption key
auto encryptionKeygen = [](llarp::SecretKey& key) { llarp::crypto::encryption_keygen(key); };
if (not loadOrCreateKey(m_encKeyPath, encryptionKey, encryptionKeygen))
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))
return false;
m_initialized = true;
if (not keygen(transkey_path, transport_key, transkey_gen))
{
log::critical(
logcat, "KeyManager::keygen failed to generate transport key line:{}", __LINE__);
return false;
}
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))

View File

@ -29,7 +29,7 @@ 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();
@ -52,37 +52,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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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()});

View File

@ -3,10 +3,9 @@
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)}
const std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s)
: conn{c}, control_stream{s}/* , remote_rc{std::move(rc)} */
{}
} // namespace llarp::link

View File

@ -11,16 +11,15 @@ namespace llarp::link
{
std::shared_ptr<oxen::quic::connection_interface> conn;
std::shared_ptr<oxen::quic::BTRequestStream> control_stream;
RemoteRC remote_rc;
// std::optional<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};
Connection(
std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s,
const RemoteRC& rc);
const std::shared_ptr<oxen::quic::connection_interface>& c,
std::shared_ptr<oxen::quic::BTRequestStream>& s);
};
} // namespace llarp::link

View File

@ -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;
});
}
if (auto itr = introsets.find(key); itr != introsets.end())
enc = itr->second.introset;
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 (_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)
Contacts::put_intro(service::EncryptedIntroSet enc)
{
_router.loop()->call([this, val]() { _rc_nodes->PutNode(val); });
}
void
Contacts::delete_rc_node_async(const dht::Key_t& val)
{
_router.loop()->call([this, val]() { _rc_nodes->DelNode(val); });
_introset_nodes->PutNode(std::move(enc));
}
} // namespace llarp

View File

@ -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

View File

@ -28,6 +28,11 @@ 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;
namespace link
{
struct Connection;
@ -56,9 +61,6 @@ namespace llarp
bool
have_conn(const RouterID& remote, bool client_only) const;
bool
deregister_peer(RouterID remote);
size_t
num_connected(bool clients_only) const;
@ -138,7 +140,8 @@ namespace llarp
struct LinkManager
{
public:
explicit LinkManager(Router& r);
static std::unique_ptr<LinkManager>
make(Router& r);
bool
send_control_message(
@ -157,6 +160,8 @@ namespace llarp
}
private:
explicit LinkManager(Router& r);
bool
send_control_message_impl(
const RouterID& remote,
@ -173,6 +178,12 @@ namespace llarp
// holds any messages we attempt to send while connections are establishing
std::unordered_map<RouterID, MessageQueue> pending_conn_msg_queue;
// when establishing a connection, the rid of the remote is placed here to be cross-
// checked by the tls verification callback
std::map<RouterID, RemoteRC> rids_pending_verification;
// in the interim of verifying an outbound connection and the creation of its link::Connection
// object, we store the rid and rc here
std::map<RouterID, RemoteRC> verified_rids;
util::DecayingHashSet<RouterID> clients{path::DEFAULT_LIFETIME};
@ -195,6 +206,9 @@ namespace llarp
void
recv_control_message(oxen::quic::message msg);
void
on_inbound_conn(oxen::quic::connection_interface& ci);
void
on_conn_open(oxen::quic::connection_interface& ci);
@ -227,17 +241,30 @@ namespace llarp
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;
@ -245,13 +272,13 @@ namespace llarp
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 RouterID& router, conn_open_hook = nullptr);
void
connect_to(const RemoteRC& rc);
connect_to(const RemoteRC& rc, conn_open_hook = nullptr, conn_closed_hook = nullptr);
void
close_connection(RouterID rid);
@ -291,11 +318,8 @@ namespace llarp
void
connect_to_random(int num_conns);
// 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 +354,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)>
@ -389,6 +404,8 @@ namespace llarp
{
try
{
log::critical(logcat, "Establishing connection to {}", remote);
auto conn_interface =
endpoint->connect(remote, link_manager.tls_creds, std::forward<Opt>(opts)...);
@ -396,9 +413,19 @@ namespace llarp
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);
log::critical(logcat, "Establishing connection to {}...", rc.router_id());
auto control_stream = conn_interface->template get_new_stream<oxen::quic::BTRequestStream>(
[](oxen::quic::Stream& s, uint64_t error_code) {
log::warning(
logcat,
"BTRequestStream closed unexpectedly (ec:{}); closing connection...",
error_code);
s.conn.close_connection(error_code);
});
link_manager.register_commands(control_stream);
itr->second = std::make_shared<link::Connection>(conn_interface, control_stream);
return true;
}

View File

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

View File

@ -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)
{

87
llarp/messages/fetch.hpp Normal file
View File

@ -0,0 +1,87 @@
#pragma once
#include "common.hpp"
#include <llarp/util/logging/buffer.hpp>
namespace llarp
{
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(const LocalRC& local_rc, size_t quantity)
{
oxenc::bt_dict_producer btdp;
btdp.append_encoded("local", oxen::quic::to_sv(local_rc.view()));
log::critical(logcat, "Serializing localRC: {}", oxenc::to_hex(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)
{
// 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 FetchRIDMessage
} // namespace llarp

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}
});

View File

@ -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

View File

@ -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,160 @@ namespace llarp
{
struct Router;
/* RC Fetch Constants */
// 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 */
inline constexpr size_t MIN_ACTIVE_RIDS{24};
// the number of rid sources that we make rid fetch requests to
inline constexpr size_t RID_SOURCE_COUNT{12};
// upper limit on how many rid fetch requests to rid sources can fail
inline constexpr size_t MAX_RID_ERRORS{4};
// 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
inline constexpr size_t 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
{
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;
}
operator bool() const
{
return verifications == CONFIRMATION_THRESHOLD;
}
bool
operator==(const Unconfirmed& other) const
{
return id == other.id;
}
bool
operator<(const Unconfirmed& other) const
{
return id < other.id;
}
};
class NodeDB
{
std::unordered_map<RouterID, RemoteRC> known_rcs;
Router& _router;
const fs::path _root;
const std::function<void(std::function<void()>)> _disk;
Router& router;
const fs::path m_Root;
const std::function<void(std::function<void()>)> disk;
llarp_time_t _next_flush_time;
llarp_time_t m_NextFlushAt;
/******** 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;
std::set<RemoteRC> _bootstrap_seeds;
std::set<RouterID> _seeds;
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}, _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
@ -40,97 +185,91 @@ namespace llarp
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;
// 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};
bool
want_rc(const RouterID& rid) const;
public:
void
set_bootstrap_routers(const std::set<RemoteRC>& rcs);
explicit NodeDB(
fs::path rootdir, std::function<void(std::function<void()>)> diskCaller, Router* r);
const std::unordered_set<RouterID>&
whitelist() const
/// in memory nodedb
NodeDB();
std::set<RouterID>&
seeds()
{
return router_whitelist;
return _seeds;
}
const std::unordered_set<RouterID>&
greylist() const
const std::set<RouterID>&
get_known_rids() const
{
return router_greylist;
return known_rids;
}
const std::unordered_set<RouterID>&
get_registered_routers() const
{
return registered_routers;
}
const std::unordered_map<RouterID, RemoteRC>&
get_rcs() const
const std::set<RemoteRC>&
get_known_rcs() const
{
return known_rcs;
}
const std::unordered_map<RouterID, rc_time>&
get_last_rc_update_times() const
std::optional<RemoteRC>
get_rc_by_rid(const RouterID& rid);
bool
needs_initial_fetch() const
{
return last_rc_update_times;
return _needs_initial_fetch;
}
// 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();
bool
needs_rebootstrap() const
{
return _needs_rebootstrap;
}
void
ingest_rcs(RouterID source, std::vector<RemoteRC> rcs, rc_time timestamp);
ingest_bootstrap_seed();
bool
ingest_fetched_rcs(std::set<RemoteRC> rcs, rc_time timestamp);
bool
process_fetched_rcs(std::set<RemoteRC>& rcs);
void
ingest_router_ids(RouterID source, std::vector<RouterID> ids);
ingest_rid_fetch_responses(const RouterID& source, std::set<RouterID> ids = {});
bool
process_fetched_rids();
void
fetch_rcs();
fetch_initial();
// RouterContact fetching
void
fetch_router_ids();
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
select_router_id_sources(std::unordered_set<RouterID> excluded = {});
fetch_rids(bool initial = false);
void
post_fetch_rids(bool initial = false);
void
fetch_rids_result(bool initial = false);
// Bootstrap fallback fetching
void
fallback_to_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 +299,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 +307,89 @@ 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;
}
const std::set<RemoteRC>&
bootstrap_seeds() const
{
return _bootstrap_seeds;
}
std::set<RemoteRC>&
bootstrap_seeds()
{
return _bootstrap_seeds;
}
void
set_bootstrap_routers(BootstrapList& from_router);
const std::set<RouterID>&
whitelist() const
{
return known_rids;
}
const std::set<RouterID>&
greylist() const
{
return router_greylist;
}
const std::set<RouterID>&
get_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 +401,7 @@ namespace llarp
/// the number of RCs that are loaded from disk
size_t
num_loaded() const;
num_rcs() const;
/// do periodic tasks like flush to disk and expiration
void
@ -212,25 +423,55 @@ namespace llarp
std::optional<RemoteRC>
get_rc(RouterID pk) const;
template <typename Filter>
std::optional<RemoteRC>
GetRandom(Filter visit) const
get_random_rc() const;
std::optional<std::vector<RemoteRC>>
get_n_random_rcs(size_t n) const;
/** 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.
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) 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)
{
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);
// Remove the ones we are replacing from current:
current.erase(replace.begin(), replace.end());
std::shuffle(known_rcs.begin(), known_rcs.end(), llarp::csrng);
// 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());
for (const auto entry : known_rcs)
{
if (visit(entry->second))
return entry->second;
}
return std::nullopt;
});
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
@ -238,9 +479,9 @@ namespace llarp
void
VisitAll(Visit visit) const
{
router.loop()->call([this, visit]() {
_router.loop()->call([this, visit]() {
for (const auto& item : known_rcs)
visit(item.second);
visit(item);
});
}
@ -253,24 +494,72 @@ namespace llarp
void
RemoveIf(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 +569,23 @@ 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, rc_time now = time_point_now());
};
} // 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

View File

@ -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};

View File

@ -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);

View File

@ -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;
}
@ -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;

View File

@ -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();

View File

@ -45,7 +45,7 @@ namespace llarp
, _disk_thread{_lmq->add_tagged_thread("disk")}
, _rpc_server{nullptr}
, _randomStartDelay{platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000} : 0s}
, _link_manager{*this}
// , _link_manager{*this}
, _hidden_service_context{this}
{
_key_manager = std::make_shared<KeyManager>();
@ -59,9 +59,7 @@ namespace llarp
}
Router::~Router()
{
_contacts.reset();
}
{}
// TODO: investigate changes needed for libquic integration
// still needed at all?
@ -78,7 +76,6 @@ namespace llarp
llarp::LogTrace("Router::PumpLL() end");
}
// TOFIX: this
util::StatusObject
Router::ExtractStatus() const
{
@ -87,12 +84,10 @@ namespace llarp
return util::StatusObject{
{"running", true},
{"numNodesKnown", _node_db->num_loaded()},
{"contacts", _contacts->ExtractStatus()},
{"numNodesKnown", _node_db->num_rcs()},
{"services", _hidden_service_context.ExtractStatus()},
{"exit", _exit_context.ExtractStatus()},
{"links", _link_manager.extract_status()},
/* {"outboundMessages", _outboundMessageHandler.ExtractStatus()} */};
{"links", _link_manager->extract_status()}};
}
// TODO: investigate changes needed for libquic integration
@ -104,7 +99,7 @@ namespace llarp
auto services = _hidden_service_context.ExtractStatus();
auto link_types = _link_manager.extract_status();
auto link_types = _link_manager->extract_status();
uint64_t tx_rate = 0;
uint64_t rx_rate = 0;
@ -177,7 +172,7 @@ namespace llarp
{"uptime", to_json(Uptime())},
{"numPathsBuilt", pathsCount},
{"numPeersConnected", peers},
{"numRoutersKnown", _node_db->num_loaded()},
{"numRoutersKnown", _node_db->num_rcs()},
{"ratio", ratio},
{"txRate", tx_rate},
{"rxRate", rx_rate},
@ -193,6 +188,18 @@ namespace llarp
return stats;
}
bool
Router::needs_initial_fetch() const
{
return _node_db->needs_initial_fetch();
}
bool
Router::needs_rebootstrap() const
{
return _node_db->needs_rebootstrap();
}
void
Router::Freeze()
{
@ -212,19 +219,19 @@ namespace llarp
std::unordered_set<RouterID> peer_pubkeys;
for_each_connection([&peer_pubkeys](link::Connection& conn) {
peer_pubkeys.emplace(conn.remote_rc.router_id());
peer_pubkeys.emplace(conn.conn->remote_key());
});
loop()->call([this, &peer_pubkeys]() {
for (auto& pk : peer_pubkeys)
_link_manager.close_connection(pk);
_link_manager->close_connection(pk);
});
}
void
Router::persist_connection_until(const RouterID& remote, llarp_time_t until)
{
_link_manager.set_conn_persist(remote, until);
_link_manager->set_conn_persist(remote, until);
}
std::optional<RouterID>
@ -235,10 +242,11 @@ namespace llarp
return node_db()->get_random_whitelist_router();
}
if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; }))
if (auto maybe = node_db()->get_random_rc())
{
return maybe->router_id();
}
return std::nullopt;
}
@ -251,19 +259,19 @@ namespace llarp
void
Router::connect_to(const RouterID& rid)
{
_link_manager.connect_to(rid);
_link_manager->connect_to(rid);
}
void
Router::connect_to(const RemoteRC& rc)
{
_link_manager.connect_to(rc);
_link_manager->connect_to(rc);
}
bool
Router::send_data_message(const RouterID& remote, std::string payload)
{
return _link_manager.send_data_message(remote, std::move(payload));
return _link_manager->send_data_message(remote, std::move(payload));
}
bool
@ -273,20 +281,20 @@ namespace llarp
std::string body,
std::function<void(oxen::quic::message m)> func)
{
return _link_manager.send_control_message(
return _link_manager->send_control_message(
remote, std::move(ep), std::move(body), std::move(func));
}
void
Router::for_each_connection(std::function<void(link::Connection&)> func)
{
return _link_manager.for_each_connection(func);
return _link_manager->for_each_connection(func);
}
bool
Router::EnsureIdentity()
{
_encryption = _key_manager->encryptionKey;
_encryption = _key_manager->encryption_key;
if (is_service_node())
{
@ -329,13 +337,19 @@ namespace llarp
}
else
{
_identity = _key_manager->identityKey;
_identity = _key_manager->identity_key;
}
if (_identity.IsZero())
{
log::critical(logcat, "FUCK @ line:{}", __LINE__);
return false;
}
if (_encryption.IsZero())
{
log::critical(logcat, "FUCK @ line:{}", __LINE__);
return false;
}
return true;
}
@ -352,12 +366,14 @@ namespace llarp
// Backwards compat: before 0.9.10 we used `type=file` with `file=|-|stdout` for print mode
auto log_type = conf.logging.type;
if (log_type == log::Type::File
&& (conf.logging.file == "stdout" || conf.logging.file == "-" || conf.logging.file.empty()))
log_type = log::Type::Print;
if (log::get_level_default() != log::Level::off)
log::reset_level(conf.logging.level);
log::clear_sinks();
log::add_sink(log_type, log_type == log::Type::System ? "lokinet" : conf.logging.file);
@ -402,6 +418,7 @@ namespace llarp
throw std::runtime_error("KeyManager failed to initialize");
log::debug(logcat, "Initializing from configuration");
if (!from_config(conf))
throw std::runtime_error("FromConfig() failed");
@ -421,7 +438,7 @@ namespace llarp
Router::insufficient_peers() const
{
constexpr int KnownPeerWarningThreshold = 5;
return node_db()->num_loaded() < KnownPeerWarningThreshold;
return node_db()->num_rcs() < KnownPeerWarningThreshold;
}
std::optional<std::string>
@ -429,7 +446,7 @@ namespace llarp
{
// If we're in the white or gray list then we *should* be establishing connections to other
// routers, so if we have almost no peers then something is almost certainly wrong.
if (appears_funded() and insufficient_peers())
if (appears_funded() and insufficient_peers() and not _testing_disabled)
return "too few peer connections; lokinet is not adequately connected to the network";
return std::nullopt;
}
@ -445,14 +462,6 @@ namespace llarp
is_running.store(false);
}
bool
Router::ParseRoutingMessageBuffer(
const llarp_buffer_t&, path::AbstractHopHandler&, const PathID_t&)
{
// TODO: will go away with the removal of flush upstream/downstream
return false;
}
bool
Router::have_snode_whitelist() const
{
@ -462,25 +471,27 @@ namespace llarp
bool
Router::appears_decommed() const
{
return have_snode_whitelist() and node_db()->greylist().count(pubkey());
return _is_service_node and have_snode_whitelist() and node_db()->greylist().count(pubkey());
}
bool
Router::appears_funded() const
{
return have_snode_whitelist() and node_db()->is_connection_allowed(pubkey());
return _is_service_node and have_snode_whitelist()
and node_db()->is_connection_allowed(pubkey());
}
bool
Router::appears_registered() const
{
return have_snode_whitelist() and node_db()->get_registered_routers().count(pubkey());
return _is_service_node and have_snode_whitelist()
and node_db()->get_registered_routers().count(pubkey());
}
bool
Router::can_test_routers() const
{
return appears_funded();
return appears_funded() and not _testing_disabled;
}
bool
@ -501,21 +512,22 @@ namespace llarp
}
size_t
Router::NumberOfConnectedRouters() const
Router::num_router_connections() const
{
return _link_manager.get_num_connected();
return _link_manager->get_num_connected();
}
size_t
Router::NumberOfConnectedClients() const
Router::num_client_connections() const
{
return _link_manager.get_num_connected_clients();
return _link_manager->get_num_connected_clients();
}
void
Router::save_rc()
{
_node_db->put_rc(router_contact.view());
// _node_db->put_rc(router_contact.view());
log::info(logcat, "Saving RC file to {}", our_rc_file);
queue_disk_io([&]() { router_contact.write(our_rc_file); });
}
@ -524,67 +536,84 @@ namespace llarp
{
// Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.net_id);
if (!conf.router.net_id.empty()
&& strcmp(conf.router.net_id.c_str(), llarp::LOKINET_DEFAULT_NETID) != 0)
const auto& netid = conf.router.net_id;
if (not netid.empty() and netid != llarp::LOKINET_DEFAULT_NETID)
{
const auto& netid = conf.router.net_id;
llarp::LogWarn(
"!!!! you have manually set netid to be '",
log::critical(
logcat,
"Network ID set to {}, which is not {}! Lokinet will attempt to run on the specified "
"network",
netid,
"' which does not equal '",
llarp::LOKINET_DEFAULT_NETID,
"' you will run as a different network, good luck "
"and don't forget: something something MUH traffic "
"shape correlation !!!!");
llarp::LOKINET_DEFAULT_NETID);
_testnet = netid == llarp::LOKINET_TESTNET_NETID;
_testing_disabled = conf.lokid.disable_testing;
if (_testing_disabled and not _testnet)
throw std::runtime_error{"Error: reachability testing can only be disabled on testnet!"};
}
// Router config
_link_manager.max_connected_routers = conf.router.max_connected_routers;
_link_manager.min_connected_routers = conf.router.min_connected_routers;
client_router_connections = conf.router.client_router_connections;
encryption_keyfile = _key_manager->m_encKeyPath;
our_rc_file = _key_manager->m_rcPath;
transport_keyfile = _key_manager->m_transportKeyPath;
identity_keyfile = _key_manager->m_idKeyPath;
encryption_keyfile = _key_manager->enckey_path;
our_rc_file = _key_manager->rc_path;
transport_keyfile = _key_manager->transkey_path;
identity_keyfile = _key_manager->idkey_path;
if (auto maybe_ip = conf.links.public_addr)
_ourAddress = var::visit([](auto&& ip) { return SockAddr{ip}; }, *maybe_ip);
else if (auto maybe_ip = conf.router.public_ip)
_ourAddress = var::visit([](auto&& ip) { return SockAddr{ip}; }, *maybe_ip);
std::optional<std::string> paddr = (conf.router.public_ip) ? conf.router.public_ip
: (conf.links.public_addr) ? conf.links.public_addr
: std::nullopt;
std::optional<uint16_t> pport = (conf.router.public_port) ? conf.router.public_port
: (conf.links.public_port) ? conf.links.public_port
: std::nullopt;
if (_ourAddress)
if (pport.has_value() and not paddr.has_value())
throw std::runtime_error{"If public-port is specified, public-addr must be as well!"};
if (conf.links.listen_addr)
{
if (auto maybe_port = conf.links.public_port)
_ourAddress->setPort(*maybe_port);
else if (auto maybe_port = conf.router.public_port)
_ourAddress->setPort(*maybe_port);
else
throw std::runtime_error{"public ip provided without public port"};
log::debug(logcat, "Using {} for our public address", *_ourAddress);
_listen_address = *conf.links.listen_addr;
}
else
log::debug(logcat, "No explicit public address given; will auto-detect during link setup");
{
if (paddr or pport)
throw std::runtime_error{"Must specify [bind]:listen in config with public ip/addr!"};
if (auto maybe_addr = net().get_best_public_address(true, DEFAULT_LISTEN_PORT))
_listen_address = std::move(*maybe_addr);
else
throw std::runtime_error{"Could not find net interface on current platform!"};
}
_public_address = (not paddr and not pport)
? _listen_address
: oxen::quic::Address{*paddr, pport ? *pport : DEFAULT_LISTEN_PORT};
RouterContact::BLOCK_BOGONS = conf.router.block_bogons;
auto& networkConfig = conf.network;
/// build a set of strictConnectPubkeys
std::unordered_set<RouterID> strictConnectPubkeys;
if (not networkConfig.strict_connect.empty())
{
const auto& val = networkConfig.strict_connect;
if (is_service_node())
throw std::runtime_error("cannot use strict-connect option as service node");
if (val.size() < 2)
throw std::runtime_error(
"Must specify more than one strict-connect router if using strict-connect");
strictConnectPubkeys.insert(val.begin(), val.end());
_node_db->pinned_edges().insert(val.begin(), val.end());
log::debug(logcat, "{} strict-connect routers configured", val.size());
}
std::vector<fs::path> configRouters = conf.connect.routers;
configRouters.insert(
configRouters.end(), conf.bootstrap.files.begin(), conf.bootstrap.files.end());
@ -592,31 +621,68 @@ namespace llarp
// <DATA_DIR>/bootstrap.signed. If this isn't present, leave a useful error message
// TODO: use constant
fs::path defaultBootstrapFile = conf.router.data_dir / "bootstrap.signed";
if (configRouters.empty() and conf.bootstrap.routers.empty())
{
if (fs::exists(defaultBootstrapFile))
{
configRouters.push_back(defaultBootstrapFile);
}
}
bootstrap_rc_list.clear();
// BootstrapList _bootstrap_rc_list;
auto& node_bstrap = _node_db->bootstrap_list();
auto clear_bad_rcs = [&]() mutable {
log::critical(logcat, "Clearing bad RCs...");
// in case someone has an old bootstrap file and is trying to use a bootstrap
// that no longer exists
for (auto it = node_bstrap.begin(); it != node_bstrap.end();)
{
if (it->is_obsolete_bootstrap())
log::critical(logcat, "ignoring obsolete bootstrap RC: {}", it->router_id());
else if (not it->verify())
log::critical(logcat, "ignoring invalid bootstrap RC: {}", it->router_id());
else
{
++it;
continue;
}
// we are in one of the above error cases that we warned about:
it = node_bstrap.erase(it);
}
};
for (const auto& router : configRouters)
{
log::debug(logcat, "Loading bootstrap router list from {}", defaultBootstrapFile);
bootstrap_rc_list.read_from_file(router);
node_bstrap.read_from_file(router);
// _bootstrap_rc_list.read_from_file(router);
}
for (const auto& rc : conf.bootstrap.routers)
{
bootstrap_rc_list.emplace(rc);
// _bootstrap_rc_list.emplace(rc);
node_bstrap.emplace(rc);
}
if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode)
_bootstrap_seed = conf.bootstrap.seednode;
if (_bootstrap_seed)
log::critical(logcat, "We are a bootstrap seed node!");
if (node_bstrap.empty() and not _bootstrap_seed)
{
log::warning(logcat, "Warning: bootstrap list is empty and we are not a seed node");
auto fallbacks = llarp::load_bootstrap_fallbacks();
if (bootstrap_rc_list.empty() and not conf.bootstrap.seednode)
if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end())
{
// _bootstrap_rc_list.merge(itr->second);
node_bstrap.merge(itr->second);
}
if (node_bstrap.empty())
{
// empty after trying fallback, if set
log::error(
@ -624,43 +690,23 @@ namespace llarp
"No bootstrap routers were loaded. The default bootstrap file {} does not exist, and "
"loading fallback bootstrap RCs failed.",
defaultBootstrapFile);
throw std::runtime_error("No bootstrap nodes available.");
}
log::critical(logcat, "Loaded {} default fallback bootstrap routers!", node_bstrap.size());
}
// in case someone has an old bootstrap file and is trying to use a bootstrap
// that no longer exists
for (auto it = bootstrap_rc_list.begin(); it != bootstrap_rc_list.end();)
{
if (it->is_obsolete_bootstrap())
log::warning(logcat, "ignoring obsolete boostrap RC: {}", it->router_id());
else if (not it->verify())
log::warning(logcat, "ignoring invalid bootstrap RC: {}", it->router_id());
else
{
++it;
continue;
}
clear_bad_rcs();
log::critical(logcat, "We have {} bootstrap routers!", node_bstrap.size());
// we are in one of the above error cases that we warned about:
it = bootstrap_rc_list.erase(it);
}
// node_db()->set_bootstrap_routers(_bootstrap_rc_list);
node_db()->set_bootstrap_routers(bootstrap_rc_list);
if (conf.bootstrap.seednode)
LogInfo("we are a seed node");
else
LogInfo("Loaded ", bootstrap_rc_list.size(), " bootstrap routers");
// Init components after relevant config settings loaded
_link_manager.init();
// FIXME: kludge for now, will be part of larger cleanup effort.
if (_is_service_node)
InitInboundLinks();
else
InitOutboundLinks();
// TODO: RC refactor here
// if (_is_service_node)
// init_inbounds();
// else
// init_outbounds();
// profiling
_profile_file = conf.router.data_dir / "profiles.dat";
@ -695,13 +741,18 @@ namespace llarp
}
bool
Router::IsBootstrapNode(const RouterID r) const
Router::is_bootstrap_node(const RouterID r) const
{
return std::count_if(
bootstrap_rc_list.begin(),
bootstrap_rc_list.end(),
[r](const RemoteRC& rc) -> bool { return rc.router_id() == r; })
> 0;
if (_node_db->has_bootstraps())
{
const auto& b = _node_db->bootstrap_list();
return std::count_if(
b.begin(),
b.end(),
[r](const RemoteRC& rc) -> bool { return rc.router_id() == r; })
> 0;
}
return false;
}
bool
@ -715,24 +766,27 @@ namespace llarp
Router::report_stats()
{
const auto now = llarp::time_now_ms();
log::info(
log::critical(
logcat,
"{} RCs loaded with {} bootstrap peers and {} router connections!",
node_db()->num_loaded(),
bootstrap_rc_list.size(),
NumberOfConnectedRouters());
_node_db->num_rcs(),
_node_db->num_bootstraps(),
num_router_connections());
if (is_service_node())
{
log::info(
log::critical(
logcat,
"Local service node has {} client connections since last RC update ({} to expiry)",
NumberOfConnectedClients(),
num_client_connections(),
router_contact.age(now),
router_contact.time_to_expiry(now));
}
if (_last_stats_report > 0s)
log::info(logcat, "Last reported stats time {}", now - _last_stats_report);
_last_stats_report = now;
}
@ -747,9 +801,9 @@ namespace llarp
fmt::format_to(
out,
" snode | known/svc/clients: {}/{}/{}",
node_db()->num_loaded(),
NumberOfConnectedRouters(),
NumberOfConnectedClients());
node_db()->num_rcs(),
num_router_connections(),
num_client_connections());
fmt::format_to(
out,
" | {} active paths | block {} ",
@ -765,10 +819,7 @@ namespace llarp
else
{
fmt::format_to(
out,
" client | known/connected: {}/{}",
node_db()->num_loaded(),
NumberOfConnectedRouters());
out, " client | known/connected: {}/{}", node_db()->num_rcs(), num_router_connections());
if (auto ep = hidden_service_context().GetDefault())
{
@ -819,50 +870,68 @@ namespace llarp
// (relay-only) if we have fetched the relay list from oxend and
// we are registered and funded, we want to gossip our RC periodically
auto now_timepoint = std::chrono::system_clock::time_point(now);
if (is_snode and appears_funded() and (now_timepoint > next_rc_gossip))
{
log::info(logcat, "regenerating and gossiping RC");
router_contact.resign();
save_rc();
auto view = router_contact.view();
_link_manager.gossip_rc(
pubkey(), std::string{reinterpret_cast<const char*>(view.data()), view.size()});
last_rc_gossip = now_timepoint;
// 1min to 5min before "stale time" is next gossip time
auto random_delta =
std::chrono::seconds{std::uniform_int_distribution<size_t>{60, 300}(llarp::csrng)};
next_rc_gossip = now_timepoint + RouterContact::STALE_AGE - random_delta;
if (is_snode)
{
if (appears_funded() and now_timepoint > next_rc_gossip)
{
log::info(logcat, "regenerating and gossiping RC");
router_contact.resign();
save_rc();
auto view = router_contact.view();
_link_manager->gossip_rc(
pubkey(), std::string{reinterpret_cast<const char*>(view.data()), view.size()});
last_rc_gossip = now_timepoint;
// 1min to 5min before "stale time" is next gossip time
auto random_delta =
std::chrono::seconds{std::uniform_int_distribution<size_t>{60, 300}(llarp::csrng)};
next_rc_gossip = now_timepoint + RouterContact::STALE_AGE - random_delta;
}
report_stats();
}
if (not is_snode)
if (needs_initial_fetch())
{
if (not _config->bootstrap.seednode)
node_db()->fetch_initial();
}
else if (needs_rebootstrap() and now_timepoint > next_bootstrap_attempt)
{
node_db()->fallback_to_bootstrap();
}
else
{
// (client-only) periodically fetch updated RCs
if (now_timepoint - last_rc_fetch > RC_UPDATE_INTERVAL)
{
node_db()->fetch_rcs();
last_rc_fetch = now_timepoint;
}
// (client-only) periodically fetch updated RouterID list
if (now_timepoint - last_routerid_fetch > ROUTERID_UPDATE_INTERVAL)
if (now_timepoint - last_rid_fetch > ROUTERID_UPDATE_INTERVAL)
{
node_db()->fetch_router_ids();
last_routerid_fetch = now_timepoint;
node_db()->fetch_rids();
}
}
// remove RCs for nodes that are no longer allowed by network policy
node_db()->RemoveIf([&](const RemoteRC& rc) -> bool {
// don't purge bootstrap nodes from nodedb
if (IsBootstrapNode(rc.router_id()))
if (is_bootstrap_node(rc.router_id()))
{
log::trace(logcat, "Not removing {}: is bootstrap node", rc.router_id());
return false;
}
// if for some reason we stored an RC that isn't a valid router
// purge this entry
if (not rc.is_public_router())
if (not rc.is_public_addressable())
{
log::debug(logcat, "Removing {}: not a valid router", rc.router_id());
return true;
@ -916,17 +985,18 @@ namespace llarp
// mark peers as de-registered
for (auto& peer : close_peers)
_link_manager.deregister_peer(peer);
_link_manager.close_connection(peer);
}
*/
_link_manager.check_persisting_conns(now);
_link_manager->check_persisting_conns(now);
size_t connected = NumberOfConnectedRouters();
size_t connected = num_router_connections();
size_t connectToNum = _link_manager.min_connected_routers;
const auto& pinned_edges = _node_db->get_pinned_edges();
size_t connectToNum = _link_manager->client_router_connections;
const auto& pinned_edges = _node_db->pinned_edges();
const auto pinned_count = pinned_edges.size();
if (pinned_count > 0 && connectToNum > pinned_count)
{
connectToNum = pinned_count;
@ -952,7 +1022,7 @@ namespace llarp
log::error(
logcat,
"We appear to be an active service node, but have only {} known peers.",
node_db()->num_loaded());
node_db()->num_rcs());
_next_decomm_warning = now + DecommissionWarnInterval;
}
}
@ -963,7 +1033,7 @@ namespace llarp
{
size_t dlt = connectToNum - connected;
LogDebug("connecting to ", dlt, " random routers to keep alive");
_link_manager.connect_to_random(dlt);
_link_manager->connect_to_random(dlt);
}
_hidden_service_context.Tick(now);
@ -986,10 +1056,10 @@ namespace llarp
bool
Router::GetRandomConnectedRouter(RemoteRC& result) const
{
return _link_manager.get_random_connected(result);
return _link_manager->get_random_connected(result);
}
const std::unordered_set<RouterID>&
const std::set<RouterID>&
Router::get_whitelist() const
{
return _node_db->whitelist();
@ -1017,14 +1087,22 @@ namespace llarp
bool
Router::Run()
{
log::critical(logcat, "{} called", __PRETTY_FUNCTION__);
if (is_running || is_stopping)
return false;
router_contact = LocalRC::make(identity(), public_ip());
router_contact = LocalRC::make(
identity(), _is_service_node and _public_address ? *_public_address : _listen_address);
if (is_service_node() and not router_contact.is_public_router())
_link_manager = LinkManager::make(*this);
// Init components after relevant config settings loaded
_link_manager->init();
if (is_service_node())
{
if (not router_contact.is_public_router())
if (not router_contact.is_public_addressable())
{
log::error(logcat, "Router is configured as relay but has no reachable addresses!");
return false;
@ -1039,7 +1117,7 @@ namespace llarp
}
log::info(logcat, "Router initialized as service node!");
const RouterID us = pubkey();
// relays do not use profiling
router_profiling().Disable();
}
@ -1061,24 +1139,22 @@ namespace llarp
log::info(logcat, "Loading NodeDB from disk...");
_node_db->load_from_disk();
_node_db->store_bootstraps();
_contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this);
oxen::log::flush();
for (const auto& rc : bootstrap_rc_list)
{
node_db()->put_rc(rc);
_contacts->rc_nodes()->PutNode(rc);
log::info(logcat, "Added bootstrap node (rid: {})", rc.router_id());
}
log::info(logcat, "Router populated NodeDB with {} routers", _node_db->num_loaded());
log::info(logcat, "Creating Introset Contacts...");
_contacts = std::make_unique<Contacts>(*this);
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
_route_poker->start();
is_running.store(true);
_started_at = now();
if (is_service_node())
if (is_service_node() and not _testing_disabled)
{
// do service node testing if we are in service node whitelist mode
_loop->call_every(consensus::REACHABILITY_TESTING_TIMER_INTERVAL, weak_from_this(), [this] {
@ -1090,7 +1166,9 @@ namespace llarp
// yet when we expect to have one.
if (not can_test_routers())
return;
auto tests = router_testing.get_failing();
if (auto maybe = router_testing.next_random(this))
{
tests.emplace_back(*maybe, 0);
@ -1111,54 +1189,34 @@ namespace llarp
// try to make a session to this random router
// this will do a dht lookup if needed
_link_manager.connect_to(router);
/*
* TODO: container of pending snode test routers to be queried on
* connection success/failure, then do this stuff there.
_outboundSessionMaker.CreateSessionTo(
router, [previous_fails = fails, this](const auto& router, const auto result) {
auto rpc = RpcClient();
if (result != SessionResult::Establish)
_link_manager->test_reachability(
router,
[this, rid = router, previous = fails](oxen::quic::connection_interface& conn) {
log::info(
logcat,
"Successful SN reachability test to {}{}",
rid,
previous ? "after {} previous failures"_format(previous) : "");
router_testing.remove_node_from_failing(rid);
_rpc_client->InformConnection(rid, true);
conn.close_connection();
},
[this, rid = router, previous = fails](
oxen::quic::connection_interface&, uint64_t ec) {
if (ec != 0)
{
// failed connection mark it as so
m_routerTesting.add_failing_node(router, previous_fails);
LogInfo(
"FAILED SN connection test to ",
router,
" (",
previous_fails + 1,
" consecutive failures) result=",
result);
}
else
{
m_routerTesting.remove_node_from_failing(router);
if (previous_fails > 0)
{
LogInfo(
"Successful SN connection test to ",
router,
" after ",
previous_fails,
" failures");
}
else
{
LogDebug("Successful SN connection test to ", router);
}
}
if (rpc)
{
// inform as needed
rpc->InformConnection(router, result == SessionResult::Establish);
log::info(
logcat,
"Unsuccessful SN reachability test to {} after {} previous failures",
rid,
previous);
router_testing.add_failing_node(rid, previous);
}
});
*/
}
});
}
llarp::sys::service_manager->ready();
return is_running;
}
@ -1201,7 +1259,7 @@ namespace llarp
void
Router::StopLinks()
{
_link_manager.stop();
_link_manager->stop();
}
void
@ -1218,7 +1276,7 @@ namespace llarp
LogWarn("stopping router hard");
llarp::sys::service_manager->stopping();
hidden_service_context().StopAll();
_exit_context.Stop();
_exit_context.stop();
StopLinks();
Close();
}
@ -1238,27 +1296,27 @@ namespace llarp
}
is_stopping.store(true);
if (auto level = log::get_level_default();
level > log::Level::info and level != log::Level::off)
log::reset_level(log::Level::info);
log::info(logcat, "stopping");
log::info(logcat, "stopping service manager...");
llarp::sys::service_manager->stopping();
log::debug(logcat, "stopping hidden service context");
log::debug(logcat, "stopping hidden service context...");
hidden_service_context().StopAll();
llarp::sys::service_manager->stopping();
log::debug(logcat, "stopping exit context");
_exit_context.Stop();
llarp::sys::service_manager->stopping();
log::debug(logcat, "final upstream pump");
llarp::sys::service_manager->stopping();
log::debug(logcat, "final links pump");
log::debug(logcat, "stopping exit context...");
_exit_context.stop();
_loop->call_later(200ms, [this] { AfterStopIssued(); });
}
bool
Router::HasSessionTo(const RouterID& remote) const
{
return _link_manager.have_connection_to(remote);
return _link_manager->have_connection_to(remote);
}
std::string
@ -1277,19 +1335,20 @@ namespace llarp
Router::ConnectToRandomRouters(int _want)
{
const size_t want = _want;
auto connected = NumberOfConnectedRouters();
auto connected = num_router_connections();
if (connected >= want)
return;
_link_manager.connect_to_random(want);
_link_manager->connect_to_random(want);
}
bool
Router::init_service_node()
{
LogInfo("accepting transit traffic");
paths.AllowTransit();
_contacts->set_transit_allowed(true);
_exit_context.AddExitEndpoint("default", _config->network, _config->dns);
log::info(logcat, "Router accepting transit traffic...");
paths.allow_transit();
_exit_context.add_exit_endpoint("default", _config->network, _config->dns);
return true;
}
@ -1315,13 +1374,13 @@ namespace llarp
}
oxen::quic::Address
Router::public_ip() const
Router::listen_addr() const
{
return _local_addr;
return _listen_address;
}
void
Router::InitInboundLinks()
Router::init_inbounds()
{
// auto addrs = _config->links.InboundListenAddrs;
// if (is_service_node and addrs.empty())
@ -1370,7 +1429,7 @@ namespace llarp
}
void
Router::InitOutboundLinks()
Router::init_outbounds()
{
// auto addrs = config()->links.OutboundLinks;
// if (addrs.empty())

View File

@ -37,15 +37,6 @@
#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
@ -64,14 +55,14 @@ namespace llarp
struct Router : std::enable_shared_from_this<Router>
{
friend class NodeDB;
explicit Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform);
~Router();
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 +86,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 +105,7 @@ namespace llarp
std::shared_ptr<NodeDB> _node_db;
llarp_time_t _started_at;
const oxenmq::TaggedThreadID _disk_thread;
oxen::quic::Network _net;
// oxen::quic::Network _net; // DISCUSS: we don't use this anywhere..?
llarp_time_t _last_stats_report = 0s;
llarp_time_t _next_decomm_warning = time_now_ms() + 15s;
@ -126,24 +123,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,7 +147,41 @@ namespace llarp
bool
insufficient_peers() const;
protected:
// bool _needs_initial_fetch{true};
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 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:
bool
is_bootstrap_seed() const
{
return _bootstrap_seed;
}
int
required_num_client_conns() const
{
return client_router_connections;
}
RouterID
local_rid() const
{
return RouterID{pubkey()};
}
bool
needs_initial_fetch() const;
bool
needs_rebootstrap() const;
void
for_each_connection(std::function<void(link::Connection&)> func);
@ -169,10 +191,16 @@ namespace llarp
void
connect_to(const RemoteRC& rc);
Contacts*
const Contacts&
contacts() const
{
return _contacts.get();
return *_contacts;
}
Contacts&
contacts()
{
return *_contacts;
}
std::shared_ptr<Config>
@ -205,10 +233,16 @@ namespace llarp
LinkManager&
link_manager()
{
return _link_manager;
return *_link_manager;
}
inline int
const LinkManager&
link_manager() const
{
return *_link_manager;
}
int
outbound_udp_socket() const
{
return _outbound_udp_socket;
@ -275,7 +309,7 @@ namespace llarp
}
oxen::quic::Address
public_ip() const;
listen_addr() const;
util::StatusObject
ExtractStatus() const;
@ -283,7 +317,7 @@ namespace llarp
util::StatusObject
ExtractSummaryStatus() const;
const std::unordered_set<RouterID>&
const std::set<RouterID>&
get_whitelist() const;
void
@ -369,10 +403,10 @@ namespace llarp
status_line();
void
InitInboundLinks();
init_inbounds();
void
InitOutboundLinks();
init_outbounds();
std::optional<RouterID>
GetRandomGoodRouter();
@ -466,7 +500,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,23 +512,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;
num_client_connections() const;
bool
GetRandomConnectedRouter(RemoteRC& result) const;

View File

@ -6,7 +6,6 @@
#include "util/bencode.hpp"
#include "util/buffer.hpp"
#include "util/file.hpp"
#include "util/time.hpp"
#include <oxenc/bt_serialize.h>
@ -57,19 +56,19 @@ namespace llarp
}
auto netid = data.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");
auto pubkey = data.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);
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);
_timestamp = rc_time{std::chrono::seconds{data.require<int64_t>("t")}};
_timestamp = rc_time{std::chrono::seconds{data.require<uint64_t>("t")}};
auto ver = data.require<ustring_view>("v");
@ -103,7 +102,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()}};
@ -222,10 +221,11 @@ namespace llarp
}
bool
RouterContact::is_public_router() const
RouterContact::is_public_addressable() const
{
if (_router_version.empty())
return false;
return _addr.is_addressable();
}

View File

@ -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).
@ -177,7 +176,7 @@ namespace llarp
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
@ -208,6 +207,8 @@ namespace llarp
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
@ -237,6 +238,9 @@ namespace llarp
explicit LocalRC(std::string payload, const SecretKey sk);
~LocalRC() = default;
RemoteRC
to_remote();
void
resign();
@ -298,8 +302,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());
}
};
@ -311,15 +314,18 @@ namespace llarp
void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
explicit RemoteRC(oxenc::bt_dict_consumer btdc);
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;
}
explicit RemoteRC(oxenc::bt_dict_consumer btdc);
~RemoteRC() = default;
std::string_view
@ -360,10 +366,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

View File

@ -22,10 +22,18 @@ 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();
}
RemoteRC
LocalRC::to_remote()
{
resign();
return RemoteRC{view()};
}
LocalRC::LocalRC(std::string payload, const SecretKey sk) : _secret_key{std::move(sk)}
{
_router_id = llarp::seckey_to_pubkey(_secret_key);
@ -47,13 +55,13 @@ namespace llarp
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
auto err = "Unable to verify expired LocalRC!";
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"};
throw std::runtime_error{"Failed to verify LocalRC"};
});
}
catch (const std::exception& e)
@ -78,7 +86,7 @@ namespace llarp
return sig;
});
_payload = btdp.view<unsigned char>();
_payload = ustring{btdp.view<unsigned char>()};
}
void
@ -123,8 +131,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

View File

@ -16,32 +16,13 @@ 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};
}
}
@ -60,13 +41,20 @@ namespace llarp
if (net->IsBogon(addr().in4()) and BLOCK_BOGONS)
{
auto err = "Unable to verify expired RemoteRC!";
auto err = "Unable to verify expired RemoteRC address!";
log::info(logcat, err);
throw std::runtime_error{err};
}
log::error(
log::Cat("FIXME"),
"ABOUT TO VERIFY THIS: {}, WITH SIG {}, SIGNED BY {}",
oxenc::to_hex(msg),
oxenc::to_hex(sig),
router_id().ToHex());
if (not crypto::verify(router_id(), msg, sig))
throw std::runtime_error{"Failed to verify RemoteRC"};
throw std::runtime_error{"Failed to verify RemoteRC signature"};
});
}
@ -74,11 +62,11 @@ namespace llarp
RemoteRC::read(const fs::path& fname)
{
ustring buf;
buf.reserve(MAX_RC_SIZE);
buf.resize(MAX_RC_SIZE);
try
{
util::file_to_buffer(fname, buf.data(), MAX_RC_SIZE);
util::file_to_buffer(fname, buf.data(), buf.size());
oxenc::bt_dict_consumer btdc{buf};
bt_load(btdc);
@ -88,7 +76,7 @@ namespace llarp
}
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;
}

View File

@ -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())

View File

@ -21,6 +21,9 @@ namespace llarp
RouterID(const Data& data) : PubKey(data)
{}
RouterID(ustring_view data) : PubKey(data.data())
{}
util::StatusObject
ExtractStatus() const;
@ -30,8 +33,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 +55,6 @@ namespace llarp
template <>
constexpr inline bool IsToStringFormattable<RouterID> = true;
} // namespace llarp
namespace std

View File

@ -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);
});
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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()));
}

View File

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

View File

@ -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")};

View File

@ -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()
{

View File

@ -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();