You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/llarp/router/router.cpp

1344 lines
36 KiB
C++

#include <memory>
#include <router/router.hpp>
#include <config/config.hpp>
5 years ago
#include <constants/limits.hpp>
#include <constants/proto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <crypto/crypto.hpp>
#include <dht/context.hpp>
#include <dht/node.hpp>
#include <iwp/iwp.hpp>
#include <link/server.hpp>
#include <messages/link_message.hpp>
#include <net/net.hpp>
#include <rpc/rpc.hpp>
#include <util/buffer.hpp>
#include <util/encode.hpp>
#include <util/logging/file_logger.hpp>
#include <util/logging/json_logger.hpp>
#include <util/logging/logger_syslog.hpp>
#include <util/logging/logger.hpp>
#include <util/meta/memfn.hpp>
#include <util/str.hpp>
#include <ev/ev.hpp>
7 years ago
#include "tooling/router_event.hpp"
#include <fstream>
#include <cstdlib>
#include <iterator>
#include <unordered_map>
#include <utility>
#if defined(ANDROID) || defined(IOS)
#include <unistd.h>
#endif
4 years ago
#if defined(WITH_SYSTEMD)
#include <systemd/sd-daemon.h>
#endif
static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 1s;
namespace llarp
{
Router::Router(std::shared_ptr< llarp::thread::ThreadPool > _tp,
llarp_ev_loop_ptr __netloop, std::shared_ptr< Logic > l)
: ready(false)
, _netloop(std::move(__netloop))
, cryptoworker(std::move(_tp))
, _logic(std::move(l))
, paths(this)
, _exitContext(this)
5 years ago
, disk(std::make_shared< llarp::thread::ThreadPool >(1, 1000,
"diskworker"))
, _dht(llarp_dht_context_new(this))
, inbound_link_msg_parser(this)
, _hiddenServiceContext(this)
#ifdef LOKINET_HIVE
, _randomStartDelay(
std::chrono::milliseconds((llarp::randint() % 1250) + 2000))
#else
4 years ago
, _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10))
#endif
{
m_keyManager = std::make_shared< KeyManager >();
// set rational defaults
this->ip4addr.sin_family = AF_INET;
this->ip4addr.sin_port = htons(1090);
_stopping.store(false);
_running.store(false);
4 years ago
_lastTick = llarp::time_now_ms();
4 years ago
m_NextExploreAt = Clock_t::now();
}
Router::~Router()
{
llarp_dht_context_free(_dht);
}
5 years ago
util::StatusObject
Router::ExtractStatus() const
{
if(_running)
{
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()}};
}
else
{
return util::StatusObject{{"running", false}};
}
}
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);
6 years ago
}
void
Router::PersistSessionUntil(const RouterID &remote, llarp_time_t until)
6 years ago
{
_linkManager.PersistSessionUntil(remote, until);
}
4 years ago
void
Router::GossipRCIfNeeded(const RouterContact rc)
{
/// if we are not a service node forget about gossip
if(not IsServiceNode())
return;
/// wait for random uptime
if(std::chrono::milliseconds{Uptime()} < _randomStartDelay)
4 years ago
return;
_rcGossiper.GossipRC(rc);
}
6 years ago
bool
Router::GetRandomGoodRouter(RouterID &router)
{
if(whitelistRouters)
{
return _rcLookupHandler.GetRandomWhitelistRouter(router);
}
5 years ago
auto pick_router = [&](auto &collection) -> bool {
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;
5 years ago
};
De-abseil, part 2: mutex, locks, (most) time - util::Mutex is now a std::shared_timed_mutex, which is capable of exclusive and shared locks. - util::Lock is still present as a std::lock_guard<util::Mutex>. - the locking annotations are preserved, but updated to the latest supported by clang rather than using abseil's older/deprecated ones. - ACQUIRE_LOCK macro is gone since we don't pass mutexes by pointer into locks anymore (WTF abseil). - ReleasableLock is gone. Instead there are now some llarp::util helper methods to obtain unique and/or shared locks: - `auto lock = util::unique_lock(mutex);` gets an RAII-but-also unlockable object (std::unique_lock<T>, with T inferred from `mutex`). - `auto lock = util::shared_lock(mutex);` gets an RAII shared (i.e. "reader") lock of the mutex. - `auto lock = util::unique_locks(mutex1, mutex2, mutex3);` can be used to atomically lock multiple mutexes at once (returning a tuple of the locks). This are templated on the mutex which makes them a bit more flexible than using a concrete type: they can be used for any type of lockable mutex, not only util::Mutex. (Some of the code here uses them for getting locks around a std::mutex). Until C++17, using the RAII types is painfully verbose: ```C++ // pre-C++17 - needing to figure out the mutex type here is annoying: std::unique_lock<util::Mutex> lock(mutex); // pre-C++17 and even more verbose (but at least the type isn't needed): std::unique_lock<decltype(mutex)> lock(mutex); // our compromise: auto lock = util::unique_lock(mutex); // C++17: std::unique_lock lock(mutex); ``` All of these functions will also warn (under gcc or clang) if you discard the return value. You can also do fancy things like `auto l = util::unique_lock(mutex, std::adopt_lock)` (which lets a lock take over an already-locked mutex). - metrics code is gone, which also removes a big pile of code that was only used by metrics: - llarp::util::Scheduler - llarp::thread::TimerQueue - llarp::util::Stopwatch
4 years ago
auto l = util::shared_lock(nodedb()->access);
5 years ago
return pick_router(nodedb()->entries);
6 years ago
}
5 years ago
void
Router::PumpLL()
{
static constexpr size_t PumpJobThreshhold = 50;
static constexpr auto PumpInterval = 25ms;
const auto now = Now();
if(_stopping.load())
return;
if(_logic->numPendingJobs() >= PumpJobThreshhold
&& _lastPump + PumpInterval >= now)
{
return;
}
_lastPump = now;
paths.PumpDownstream();
paths.PumpUpstream();
_outboundMessageHandler.Tick();
_linkManager.PumpLinks();
5 years ago
}
bool
Router::SendToOrQueue(const RouterID &remote, const ILinkMessage *msg,
SendStatusHandler handler)
{
if(handler == nullptr)
{
using std::placeholders::_1;
handler = std::bind(&Router::MessageSent, this, remote, _1);
}
return _outboundMessageHandler.QueueMessage(remote, msg, handler);
}
void
5 years ago
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)
6 years ago
{
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");
6 years ago
}
bool
Router::EnsureIdentity()
{
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
}
6 years ago
_identity = m_keyManager->identityKey;
_encryption = m_keyManager->encryptionKey;
if(_identity.IsZero())
return false;
if(_encryption.IsZero())
return false;
return true;
}
6 years ago
bool
Router::Configure(Config *conf, llarp_nodedb *nodedb)
{
if(nodedb == nullptr)
{
LogError(
"Attempting to Router::Configure but passed null nodedb pointer");
return false;
}
_nodedb = nodedb;
if(not m_keyManager->initialize(*conf, true))
return false;
if(!FromConfig(conf))
return false;
if(!InitOutboundLinks())
return false;
return EnsureIdentity();
}
5 years ago
/// called in disk worker thread
void
Router::HandleSaveRC() const
5 years ago
{
std::string fname = our_rc_file.string();
_rc.Write(fname.c_str());
5 years ago
}
bool
Router::SaveRC()
6 years ago
{
LogDebug("verify RC signature");
if(!_rc.Verify(Now()))
{
Dump< MAX_RC_SIZE >(rc());
LogError("RC is invalid, not saving");
return false;
}
diskworker()->addJob(std::bind(&Router::HandleSaveRC, this));
5 years ago
return true;
6 years ago
}
6 years ago
bool
Router::IsServiceNode() const
{
return m_isServiceNode;
}
6 years ago
void
Router::Close()
{
LogInfo("closing router");
5 years ago
llarp_ev_loop_stop(_netloop);
disk->stop();
disk->shutdown();
_running.store(false);
}
void
Router::handle_router_ticker()
{
ticker_job_id = 0;
Tick();
ScheduleTicker(ROUTER_TICK_INTERVAL);
}
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);
}
size_t
Router::NumberOfConnectedRouters() const
{
return _linkManager.NumberOfConnectedRouters();
}
size_t
Router::NumberOfConnectedClients() const
{
return _linkManager.NumberOfConnectedClients();
}
bool
Router::UpdateOurRC(bool rotateKeys)
{
SecretKey nextOnionKey;
RouterContact nextRC = _rc;
if(rotateKeys)
{
CryptoManager::instance()->encryption_keygen(nextOnionKey);
std::string f = encryption_keyfile.string();
5 years ago
// TODO: use disk worker
if(nextOnionKey.SaveToFile(f.c_str()))
{
nextRC.enckey = seckey_topublic(nextOnionKey);
_encryption = nextOnionKey;
}
}
if(!nextRC.Sign(identity()))
return false;
if(!nextRC.Verify(time_now_ms(), false))
return false;
_rc = std::move(nextRC);
if(rotateKeys)
{
// propagate RC by renegotiating sessions
ForEachPeer([](ILinkSession *s) {
if(s->RenegotiateSession())
LogInfo("renegotiated session");
else
LogWarn("failed to renegotiate session");
});
}
return SaveRC();
5 years ago
}
bool
Router::FromConfig(Config *conf)
{
// Set netid before anything else
if(!conf->router.netId().empty()
&& strcmp(conf->router.netId().c_str(), llarp::DEFAULT_NETID))
{
const auto &netid = conf->router.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()));
// reset netid in our rc
_rc.netID = llarp::NetID();
}
const auto linktypename = conf->router.defaultLinkProto();
_defaultLinkType = LinkFactory::TypeFromName(linktypename);
if(_defaultLinkType == LinkFactory::LinkType::eLinkUnknown)
{
LogError("failed to set link type to '", linktypename,
"' as that is invalid");
return false;
}
// IWP config
m_OutboundPort = std::get< LinksConfig::Port >(conf->links.outboundLink());
// Router config
_rc.SetNick(conf->router.nickname());
_outboundSessionMaker.maxConnectedRouters =
conf->router.maxConnectedRouters();
_outboundSessionMaker.minConnectedRouters =
conf->router.minConnectedRouters();
encryption_keyfile = conf->router.encryptionKeyfile();
our_rc_file = conf->router.ourRcFile();
transport_keyfile = conf->router.transportKeyfile();
addrInfo = conf->router.addrInfo();
publicOverride = conf->router.publicOverride();
ip4addr = conf->router.ip4addr();
if(!conf->router.blockBogons().value_or(true))
{
RouterContact::BlockBogons = false;
}
// Lokid Config
usingSNSeed = conf->lokid.usingSNSeed;
ident_keyfile = conf->lokid.ident_keyfile;
whitelistRouters = conf->lokid.whitelistRouters;
lokidRPCAddr = conf->lokid.lokidRPCAddr;
lokidRPCUser = conf->lokid.lokidRPCUser;
lokidRPCPassword = conf->lokid.lokidRPCPassword;
// TODO: add config flag for "is service node"
if(conf->links.inboundLinks().size())
{
m_isServiceNode = true;
}
std::set< RouterID > strictConnectPubkeys;
if(!conf->network.strictConnect().empty())
{
const auto &val = conf->network.strictConnect();
if(IsServiceNode())
{
llarp::LogError("cannot use strict-connect option as service node");
return false;
}
llarp::RouterID snode;
llarp::PubKey pk;
if(pk.FromString(val))
{
if(strictConnectPubkeys.emplace(pk).second)
llarp::LogInfo("added ", pk, " to strict connect list");
else
llarp::LogWarn("duplicate key for strict connect: ", pk);
}
else if(snode.FromString(val))
{
if(strictConnectPubkeys.insert(snode).second)
{
llarp::LogInfo("added ", snode, " to strict connect list");
netConfig.emplace("strict-connect", val);
}
else
llarp::LogWarn("duplicate key for strict connect: ", snode);
}
else
llarp::LogError("invalid key for strict-connect: ", val);
}
llarp::LogWarn("Bootstrap routers list size: ",
conf->bootstrap.routers.size());
std::vector< std::string > configRouters = conf->connect.routers;
configRouters.insert(configRouters.end(), conf->bootstrap.routers.begin(),
conf->bootstrap.routers.end());
BootstrapList b_list;
for(const auto &router : configRouters)
{
bool isListFile = false;
{
std::ifstream inf(router, std::ios::binary);
if(inf.is_open())
{
const char ch = inf.get();
isListFile = ch == 'l';
}
}
if(isListFile)
{
if(not BDecodeReadFile(router.c_str(), b_list))
{
LogWarn("failed to read bootstrap list file '", router, "'");
return false;
}
}
else
{
RouterContact rc;
if(not rc.Read(router.c_str()))
{
llarp::LogWarn("failed to decode bootstrap RC, file='", router,
"' rc=", rc);
return false;
}
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, _logic);
_outboundSessionMaker.Init(&_linkManager, &_rcLookupHandler,
&_routerProfiling, _logic, _nodedb,
threadpool());
_linkManager.Init(&_outboundSessionMaker);
_rcLookupHandler.Init(_dht, _nodedb, threadpool(), &_linkManager,
&_hiddenServiceContext, strictConnectPubkeys,
bootstrapRCList, whitelistRouters, m_isServiceNode);
if(!usingSNSeed)
{
ident_keyfile = conf->router.identKeyfile();
}
// create inbound links, if we are a service node
for(const auto &serverConfig : conf->links.inboundLinks())
{
// get default factory
auto inboundLinkFactory = LinkFactory::Obtain(_defaultLinkType, true);
// for each option if provided ...
for(const auto &opt : std::get< LinksConfig::Options >(serverConfig))
{
// try interpreting it as a link type
const auto linktype = LinkFactory::TypeFromName(opt);
if(linktype != LinkFactory::LinkType::eLinkUnknown)
{
// override link factory if it's a valid link type
auto factory = LinkFactory::Obtain(linktype, true);
if(factory)
{
inboundLinkFactory = std::move(factory);
break;
}
}
}
auto server = inboundLinkFactory(
m_keyManager, util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnSessionEstablished,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&IOutboundSessionMaker::OnConnectTimeout,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::SessionClosed, this),
util::memFn(&AbstractRouter::PumpLL, this));
const auto &key = std::get< LinksConfig::Interface >(serverConfig);
int af = std::get< LinksConfig::AddressFamily >(serverConfig);
uint16_t port = std::get< LinksConfig::Port >(serverConfig);
llarp::LogWarn("tun: ", key, " -- af: ", af, " -- port: ", port);
if(!server->Configure(netloop(), key, af, port))
{
LogError("failed to bind inbound link on ", key, " port ", port);
return false;
}
_linkManager.AddLink(std::move(server), true);
}
// set network config
netConfig = conf->network.netConfig();
// Network config
if(conf->network.enableProfiling().has_value())
5 years ago
{
if(not conf->network.enableProfiling().value())
{
routerProfiling().Disable();
LogWarn("router profiling explicitly disabled");
}
5 years ago
}
if(!conf->network.routerProfilesFile().empty())
{
routerProfilesFile = conf->network.routerProfilesFile();
routerProfiling().Load(routerProfilesFile.c_str());
llarp::LogInfo("setting profiles to ", routerProfilesFile);
}
// API config
enableRPCServer = conf->api.enableRPCServer();
rpcBindAddr = conf->api.rpcBindAddr();
// Services config
for(const auto &service : conf->services.services)
{
if(LoadHiddenServiceConfig(service.second))
{
llarp::LogInfo("loaded hidden service config for ", service.first);
}
else
{
llarp::LogWarn("failed to load hidden service config for ",
service.first);
}
}
// Logging config
auto logfile = conf->logging.m_LogFile;
if(conf->logging.m_LogJSON)
5 years ago
{
LogContext::Instance().logStream = std::make_unique< JSONLogStream >(
diskworker(), logfile, 100ms, logfile != stdout);
5 years ago
}
else if(logfile != stdout)
5 years ago
{
LogContext::Instance().logStream =
std::make_unique< FileLogStream >(diskworker(), logfile, 100ms, true);
5 years ago
}
5 years ago
netConfig.insert(conf->dns.netConfig.begin(), conf->dns.netConfig.end());
return true;
}
bool
Router::CheckRenegotiateValid(RouterContact newrc, RouterContact oldrc)
5 years ago
{
return _rcLookupHandler.CheckRenegotiateValid(newrc, oldrc);
5 years ago
}
bool
Router::IsBootstrapNode(const RouterID r) const
{
return std::count_if(
bootstrapRCList.begin(), bootstrapRCList.end(),
[r](const RouterContact &rc) -> bool { return rc.pubkey == r; })
> 0;
}
5 years ago
bool
Router::ShouldReportStats(llarp_time_t now) const
{
static constexpr auto ReportStatsInterval = 1h;
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())
{
LogInfo(NumberOfConnectedClients(), " client connections");
LogInfo(_rc.Age(now), " since we last updated our RC");
LogInfo(_rc.TimeUntilExpires(now), " until our RC expires");
}
LogInfo(m_LastStatsReport, " last reported stats");
m_LastStatsReport = now;
}
void
Router::Tick()
{
5 years ago
if(_stopping)
return;
// LogDebug("tick router");
const auto now = Now();
5 years ago
#if defined(WITH_SYSTEMD)
4 years ago
{
std::stringstream ss;
ss << "WATCHDOG=1\nSTATUS=v" << llarp::VERSION_STR;
4 years ago
if(IsServiceNode())
{
ss << " snode | known/svc/clients: " << nodedb()->num_loaded() << "/"
<< NumberOfConnectedRouters() << "/" << NumberOfConnectedClients()
<< " | " << pathContext().CurrentTransitPaths() << " active paths";
}
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;
4 years ago
});
}
const auto status = ss.str();
::sd_notify(0, status.c_str());
}
#endif
routerProfiling().Tick();
if(ShouldReportStats(now))
{
ReportStats();
}
_rcGossiper.Decay(now);
4 years ago
_rcLookupHandler.PeriodicUpdate(now);
const bool isSvcNode = IsServiceNode();
if(_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000))
|| (now - _rc.last_updated) > rcRegenInterval)
5 years ago
{
LogInfo("regenerating RC");
if(!UpdateOurRC(false))
LogError("Failed to update our RC");
}
4 years ago
else
{
GossipRCIfNeeded(_rc);
}
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
4 years ago
// remove RCs for nodes that are no longer allowed by network policy
nodedb()->RemoveIf([&](const RouterContact &rc) -> bool {
4 years ago
// don't purge bootstrap nodes from nodedb
4 years ago
if(IsBootstrapNode(rc.pubkey))
return false;
// if for some reason we stored an RC that isn't a valid router
// purge this entry
4 years ago
if(not rc.IsPublicRouter())
return true;
4 years ago
// clients have a notion of a whilelist
// we short circuit logic here so we dont remove
// routers that are not whitelisted for first hops
4 years ago
if(not isSvcNode)
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
4 years ago
// in the whitelist OR if there is no whitelist don't remove
4 years ago
return not _rcLookupHandler.RemoteIsAllowed(rc.pubkey);
});
5 years ago
_linkManager.CheckPersistingSessions(now);
size_t connected = NumberOfConnectedRouters();
if(not isSvcNode)
{
connected += _linkManager.NumberOfPendingConnections();
}
4 years ago
const int interval = isSvcNode ? 5 : 2;
const auto timepoint_now = Clock_t::now();
if(timepoint_now >= m_NextExploreAt)
{
_rcLookupHandler.ExploreNetwork();
4 years ago
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);
}
_hiddenServiceContext.Tick(now);
_exitContext.Tick(now);
if(rpcCaller)
rpcCaller->Tick(now);
// save profiles
5 years ago
if(routerProfiling().ShouldSave(now))
{
diskworker()->addJob(
[&]() { routerProfiling().Save(routerProfilesFile.c_str()); });
5 years ago
}
// save nodedb
if(nodedb()->ShouldSaveToDisk(now))
{
nodedb()->AsyncFlushToDisk();
}
// get connected peers
std::set< dht::Key_t > peersWeHave;
_linkManager.ForEachPeer([&peersWeHave](ILinkSession *s) {
if(!s->IsEstablished())
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;
});
// expire paths
paths.ExpirePaths(now);
// update tick timestamp
_lastTick = llarp::time_now_ms();
}
bool
Router::Sign(Signature &sig, const llarp_buffer_t &buf) const
{
return CryptoManager::instance()->sign(sig, identity(), buf);
}
void
Router::ScheduleTicker(llarp_time_t interval)
{
ticker_job_id = _logic->call_later(
interval, std::bind(&Router::handle_router_ticker, this));
}
void
Router::SessionClosed(RouterID remote)
6 years ago
{
5 years ago
dht::Key_t k(remote);
dht()->impl->Nodes()->DelNode(k);
LogInfo("Session to ", remote, " fully closed");
6 years ago
}
bool
Router::GetRandomConnectedRouter(RouterContact &result) const
{
return _linkManager.GetRandomConnectedRouter(result);
}
6 years ago
void
Router::HandleDHTLookupForExplore(RouterID /*remote*/,
const std::vector< RouterContact > &results)
6 years ago
{
for(const auto &rc : results)
{
_rcLookupHandler.CheckRC(rc);
}
}
6 years ago
// TODO: refactor callers and remove this function
void
Router::LookupRouter(RouterID remote, RouterLookupHandler resultHandler)
{
_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
Router::SetRouterWhitelist(const std::vector< RouterID > &routers)
{
_rcLookupHandler.SetRouterWhitelist(routers);
}
/// this function ensure there are sane defualts in a net config
static void
EnsureNetConfigDefaultsSane(
std::unordered_multimap< std::string, std::string > &netConfig)
{
static const std::unordered_map< std::string,
std::function< std::string(void) > >
netConfigDefaults = {
{"ifname", llarp::FindFreeTun},
{"ifaddr", llarp::FindFreeRange},
{"local-dns", []() -> std::string { return "127.0.0.1:53"; }}};
// populate with fallback defaults if values not present
auto itr = netConfigDefaults.begin();
while(itr != netConfigDefaults.end())
{
auto found = netConfig.find(itr->first);
if(found == netConfig.end() || found->second.empty())
{
5 years ago
auto val = itr->second();
if(!val.empty())
netConfig.emplace(itr->first, std::move(val));
}
++itr;
}
}
bool
Router::StartJsonRpc()
{
if(_running || _stopping)
return false;
if(enableRPCServer)
{
if(rpcBindAddr.empty())
{
rpcBindAddr = DefaultRPCBindAddr;
}
rpcServer = std::make_unique< rpc::Server >(this);
while(!rpcServer->Start(rpcBindAddr))
{
LogError("failed to bind jsonrpc to ", rpcBindAddr);
#if defined(ANDROID) || defined(RPI)
sleep(1);
#else
std::this_thread::sleep_for(std::chrono::seconds(1));
#endif
}
LogInfo("Bound RPC server to ", rpcBindAddr);
}
return true;
}
bool
Router::Run()
{
if(_running || _stopping)
return false;
if(whitelistRouters)
{
rpcCaller = std::make_unique< rpc::Caller >(this);
rpcCaller->SetAuth(lokidRPCUser, lokidRPCPassword);
while(!rpcCaller->Start(lokidRPCAddr))
{
LogError("failed to start jsonrpc caller to ", lokidRPCAddr);
#if defined(ANDROID) || defined(RPI)
sleep(1);
#else
std::this_thread::sleep_for(std::chrono::seconds(1));
#endif
}
LogInfo("RPC Caller to ", lokidRPCAddr, " started");
}
if(!cryptoworker->start())
{
LogError("crypto worker failed to start");
return false;
}
if(!disk->start())
{
LogError("disk worker failed to start");
return false;
}
routerProfiling().Load(routerProfilesFile.c_str());
Addr publicAddr(this->addrInfo);
if(this->publicOverride)
{
LogDebug("public address:port ", publicAddr);
}
5 years ago
// set public signing key
_rc.pubkey = seckey_topublic(identity());
4 years ago
// set router version if service node
if(IsServiceNode())
{
_rc.routerVersion = RouterVersion(llarp::VERSION, LLARP_PROTO_VERSION);
4 years ago
}
5 years ago
AddressInfo ai;
_linkManager.ForEachInboundLink([&](LinkLayer_ptr link) {
5 years ago
if(link->GetOurAddressInfo(ai))
{
5 years ago
// override ip and port
if(this->publicOverride)
{
5 years ago
ai.ip = *publicAddr.addr6();
ai.port = publicAddr.port();
}
if(RouterContact::BlockBogons && IsBogon(ai.ip))
return;
5 years ago
_rc.addrs.push_back(ai);
5 years ago
if(ExitEnabled())
{
const llarp::Addr addr(ai);
5 years ago
const nuint32_t a{addr.addr4()->s_addr};
5 years ago
_rc.exits.emplace_back(_rc.pubkey, a);
LogInfo("Exit relay started, advertised as exiting at: ", a);
5 years ago
}
}
});
// set public encryption key
_rc.enckey = seckey_topublic(encryption());
5 years ago
LogInfo("Signing rc...");
if(!_rc.Sign(identity()))
{
LogError("failed to sign rc");
return false;
}
if(!SaveRC())
{
LogError("failed to save RC");
return false;
}
_outboundSessionMaker.SetOurRouter(pubkey());
if(!_linkManager.StartLinks(_logic, cryptoworker))
{
LogWarn("One or more links failed to start.");
return false;
}
EnsureNetConfigDefaultsSane(netConfig);
const auto limits =
IsServiceNode() ? llarp::limits::snode : llarp::limits::client;
_outboundSessionMaker.minConnectedRouters = std::max(
_outboundSessionMaker.minConnectedRouters, limits.DefaultMinRouters);
_outboundSessionMaker.maxConnectedRouters = std::max(
_outboundSessionMaker.maxConnectedRouters, limits.DefaultMaxRouters);
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
_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;
}
if(!CreateDefaultHiddenService())
{
LogError("failed to set up default network endpoint");
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));
ScheduleTicker(ROUTER_TICK_INTERVAL);
_running.store(true);
_startedAt = Now();
#if defined(WITH_SYSTEMD)
::sd_notify(0, "READY=1");
#endif
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;
return 0s;
}
void
Router::AfterStopLinks()
{
Close();
}
void
Router::AfterStopIssued()
{
StopLinks();
nodedb()->AsyncFlushToDisk();
_logic->call_later(200ms, std::bind(&Router::AfterStopLinks, this));
}
void
Router::StopLinks()
{
_linkManager.Stop();
}
void
Router::Stop()
{
if(!_running)
return;
if(_stopping)
return;
_stopping.store(true);
LogContext::Instance().RevertRuntimeLevel();
LogInfo("stopping router");
4 years ago
#if defined(WITH_SYSTEMD)
sd_notify(0, "STOPPING=1\nSTATUS=Shutting down");
#endif
hiddenServiceContext().StopAll();
_exitContext.Stop();
if(rpcServer)
rpcServer->Stop();
paths.PumpUpstream();
_linkManager.PumpLinks();
_logic->call_later(200ms, std::bind(&Router::AfterStopIssued, this));
}
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
Router::ConnectToRandomRouters(int _want)
{
const size_t want = _want;
auto connected = NumberOfConnectedRouters();
if(not IsServiceNode())
{
connected += _linkManager.NumberOfPendingConnections();
}
if(connected >= want)
return;
_outboundSessionMaker.ConnectToRandomRouters(want);
}
bool
Router::InitServiceNode()
{
LogInfo("accepting transit traffic");
paths.AllowTransit();
llarp_dht_allow_transit(dht());
return _exitContext.AddExitEndpoint("default-connectivity", netConfig);
}
bool
Router::ValidateConfig(Config * /*conf*/) const
{
return true;
}
bool
Router::Reconfigure(Config *)
6 years ago
{
// TODO: implement me
return true;
}
6 years ago
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;
}
bool
5 years ago
Router::InitOutboundLinks()
{
const auto linkTypeName = LinkFactory::NameFromType(_defaultLinkType);
LogInfo("initialize outbound link: ", linkTypeName);
auto factory = LinkFactory::Obtain(_defaultLinkType, false);
if(factory == nullptr)
{
LogError("cannot initialize outbound link of type '", linkTypeName,
"' as it has no implementation");
return false;
}
auto link =
factory(m_keyManager, util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnSessionEstablished,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&IOutboundSessionMaker::OnConnectTimeout,
&_outboundSessionMaker),
5 years ago
util::memFn(&AbstractRouter::SessionClosed, this),
util::memFn(&AbstractRouter::PumpLL, this));
if(!link)
return false;
const auto afs = {AF_INET, AF_INET6};
for(const auto af : afs)
{
if(!link->Configure(netloop(), "*", af, m_OutboundPort))
5 years ago
continue;
_linkManager.AddLink(std::move(link), false);
return true;
}
return false;
}
bool
Router::CreateDefaultHiddenService()
{
// add endpoint
return hiddenServiceContext().AddDefaultEndpoint(netConfig);
}
bool
Router::LoadHiddenServiceConfig(string_view fname)
6 years ago
{
LogDebug("opening hidden service config ", fname);
service::Config conf;
if(!conf.Load(fname))
6 years ago
return false;
for(const auto &config : conf.services)
{
service::Config::section_t filteredConfig;
mergeHiddenServiceConfig(config.second, filteredConfig.second);
filteredConfig.first = config.first;
if(!hiddenServiceContext().AddEndpoint(filteredConfig))
return false;
}
return true;
6 years ago
}
void
Router::MessageSent(const RouterID &remote, SendStatus status)
{
if(status == SendStatus::Success)
{
LogDebug("Message successfully sent to ", remote);
}
else
{
LogDebug("Message failed sending to ", remote);
}
}
6 years ago
} // namespace llarp