mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
Implement fetch RouterIDs method and usage
Periodically clients will fetch the set of RouterIDs for all relays on the network. It will request this list from a number (12, currently) of relays, but as we are likely to be requesting from more relays than we want to have edge connections, this request will itself be relayed to the target source via one of our edges. As we can't trust our edge to do this honestly, the responses are signed by the source relay. TODO: the responses from all (12) relays are collected, then processed together. The reconciliation of their responses is not yet implemented. TODO: the source selection for this method obviously requires sources to begin with, but this is the method by which we learn of those...bootstrapping is still a bit in-progress, and will need to be finished for this. TODO: make Router call this periodically, as with RC fetching.
This commit is contained in:
parent
6952e8f705
commit
c30a4dd44a
|
@ -7,6 +7,7 @@
|
||||||
#include <llarp/messages/exit.hpp>
|
#include <llarp/messages/exit.hpp>
|
||||||
#include <llarp/messages/path.hpp>
|
#include <llarp/messages/path.hpp>
|
||||||
#include <llarp/messages/rc.hpp>
|
#include <llarp/messages/rc.hpp>
|
||||||
|
#include <llarp/messages/router_id.hpp>
|
||||||
#include <llarp/nodedb.hpp>
|
#include <llarp/nodedb.hpp>
|
||||||
#include <llarp/path/path.hpp>
|
#include <llarp/path/path.hpp>
|
||||||
#include <llarp/router/router.hpp>
|
#include <llarp/router/router.hpp>
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include <oxenc/bt_producer.h>
|
#include <oxenc/bt_producer.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <exception>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
|
@ -544,6 +546,120 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LinkManager::fetch_router_ids(const RouterID& source)
|
||||||
|
{
|
||||||
|
if (ep.conns.empty())
|
||||||
|
{
|
||||||
|
log::debug(link_cat, "Not attempting to fetch Router IDs: not connected to any relays.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: randomize? Also, keep track of successful responses and drop this edge
|
||||||
|
// if not many come back successfully.
|
||||||
|
RouterID edge = ep.conns.begin()->first;
|
||||||
|
send_control_message(
|
||||||
|
edge,
|
||||||
|
"fetch_router_ids"s,
|
||||||
|
RouterIDFetch::serialize(source),
|
||||||
|
[this, source = source, edge = std::move(edge)](oxen::quic::message m) {
|
||||||
|
if (not m)
|
||||||
|
{
|
||||||
|
log::info(
|
||||||
|
link_cat,
|
||||||
|
"Error fetching RouterIDs from source \"{}\" via edge \"{}\"",
|
||||||
|
source,
|
||||||
|
edge);
|
||||||
|
node_db->ingest_router_ids(edge, {}); // empty response == failure
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
|
btdc.required("routers");
|
||||||
|
auto router_id_strings = btdc.consume_list<std::vector<ustring>>();
|
||||||
|
btdc.require_signature("signature", [&edge](ustring_view msg, ustring_view sig) {
|
||||||
|
if (sig.size() != 64)
|
||||||
|
throw std::runtime_error{"Invalid signature: not 64 bytes"};
|
||||||
|
if (not crypto::verify(edge, msg, sig))
|
||||||
|
throw std::runtime_error{
|
||||||
|
"Failed to verify signature for fetch RouterIDs response."};
|
||||||
|
});
|
||||||
|
std::vector<RouterID> router_ids;
|
||||||
|
for (const auto& s : router_id_strings)
|
||||||
|
{
|
||||||
|
if (s.size() != RouterID::SIZE)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "Got bad RouterID from edge \"{}\".", edge);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router_ids.emplace_back(s.data());
|
||||||
|
}
|
||||||
|
node_db->ingest_router_ids(edge, std::move(router_ids));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
log::info(link_cat, "Error handling fetch RouterIDs response: {}", e.what());
|
||||||
|
}
|
||||||
|
node_db->ingest_router_ids(edge, {}); // empty response == failure
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LinkManager::handle_fetch_router_ids(oxen::quic::message m)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
|
|
||||||
|
auto source = btdc.require<std::string_view>("source");
|
||||||
|
|
||||||
|
// if bad request, silently fail
|
||||||
|
if (source.size() != RouterID::SIZE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto source_rid = RouterID{reinterpret_cast<const byte_t*>(source.data())};
|
||||||
|
const auto our_rid = RouterID{router().pubkey()};
|
||||||
|
|
||||||
|
if (source_rid == our_rid)
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_producer btdp;
|
||||||
|
{
|
||||||
|
auto btlp = btdp.append_list("routers");
|
||||||
|
for (const auto& relay : node_db->whitelist())
|
||||||
|
{
|
||||||
|
btlp.append(relay.ToView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
btdp.append_signature("signature", [this](ustring_view to_sign) {
|
||||||
|
std::array<unsigned char, 64> sig;
|
||||||
|
|
||||||
|
if (!crypto::sign(const_cast<unsigned char*>(sig.data()), _router.identity(), to_sign))
|
||||||
|
throw std::runtime_error{"Failed to sign fetch RouterIDs response"};
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
});
|
||||||
|
m.respond(std::move(btdp).str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_control_message(
|
||||||
|
source_rid,
|
||||||
|
"fetch_router_ids"s,
|
||||||
|
m.body_str(),
|
||||||
|
[source_rid = std::move(source_rid),
|
||||||
|
orig_mess = std::move(m)](oxen::quic::message m) mutable {
|
||||||
|
if (not m.timed_out)
|
||||||
|
orig_mess.respond(m.body_str());
|
||||||
|
// on timeout, just silently drop (as original requester will just time out anyway)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
log::info(link_cat, "Error fulfilling fetch RouterIDs request: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LinkManager::have_connection_to(const RouterID& remote, bool client_only) const
|
LinkManager::have_connection_to(const RouterID& remote, bool client_only) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -232,6 +232,12 @@ namespace llarp
|
||||||
void
|
void
|
||||||
handle_fetch_rcs(oxen::quic::message m);
|
handle_fetch_rcs(oxen::quic::message m);
|
||||||
|
|
||||||
|
void
|
||||||
|
fetch_router_ids(const RouterID& source);
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_fetch_router_ids(oxen::quic::message m);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
have_connection_to(const RouterID& remote, bool client_only = false) const;
|
have_connection_to(const RouterID& remote, bool client_only = false) const;
|
||||||
|
|
||||||
|
|
17
llarp/messages/router_id.hpp
Normal file
17
llarp/messages/router_id.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#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
|
|
@ -114,7 +114,27 @@ namespace llarp
|
||||||
|
|
||||||
void
|
void
|
||||||
NodeDB::rotate_rc_source()
|
NodeDB::rotate_rc_source()
|
||||||
{}
|
{
|
||||||
|
auto conn_count = router.link_manager().get_num_connected();
|
||||||
|
if (conn_count == 0)
|
||||||
|
{
|
||||||
|
// not connected to any nodes yet, so no sensible source
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RemoteRC new_source{};
|
||||||
|
router.link_manager().get_random_connected(new_source);
|
||||||
|
if (conn_count == 1)
|
||||||
|
{
|
||||||
|
// only one connection, use it
|
||||||
|
rc_fetch_source = new_source.router_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (new_source.router_id() == rc_fetch_source)
|
||||||
|
{
|
||||||
|
router.link_manager().get_random_connected(new_source);
|
||||||
|
}
|
||||||
|
rc_fetch_source = new_source.router_id();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: trust model
|
// TODO: trust model
|
||||||
void
|
void
|
||||||
|
@ -145,7 +165,16 @@ namespace llarp
|
||||||
router_id_response_count++;
|
router_id_response_count++;
|
||||||
if (router_id_response_count == router_id_fetch_sources.size())
|
if (router_id_response_count == router_id_fetch_sources.size())
|
||||||
{
|
{
|
||||||
// TODO: reconcile all the responses
|
// TODO: reconcile all the responses, for now just insert all
|
||||||
|
for (const auto& [rid, responses] : router_id_fetch_responses)
|
||||||
|
{
|
||||||
|
// TODO: empty == failure, handle that case
|
||||||
|
for (const auto& response : responses)
|
||||||
|
{
|
||||||
|
client_known_routers.insert(std::move(response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router_id_fetch_in_progress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +195,61 @@ namespace llarp
|
||||||
rc_fetch_source, last_rc_update_relay_timestamp, std::move(needed));
|
rc_fetch_source, last_rc_update_relay_timestamp, std::move(needed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NodeDB::fetch_router_ids()
|
||||||
|
{
|
||||||
|
if (router_id_fetch_in_progress)
|
||||||
|
return;
|
||||||
|
if (router_id_fetch_sources.empty())
|
||||||
|
select_router_id_sources({});
|
||||||
|
|
||||||
|
// if we *still* don't have fetch sources, we can't exactly fetch...
|
||||||
|
if (router_id_fetch_sources.empty())
|
||||||
|
{
|
||||||
|
log::info(logcat, "Attempting to fetch RouterIDs, but have no source from which to do so.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router_id_fetch_in_progress = true;
|
||||||
|
router_id_response_count = 0;
|
||||||
|
router_id_fetch_responses.clear();
|
||||||
|
for (const auto& rid : router_id_fetch_sources)
|
||||||
|
router.link_manager().fetch_router_ids(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NodeDB::select_router_id_sources(std::unordered_set<RouterID> excluded)
|
||||||
|
{
|
||||||
|
// TODO: bootstrapping should be finished before this is called, so this
|
||||||
|
// shouldn't happen; need to make sure that's the case.
|
||||||
|
if (client_known_routers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// keep using any we've been using, but remove `excluded` ones
|
||||||
|
for (const auto& r : excluded)
|
||||||
|
router_id_fetch_sources.erase(r);
|
||||||
|
|
||||||
|
// only know so many routers, so no need to randomize
|
||||||
|
if (client_known_routers.size() <= (ROUTER_ID_SOURCE_COUNT + excluded.size()))
|
||||||
|
{
|
||||||
|
for (const auto& r : client_known_routers)
|
||||||
|
{
|
||||||
|
if (excluded.count(r))
|
||||||
|
continue;
|
||||||
|
router_id_fetch_sources.insert(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// select at random until we have chosen enough
|
||||||
|
while (router_id_fetch_sources.size() < ROUTER_ID_SOURCE_COUNT)
|
||||||
|
{
|
||||||
|
RouterID r;
|
||||||
|
std::sample(client_known_routers.begin(), client_known_routers.end(), &r, 1, csrng);
|
||||||
|
if (excluded.count(r) == 0)
|
||||||
|
router_id_fetch_sources.insert(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NodeDB::set_router_whitelist(
|
NodeDB::set_router_whitelist(
|
||||||
const std::vector<RouterID>& whitelist,
|
const std::vector<RouterID>& whitelist,
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace llarp
|
||||||
std::unordered_map<RouterID, rc_time> last_rc_update_times;
|
std::unordered_map<RouterID, rc_time> last_rc_update_times;
|
||||||
|
|
||||||
// Router list for clients
|
// Router list for clients
|
||||||
std::unordered_set<RouterID> client_known_rcs;
|
std::unordered_set<RouterID> client_known_routers;
|
||||||
|
|
||||||
// only ever use to specific edges as path first-hops
|
// only ever use to specific edges as path first-hops
|
||||||
std::unordered_set<RouterID> pinned_edges;
|
std::unordered_set<RouterID> pinned_edges;
|
||||||
|
@ -62,10 +62,12 @@ namespace llarp
|
||||||
// rc update info
|
// rc update info
|
||||||
RouterID rc_fetch_source;
|
RouterID rc_fetch_source;
|
||||||
rc_time last_rc_update_relay_timestamp;
|
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_set<RouterID> router_id_fetch_sources;
|
||||||
std::unordered_map<RouterID, std::vector<RouterID>> router_id_fetch_responses;
|
std::unordered_map<RouterID, std::vector<RouterID>> router_id_fetch_responses;
|
||||||
// process responses once all are received (or failed/timed out)
|
// process responses once all are received (or failed/timed out)
|
||||||
size_t router_id_response_count{0};
|
size_t router_id_response_count{0};
|
||||||
|
bool router_id_fetch_in_progress{false};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
want_rc(const RouterID& rid) const;
|
want_rc(const RouterID& rid) const;
|
||||||
|
@ -104,7 +106,7 @@ namespace llarp
|
||||||
return last_rc_update_times;
|
return last_rc_update_times;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we receive a set of RCs from our current RC source relay, we consider
|
// 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.
|
// 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
|
// When using a new RC fetch relay, we first re-fetch the full RC list and, if
|
||||||
|
@ -124,6 +126,12 @@ namespace llarp
|
||||||
void
|
void
|
||||||
update_rcs();
|
update_rcs();
|
||||||
|
|
||||||
|
void
|
||||||
|
fetch_router_ids();
|
||||||
|
|
||||||
|
void
|
||||||
|
select_router_id_sources(std::unordered_set<RouterID> excluded);
|
||||||
|
|
||||||
void
|
void
|
||||||
set_router_whitelist(
|
set_router_whitelist(
|
||||||
const std::vector<RouterID>& whitelist,
|
const std::vector<RouterID>& whitelist,
|
||||||
|
|
Loading…
Reference in a new issue