lokinet/llarp/router/router.cpp

1355 lines
35 KiB
C++
Raw Normal View History

#include <memory>
#include <router/router.hpp>
#include <config/config.hpp>
2019-09-01 12:10:49 +00:00
#include <constants/proto.hpp>
#include <constants/files.hpp>
#include <crypto/crypto_libsodium.hpp>
2019-09-01 12:10:49 +00:00
#include <crypto/crypto.hpp>
#include <dht/context.hpp>
2019-01-19 18:16:40 +00:00
#include <dht/node.hpp>
2019-03-29 15:17:49 +00:00
#include <iwp/iwp.hpp>
2018-12-12 01:32:10 +00:00
#include <link/server.hpp>
#include <messages/link_message.hpp>
#include <net/net.hpp>
#include <net/route.hpp>
#include <stdexcept>
#include <util/buffer.hpp>
2019-09-01 12:10:49 +00:00
#include <util/logging/file_logger.hpp>
#include <util/logging/json_logger.hpp>
#include <util/logging/logger_syslog.hpp>
#include <util/logging/logger.hpp>
2019-09-01 12:38:03 +00:00
#include <util/meta/memfn.hpp>
#include <util/str.hpp>
2019-09-05 17:39:09 +00:00
#include <ev/ev.hpp>
2020-06-11 19:02:34 +00:00
#include <tooling/peer_stats_event.hpp>
2017-11-28 14:05:31 +00:00
#include "tooling/router_event.hpp"
#include "util/status.hpp"
2018-05-20 17:45:47 +00:00
#include <fstream>
2018-10-09 01:38:25 +00:00
#include <cstdlib>
#include <iterator>
#include <unordered_map>
2019-09-01 12:10:49 +00:00
#include <utility>
#if defined(ANDROID) || defined(IOS)
2018-11-08 12:31:50 +00:00
#include <unistd.h>
#endif
2020-02-25 22:32:57 +00:00
#if defined(WITH_SYSTEMD)
#include <systemd/sd-daemon.h>
#endif
2020-05-19 18:53:03 +00:00
#include <lokimq/lokimq.h>
static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 1s;
2018-05-20 17:45:47 +00:00
namespace llarp
{
Router::Router(llarp_ev_loop_ptr __netloop, std::shared_ptr<Logic> l)
: ready(false)
2020-05-19 18:53:03 +00:00
, m_lmq(std::make_shared<lokimq::LokiMQ>())
2019-07-30 23:42:13 +00:00
, _netloop(std::move(__netloop))
, _logic(std::move(l))
, paths(this)
, _exitContext(this)
, _dht(llarp_dht_context_new(this))
, m_DiskThread(m_lmq->add_tagged_thread("disk"))
, inbound_link_msg_parser(this)
2019-02-22 16:21:05 +00:00
, _hiddenServiceContext(this)
, m_RPCServer(new rpc::RpcServer(m_lmq, this))
#ifdef LOKINET_HIVE
, _randomStartDelay(std::chrono::milliseconds((llarp::randint() % 1250) + 2000))
#else
2020-02-05 16:16:46 +00:00
, _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10))
#endif
2020-05-20 11:41:42 +00:00
, m_lokidRpcClient(std::make_shared<rpc::LokidRpcClient>(m_lmq, this))
2018-06-20 17:45:44 +00:00
{
m_keyManager = std::make_shared<KeyManager>();
2020-10-02 09:51:52 +00:00
// for lokid, so we don't close the connection when syncing the whitelist
m_lmq->MAX_MSG_SIZE = -1;
_stopping.store(false);
_running.store(false);
_lastTick = llarp::time_now_ms();
2020-01-18 20:47:36 +00:00
m_NextExploreAt = Clock_t::now();
2018-06-20 17:45:44 +00:00
}
Router::~Router()
2018-09-19 13:27:15 +00:00
{
llarp_dht_context_free(_dht);
2018-09-19 13:27:15 +00:00
}
2019-02-11 17:14:43 +00:00
util::StatusObject
Router::ExtractStatus() const
2019-02-08 19:43:25 +00:00
{
if (_running)
{
util::StatusObject peerStatsObj = nullptr;
if (m_peerDb)
peerStatsObj = m_peerDb->ExtractStatus();
2020-06-25 21:59:42 +00:00
return util::StatusObject{{"running", true},
{"numNodesKnown", _nodedb->num_loaded()},
{"dht", _dht->impl->ExtractStatus()},
{"services", _hiddenServiceContext.ExtractStatus()},
{"exit", _exitContext.ExtractStatus()},
{"links", _linkManager.ExtractStatus()},
{"outboundMessages", _outboundMessageHandler.ExtractStatus()},
{"peerStats", peerStatsObj}};
}
else
{
2019-10-04 19:43:30 +00:00
return util::StatusObject{{"running", false}};
}
2019-02-08 19:43:25 +00:00
}
bool
Router::HandleRecvLinkMessageBuffer(ILinkSession* session, const llarp_buffer_t& buf)
{
if (_stopping)
return true;
if (!session)
{
LogWarn("no link session");
return false;
}
return inbound_link_msg_parser.ProcessFrom(session, buf);
2018-10-07 15:29:36 +00:00
}
void
Router::PersistSessionUntil(const RouterID& remote, llarp_time_t until)
2018-10-07 15:29:36 +00:00
{
_linkManager.PersistSessionUntil(remote, until);
2018-06-01 14:08:54 +00:00
}
2020-01-30 17:23:16 +00:00
void
Router::GossipRCIfNeeded(const RouterContact rc)
{
if (disableGossipingRC_TestingOnly())
return;
2020-01-30 17:23:16 +00:00
/// if we are not a service node forget about gossip
if (not IsServiceNode())
2020-01-30 17:23:16 +00:00
return;
/// wait for random uptime
if (std::chrono::milliseconds{Uptime()} < _randomStartDelay)
2020-01-30 17:23:16 +00:00
return;
_rcGossiper.GossipRC(rc);
}
2018-12-13 00:03:19 +00:00
bool
Router::GetRandomGoodRouter(RouterID& router)
2018-12-13 00:03:19 +00:00
{
if (whitelistRouters)
{
return _rcLookupHandler.GetRandomWhitelistRouter(router);
}
auto pick_router = [&](auto& collection) -> bool {
2019-05-14 14:36:18 +00:00
const auto sz = collection.size();
auto itr = collection.begin();
if (sz == 0)
return false;
if (sz > 1)
std::advance(itr, randint() % sz);
router = itr->first;
return true;
2019-05-14 14:36:18 +00:00
};
util::Lock l{nodedb()->access};
2019-05-14 14:36:18 +00:00
return pick_router(nodedb()->entries);
2018-12-13 00:03:19 +00:00
}
2019-04-30 16:07:17 +00:00
void
Router::PumpLL()
{
llarp::LogTrace("Router::PumpLL() start");
if (_stopping.load())
return;
paths.PumpDownstream();
2019-11-04 18:25:05 +00:00
paths.PumpUpstream();
_outboundMessageHandler.Tick();
_linkManager.PumpLinks();
llarp::LogTrace("Router::PumpLL() end");
2019-04-30 16:07:17 +00:00
}
bool
Router::SendToOrQueue(const RouterID& remote, const ILinkMessage* msg, SendStatusHandler handler)
2018-06-14 15:10:31 +00:00
{
return _outboundMessageHandler.QueueMessage(remote, msg, handler);
}
void
Router::ForEachPeer(std::function<void(const ILinkSession*, bool)> visit, bool randomize) const
{
_linkManager.ForEachPeer(visit, randomize);
}
void
Router::ForEachPeer(std::function<void(ILinkSession*)> visit)
{
_linkManager.ForEachPeer(visit);
}
void
Router::try_connect(fs::path rcfile)
2018-05-16 18:13:18 +00:00
{
RouterContact remote;
if (!remote.Read(rcfile.string().c_str()))
{
LogError("failure to decode or verify of remote RC");
return;
}
if (remote.Verify(Now()))
{
LogDebug("verified signature");
_outboundSessionMaker.CreateSessionTo(remote, nullptr);
}
else
LogError(rcfile, " contains invalid RC");
2018-05-16 18:13:18 +00:00
}
2018-05-20 17:45:47 +00:00
bool
Router::EnsureIdentity()
{
2020-05-20 11:41:42 +00:00
_encryption = m_keyManager->encryptionKey;
if (whitelistRouters)
{
#if defined(ANDROID) || defined(IOS)
LogError("running a service node on mobile device is not possible.");
return false;
#else
#if defined(_WIN32)
LogError("running a service node on windows is not possible.");
return false;
#endif
#endif
constexpr int maxTries = 5;
int numTries = 0;
while (numTries < maxTries)
{
numTries++;
try
{
_identity = RpcClient()->ObtainIdentityKey();
LogWarn("Obtained lokid identity keys");
break;
}
catch (const std::exception& e)
{
LogWarn(
"Failed attempt ",
numTries,
" of ",
maxTries,
" to get lokid identity keys because: ",
e.what());
if (numTries == maxTries)
throw;
}
}
2020-05-20 11:41:42 +00:00
}
else
{
_identity = m_keyManager->identityKey;
}
if (_identity.IsZero())
return false;
if (_encryption.IsZero())
return false;
return true;
}
2018-01-29 14:27:24 +00:00
bool
2020-08-27 12:43:53 +00:00
Router::Configure(std::shared_ptr<Config> c, bool isRouter, llarp_nodedb* nodedb)
{
2020-08-27 12:43:53 +00:00
m_Config = c;
auto& conf = *m_Config;
whitelistRouters = conf.lokid.whitelistRouters;
if (whitelistRouters)
lokidRPCAddr = lokimq::address(conf.lokid.lokidRPCAddr);
enableRPCServer = conf.api.m_enableRPCServer;
if (enableRPCServer)
rpcBindAddr = lokimq::address(conf.api.m_rpcBindAddr);
2020-05-20 11:41:42 +00:00
if (not StartRpcServer())
throw std::runtime_error("Failed to start rpc server");
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
if (conf.router.m_workerThreads > 0)
m_lmq->set_general_threads(conf.router.m_workerThreads);
2020-05-20 11:41:42 +00:00
m_lmq->start();
_nodedb = nodedb;
m_isServiceNode = conf.router.m_isRelay;
2020-05-20 11:41:42 +00:00
if (whitelistRouters)
{
m_lokidRpcClient->ConnectAsync(lokidRPCAddr);
2020-05-20 11:41:42 +00:00
}
// fetch keys
if (not m_keyManager->initialize(conf, true, isRouter))
throw std::runtime_error("KeyManager failed to initialize");
if (!FromConfig(conf))
throw std::runtime_error("FromConfig() failed");
if (!InitOutboundLinks())
throw std::runtime_error("InitOutboundLinks() failed");
if (not EnsureIdentity())
throw std::runtime_error("EnsureIdentity() failed");
m_RoutePoker.Init(this);
return true;
}
2018-11-21 14:10:02 +00:00
2019-03-25 15:41:37 +00:00
/// called in disk worker thread
void
Router::HandleSaveRC() const
2019-03-25 15:41:37 +00:00
{
std::string fname = our_rc_file.string();
_rc.Write(fname.c_str());
2019-03-25 15:41:37 +00:00
}
bool
Router::SaveRC()
2018-08-18 14:01:21 +00:00
{
LogDebug("verify RC signature");
if (!_rc.Verify(Now()))
{
Dump<MAX_RC_SIZE>(rc());
LogError("RC is invalid, not saving");
return false;
}
QueueDiskIO([&]() { HandleSaveRC(); });
2019-03-25 15:41:37 +00:00
return true;
2018-06-20 12:34:48 +00:00
}
2018-05-29 13:40:26 +00:00
bool
Router::IsServiceNode() const
{
return m_isServiceNode;
}
2018-05-30 20:56:47 +00:00
void
Router::Close()
{
2020-08-21 19:09:13 +00:00
if (_onDown)
_onDown();
LogInfo("closing router");
2019-06-02 21:17:05 +00:00
llarp_ev_loop_stop(_netloop);
2020-02-28 01:23:36 +00:00
_running.store(false);
}
void
Router::handle_router_ticker()
2018-11-28 15:18:18 +00:00
{
ticker_job_id = 0;
2020-02-24 19:40:45 +00:00
Tick();
ScheduleTicker(ROUTER_TICK_INTERVAL);
2018-11-28 15:18:18 +00:00
}
bool
Router::ParseRoutingMessageBuffer(
const llarp_buffer_t& buf, routing::IMessageHandler* h, const PathID_t& rxid)
{
return inbound_routing_msg_parser.ParseMessageBuffer(buf, h, rxid, this);
}
bool
Router::ConnectionToRouterAllowed(const RouterID& router) const
{
return _rcLookupHandler.RemoteIsAllowed(router);
2019-05-09 15:36:39 +00:00
}
size_t
Router::NumberOfConnectedRouters() const
{
return _linkManager.NumberOfConnectedRouters();
2019-05-09 15:36:39 +00:00
}
size_t
Router::NumberOfConnectedClients() const
{
return _linkManager.NumberOfConnectedClients();
}
2018-06-19 17:11:24 +00:00
bool
Router::UpdateOurRC(bool rotateKeys)
{
SecretKey nextOnionKey;
RouterContact nextRC = _rc;
if (rotateKeys)
{
CryptoManager::instance()->encryption_keygen(nextOnionKey);
2019-01-29 13:20:27 +00:00
std::string f = encryption_keyfile.string();
2019-03-25 15:41:37 +00:00
// TODO: use disk worker
if (nextOnionKey.SaveToFile(f.c_str()))
2019-01-29 13:20:27 +00:00
{
nextRC.enckey = seckey_topublic(nextOnionKey);
_encryption = nextOnionKey;
2019-01-29 13:20:27 +00:00
}
}
if (!nextRC.Sign(identity()))
return false;
if (!nextRC.Verify(time_now_ms(), false))
return false;
_rc = std::move(nextRC);
if (rotateKeys)
2020-01-21 17:31:48 +00:00
{
// propagate RC by renegotiating sessions
ForEachPeer([](ILinkSession* s) {
if (s->RenegotiateSession())
2020-01-21 17:31:48 +00:00
LogInfo("renegotiated session");
else
LogWarn("failed to renegotiate session");
});
}
if (IsServiceNode())
return SaveRC();
else
return true;
2019-03-25 15:41:37 +00:00
}
2019-07-12 17:21:29 +00:00
bool
Router::FromConfig(const Config& conf)
{
2019-07-07 11:29:44 +00:00
// Set netid before anything else
if (!conf.router.m_netId.empty() && strcmp(conf.router.m_netId.c_str(), llarp::DEFAULT_NETID))
{
const auto& netid = conf.router.m_netId;
llarp::LogWarn(
"!!!! you have manually set netid to be '",
netid,
"' which does not equal '",
llarp::DEFAULT_NETID,
"' you will run as a different network, good luck "
"and don't forget: something something MUH traffic "
"shape correlation !!!!");
NetID::DefaultValue() = NetID(reinterpret_cast<const byte_t*>(netid.c_str()));
2019-07-07 11:29:44 +00:00
// reset netid in our rc
_rc.netID = llarp::NetID();
}
2019-07-07 11:29:44 +00:00
// IWP config
m_OutboundPort = conf.links.m_OutboundLink.port;
2019-07-12 17:21:29 +00:00
// Router config
_rc.SetNick(conf.router.m_nickname);
_outboundSessionMaker.maxConnectedRouters = conf.router.m_maxConnectedRouters;
_outboundSessionMaker.minConnectedRouters = conf.router.m_minConnectedRouters;
2020-06-04 18:38:35 +00:00
encryption_keyfile = m_keyManager->m_encKeyPath;
our_rc_file = m_keyManager->m_rcPath;
transport_keyfile = m_keyManager->m_transportKeyPath;
ident_keyfile = m_keyManager->m_idKeyPath;
_ourAddress = conf.router.m_publicAddress;
RouterContact::BlockBogons = conf.router.m_blockBogons;
2019-08-26 23:29:17 +00:00
2019-07-12 17:21:29 +00:00
// Lokid Config
whitelistRouters = conf.lokid.whitelistRouters;
lokidRPCAddr = lokimq::address(conf.lokid.lokidRPCAddr);
2019-07-12 17:21:29 +00:00
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
m_isServiceNode = conf.router.m_isRelay;
2020-06-04 18:38:35 +00:00
auto& networkConfig = conf.network;
2020-05-04 16:33:44 +00:00
2020-04-27 15:24:05 +00:00
/// build a set of strictConnectPubkeys (
/// TODO: make this consistent with config -- do we support multiple strict connections
// or not?
std::set<RouterID> strictConnectPubkeys;
2020-04-27 15:24:05 +00:00
if (not networkConfig.m_strictConnect.empty())
{
2020-04-27 15:24:05 +00:00
const auto& val = networkConfig.m_strictConnect;
if (IsServiceNode())
throw std::runtime_error("cannot use strict-connect option as service node");
2020-04-27 15:24:05 +00:00
// try as a RouterID and as a PubKey, convert to RouterID if needed
llarp::RouterID snode;
llarp::PubKey pk;
if (pk.FromString(val))
2020-04-27 15:24:05 +00:00
strictConnectPubkeys.emplace(pk);
else if (snode.FromString(val))
2020-04-27 15:24:05 +00:00
strictConnectPubkeys.insert(snode);
else
2020-04-27 15:24:05 +00:00
throw std::invalid_argument(stringify("invalid key for strict-connect: ", val));
}
std::vector<fs::path> configRouters = conf.connect.routers;
configRouters.insert(
configRouters.end(), conf.bootstrap.routers.begin(), conf.bootstrap.routers.end());
// if our conf had no bootstrap files specified, try the default location of
// <DATA_DIR>/bootstrap.signed. If this isn't present, leave a useful error message
if (configRouters.empty())
{
// TODO: use constant
fs::path defaultBootstrapFile = conf.router.m_dataDir / "bootstrap.signed";
if (fs::exists(defaultBootstrapFile))
2020-06-08 12:42:10 +00:00
{
configRouters.push_back(defaultBootstrapFile);
2020-06-08 12:42:10 +00:00
}
else if (not conf.bootstrap.seednode)
{
LogError("No bootstrap files specified in config file, and the default");
LogError("bootstrap file ", defaultBootstrapFile, " does not exist.");
2020-04-29 20:19:48 +00:00
LogError("Please provide a bootstrap file (e.g. run 'lokinet-bootstrap)'");
throw std::runtime_error("No bootstrap files available.");
}
}
BootstrapList b_list;
for (const auto& router : configRouters)
{
bool isListFile = false;
{
std::ifstream inf(router.c_str(), std::ios::binary);
if (inf.is_open())
{
const char ch = inf.get();
isListFile = ch == 'l';
}
}
if (isListFile)
{
if (not BDecodeReadFile(router, b_list))
{
throw std::runtime_error(stringify("failed to read bootstrap list file '", router, "'"));
}
}
else
{
RouterContact rc;
if (not rc.Read(router))
{
throw std::runtime_error(
stringify("failed to decode bootstrap RC, file='", router, "' rc=", rc));
}
b_list.insert(rc);
}
}
for (auto& rc : b_list)
{
if (not rc.Verify(Now()))
{
LogWarn("ignoring invalid RC: ", RouterID(rc.pubkey));
continue;
}
bootstrapRCList.emplace(std::move(rc));
}
LogInfo("Loaded ", bootstrapRCList.size(), " bootstrap routers");
// Init components after relevant config settings loaded
_outboundMessageHandler.Init(&_linkManager, &_rcLookupHandler, _logic);
_outboundSessionMaker.Init(
2020-06-15 17:37:57 +00:00
this,
&_linkManager,
&_rcLookupHandler,
&_routerProfiling,
_logic,
_nodedb,
util::memFn(&AbstractRouter::QueueWork, this));
_linkManager.Init(&_outboundSessionMaker);
_rcLookupHandler.Init(
_dht,
_nodedb,
util::memFn(&AbstractRouter::QueueWork, this),
&_linkManager,
&_hiddenServiceContext,
strictConnectPubkeys,
bootstrapRCList,
whitelistRouters,
m_isServiceNode);
std::vector<LinksConfig::LinkInfo> inboundLinks = conf.links.m_InboundLinks;
if (inboundLinks.empty() and m_isServiceNode)
{
const auto& publicAddr = conf.router.m_publicAddress;
if (publicAddr.isEmpty() or not publicAddr.hasPort())
{
throw std::runtime_error(
"service node enabled but could not find a public IP to bind to; you need to set the "
"public-ip= and public-port= options");
}
inboundLinks.push_back(LinksConfig::LinkInfo{"0.0.0.0", AF_INET, *publicAddr.getPort()});
}
// create inbound links, if we are a service node
for (const LinksConfig::LinkInfo& serverConfig : inboundLinks)
{
auto server = iwp::NewInboundLink(
m_keyManager,
util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
2019-08-07 16:33:29 +00:00
util::memFn(&AbstractRouter::Sign, this),
nullptr,
util::memFn(&Router::ConnectionEstablished, this),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&Router::ConnectionTimedOut, this),
util::memFn(&AbstractRouter::SessionClosed, this),
util::memFn(&AbstractRouter::PumpLL, this),
util::memFn(&AbstractRouter::QueueWork, this));
2020-03-19 22:20:45 +00:00
const std::string& key = serverConfig.interface;
int af = serverConfig.addressFamily;
uint16_t port = serverConfig.port;
if (!server->Configure(netloop(), key, af, port))
{
throw std::runtime_error(stringify("failed to bind inbound link on ", key, " port ", port));
}
_linkManager.AddLink(std::move(server), true);
}
// profiling
_profilesFile = conf.router.m_dataDir / "profiles.dat";
// Network config
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
if (conf.network.m_enableProfiling.value_or(false))
2018-12-19 17:48:29 +00:00
{
LogInfo("router profiling enabled");
if (not fs::exists(_profilesFile))
{
LogInfo("no profiles file at ", _profilesFile, " skipping");
}
else
{
LogInfo("loading router profiles from ", _profilesFile);
routerProfiling().Load(_profilesFile);
}
2018-12-19 17:48:29 +00:00
}
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
else
{
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
routerProfiling().Disable();
LogInfo("router profiling disabled");
}
// API config
if (not IsServiceNode())
{
hiddenServiceContext().AddEndpoint(conf);
}
2020-05-26 17:03:21 +00:00
// peer stats
Config file improvements (#1397) * Config file API/comment improvements API improvements: ================= Make the config API use position-independent tag parameters (Required, Default{123}, MultiValue) rather than a sequence of bools with overloads. For example, instead of: conf.defineOption<int>("a", "b", false, true, 123, [] { ... }); you now write: conf.defineOption<int>("a", "b", MultiValue, Default{123}, [] { ... }); The tags are: - Required - MultiValue - Default{value} plus new abilities (see below): - Hidden - RelayOnly - ClientOnly - Comment{"line1", "line2", "line3"} Made option definition more powerful: ===================================== - `Hidden` allows you to define an option that won't show up in the generated config file if it isn't set. - `RelayOnly`/`ClientOnly` sets up an option that is only accepted and only shows up for relay or client configs. (If neither is specified the option shows up in both modes). - `Comment{...}` lets the option comments be specified as part of the defineOption. Comment improvements ==================== - Rewrote comments for various options to expand on details. - Inlined all the comments with the option definitions. - Several options that were missing comments got comments added. - Made various options for deprecated and or internal options hidden by default so that they don't show up in a default config file. - show the section comment (but not option comments) *after* the [section] tag instead of before it as it makes more sense that way (particularly for the [bind] section which has a new long comment to describe how it works). Disable profiling by default ============================ We had this weird state where we use and store profiling by default but never *load* it when starting up. This commit makes us just not use profiling at all unless explicitly enabled. Other misc changes: =================== - change default worker threads to 0 (= num cpus) instead of 1, and fix it to allow 0. - Actually apply worker-threads option - fixed default data-dir value erroneously having quotes around it - reordered ifname/ifaddr/mapaddr (was previously mapaddr/ifaddr/ifname) as mapaddr is a sort of specialization of ifaddr and so makes more sense to come after it (particularly because it now references ifaddr in its help message). - removed peer-stats option (since we always require it for relays and never use it for clients) - removed router profiles filename option (this doesn't need to be configurable) - removed defunct `service-node-seed` option - Change default logging output file to "" (which means stdout), and also made "-" work for stdout. * Router hive compilation fixes * Comments for SNApp SRV settings in ini file * Add extra blank line after section comments * Better deprecated option handling Allow {client,relay}-only options in {relay,client} configs to be specified as implicitly deprecated options: they warn, and don't set anything. Add an explicit `Deprecated` tag and move deprecated option handling into definition.cpp. * Move backwards compat options into section definitions Keep the "addBackwardsCompatibleConfigOptions" only for options in sections that no longer exist. * Fix INI parsing issues & C++17-ify - don't allow inline comments because it seems they aren't allowed in ini formats in general, and is going to cause problems if there is a comment character in a value (e.g. an exit auth string). Additionally it was breaking on a line such as: # some comment; see? because it was treating only `; see?` as the comment and then producing an error message about the rest of the line being invalid. - make section parsing stricter: the `[` and `]` have to be at the beginning at end of the line now (after stripping whitespace). - Move whitespace stripping to the top since everything in here does it. - chop off string_view suffix/prefix rather than maintaining position values - fix potential infinite loop/segfault when given a line such as `]foo[` * Make config parsing failure fatal Load() LogError's and returns false on failure, so we weren't aborting on config file errors. * Formatting: allow `{}` for empty functions/structs Instead of using two lines when empty: { } * Make default dns bind 127.0.0.1 on non-Linux * Don't show empty section; fix tests We can conceivably have sections that only make sense for clients or relays, and so want to completely omit that section if we have no options for the type of config being generated. Also fixes missing empty lines between tests. Co-authored-by: Thomas Winget <tewinget@gmail.com>
2020-10-07 22:22:58 +00:00
if (IsServiceNode())
2020-05-26 17:03:21 +00:00
{
LogInfo("Initializing peerdb...");
2020-05-27 01:57:27 +00:00
m_peerDb = std::make_shared<PeerDb>();
m_peerDb->configure(conf.router);
}
2020-05-26 17:03:21 +00:00
// Logging config
LogContext::Instance().Initialize(
conf.logging.m_logLevel,
conf.logging.m_logType,
conf.logging.m_logFile,
conf.router.m_nickname,
util::memFn(&AbstractRouter::QueueDiskIO, this));
2018-12-19 17:48:29 +00:00
return true;
}
bool
Router::CheckRenegotiateValid(RouterContact newrc, RouterContact oldrc)
2019-03-31 15:09:59 +00:00
{
return _rcLookupHandler.CheckRenegotiateValid(newrc, oldrc);
2019-03-31 15:09:59 +00:00
}
bool
2019-05-09 15:36:39 +00:00
Router::IsBootstrapNode(const RouterID r) const
{
2019-05-09 15:36:39 +00:00
return std::count_if(
bootstrapRCList.begin(),
bootstrapRCList.end(),
[r](const RouterContact& rc) -> bool { return rc.pubkey == r; })
2019-05-09 15:36:39 +00:00
> 0;
}
2019-04-05 14:58:22 +00:00
2019-07-15 16:56:09 +00:00
bool
Router::ShouldReportStats(llarp_time_t now) const
{
2020-02-24 19:40:45 +00:00
static constexpr auto ReportStatsInterval = 1h;
2019-07-15 16:56:09 +00:00
return now - m_LastStatsReport > ReportStatsInterval;
}
void
Router::ReportStats()
{
const auto now = Now();
LogInfo(nodedb()->num_loaded(), " RCs loaded");
LogInfo(bootstrapRCList.size(), " bootstrap peers");
LogInfo(NumberOfConnectedRouters(), " router connections");
if (IsServiceNode())
2019-07-15 16:56:09 +00:00
{
LogInfo(NumberOfConnectedClients(), " client connections");
LogInfo(_rc.Age(now), " since we last updated our RC");
LogInfo(_rc.TimeUntilExpires(now), " until our RC expires");
2019-07-15 16:56:09 +00:00
}
if (m_LastStatsReport > 0s)
LogInfo(now - m_LastStatsReport, " last reported stats");
2019-07-15 16:56:09 +00:00
m_LastStatsReport = now;
}
void
Router::Tick()
2018-06-19 17:11:24 +00:00
{
if (_stopping)
2019-04-23 18:29:42 +00:00
return;
// LogDebug("tick router");
2019-11-05 16:58:53 +00:00
const auto now = Now();
2018-12-19 17:48:29 +00:00
#if defined(WITH_SYSTEMD)
2020-02-25 22:32:57 +00:00
{
std::stringstream ss;
ss << "WATCHDOG=1\nSTATUS=v" << llarp::VERSION_STR;
if (IsServiceNode())
2020-02-25 22:32:57 +00:00
{
ss << " snode | known/svc/clients: " << nodedb()->num_loaded() << "/"
<< NumberOfConnectedRouters() << "/" << NumberOfConnectedClients() << " | "
<< pathContext().CurrentTransitPaths() << " active paths";
2020-02-25 22:32:57 +00:00
}
else
{
ss << " client | known/connected: " << nodedb()->num_loaded() << "/"
<< NumberOfConnectedRouters() << " | path success: ";
hiddenServiceContext().ForEachService([&ss](const auto& name, const auto& ep) {
ss << " [" << name << " " << std::setprecision(4)
<< (100.0 * ep->CurrentBuildStats().SuccessRatio()) << "%]";
return true;
});
2020-02-25 22:32:57 +00:00
}
const auto status = ss.str();
::sd_notify(0, status.c_str());
}
#endif
2019-03-04 17:03:18 +00:00
routerProfiling().Tick();
if (ShouldReportStats(now))
2019-07-15 16:56:09 +00:00
{
ReportStats();
}
2020-02-24 19:40:45 +00:00
_rcGossiper.Decay(now);
2020-01-30 17:23:16 +00:00
_rcLookupHandler.PeriodicUpdate(now);
2019-07-15 16:56:09 +00:00
const bool isSvcNode = IsServiceNode();
if (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000))
|| (now - _rc.last_updated) > rcRegenInterval)
2018-12-19 17:48:29 +00:00
{
LogInfo("regenerating RC");
if (!UpdateOurRC(false))
LogError("Failed to update our RC");
}
else
{
GossipRCIfNeeded(_rc);
2020-01-30 17:23:16 +00:00
}
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
2020-03-08 12:09:48 +00:00
// remove RCs for nodes that are no longer allowed by network policy
nodedb()->RemoveIf([&](const RouterContact& rc) -> bool {
2020-03-09 15:08:56 +00:00
// don't purge bootstrap nodes from nodedb
if (IsBootstrapNode(rc.pubkey))
2020-03-08 12:09:48 +00:00
return false;
// if for some reason we stored an RC that isn't a valid router
// purge this entry
if (not rc.IsPublicRouter())
2020-03-08 12:09:48 +00:00
return true;
2020-03-08 12:12:23 +00:00
// clients have a notion of a whilelist
// we short circuit logic here so we dont remove
// routers that are not whitelisted for first hops
if (not isSvcNode)
2020-03-08 12:09:48 +00:00
return false;
// if we have a whitelist enabled and we don't
// have the whitelist yet don't remove the entry
if (whitelistRouters and not gotWhitelist)
return false;
// if we have no whitelist enabled or we have
// the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not
2020-03-09 15:05:40 +00:00
// in the whitelist OR if there is no whitelist don't remove
2020-03-08 12:09:48 +00:00
return not _rcLookupHandler.RemoteIsAllowed(rc.pubkey);
});
2019-02-25 12:46:40 +00:00
// find all deregistered relays
std::unordered_set<PubKey, PubKey::Hash> closePeers;
_linkManager.ForEachPeer([&](auto session) {
if (whitelistRouters and not gotWhitelist)
return;
if (not session)
return;
const auto pk = session->GetPubKey();
if (session->IsRelay() and not _rcLookupHandler.RemoteIsAllowed(pk))
{
closePeers.emplace(pk);
}
});
// mark peers as de-registered
for (auto& peer : closePeers)
_linkManager.DeregisterPeer(std::move(peer));
_linkManager.CheckPersistingSessions(now);
if (HasClientExit())
{
m_RoutePoker.Enable();
m_RoutePoker.Update();
}
else
m_RoutePoker.Disable();
2019-12-03 17:03:19 +00:00
size_t connected = NumberOfConnectedRouters();
if (not isSvcNode)
2019-12-03 17:03:19 +00:00
{
connected += _linkManager.NumberOfPendingConnections();
}
const int interval = isSvcNode ? 5 : 2;
2020-01-18 20:46:22 +00:00
const auto timepoint_now = Clock_t::now();
if (timepoint_now >= m_NextExploreAt)
2020-01-18 20:46:22 +00:00
{
2020-01-18 20:55:50 +00:00
_rcLookupHandler.ExploreNetwork();
2020-01-18 20:46:22 +00:00
m_NextExploreAt = timepoint_now + std::chrono::seconds(interval);
}
size_t connectToNum = _outboundSessionMaker.minConnectedRouters;
const auto strictConnect = _rcLookupHandler.NumberOfStrictConnectRouters();
if (strictConnect > 0 && connectToNum > strictConnect)
{
connectToNum = strictConnect;
}
if (connected < connectToNum)
{
size_t dlt = connectToNum - connected;
LogInfo("connecting to ", dlt, " random routers to keep alive");
_outboundSessionMaker.ConnectToRandomRouters(dlt);
2018-08-14 21:17:18 +00:00
}
2019-11-05 16:58:53 +00:00
_hiddenServiceContext.Tick(now);
_exitContext.Tick(now);
2019-11-05 16:58:53 +00:00
2020-01-14 17:01:41 +00:00
// save profiles
if (routerProfiling().ShouldSave(now))
2019-03-25 15:41:37 +00:00
{
QueueDiskIO([&]() { routerProfiling().Save(_profilesFile); });
2019-03-25 15:41:37 +00:00
}
2020-01-14 17:01:41 +00:00
// save nodedb
if (nodedb()->ShouldSaveToDisk(now))
2020-01-14 17:01:41 +00:00
{
nodedb()->AsyncFlushToDisk();
}
2020-05-26 17:03:21 +00:00
2020-06-04 16:00:30 +00:00
if (m_peerDb)
{
// TODO: throttle this?
// TODO: need to capture session stats when session terminates / is removed from link manager
_linkManager.updatePeerDb(m_peerDb);
if (m_peerDb->shouldFlush(now))
{
LogDebug("Queing database flush...");
QueueDiskIO([this]() { m_peerDb->flushDatabase(); });
2020-06-04 16:00:30 +00:00
}
}
2020-05-26 17:03:21 +00:00
2019-09-10 14:16:32 +00:00
// get connected peers
std::set<dht::Key_t> peersWeHave;
_linkManager.ForEachPeer([&peersWeHave](ILinkSession* s) {
if (!s->IsEstablished())
2019-09-10 14:16:32 +00:00
return;
peersWeHave.emplace(s->GetPubKey());
});
// remove any nodes we don't have connections to
_dht->impl->Nodes()->RemoveIf(
[&peersWeHave](const dht::Key_t& k) -> bool { return peersWeHave.count(k) == 0; });
2019-11-05 16:58:53 +00:00
// expire paths
paths.ExpirePaths(now);
// update tick timestamp
_lastTick = llarp::time_now_ms();
}
2018-08-22 16:19:51 +00:00
bool
Router::Sign(Signature& sig, const llarp_buffer_t& buf) const
{
return CryptoManager::instance()->sign(sig, identity(), buf);
}
void
2020-02-24 19:40:45 +00:00
Router::ScheduleTicker(llarp_time_t interval)
2018-06-06 12:46:26 +00:00
{
ticker_job_id = _logic->call_later(interval, std::bind(&Router::handle_router_ticker, this));
2018-06-06 12:46:26 +00:00
}
void
Router::SessionClosed(RouterID remote)
2018-06-20 12:34:48 +00:00
{
2019-02-25 12:46:40 +00:00
dht::Key_t k(remote);
dht()->impl->Nodes()->DelNode(k);
LogInfo("Session to ", remote, " fully closed");
if (IsServiceNode())
return;
RouterContact rc;
if (not nodedb()->Get(remote, rc))
return;
m_RoutePoker.DelRoute(rc.addrs[0].toIpAddress().toIP());
2018-06-20 12:34:48 +00:00
}
void
Router::ConnectionTimedOut(ILinkSession* session)
{
if (m_peerDb)
{
RouterID id{session->GetPubKey()};
// TODO: make sure this is a public router (on whitelist)?
m_peerDb->modifyPeerStats(id, [&](PeerStats& stats) { stats.numConnectionTimeouts++; });
}
_outboundSessionMaker.OnConnectTimeout(session);
}
bool
2020-06-11 19:02:34 +00:00
Router::ConnectionEstablished(ILinkSession* session, bool inbound)
{
2020-06-11 19:02:34 +00:00
RouterID id{session->GetPubKey()};
if (m_peerDb)
{
// TODO: make sure this is a public router (on whitelist)?
m_peerDb->modifyPeerStats(id, [&](PeerStats& stats) { stats.numConnectionSuccesses++; });
}
2020-06-11 19:02:34 +00:00
NotifyRouterEvent<tooling::LinkSessionEstablishedEvent>(pubkey(), id, inbound);
return _outboundSessionMaker.OnSessionEstablished(session);
}
bool
Router::GetRandomConnectedRouter(RouterContact& result) const
2018-06-06 12:46:26 +00:00
{
return _linkManager.GetRandomConnectedRouter(result);
2018-06-06 12:46:26 +00:00
}
2018-05-30 20:56:47 +00:00
void
Router::HandleDHTLookupForExplore(RouterID /*remote*/, const std::vector<RouterContact>& results)
2018-07-03 13:33:37 +00:00
{
for (const auto& rc : results)
{
_rcLookupHandler.CheckRC(rc);
}
2018-06-01 14:08:54 +00:00
}
2018-06-13 12:58:51 +00:00
// TODO: refactor callers and remove this function
void
Router::LookupRouter(RouterID remote, RouterLookupHandler resultHandler)
2018-10-09 12:06:30 +00:00
{
_rcLookupHandler.GetRC(
remote,
[=](const RouterID& id, const RouterContact* const rc, const RCRequestResult result) {
(void)id;
if (resultHandler)
{
std::vector<RouterContact> routers;
if (result == RCRequestResult::Success && rc != nullptr)
{
routers.push_back(*rc);
}
resultHandler(routers);
}
});
}
void
2020-05-19 18:53:03 +00:00
Router::SetRouterWhitelist(const std::vector<RouterID> routers)
{
_rcLookupHandler.SetRouterWhitelist(routers);
}
bool
2020-05-20 11:41:42 +00:00
Router::StartRpcServer()
{
if (enableRPCServer)
2018-10-09 12:06:30 +00:00
{
2020-05-21 14:09:45 +00:00
m_RPCServer->AsyncServeRPC(rpcBindAddr);
LogInfo("Bound RPC server to ", rpcBindAddr);
2018-10-09 12:06:30 +00:00
}
return true;
}
bool
Router::Run()
{
if (_running || _stopping)
return false;
// set public signing key
_rc.pubkey = seckey_topublic(identity());
// set router version if service node
if (IsServiceNode())
2020-01-25 16:28:07 +00:00
{
2020-01-25 17:21:28 +00:00
_rc.routerVersion = RouterVersion(llarp::VERSION, LLARP_PROTO_VERSION);
}
2019-04-08 18:21:01 +00:00
_linkManager.ForEachInboundLink([&](LinkLayer_ptr link) {
AddressInfo ai;
if (link->GetOurAddressInfo(ai))
{
// override ip and port
if (not _ourAddress.isEmpty())
2019-04-08 18:21:01 +00:00
{
ai.fromIpAddress(_ourAddress);
2019-04-08 18:21:01 +00:00
}
if (RouterContact::BlockBogons && IsBogon(ai.ip))
return;
LogInfo("adding address: ", ai);
_rc.addrs.push_back(ai);
}
});
2020-08-19 19:10:11 +00:00
if (ExitEnabled() and IsServiceNode())
{
LogError("exit mode not supported while service node");
return false;
}
if (IsServiceNode() and not _rc.IsPublicRouter())
{
LogError("we are configured as relay but have no reachable addresses");
return false;
}
// set public encryption key
_rc.enckey = seckey_topublic(encryption());
2019-04-08 18:21:01 +00:00
LogInfo("Signing rc...");
if (!_rc.Sign(identity()))
{
LogError("failed to sign rc");
return false;
}
if (IsServiceNode())
{
if (!SaveRC())
{
LogError("failed to save RC");
return false;
}
}
_outboundSessionMaker.SetOurRouter(pubkey());
if (!_linkManager.StartLinks(_logic))
{
LogWarn("One or more links failed to start.");
return false;
}
if (IsServiceNode())
{
// initialize as service node
if (!InitServiceNode())
{
LogError("Failed to initialize service node");
return false;
}
const RouterID us = pubkey();
LogInfo("initalized service node: ", us);
// init gossiper here
2020-03-04 00:50:20 +00:00
_rcGossiper.Init(&_linkManager, us, this);
// relays do not use profiling
routerProfiling().Disable();
}
else
{
// we are a client
// regenerate keys and resign rc before everything else
CryptoManager::instance()->identity_keygen(_identity);
CryptoManager::instance()->encryption_keygen(_encryption);
_rc.pubkey = seckey_topublic(identity());
_rc.enckey = seckey_topublic(encryption());
if (!_rc.Sign(identity()))
{
LogError("failed to regenerate keys and sign RC");
return false;
}
}
LogInfo("starting hidden service context...");
if (!hiddenServiceContext().StartAll())
{
LogError("Failed to start hidden service context");
return false;
}
{
ssize_t loaded = _nodedb->LoadAll();
llarp::LogInfo("loaded ", loaded, " RCs");
if (loaded < 0)
{
// shouldn't be possible
return false;
}
}
llarp_dht_context_start(dht(), pubkey());
for (const auto& rc : bootstrapRCList)
{
if (this->nodedb()->Insert(rc))
{
LogInfo("added bootstrap node ", RouterID(rc.pubkey));
}
else
{
LogError("Failed to add bootstrap node ", RouterID(rc.pubkey));
}
_dht->impl->Nodes()->PutNode(rc);
}
LogInfo("have ", _nodedb->num_loaded(), " routers");
_netloop->add_ticker(std::bind(&Router::PumpLL, this));
2020-02-24 19:40:45 +00:00
ScheduleTicker(ROUTER_TICK_INTERVAL);
_running.store(true);
_startedAt = Now();
#if defined(WITH_SYSTEMD)
::sd_notify(0, "READY=1");
#endif
2019-12-09 13:08:30 +00:00
LogContext::Instance().DropToRuntimeLevel();
return _running;
}
bool
Router::IsRunning() const
{
return _running;
}
llarp_time_t
Router::Uptime() const
{
const llarp_time_t _now = Now();
if (_startedAt > 0s && _now > _startedAt)
return _now - _startedAt;
2020-02-24 19:40:45 +00:00
return 0s;
}
void
Router::AfterStopLinks()
{
Close();
m_lmq.reset();
}
void
Router::AfterStopIssued()
{
StopLinks();
nodedb()->AsyncFlushToDisk();
2020-02-24 19:40:45 +00:00
_logic->call_later(200ms, std::bind(&Router::AfterStopLinks, this));
}
void
Router::StopLinks()
{
_linkManager.Stop();
2018-06-10 14:05:48 +00:00
}
void
Router::Die()
{
if (!_running)
return;
if (_stopping)
return;
_stopping.store(true);
LogContext::Instance().RevertRuntimeLevel();
LogWarn("stopping router hard");
#if defined(WITH_SYSTEMD)
sd_notify(0, "STOPPING=1\nSTATUS=Shutting down HARD");
#endif
hiddenServiceContext().StopAll();
_exitContext.Stop();
StopLinks();
Close();
}
void
Router::Stop()
2018-11-26 13:29:45 +00:00
{
if (!_running)
return;
if (_stopping)
return;
_stopping.store(true);
2019-12-09 13:08:30 +00:00
LogContext::Instance().RevertRuntimeLevel();
LogInfo("stopping router");
2020-02-25 22:32:57 +00:00
#if defined(WITH_SYSTEMD)
sd_notify(0, "STOPPING=1\nSTATUS=Shutting down");
#endif
2019-02-22 16:21:05 +00:00
hiddenServiceContext().StopAll();
_exitContext.Stop();
paths.PumpUpstream();
_linkManager.PumpLinks();
2020-02-24 19:40:45 +00:00
_logic->call_later(200ms, std::bind(&Router::AfterStopIssued, this));
2018-11-26 13:29:45 +00:00
}
2018-08-02 23:30:34 +00:00
bool
Router::HasSessionTo(const RouterID& remote) const
{
return _linkManager.HasSessionTo(remote);
}
std::string
Router::ShortName() const
{
return RouterID(pubkey()).ToString().substr(0, 8);
}
uint32_t
Router::NextPathBuildNumber()
{
return path_build_count++;
}
void
2019-12-03 17:03:19 +00:00
Router::ConnectToRandomRouters(int _want)
{
2019-12-03 17:03:19 +00:00
const size_t want = _want;
auto connected = NumberOfConnectedRouters();
if (not IsServiceNode())
2019-12-03 17:03:19 +00:00
{
connected += _linkManager.NumberOfPendingConnections();
}
if (connected >= want)
2019-12-03 17:03:19 +00:00
return;
_outboundSessionMaker.ConnectToRandomRouters(want);
}
bool
Router::InitServiceNode()
2018-11-26 13:29:45 +00:00
{
LogInfo("accepting transit traffic");
paths.AllowTransit();
llarp_dht_allow_transit(dht());
_exitContext.AddExitEndpoint("default-connectivity", m_Config->network, m_Config->dns);
return true;
2018-11-26 13:29:45 +00:00
}
2018-06-14 17:35:12 +00:00
bool
Router::TryConnectAsync(RouterContact rc, uint16_t tries)
{
(void)tries;
if (rc.pubkey == pubkey())
{
return false;
}
if (!_rcLookupHandler.RemoteIsAllowed(rc.pubkey))
{
return false;
}
_outboundSessionMaker.CreateSessionTo(rc, nullptr);
return true;
}
void
Router::QueueWork(std::function<void(void)> func)
{
m_lmq->job(std::move(func));
}
void
Router::QueueDiskIO(std::function<void(void)> func)
{
m_lmq->job(std::move(func), m_DiskThread);
}
bool
Router::HasClientExit() const
{
if (IsServiceNode())
return false;
const auto ep = hiddenServiceContext().GetDefault();
return ep and ep->HasExit();
}
bool
2019-01-05 13:45:05 +00:00
Router::InitOutboundLinks()
2018-06-18 22:03:50 +00:00
{
auto link = iwp::NewOutboundLink(
m_keyManager,
util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&AbstractRouter::Sign, this),
[&](llarp::RouterContact rc) {
if (IsServiceNode())
return;
llarp::LogTrace(
"Before connect, outbound link adding route to (",
rc.addrs[0].toIpAddress().toIP(),
") via gateway.");
m_RoutePoker.AddRoute(rc.addrs[0].toIpAddress().toIP());
},
util::memFn(&Router::ConnectionEstablished, this),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&Router::ConnectionTimedOut, this),
util::memFn(&AbstractRouter::SessionClosed, this),
util::memFn(&AbstractRouter::PumpLL, this),
util::memFn(&AbstractRouter::QueueWork, this));
if (!link)
throw std::runtime_error("NewOutboundLink() failed to provide a link");
2019-08-07 16:33:29 +00:00
const auto afs = {AF_INET, AF_INET6};
for (const auto af : afs)
{
2020-04-29 20:19:48 +00:00
if (not link->Configure(netloop(), "*", af, m_OutboundPort))
2019-01-05 13:48:00 +00:00
continue;
2020-04-29 20:19:48 +00:00
2019-08-07 16:33:29 +00:00
_linkManager.AddLink(std::move(link), false);
return true;
}
throw std::runtime_error(
stringify("Failed to init AF_INET and AF_INET6 on port ", m_OutboundPort));
}
2018-06-21 13:33:42 +00:00
void
Router::MessageSent(const RouterID& remote, SendStatus status)
{
if (status == SendStatus::Success)
{
LogDebug("Message successfully sent to ", remote);
}
else
{
2019-09-09 11:36:21 +00:00
LogDebug("Message failed sending to ", remote);
}
}
void
Router::HandleRouterEvent(tooling::RouterEventPtr event) const
{
LogDebug(event->ToString());
}
2018-02-01 13:21:00 +00:00
} // namespace llarp