diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 8d1dd9e35..657c20cb2 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -24,7 +24,6 @@ lokinet_add_library(lokinet-core-utils exit/policy.cpp # net/ handlers/exit.cpp # link/ exit/ handlers/tun.cpp - router/rc_gossiper.cpp service/auth.cpp # config/ service/context.cpp service/identity.cpp @@ -109,7 +108,6 @@ lokinet_add_library(lokinet-time-place # lokinet-platform holds all platform specific code lokinet_add_library(lokinet-platform net/interface_info.cpp - router/rc_lookup_handler.cpp vpn/packet_router.cpp vpn/platform.cpp ) diff --git a/llarp/consensus/reachability_testing.cpp b/llarp/consensus/reachability_testing.cpp index a248f9cd7..1f5068255 100644 --- a/llarp/consensus/reachability_testing.cpp +++ b/llarp/consensus/reachability_testing.cpp @@ -104,7 +104,7 @@ namespace llarp::consensus // We exhausted the queue so repopulate it and try again testing_queue.clear(); - const auto all = router->router_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); diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index ecdafff65..82dd4d44b 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -272,35 +272,8 @@ namespace llarp::exit if (numHops == 1) { - auto r = router; - if (const auto maybe = r->node_db()->get_rc(exit_router); maybe.has_value()) - r->connect_to(*maybe); - else - r->lookup_router(exit_router, [r](oxen::quic::message m) mutable { - if (m) - { - std::string payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - payload = btdc.require("RC"); - } - catch (...) - { - log::warning(link_cat, "Failed to parse Find Router response!"); - throw; - } - - RemoteRC result{std::move(payload)}; - r->node_db()->put_rc_if_newer(result); - r->connect_to(result); - } - else - { - r->link_manager().handle_find_router_error(std::move(m)); - } - }); + if (const auto maybe = router->node_db()->get_rc(exit_router); maybe.has_value()) + router->connect_to(*maybe); } else if (UrgentBuild(now)) BuildOneAlignedTo(exit_router); diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 64f07d001..ffab0a37c 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -2,8 +2,8 @@ #include #include +#include #include -#include #include #include @@ -243,9 +243,8 @@ namespace llarp::handlers { if (msg.questions[0].IsName("random.snode")) { - RouterID random; - if (GetRouter()->GetRandomGoodRouter(random)) - msg.AddCNAMEReply(random.ToString(), 1); + if (auto random = GetRouter()->GetRandomGoodRouter()) + msg.AddCNAMEReply(random->ToString(), 1); else msg.AddNXReply(); } @@ -263,11 +262,10 @@ namespace llarp::handlers const bool isV4 = msg.questions[0].qtype == dns::qTypeA; if (msg.questions[0].IsName("random.snode")) { - RouterID random; - if (GetRouter()->GetRandomGoodRouter(random)) + if (auto random = GetRouter()->GetRandomGoodRouter()) { - msg.AddCNAMEReply(random.ToString(), 1); - auto ip = ObtainServiceNodeIP(random); + msg.AddCNAMEReply(random->ToString(), 1); + auto ip = ObtainServiceNodeIP(*random); msg.AddINReply(ip, false); } else @@ -333,7 +331,7 @@ namespace llarp::handlers void ExitEndpoint::ObtainSNodeSession(const RouterID& rid, exit::SessionReadyFunc obtain_cb) { - if (not router->rc_lookup_handler().is_session_allowed(rid)) + if (not router->node_db()->is_connection_allowed(rid)) { obtain_cb(nullptr); return; diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index ad288017b..15db0ebc7 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -606,34 +606,11 @@ namespace llarp::handlers RouterID snode; if (snode.FromString(qname)) { - router()->lookup_router( - snode, [r = router(), msg = std::move(msg), reply](oxen::quic::message m) mutable { - if (m) - { - std::string payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - payload = btdc.require("RC"); - } - catch (...) - { - log::warning(link_cat, "Failed to parse Find Router response!"); - throw; - } - - r->node_db()->put_rc_if_newer(RemoteRC{payload}); - msg.AddTXTReply(payload); - } - else - { - msg.AddNXReply(); - r->link_manager().handle_find_router_error(std::move(m)); - } - - reply(msg); - }); + if (auto rc = router()->node_db()->get_rc(snode)) + msg.AddTXTReply(std::string{rc->view()}); + else + msg.AddNXReply(); + reply(msg); return true; } @@ -705,10 +682,9 @@ namespace llarp::handlers { if (is_random_snode(msg)) { - RouterID random; - if (router()->GetRandomGoodRouter(random)) + if (auto random = router()->GetRandomGoodRouter()) { - msg.AddCNAMEReply(random.ToString(), 1); + msg.AddCNAMEReply(random->ToString(), 1); } else msg.AddNXReply(); @@ -755,11 +731,10 @@ namespace llarp::handlers // on MacOS this is a typeA query else if (is_random_snode(msg)) { - RouterID random; - if (router()->GetRandomGoodRouter(random)) + if (auto random = router()->GetRandomGoodRouter()) { - msg.AddCNAMEReply(random.ToString(), 1); - return ReplyToSNodeDNSWhenReady(random, std::make_shared(msg), isV6); + msg.AddCNAMEReply(random->ToString(), 1); + return ReplyToSNodeDNSWhenReady(*random, std::make_shared(msg), isV6); } msg.AddNXReply(); diff --git a/llarp/link/contacts.cpp b/llarp/link/contacts.cpp index de184ea52..a31a231db 100644 --- a/llarp/link/contacts.cpp +++ b/llarp/link/contacts.cpp @@ -71,13 +71,6 @@ namespace llarp return obj; } - bool - Contacts::lookup_router(const RouterID& rid, std::function func) - { - return _router.send_control_message( - rid, "find_router", FindRouterMessage::serialize(rid, false, false), std::move(func)); - } - void Contacts::put_rc_node_async(const dht::RCNode& val) { diff --git a/llarp/link/contacts.hpp b/llarp/link/contacts.hpp index c46c3a6c7..8423a346f 100644 --- a/llarp/link/contacts.hpp +++ b/llarp/link/contacts.hpp @@ -45,9 +45,6 @@ namespace llarp util::StatusObject ExtractStatus() const; - bool - lookup_router(const RouterID&, std::function = nullptr); - void put_rc_node_async(const dht::RCNode& val); diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index 6c6a10c48..6f75b3d8d 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -151,6 +150,11 @@ namespace llarp [this, &rid, msg = std::move(m)]() mutable { handle_path_control(std::move(msg), rid); }); }); + s->register_command("gossip_rc"s, [this, rid](oxen::quic::message m) { + _router.loop()->call( + [this, msg = std::move(m)]() mutable { handle_gossip_rc(std::move(msg)); }); + }); + for (auto& method : direct_requests) { s->register_command( @@ -243,21 +247,17 @@ namespace llarp return true; } - _router.loop()->call([this, remote, endpoint, body, f = std::move(func)]() { - auto pending = PendingControlMessage(body, endpoint, f); + _router.loop()->call([this, + remote, + endpoint = std::move(endpoint), + body = std::move(body), + f = std::move(func)]() { + auto pending = PendingControlMessage(std::move(body), std::move(endpoint), f); auto [itr, b] = pending_conn_msg_queue.emplace(remote, MessageQueue()); itr->second.push_back(std::move(pending)); - rc_lookup->get_rc(remote, [this]([[maybe_unused]] auto rid, auto rc, auto success) { - if (success) - { - _router.node_db()->put_rc_if_newer(*rc); - connect_to(*rc); - } - else - log::warning(quic_cat, "Do something intelligent here for error handling"); - }); + connect_to(remote); }); return false; @@ -275,21 +275,13 @@ namespace llarp return true; } - _router.loop()->call([&]() { + _router.loop()->call([this, body = std::move(body), remote]() { auto pending = PendingDataMessage(body); auto [itr, b] = pending_conn_msg_queue.emplace(remote, MessageQueue()); itr->second.push_back(std::move(pending)); - rc_lookup->get_rc(remote, [this]([[maybe_unused]] auto rid, auto rc, auto success) { - if (success) - { - _router.node_db()->put_rc_if_newer(*rc); - connect_to(*rc); - } - else - log::warning(quic_cat, "Do something intelligent here for error handling"); - }); + connect_to(remote); }); return false; @@ -304,15 +296,13 @@ namespace llarp void LinkManager::connect_to(const RouterID& rid) { - rc_lookup->get_rc(rid, [this]([[maybe_unused]] auto rid, auto rc, auto success) { - if (success) - { - _router.node_db()->put_rc_if_newer(*rc); - connect_to(*rc); - } - else - log::warning(quic_cat, "Do something intelligent here for error handling"); - }); + auto rc = node_db->get_rc(rid); + if (rc) + { + connect_to(*rc); + } + else + log::warning(quic_cat, "Do something intelligent here for error handling"); } // This function assumes the RC has already had its signature verified and connection is allowed. @@ -400,6 +390,43 @@ namespace llarp }); } + void + LinkManager::gossip_rc(const RouterID& rc_rid, std::string serialized_rc) + { + for (auto& [rid, conn] : ep.conns) + { + // don't send back to the owner... + if (rid == rc_rid) + continue; + // don't gossip RCs to clients + if (not conn->remote_is_relay) + continue; + + send_control_message(rid, "gossip_rc", serialized_rc); + } + } + + void + LinkManager::handle_gossip_rc(oxen::quic::message m) + { + try + { + RemoteRC rc{m.body()}; + + if (node_db->put_rc_if_newer(rc)) + { + log::info(link_cat, "Received updated RC, forwarding to relay peers."); + gossip_rc(rc.router_id(), m.body_str()); + } + else + log::debug(link_cat, "Received known or old RC, not storing or forwarding."); + } + catch (const std::exception& e) + { + log::info(link_cat, "Recieved invalid RC, dropping on the floor."); + } + } + bool LinkManager::have_connection_to(const RouterID& remote, bool client_only) const { @@ -486,10 +513,9 @@ namespace llarp } void - LinkManager::init(RCLookupHandler* rcLookup) + LinkManager::init() { is_stopping = false; - rc_lookup = rcLookup; node_db = _router.node_db(); } @@ -509,7 +535,7 @@ namespace llarp { exclude.insert(maybe_other->router_id()); - if (not rc_lookup->is_session_allowed(maybe_other->router_id())) + if (not node_db->is_connection_allowed(maybe_other->router_id())) continue; connect_to(*maybe_other); @@ -596,225 +622,6 @@ namespace llarp } } - void - LinkManager::handle_find_router(std::string_view body, std::function respond) - { - std::string target_key; - bool is_exploratory, is_iterative; - - try - { - oxenc::bt_dict_consumer btdc{body}; - - is_exploratory = btdc.require("E"); - is_iterative = btdc.require("I"); - target_key = btdc.require("K"); - } - catch (const std::exception& e) - { - log::warning(link_cat, "Exception: {}", e.what()); - respond(messages::ERROR_RESPONSE); - return; - } - - // TODO: do we need a replacement for dht.AllowTransit() etc here? - - RouterID target_rid; - target_rid.FromString(target_key); - - const auto target_addr = dht::Key_t{reinterpret_cast(target_key.data())}; - const auto& local_rid = _router.rc().router_id(); - const auto local_key = dht::Key_t{local_rid}; - - if (is_exploratory) - { - std::string neighbors{}; - - const auto closest_rcs = - _router.node_db()->find_many_closest_to(target_addr, RC_LOOKUP_STORAGE_REDUNDANCY); - - for (const auto& rc : closest_rcs) - { - const auto& rid = rc.router_id(); - if (_router.router_profiling().IsBadForConnect(rid) || target_rid == rid - || local_rid == rid) - continue; - - neighbors += rid.bt_encode(); - } - - respond(serialize_response( - {{messages::STATUS_KEY, FindRouterMessage::RETRY_EXP}, {"TARGET", neighbors}})); - } - else - { - const auto closest_rc = _router.node_db()->find_closest_to(target_addr); - const auto& closest_rid = closest_rc.router_id(); - const auto closest_key = dht::Key_t{closest_rid}; - - if (target_addr == closest_key) - { - if (closest_rc.expires_within_delta(llarp::time_now_ms())) - { - send_control_message( - target_rid, - "find_router", - FindRouterMessage::serialize(target_rid, false, false), - [respond = std::move(respond)](oxen::quic::message msg) mutable { - respond(msg.body_str()); - }); - } - else - { - respond(serialize_response({{"RC", closest_rc.view()}})); - } - } - else if (not is_iterative) - { - if ((closest_key ^ target_addr) < (local_key ^ target_addr)) - { - send_control_message( - closest_rid, - "find_router", - FindRouterMessage::serialize(closest_rid, false, false), - [respond = std::move(respond)](oxen::quic::message msg) mutable { - respond(msg.body_str()); - }); - } - else - { - respond(serialize_response( - {{messages::STATUS_KEY, FindRouterMessage::RETRY_ITER}, - {"TARGET", reinterpret_cast(target_addr.data())}})); - } - } - else - { - respond(serialize_response( - {{messages::STATUS_KEY, FindRouterMessage::RETRY_NEW}, - {"TARGET", reinterpret_cast(closest_rid.data())}})); - } - } - } - - void - LinkManager::handle_find_router_response(oxen::quic::message m) - { - if (m.timed_out) - { - log::info(link_cat, "FindRouterMessage timed out!"); - return; - } - - std::string status, payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - - if (m) - payload = btdc.require("RC"); - else - { - payload = btdc.require("RECIPIENT"); - status = btdc.require("TARGET"); - } - } - catch (const std::exception& e) - { - log::warning(link_cat, "Exception: {}", e.what()); - return; - } - - if (m) - { - _router.node_db()->put_rc_if_newer(RemoteRC{payload}); - } - else - { - if (status == "ERROR") - { - log::info(link_cat, "FindRouterMessage failed with remote exception!"); - // Do something smart here probably - return; - } - - RouterID target{reinterpret_cast(payload.data())}; - - if (status == FindRouterMessage::RETRY_EXP) - { - log::info(link_cat, "FindRouterMessage failed, retrying as exploratory!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, false, true)); - } - else if (status == FindRouterMessage::RETRY_ITER) - { - log::info(link_cat, "FindRouterMessage failed, retrying as iterative!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, true, false)); - } - else if (status == FindRouterMessage::RETRY_NEW) - { - log::info(link_cat, "FindRouterMessage failed, retrying with new recipient!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, false, false)); - } - } - } - - void - LinkManager::handle_find_router_error(oxen::quic::message&& m) - { - if (m.timed_out) - { - log::info(link_cat, "FindRouterMessage timed out!"); - return; - } - - std::string status, payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - - payload = btdc.require("RECIPIENT"); - status = btdc.require("TARGET"); - } - catch (const std::exception& e) - { - log::warning(link_cat, "Exception: {}", e.what()); - return; - } - - if (status == "ERROR") - { - log::info(link_cat, "FindRouterMessage failed with remote exception!"); - // Do something smart here probably - return; - } - - RouterID target{reinterpret_cast(payload.data())}; - - if (status == FindRouterMessage::RETRY_EXP) - { - log::info(link_cat, "FindRouterMessage failed, retrying as exploratory!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, false, true)); - } - else if (status == FindRouterMessage::RETRY_ITER) - { - log::info(link_cat, "FindRouterMessage failed, retrying as iterative!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, true, false)); - } - else if (status == FindRouterMessage::RETRY_NEW) - { - log::info(link_cat, "FindRouterMessage failed, retrying with new recipient!"); - send_control_message( - target, "find_router", FindRouterMessage::serialize(target, false, false)); - } - } - void LinkManager::handle_publish_intro(std::string_view body, std::function respond) { @@ -1606,7 +1413,7 @@ namespace llarp if (not hop) return; - // if terminal hop, payload should contain a request (e.g. "find_router"); handle and respond. + // if terminal hop, payload should contain a request (e.g. "find_name"); handle and respond. if (hop->terminal_hop) { hop->onion(payload, nonce, false); diff --git a/llarp/link/link_manager.hpp b/llarp/link/link_manager.hpp index 1f05c8c3b..9967123cc 100644 --- a/llarp/link/link_manager.hpp +++ b/llarp/link/link_manager.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ namespace namespace llarp { struct LinkManager; + class NodeDB; namespace link { @@ -176,7 +176,6 @@ namespace llarp util::DecayingHashSet clients{path::DEFAULT_LIFETIME}; - RCLookupHandler* rc_lookup; std::shared_ptr node_db; oxen::quic::Address addr; @@ -221,6 +220,12 @@ namespace llarp return addr; } + void + gossip_rc(const RouterID& rc_rid, std::string serialized_rc); + + void + handle_gossip_rc(oxen::quic::message m); + bool have_connection_to(const RouterID& remote, bool client_only = false) const; @@ -261,7 +266,7 @@ namespace llarp extract_status() const; void - init(RCLookupHandler* rcLookup); + init(); void for_each_connection(std::function func); @@ -288,9 +293,6 @@ namespace llarp handle_find_intro(std::string_view body, std::function respond); // relay void handle_publish_intro(std::string_view body, std::function respond); // relay - void - handle_find_router( - std::string_view body, std::function respond); // relay + path // Path messages void @@ -314,7 +316,6 @@ namespace llarp void (LinkManager::*)(std::string_view body, std::function respond)> path_requests = { {"find_name"sv, &LinkManager::handle_find_name}, - {"find_router"sv, &LinkManager::handle_find_router}, {"publish_intro"sv, &LinkManager::handle_publish_intro}, {"find_intro"sv, &LinkManager::handle_find_intro}}; /* @@ -327,14 +328,12 @@ namespace llarp */ // these requests are direct, i.e. not over a path; - // only "find_router" makes sense client->relay, // 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 respond)> direct_requests = { - {"find_router"sv, &LinkManager::handle_find_router}, {"publish_intro"sv, &LinkManager::handle_publish_intro}, {"find_intro"sv, &LinkManager::handle_find_intro}}; @@ -350,7 +349,6 @@ namespace llarp void handle_find_name_response(oxen::quic::message); void handle_find_intro_response(oxen::quic::message); void handle_publish_intro_response(oxen::quic::message); - void handle_find_router_response(oxen::quic::message); // Path responses void handle_path_latency_response(oxen::quic::message); @@ -363,18 +361,11 @@ namespace llarp std::unordered_map rpc_responses = { {"find_name", &LinkManager::handle_find_name_response}, - {"find_router", &LinkManager::handle_find_router_response}, {"publish_intro", &LinkManager::handle_publish_intro_response}, {"find_intro", &LinkManager::handle_find_intro_response}, {"update_exit", &LinkManager::handle_update_exit_response}, {"obtain_exit", &LinkManager::handle_obtain_exit_response}, {"close_exit", &LinkManager::handle_close_exit_response}}; - - public: - // Public response functions and error handling functions invoked elsehwere. These take - // r-value references s.t. that message is taken out of calling scope - void - handle_find_router_error(oxen::quic::message&& m); }; namespace link @@ -438,7 +429,6 @@ namespace llarp std::unordered_map rpc_commands = { {"find_name", &handle_find_name}, - {"find_router", &handle_find_router}, // ... }; diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index a5955003d..092bebb6d 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -14,9 +14,6 @@ static const std::string RC_FILE_EXT = ".signed"; namespace llarp { - NodeDB::Entry::Entry(RemoteRC value) : rc(std::move(value)), insertedAt(llarp::time_now_ms()) - {} - static void EnsureSkiplist(fs::path nodedbDir) { @@ -72,8 +69,8 @@ namespace llarp // make copy of all rcs std::vector copy; - for (const auto& item : entries) - copy.push_back(item.second.rc); + for (const auto& item : known_rcs) + copy.push_back(item.second); // flush them to disk in one big job // TODO: split this up? idk maybe some day... @@ -99,6 +96,81 @@ namespace llarp return m_Root / skiplistDir / fname; } + bool + NodeDB::want_rc(const RouterID& rid) const + { + if (not router.is_service_node()) + return true; + return registered_routers.count(rid); + } + + void + NodeDB::set_bootstrap_routers(const std::set& rcs) + { + bootstraps.clear(); // this function really shouldn't be called more than once, but... + for (const auto& rc : rcs) + bootstraps.emplace(rc.router_id(), rc); + } + + void + NodeDB::set_router_whitelist( + const std::vector& whitelist, + const std::vector& greylist, + const std::vector& greenlist) + { + if (whitelist.empty()) + return; + + registered_routers.clear(); + registered_routers.insert(whitelist.begin(), whitelist.end()); + registered_routers.insert(greylist.begin(), greylist.end()); + registered_routers.insert(greenlist.begin(), greenlist.end()); + + router_whitelist.clear(); + router_whitelist.insert(whitelist.begin(), whitelist.end()); + router_greylist.clear(); + router_greylist.insert(greylist.begin(), greylist.end()); + router_greenlist.clear(); + router_greenlist.insert(greenlist.begin(), greenlist.end()); + + log::info( + logcat, "lokinet service node list now has ", router_whitelist.size(), " active routers"); + } + + std::optional + NodeDB::get_random_whitelist_router() const + { + const auto sz = router_whitelist.size(); + if (sz == 0) + return std::nullopt; + auto itr = router_whitelist.begin(); + if (sz > 1) + std::advance(itr, randint() % sz); + return *itr; + } + + bool + NodeDB::is_connection_allowed(const RouterID& remote) const + { + if (pinned_edges.size() && pinned_edges.count(remote) == 0 && bootstraps.count(remote) == 0) + { + return false; + } + + if (not router.is_service_node()) + return true; + + return router_whitelist.count(remote) or router_greylist.count(remote); + } + + bool + NodeDB::is_first_hop_allowed(const RouterID& remote) const + { + if (pinned_edges.size() && pinned_edges.count(remote) == 0) + return false; + return true; + } + void NodeDB::load_from_disk() { @@ -137,10 +209,10 @@ namespace llarp return true; } - // validate signature and purge entries with invalid signatures + // validate signature and purge known_rcs with invalid signatures // load ones with valid signatures if (rc.verify()) - entries.emplace(rc.router_id(), rc); + known_rcs.emplace(rc.router_id(), rc); else purge.emplace(f); @@ -165,36 +237,33 @@ namespace llarp return; router.loop()->call([this]() { - for (const auto& item : entries) - item.second.rc.write(get_path_by_pubkey(item.first)); + for (const auto& item : known_rcs) + item.second.write(get_path_by_pubkey(item.first)); }); } bool - NodeDB::has_router(RouterID pk) const + NodeDB::has_rc(RouterID pk) const { - return router.loop()->call_get( - [this, pk]() -> bool { return entries.find(pk) != entries.end(); }); + return known_rcs.count(pk); } std::optional NodeDB::get_rc(RouterID pk) const { - return router.loop()->call_get([this, pk]() -> std::optional { - const auto itr = entries.find(pk); + const auto itr = known_rcs.find(pk); - if (itr == entries.end()) - return std::nullopt; + if (itr == known_rcs.end()) + return std::nullopt; - return itr->second.rc; - }); + return itr->second; } void NodeDB::remove_router(RouterID pk) { router.loop()->call([this, pk]() { - entries.erase(pk); + known_rcs.erase(pk); remove_many_from_disk_async({pk}); }); } @@ -202,54 +271,37 @@ namespace llarp void NodeDB::remove_stale_rcs(std::unordered_set keep, llarp_time_t cutoff) { - router.loop()->call([this, keep, cutoff]() { - std::unordered_set removed; - auto itr = entries.begin(); - while (itr != entries.end()) - { - if (itr->second.insertedAt < cutoff and keep.count(itr->second.rc.router_id()) == 0) - { - removed.insert(itr->second.rc.router_id()); - itr = entries.erase(itr); - } - else - ++itr; - } - if (not removed.empty()) - remove_many_from_disk_async(std::move(removed)); - }); + (void)keep; + (void)cutoff; + // TODO: handling of "stale" is pending change, removing here for now. } - void + bool NodeDB::put_rc(RemoteRC rc) { - router.loop()->call([this, rc]() { - const auto& rid = rc.router_id(); - entries.erase(rid); - entries.emplace(rid, rc); - }); + const auto& rid = rc.router_id(); + if (not want_rc(rid)) + return false; + known_rcs.erase(rid); + known_rcs.emplace(rid, std::move(rc)); + return true; } size_t NodeDB::num_loaded() const { - return router.loop()->call_get([this]() { return entries.size(); }); + return router.loop()->call_get([this]() { return known_rcs.size(); }); } - void + bool NodeDB::put_rc_if_newer(RemoteRC rc) { - router.loop()->call([this, rc]() { - auto itr = entries.find(rc.router_id()); - if (itr == entries.end() or itr->second.rc.other_is_newer(rc)) - { - // delete if existing - if (itr != entries.end()) - entries.erase(itr); - // add new entry - entries.emplace(rc.router_id(), rc); - } - }); + auto itr = known_rcs.find(rc.router_id()); + if (itr == known_rcs.end() or itr->second.other_is_newer(rc)) + { + return put_rc(std::move(rc)); + } + return false; } void @@ -296,10 +348,10 @@ namespace llarp return router.loop()->call_get([this, location, numRouters]() -> std::vector { std::vector all; - all.reserve(entries.size()); - for (auto& entry : entries) + all.reserve(known_rcs.size()); + for (auto& entry : known_rcs) { - all.push_back(&entry.second.rc); + all.push_back(&entry.second); } auto it_mid = numRouters < all.size() ? all.begin() + numRouters : all.end(); diff --git a/llarp/nodedb.hpp b/llarp/nodedb.hpp index 7552fc6fd..e126004b8 100644 --- a/llarp/nodedb.hpp +++ b/llarp/nodedb.hpp @@ -24,16 +24,7 @@ namespace llarp class NodeDB { - struct Entry - { - const RemoteRC rc; - llarp_time_t insertedAt; - explicit Entry(RemoteRC rc); - }; - - using NodeMap = std::unordered_map; - - NodeMap entries; + std::unordered_map known_rcs; const Router& router; const fs::path m_Root; @@ -49,14 +40,95 @@ namespace llarp fs::path get_path_by_pubkey(RouterID pk) const; + std::unordered_map bootstraps; + + // whitelist = active routers + std::unordered_set router_whitelist; + // greylist = fully funded, but decommissioned routers + std::unordered_set router_greylist; + // greenlist = registered but not fully-staked routers + std::unordered_set router_greenlist; + + // all registered relays (snodes) + std::unordered_set registered_routers; + + // only ever use to specific edges as path first-hops + std::unordered_set pinned_edges; + + bool + want_rc(const RouterID& rid) const; + public: + void + set_bootstrap_routers(const std::set& rcs); + + const std::unordered_set& + whitelist() const + { + return router_whitelist; + } + + const std::unordered_set& + greylist() const + { + return router_greylist; + } + + const std::unordered_set& + get_registered_routers() const + { + return registered_routers; + } + + void + set_router_whitelist( + const std::vector& whitelist, + const std::vector& greylist, + const std::vector& greenlist); + + std::optional + get_random_whitelist_router() const; + + // client: + // if pinned edges were specified, connections are allowed only to those and + // to the configured bootstrap nodes. otherwise, always allow. + // + // relay: + // outgoing connections are allowed only to other registered, funded relays + // (whitelist and greylist, respectively). + bool + is_connection_allowed(const RouterID& remote) const; + + // client: + // same as is_connection_allowed + // + // server: + // we only build new paths through registered, not decommissioned relays + // (i.e. whitelist) + bool + is_path_allowed(const RouterID& remote) const + { + return router_whitelist.count(remote); + } + + // if pinned edges were specified, the remote must be in that set, else any remote + // is allowed as first hop. + bool + is_first_hop_allowed(const RouterID& remote) const; + + const std::unordered_set& + get_pinned_edges() const + { + return pinned_edges; + } + explicit NodeDB( fs::path rootdir, std::function)> diskCaller, Router* r); /// in memory nodedb NodeDB(); - /// load all entries from disk syncrhonously + /// load all known_rcs from disk syncrhonously void load_from_disk(); @@ -82,7 +154,7 @@ namespace llarp /// return true if we have an rc by its ident pubkey bool - has_router(RouterID pk) const; + has_rc(RouterID pk) const; /// maybe get an rc by its ident pubkey std::optional @@ -93,44 +165,30 @@ namespace llarp GetRandom(Filter visit) const { return router.loop()->call_get([visit]() -> std::optional { - std::vector entries; - for (const auto& entry : entries) - entries.push_back(entry); + std::vector known_rcs; + for (const auto& entry : known_rcs) + known_rcs.push_back(entry); - std::shuffle(entries.begin(), entries.end(), llarp::csrng); + std::shuffle(known_rcs.begin(), known_rcs.end(), llarp::csrng); - for (const auto entry : entries) + for (const auto entry : known_rcs) { - if (visit(entry->second.rc)) - return entry->second.rc; + if (visit(entry->second)) + return entry->second; } return std::nullopt; }); } - /// visit all entries + /// visit all known_rcs template void VisitAll(Visit visit) const { router.loop()->call([this, visit]() { - for (const auto& item : entries) - visit(item.second.rc); - }); - } - - /// visit all entries inserted before a timestamp - template - void - VisitInsertedBefore(Visit visit, llarp_time_t insertedBefore) - { - router.loop()->call([this, visit, insertedBefore]() { - for (const auto& item : entries) - { - if (item.second.insertedAt < insertedBefore) - visit(item.second.rc); - } + for (const auto& item : known_rcs) + visit(item.second); }); } @@ -145,13 +203,13 @@ namespace llarp { router.loop()->call([this, visit]() { std::unordered_set removed; - auto itr = entries.begin(); - while (itr != entries.end()) + auto itr = known_rcs.begin(); + while (itr != known_rcs.end()) { - if (visit(itr->second.rc)) + if (visit(itr->second)) { - removed.insert(itr->second.rc.router_id()); - itr = entries.erase(itr); + removed.insert(itr->second.router_id()); + itr = known_rcs.erase(itr); } else ++itr; @@ -165,12 +223,14 @@ namespace llarp void remove_stale_rcs(std::unordered_set keep, llarp_time_t cutoff); - /// put this rc into the cache if it is not there or newer than the one there already - void - put_rc_if_newer(RemoteRC rc); - - /// unconditional put of rc into cache - void + /// put (or replace) the RC if we consider it valid (want_rc). returns true if put. + bool put_rc(RemoteRC rc); + + /// 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); }; } // namespace llarp diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index 4bdf31ec6..73de6465f 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -82,13 +82,6 @@ namespace llarp::path "find_name", FindNameMessage::serialize(std::move(name)), std::move(func)); } - bool - Path::find_router(std::string rid, std::function func) - { - return send_path_control_message( - "find_router", FindRouterMessage::serialize(std::move(rid), false, false), std::move(func)); - } - bool Path::send_path_control_message( std::string method, std::string body, std::function func) diff --git a/llarp/path/path.hpp b/llarp/path/path.hpp index 3be47b7f9..290f10600 100644 --- a/llarp/path/path.hpp +++ b/llarp/path/path.hpp @@ -176,9 +176,6 @@ namespace llarp bool find_name(std::string name, std::function func = nullptr); - bool - find_router(std::string rid, std::function func = nullptr); - bool find_intro( const dht::Key_t& location, diff --git a/llarp/path/pathbuilder.cpp b/llarp/path/pathbuilder.cpp index 2f51e5fae..49b080cd0 100644 --- a/llarp/path/pathbuilder.cpp +++ b/llarp/path/pathbuilder.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -575,30 +574,6 @@ namespace llarp router->router_profiling().MarkPathTimeout(p.get()); PathSet::HandlePathBuildTimeout(p); DoPathBuildBackoff(); - for (const auto& hop : p->hops) - { - const auto& target = hop.rc.router_id(); - // look up router and see if it's still on the network - log::info(path_cat, "Looking up RouterID {} due to path build timeout", target); - - router->rc_lookup_handler().get_rc( - target, - [this](auto rid, auto rc, auto success) { - if (success && rc) - { - log::info(path_cat, "Refreshed RouterContact for {}", rid); - router->node_db()->put_rc_if_newer(*rc); - } - else - { - // remove all connections to this router as it's probably not registered anymore - log::warning(path_cat, "Removing router {} due to path build timeout", rid); - router->link_manager().deregister_peer(rid); - router->node_db()->remove_router(rid); - } - }, - true); - } } void diff --git a/llarp/router/rc_gossiper.cpp b/llarp/router/rc_gossiper.cpp deleted file mode 100644 index a5da35bd3..000000000 --- a/llarp/router/rc_gossiper.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "rc_gossiper.hpp" - -#include -#include - -namespace llarp -{ - // 30 minutes - static constexpr auto RCGossipFilterDecayInterval = 30min; - // (30 minutes * 2) - 5 minutes - static constexpr auto GossipOurRCInterval = (RCGossipFilterDecayInterval * 2) - (5min); - - RCGossiper::RCGossiper() : filter(std::chrono::duration_cast(RCGossipFilterDecayInterval)) - {} - - void - RCGossiper::Init(LinkManager* l, const RouterID& ourID, Router* r) - { - rid = ourID; - link_manager = l; - router = r; - } - - bool - RCGossiper::ShouldGossipOurRC(Time_t now) const - { - return now >= (last_rc_gossip + GossipOurRCInterval); - } - - bool - RCGossiper::IsOurRC(const LocalRC& rc) const - { - return rc.router_id() == rid; - } - - void - RCGossiper::Decay(Time_t now) - { - filter.Decay(now); - } - - void - RCGossiper::Forget(const RouterID& pk) - { - filter.Remove(pk); - if (rid == pk) - last_rc_gossip = 0s; - } - - TimePoint_t - RCGossiper::NextGossipAt() const - { - if (auto maybe = LastGossipAt()) - return *maybe + GossipOurRCInterval; - return DateClock_t::now(); - } - - std::optional - RCGossiper::LastGossipAt() const - { - if (last_rc_gossip == 0s) - return std::nullopt; - return DateClock_t::time_point{last_rc_gossip}; - } - - bool - RCGossiper::GossipRC(const LocalRC& rc) - { - // only distribute public routers - if (not rc.is_public_router()) - return false; - if (link_manager == nullptr) - return false; - const RouterID pubkey(rc.router_id()); - // filter check - if (filter.Contains(pubkey)) - return false; - filter.Insert(pubkey); - - const auto now = time_now_ms(); - // is this our rc? - if (IsOurRC(rc)) - { - // should we gossip our rc? - if (not ShouldGossipOurRC(now)) - { - // nah drop it - return false; - } - // ya pop it - last_rc_gossip = now; - } - - // send a GRCM as gossip method - // DHTImmediateMessage gossip; - // gossip.msgs.emplace_back(new dht::GotRouterMessage(dht::Key_t{}, 0, {rc}, false)); - - // std::vector gossipTo; - - /* - * TODO: gossip RC via libquic - * - // select peers to gossip to - m_LinkManager->ForEachPeer( - [&](const AbstractLinkSession* peerSession, bool) { - // ensure connected session - if (not(peerSession && peerSession->IsEstablished())) - return; - // check if public router - const auto other_rc = peerSession->GetRemoteRC(); - if (not other_rc.IsPublicRouter()) - return; - gossipTo.emplace_back(other_rc.pubkey); - }, - true); - - std::unordered_set keys; - // grab the keys we want to use - std::sample( - gossipTo.begin(), gossipTo.end(), std::inserter(keys, keys.end()), MaxGossipPeers, - llarp::csrng); - - m_LinkManager->ForEachPeer([&](AbstractLinkSession* peerSession) { - if (not(peerSession && peerSession->IsEstablished())) - return; - - // exclude from gossip as we have not selected to use it - if (keys.count(peerSession->GetPubKey()) == 0) - return; - - // encode message - AbstractLinkSession::Message_t msg{}; - msg.resize(MAX_LINK_MSG_SIZE / 2); - llarp_buffer_t buf(msg); - if (not gossip.BEncode(&buf)) - return; - msg.resize(buf.cur - buf.base); - - m_router->NotifyRouterEvent(m_router->pubkey(), rc); - - // send message - peerSession->SendMessageBuffer(std::move(msg), nullptr, gossip.Priority()); - }); - * - * - */ - - return true; - } - -} // namespace llarp diff --git a/llarp/router/rc_gossiper.hpp b/llarp/router/rc_gossiper.hpp deleted file mode 100644 index a0c72fc80..000000000 --- a/llarp/router/rc_gossiper.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace llarp -{ - struct Router; - - /// The maximum number of peers we will flood a gossiped RC to when propagating an RC - constexpr size_t MaxGossipPeers = 20; - struct LinkManager; - struct LocalRC; - - struct RCGossiper - { - using Time_t = Duration_t; - - RCGossiper(); - - ~RCGossiper() = default; - - bool - GossipRC(const LocalRC& rc); - - void - Decay(Time_t now); - - bool - ShouldGossipOurRC(Time_t now) const; - - bool - IsOurRC(const LocalRC& rc) const; - - void - Init(LinkManager*, const RouterID&, Router*); - - void - Forget(const RouterID& router); - - TimePoint_t - NextGossipAt() const; - - std::optional - LastGossipAt() const; - - private: - RouterID rid; - Time_t last_rc_gossip = 0s; - LinkManager* link_manager = nullptr; - util::DecayingHashSet filter; - - Router* router; - }; -} // namespace llarp diff --git a/llarp/router/rc_lookup_handler.cpp b/llarp/router/rc_lookup_handler.cpp deleted file mode 100644 index c2df0c833..000000000 --- a/llarp/router/rc_lookup_handler.cpp +++ /dev/null @@ -1,357 +0,0 @@ -#include "rc_lookup_handler.hpp" - -#include "router.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace llarp -{ - void - RCLookupHandler::add_valid_router(const RouterID& rid) - { - router->loop()->call([this, rid]() { router_whitelist.insert(rid); }); - } - - void - RCLookupHandler::remove_valid_router(const RouterID& rid) - { - router->loop()->call([this, rid]() { router_whitelist.erase(rid); }); - } - - static void - loadColourList(std::unordered_set& beigelist, const std::vector& new_beige) - { - beigelist.clear(); - beigelist.insert(new_beige.begin(), new_beige.end()); - } - - void - RCLookupHandler::set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& greenlist) - { - if (whitelist.empty()) - return; - - router->loop()->call([this, whitelist, greylist, greenlist]() { - loadColourList(router_whitelist, whitelist); - loadColourList(router_greylist, greylist); - loadColourList(router_greenlist, greenlist); - LogInfo("lokinet service node list now has ", router_whitelist.size(), " active routers"); - }); - } - - bool - RCLookupHandler::has_received_whitelist() const - { - return router->loop()->call_get([this]() { return not router_whitelist.empty(); }); - } - - std::unordered_set - RCLookupHandler::whitelist() const - { - return router->loop()->call_get([this]() { return router_whitelist; }); - } - - void - RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup) - { - (void)rid; - (void)callback; - (void)forceLookup; - /* RC refactor pending, this will likely go away entirely - * - * - RemoteRC remoteRC; - - if (not forceLookup) - { - if (const auto maybe = node_db->get_rc(rid); maybe.has_value()) - { - remoteRC = *maybe; - - if (callback) - { - callback(rid, remoteRC, true); - } - - return; - } - } - - auto lookup_cb = [this, callback, rid](RemoteRC rc, bool success) mutable { - auto& r = link_manager->router(); - - if (not success) - { - if (callback) - callback(rid, std::nullopt, false); - return; - } - - r.node_db()->put_rc_if_newer(rc); - if (callback) - callback(rc.router_id(), rc, true); - }; - - // TODO: RC fetching and gossiping in general is being refactored, and the old method - // of look it up over a path or directly but using the same method but called - // differently is going away. It's a mess. The below will do a lookup via a path, - // relays will need a different implementation TBD. - if (!isServiceNode) - hidden_service_context->GetDefault()->lookup_router(rid, std::move(lookup_cb)); - */ - } - - bool - RCLookupHandler::is_grey_listed(const RouterID& remote) const - { - if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(remote) == 0 - && !is_remote_in_bootstrap(remote)) - { - return false; - } - - if (not isServiceNode) - return false; - - return router->loop()->call_get([this, remote]() { return router_greylist.count(remote); }); - } - - bool - RCLookupHandler::is_green_listed(const RouterID& remote) const - { - return router->loop()->call_get([this, remote]() { return router_greenlist.count(remote); }); - } - - bool - RCLookupHandler::is_registered(const RouterID& rid) const - { - return router->loop()->call_get([this, rid]() { - return router_whitelist.count(rid) || router_greylist.count(rid) - || router_greenlist.count(rid); - }); - } - - bool - RCLookupHandler::is_path_allowed(const RouterID& rid) const - { - return router->loop()->call_get([this, rid]() { - if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(rid) == 0 - && !is_remote_in_bootstrap(rid)) - { - return false; - } - - if (not isServiceNode) - return true; - - return router_whitelist.count(rid) != 0; - }); - } - - bool - RCLookupHandler::is_session_allowed(const RouterID& rid) const - { - return router->loop()->call_get([this, rid]() { - if (strict_connect_pubkeys.size() && strict_connect_pubkeys.count(rid) == 0 - && !is_remote_in_bootstrap(rid)) - { - return false; - } - - if (not isServiceNode) - return true; - - return router_whitelist.count(rid) or router_greylist.count(rid); - }); - } - - bool - RCLookupHandler::check_rc(const RemoteRC& rc) const - { - if (not is_session_allowed(rc.router_id())) - { - contacts->delete_rc_node_async(dht::Key_t{rc.router_id()}); - return false; - } - - if (not rc.verify()) - { - log::info(link_cat, "Invalid RC (rid: {})", rc.router_id()); - return false; - } - - // update nodedb if required - if (rc.is_public_router()) - { - log::info(link_cat, "Adding or updating RC (rid: {}) to nodeDB and DHT", rc.router_id()); - node_db->put_rc_if_newer(rc); - contacts->put_rc_node_async(rc); - } - - return true; - } - - size_t - RCLookupHandler::num_strict_connect_routers() const - { - return strict_connect_pubkeys.size(); - } - - bool - RCLookupHandler::get_random_whitelist_router(RouterID& rid) const - { - return router->loop()->call_get([this, rid]() mutable { - const auto sz = router_whitelist.size(); - auto itr = router_whitelist.begin(); - if (sz == 0) - return false; - if (sz > 1) - std::advance(itr, randint() % sz); - rid = *itr; - return true; - }); - } - - void - RCLookupHandler::periodic_update(llarp_time_t now) - { - // try looking up stale routers - std::unordered_set routersToLookUp; - - node_db->VisitInsertedBefore( - [&](const RouterContact& rc) { routersToLookUp.insert(rc.router_id()); }, - now - RouterContact::REPUBLISH); - - for (const auto& router : routersToLookUp) - { - get_rc(router, nullptr, true); - } - - node_db->remove_stale_rcs(boostrap_rid_list, now - RouterContact::STALE); - } - - void - RCLookupHandler::explore_network() - { - const size_t known = node_db->num_loaded(); - if (bootstrap_rc_list.empty() && known == 0) - { - LogError("we have no bootstrap nodes specified"); - } - else if (known <= bootstrap_rc_list.size()) - { - for (const auto& rc : bootstrap_rc_list) - { - const auto& rid = rc.router_id(); - log::info(link_cat, "Doing explore via bootstrap node: {}", rid); - - // TODO: replace this concept - // dht->ExploreNetworkVia(dht::Key_t{rc.pubkey}); - } - } - - if (isServiceNode) - { - static constexpr size_t LookupPerTick = 5; - - std::vector lookup_routers = router->loop()->call_get([this]() { - std::vector lookups; - lookups.reserve(LookupPerTick); - - for (const auto& r : router_whitelist) - { - if (not node_db->has_router(r)) - lookups.emplace_back(r); - } - - return lookups; - }); - - if (lookup_routers.size() > LookupPerTick) - { - std::shuffle(lookup_routers.begin(), lookup_routers.end(), llarp::csrng); - lookup_routers.resize(LookupPerTick); - } - - for (const auto& r : lookup_routers) - get_rc(r, nullptr, true); - return; - } - // service nodes gossip, not explore - if (contacts->router()->is_service_node()) - return; - - // explore via every connected peer - /* - * TODO: DHT explore via libquic - * - _linkManager->ForEachPeer([&](ILinkSession* s) { - if (!s->IsEstablished()) - return; - const RouterContact rc = s->GetRemoteRC(); - if (rc.IsPublicRouter() && (_bootstrapRCList.find(rc) == _bootstrapRCList.end())) - { - LogDebug("Doing explore via public node: ", RouterID(rc.pubkey)); - _dht->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey}); - } - }); - * - * - */ - } - - void - RCLookupHandler::init( - std::shared_ptr c, - std::shared_ptr nodedb, - EventLoop_ptr l, - std::function)> dowork, - LinkManager* linkManager, - service::Context* hiddenServiceContext, - const std::unordered_set& strictConnectPubkeys, - const std::set& bootstrapRCList, - bool isServiceNode_arg) - { - contacts = c; - node_db = std::move(nodedb); - loop = std::move(l); - work_func = std::move(dowork); - hidden_service_context = hiddenServiceContext; - strict_connect_pubkeys = strictConnectPubkeys; - bootstrap_rc_list = bootstrapRCList; - link_manager = linkManager; - router = &link_manager->router(); - isServiceNode = isServiceNode_arg; - - for (const auto& rc : bootstrap_rc_list) - { - boostrap_rid_list.insert(rc.router_id()); - } - } - - bool - RCLookupHandler::is_remote_in_bootstrap(const RouterID& remote) const - { - for (const auto& rc : bootstrap_rc_list) - { - if (rc.router_id() == remote) - { - return true; - } - } - return false; - } - -} // namespace llarp diff --git a/llarp/router/rc_lookup_handler.hpp b/llarp/router/rc_lookup_handler.hpp deleted file mode 100644 index f7b3f01f4..000000000 --- a/llarp/router/rc_lookup_handler.hpp +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -struct llarp_dht_context; - -namespace llarp -{ - class NodeDB; - struct Router; - class EventLoop; - - namespace service - { - struct Context; - } // namespace service - - struct Contacts; - struct LinkManager; - - enum class RCRequestResult - { - Success, - InvalidRouter, - RouterNotFound, - BadRC - }; - - using RCRequestCallback = - std::function, bool success)>; - - struct RCLookupHandler - { - public: - ~RCLookupHandler() = default; - - void - add_valid_router(const RouterID& router); - - void - remove_valid_router(const RouterID& router); - - void - set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& greenlist); - - bool - has_received_whitelist() const; - - void - get_rc(const RouterID& router, RCRequestCallback callback, bool forceLookup = false); - - bool - is_path_allowed(const RouterID& remote) const; - - bool - is_session_allowed(const RouterID& remote) const; - - bool - is_grey_listed(const RouterID& remote) const; - - // "greenlist" = new routers (i.e. "green") that aren't fully funded yet - bool - is_green_listed(const RouterID& remote) const; - - // registered just means that there is at least an operator stake, but doesn't require the node - // be fully funded, active, or not decommed. (In other words: it is any of the white, grey, or - // green list). - bool - is_registered(const RouterID& remote) const; - - bool - check_rc(const RemoteRC& rc) const; - - bool - get_random_whitelist_router(RouterID& router) const; - - void - periodic_update(llarp_time_t now); - - void - explore_network(); - - size_t - num_strict_connect_routers() const; - - void - init( - std::shared_ptr contacts, - std::shared_ptr nodedb, - std::shared_ptr loop, - std::function)> dowork, - LinkManager* linkManager, - service::Context* hiddenServiceContext, - const std::unordered_set& strictConnectPubkeys, - const std::set& bootstrapRCList, - bool isServiceNode_arg); - - std::unordered_set - whitelist() const; - - private: - bool - is_remote_in_bootstrap(const RouterID& remote) const; - - std::shared_ptr contacts = nullptr; - std::shared_ptr node_db; - std::shared_ptr loop; - std::function)> work_func = nullptr; - service::Context* hidden_service_context = nullptr; - LinkManager* link_manager = nullptr; - Router* router; - - /// explicit whitelist of routers we will connect to directly (not for - /// service nodes) - std::unordered_set strict_connect_pubkeys; - - std::set bootstrap_rc_list; - std::unordered_set boostrap_rid_list; - - // Now that all calls are made through the event loop, any access to these - // booleans is not guarded by a mutex - std::atomic isServiceNode = false; - - // whitelist = active routers - std::unordered_set router_whitelist; - // greylist = fully funded, but decommissioned routers - std::unordered_set router_greylist; - // greenlist = registered but not fully-staked routers - std::unordered_set router_greenlist; - }; - -} // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 6c4f51eac..8eb2c33c8 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -227,32 +227,19 @@ namespace llarp _link_manager.set_conn_persist(remote, until); } - void - Router::GossipRCIfNeeded(const LocalRC rc) - { - /// if we are not a service node forget about gossip - if (not is_service_node()) - return; - /// wait for random uptime - if (std::chrono::milliseconds{Uptime()} < _randomStartDelay) - return; - _rcGossiper.GossipRC(rc); - } - - bool - Router::GetRandomGoodRouter(RouterID& router) + std::optional + Router::GetRandomGoodRouter() { if (is_service_node()) { - return _rc_lookup_handler.get_random_whitelist_router(router); + return node_db()->get_random_whitelist_router(); } if (auto maybe = node_db()->GetRandom([](const auto&) -> bool { return true; })) { - router = maybe->router_id(); - return true; + return maybe->router_id(); } - return false; + return std::nullopt; } void @@ -273,16 +260,6 @@ namespace llarp _link_manager.connect_to(rc); } - void - Router::lookup_router(RouterID rid, std::function func) - { - _link_manager.send_control_message( - rid, - "find_router", - FindRouterMessage::serialize(std::move(rid), false, false), - std::move(func)); - } - bool Router::send_data_message(const RouterID& remote, std::string payload) { @@ -479,25 +456,25 @@ namespace llarp bool Router::have_snode_whitelist() const { - return is_service_node() and _rc_lookup_handler.has_received_whitelist(); + return whitelist_received; } bool Router::appears_decommed() const { - return have_snode_whitelist() and _rc_lookup_handler.is_grey_listed(pubkey()); + return have_snode_whitelist() and node_db()->greylist().count(pubkey()); } bool Router::appears_funded() const { - return have_snode_whitelist() and _rc_lookup_handler.is_session_allowed(pubkey()); + return have_snode_whitelist() and node_db()->is_connection_allowed(pubkey()); } bool Router::appears_registered() const { - return have_snode_whitelist() and _rc_lookup_handler.is_registered(pubkey()); + return have_snode_whitelist() and node_db()->get_registered_routers().count(pubkey()); } bool @@ -509,7 +486,7 @@ namespace llarp bool Router::SessionToRouterAllowed(const RouterID& router) const { - return _rc_lookup_handler.is_session_allowed(router); + return node_db()->is_connection_allowed(router); } bool @@ -520,7 +497,7 @@ namespace llarp // we are decom'd don't allow any paths outbound at all return false; } - return _rc_lookup_handler.is_path_allowed(router); + return node_db()->is_path_allowed(router); } size_t @@ -542,16 +519,6 @@ namespace llarp queue_disk_io([&]() { router_contact.write(our_rc_file); }); } - bool - Router::update_rc() - { - router_contact.resign(); - if (is_service_node()) - save_rc(); - - return true; - } - bool Router::from_config(const Config& conf) { @@ -679,23 +646,15 @@ namespace llarp it = bootstrap_rc_list.erase(it); } + 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(&_rc_lookup_handler); - _rc_lookup_handler.init( - _contacts, - _node_db, - _loop, - [this](std::function work) { queue_work(std::move(work)); }, - &_link_manager, - &_hidden_service_context, - strictConnectPubkeys, - bootstrap_rc_list, - _is_service_node); + _link_manager.init(); // FIXME: kludge for now, will be part of larger cleanup effort. if (_is_service_node) @@ -796,12 +755,12 @@ namespace llarp " | {} active paths | block {} ", path_context().CurrentTransitPaths(), (_rpc_client ? _rpc_client->BlockHeight() : 0)); - auto maybe_last = _rcGossiper.LastGossipAt(); + bool have_gossiped = last_rc_gossip == std::chrono::system_clock::time_point::min(); fmt::format_to( out, " | gossip: (next/last) {} / {}", - short_time_from_now(_rcGossiper.NextGossipAt()), - maybe_last ? short_time_from_now(*maybe_last) : "never"); + short_time_from_now(next_rc_gossip), + have_gossiped ? short_time_from_now(last_rc_gossip) : "never"); } else { @@ -854,36 +813,28 @@ namespace llarp report_stats(); } - _rcGossiper.Decay(now); - - _rc_lookup_handler.periodic_update(now); - - const bool has_whitelist = _rc_lookup_handler.has_received_whitelist(); const bool is_snode = is_service_node(); const bool is_decommed = appears_decommed(); - bool should_gossip = appears_funded(); - if (is_snode - and (router_contact.expires_within_delta(now, std::chrono::milliseconds(randint() % 10000)) - or (now - router_contact.timestamp().time_since_epoch()) > rc_regen_interval)) - { - LogInfo("regenerating RC"); - if (update_rc()) - { - // our rc changed so we should gossip it - should_gossip = true; - // remove our replay entry so it goes out - _rcGossiper.Forget(pubkey()); - } - else - LogError("failed to update our RC"); - } - if (should_gossip) + // (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)) { - // if we have the whitelist enabled, we have fetched the list and we are in either - // the white or grey list, we want to gossip our RC - GossipRCIfNeeded(router_contact); + 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(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{60, 300}(llarp::csrng)}; + next_rc_gossip = now_timepoint + RouterContact::STALE_AGE - random_delta; } + // 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 @@ -915,7 +866,7 @@ namespace llarp } // if we don't have the whitelist yet don't remove the entry - if (not has_whitelist) + if (not whitelist_received) { log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.router_id()); return false; @@ -924,7 +875,7 @@ namespace llarp // the whitelist enabled and we got the whitelist // check against the whitelist and remove if it's not // in the whitelist OR if there is no whitelist don't remove - if (has_whitelist and not _rc_lookup_handler.is_session_allowed(rc.router_id())) + if (not node_db()->is_connection_allowed(rc.router_id())) { log::debug(logcat, "Removing {}: not a valid router", rc.router_id()); return true; @@ -932,7 +883,9 @@ namespace llarp return false; }); - if (not is_snode or not has_whitelist) + /* TODO: this behavior seems incorrect, but fixing it will require discussion + * + if (not is_snode or not whitelist_received) { // find all deregistered relays std::unordered_set close_peers; @@ -948,23 +901,18 @@ namespace llarp for (auto& peer : close_peers) _link_manager.deregister_peer(peer); } + */ _link_manager.check_persisting_conns(now); size_t connected = NumberOfConnectedRouters(); - const int interval = is_snode ? 5 : 2; - const auto timepoint_now = std::chrono::steady_clock::now(); - if (timepoint_now >= _next_explore_at and not is_decommed) - { - _rc_lookup_handler.explore_network(); - _next_explore_at = timepoint_now + std::chrono::seconds(interval); - } size_t connectToNum = _link_manager.min_connected_routers; - const auto strictConnect = _rc_lookup_handler.num_strict_connect_routers(); - if (strictConnect > 0 && connectToNum > strictConnect) + const auto& pinned_edges = _node_db->get_pinned_edges(); + const auto pinned_count = pinned_edges.size(); + if (pinned_count > 0 && connectToNum > pinned_count) { - connectToNum = strictConnect; + connectToNum = pinned_count; } if (is_snode and now >= _next_decomm_warning) @@ -1012,14 +960,6 @@ namespace llarp _node_db->Tick(now); - std::set peer_keys; - - for_each_connection( - [&peer_keys](link::Connection& conn) { peer_keys.emplace(conn.remote_rc.router_id()); }); - - _contacts->rc_nodes()->RemoveIf( - [&peer_keys](const dht::Key_t& k) -> bool { return peer_keys.count(k) == 0; }); - paths.ExpirePaths(now); // update tick timestamp @@ -1032,13 +972,20 @@ namespace llarp return _link_manager.get_random_connected(result); } + const std::unordered_set& + Router::get_whitelist() const + { + return _node_db->whitelist(); + } + void Router::set_router_whitelist( const std::vector& whitelist, const std::vector& greylist, const std::vector& unfundedlist) { - _rc_lookup_handler.set_router_whitelist(whitelist, greylist, unfundedlist); + node_db()->set_router_whitelist(whitelist, greylist, unfundedlist); + whitelist_received = true; } bool @@ -1076,7 +1023,6 @@ namespace llarp log::info(logcat, "Router initialized as service node!"); const RouterID us = pubkey(); - _rcGossiper.Init(&_link_manager, us, this); // relays do not use profiling router_profiling().Disable(); } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 9a2973477..edf7cabad 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -1,7 +1,5 @@ #pragma once -#include "rc_gossiper.hpp" -#include "rc_lookup_handler.hpp" #include "route_poker.hpp" #include @@ -59,8 +57,6 @@ namespace llarp static constexpr size_t INTROSET_STORAGE_REDUNDANCY = (INTROSET_RELAY_REDUNDANCY * INTROSET_REQS_PER_RELAY); - static constexpr size_t RC_LOOKUP_STORAGE_REDUNDANCY{4}; - struct Contacts; struct Router : std::enable_shared_from_this @@ -122,17 +118,16 @@ namespace llarp const llarp_time_t _randomStartDelay; std::shared_ptr _rpc_client; + bool whitelist_received{false}; oxenmq::address rpc_addr; Profiling _router_profiling; fs::path _profile_file; LinkManager _link_manager{*this}; - RCLookupHandler _rc_lookup_handler; - RCGossiper _rcGossiper; - - /// how often do we resign our RC? milliseconds. - // TODO: make configurable - llarp_time_t rc_regen_interval = 1h; + 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()}; // should we be sending padded messages every interval? bool send_padding = false; @@ -150,9 +145,6 @@ namespace llarp void save_rc(); - bool - update_rc(); - bool from_config(const Config& conf); @@ -163,9 +155,6 @@ namespace llarp void for_each_connection(std::function func); - void - lookup_router(RouterID rid, std::function = nullptr); - void connect_to(const RouterID& rid); @@ -211,12 +200,6 @@ namespace llarp return _link_manager; } - RCLookupHandler& - rc_lookup_handler() - { - return _rc_lookup_handler; - } - inline int outbound_udp_socket() const { @@ -292,11 +275,8 @@ namespace llarp util::StatusObject ExtractSummaryStatus() const; - std::unordered_set - router_whitelist() const - { - return _rc_lookup_handler.whitelist(); - } + const std::unordered_set& + get_whitelist() const; void set_router_whitelist( @@ -380,17 +360,14 @@ namespace llarp std::string status_line(); - void - GossipRCIfNeeded(const LocalRC rc); - void InitInboundLinks(); void InitOutboundLinks(); - bool - GetRandomGoodRouter(RouterID& r); + std::optional + GetRandomGoodRouter(); /// initialize us as a service node /// return true on success diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index 49aa96cce..b65aa2c76 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -56,19 +56,18 @@ namespace llarp /// Timespans for RCs: - /// How long (relative to its timestamp) before an RC becomes stale. Stale records are used - /// (e.g. for path building) only if there are no non-stale records available, such as might be + /// How long (from its signing time) before an RC is considered "stale". Relays republish + /// their RCs slightly more frequently than this so that ideally this won't happen. + static constexpr auto STALE_AGE = 6h; + + /// How long (from its signing time) before an RC becomes "outdated". Outdated records are used + /// (e.g. for path building) only if there are no newer records available, such as might be /// the case when a client has been turned off for a while. - static constexpr auto STALE = 12h; + static constexpr auto OUTDATED_AGE = 12h; /// How long before an RC becomes invalid (and thus deleted). static constexpr auto LIFETIME = 30 * 24h; - /// How long before a relay updates and re-publish its RC to the network. (Relays can - /// re-publish more frequently than this if needed; this is meant to apply only if there are no - /// changes i.e. just to push out a new confirmation of the details). - static constexpr auto REPUBLISH = STALE / 2 - 5min; - ustring_view view() const { diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index bf0d759c4..dabc654da 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -846,56 +846,6 @@ namespace llarp::service } } - void - Endpoint::EnsureRouterIsKnown(const RouterID& rid) - { - if (rid.IsZero()) - return; - if (!router()->node_db()->has_router(rid)) - { - lookup_router(rid); - } - } - - bool - Endpoint::lookup_router(RouterID rid, std::function func) - { - (void)rid; - (void)func; - return false; - /* RC refactor pending, this will likely go away entirely - * - * - auto path = GetEstablishedPathClosestTo(rid); - - auto response_cb = [func = std::move(func)](std::string resp, bool timeout) { - if (timeout) - func(RouterContact{}, false); - - std::string payload; - - try - { - oxenc::bt_dict_consumer btdc{resp}; - payload = btdc.require("RC"); - } - catch (...) - { - log::warning(link_cat, "Failed to parse Find Router response!"); - func(RouterContact{}, false); - return; - } - - RouterContact result{std::move(payload)}; - - func(result, true); - }; - - path->find_router("find_router", std::move(response_cb)); - return true; - */ - } - void Endpoint::HandlePathBuilt(path::Path_ptr p) { @@ -1279,7 +1229,8 @@ namespace llarp::service this); _state->snode_sessions[snode] = session; } - EnsureRouterIsKnown(snode); + if (not router()->node_db()->has_rc(snode)) + return false; auto range = nodeSessions.equal_range(snode); auto itr = range.first; while (itr != range.second) diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 83f30ece7..266c0bf1f 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -229,14 +229,6 @@ namespace llarp bool ProcessDataMessage(std::shared_ptr msg); - /// ensure that we know a router, looks up if it doesn't - void - EnsureRouterIsKnown(const RouterID& router); - - // "find router" via closest path - bool - lookup_router(RouterID router, std::function func = nullptr); - // "find name" void lookup_name(std::string name, std::function func = nullptr) override; diff --git a/llarp/service/outbound_context.cpp b/llarp/service/outbound_context.cpp index 9fbcfb476..fe0218416 100644 --- a/llarp/service/outbound_context.cpp +++ b/llarp/service/outbound_context.cpp @@ -5,6 +5,7 @@ #include "endpoint_util.hpp" #include "protocol_type.hpp" +#include #include #include @@ -316,9 +317,6 @@ namespace llarp::service } } } - // lookup router in intro if set and unknown - if (not next_intro.router.IsZero()) - ep.EnsureRouterIsKnown(next_intro.router); if (ReadyToSend()) { @@ -407,6 +405,18 @@ namespace llarp::service std::vector intros = current_intro.intros; + // don't consider intros for which we don't have the RC for the pivot + auto itr = intros.begin(); + while (itr != intros.end()) + { + if (not ep.router()->node_db()->has_rc(itr->router)) + { + itr = intros.erase(itr); + continue; + } + itr++; + } + if (intros.size() > 1) { std::shuffle(intros.begin(), intros.end(), llarp::csrng); @@ -435,7 +445,6 @@ namespace llarp::service { if (ep.SnodeBlacklist().count(intro.router)) continue; - ep.EnsureRouterIsKnown(intro.router); if (intro.ExpiresSoon(now)) continue; if (next_intro != intro)