introset and message transmission underway

- message handling through classes that inherit from PathSet
- cleanups around link_manager
- etc etc
pull/2213/head
dr7ana 7 months ago
parent 6955f3fae0
commit 41312abab0

@ -457,9 +457,9 @@ namespace llarp
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
}
void
Crypto::randomize(const llarp_buffer_t& buff)
Crypto::randomize(uint8_t* buf, size_t len)
{
randombytes((unsigned char*)buff.base, buff.sz);
randombytes(buf, len);
}
void

@ -98,7 +98,7 @@ namespace llarp
seed_to_secretkey(llarp::SecretKey&, const llarp::IdentitySecret&);
/// randomize buffer
void
randomize(const llarp_buffer_t&);
randomize(uint8_t* buf, size_t len);
/// randomizer memory
void
randbytes(byte_t*, size_t);

@ -1,6 +1,5 @@
#include "explorenetworkjob.hpp"
#include <llarp/dht/messages/findrouter.hpp>
#include <llarp/router/router.hpp>
#include <llarp/nodedb.hpp>

@ -21,8 +21,8 @@ namespace llarp::exit
: llarp::path::Builder{r, numpaths, hoplen}
, exit_router{routerId}
, packet_write_func{std::move(writepkt)}
, m_Counter{0}
, m_LastUse{r->now()}
, _counter{0}
, _last_use{r->now()}
, m_BundleRC{false}
, m_Parent{parent}
{
@ -41,7 +41,7 @@ namespace llarp::exit
BaseSession::ExtractStatus() const
{
auto obj = path::Builder::ExtractStatus();
obj["lastExitUse"] = to_json(m_LastUse);
obj["lastExitUse"] = to_json(_last_use);
auto pub = exit_key.toPublic();
obj["exitIdentity"] = pub.ToString();
obj["endpoint"] = exit_router.ToString();
@ -80,8 +80,8 @@ namespace llarp::exit
return std::vector<RouterContact>{*maybe};
return std::nullopt;
}
else
return GetHopsAlignedToForBuild(exit_router);
return GetHopsAlignedToForBuild(exit_router);
}
bool
@ -204,7 +204,7 @@ namespace llarp::exit
llarp::net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return false;
m_LastUse = router->now();
_last_use = router->now();
m_Downstream.emplace(counter, pkt);
return true;
}
@ -231,7 +231,7 @@ namespace llarp::exit
{
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
return queue.back().PutBuffer(llarp_buffer_t{pkt}, _counter++);
}
auto& back = queue.back();
// pack to nearest N
@ -239,10 +239,10 @@ namespace llarp::exit
{
queue.emplace_back();
queue.back().protocol = t;
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
return queue.back().PutBuffer(llarp_buffer_t{pkt}, _counter++);
}
back.protocol = t;
return back.PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
return back.PutBuffer(llarp_buffer_t{pkt}, _counter++);
}
bool
@ -257,7 +257,7 @@ namespace llarp::exit
bool
BaseSession::IsExpired(llarp_time_t now) const
{
return now > m_LastUse && now - m_LastUse > LifeSpan;
return now > _last_use && now - _last_use > LifeSpan;
}
bool
@ -296,6 +296,7 @@ namespace llarp::exit
for (auto& [i, queue] : m_Upstream)
queue.clear();
m_Upstream.clear();
if (numHops == 1)
{
auto r = router;
@ -374,23 +375,25 @@ namespace llarp::exit
}
void
SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
ExitSession::send_packet_to_remote(std::string buf)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
pkt.ZeroAddresses();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
// QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
}
void
ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
SNodeSession::send_packet_to_remote(std::string buf)
{
net::IPPacket pkt{buf.view_all()};
if (pkt.empty())
return;
pkt.ZeroSourceAddress();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
// QueueUpstreamTraffic(std::move(pkt), llarp::routing::EXIT_PAD_SIZE, t);
}
} // namespace llarp::exit

@ -61,12 +61,6 @@ namespace llarp
util::StatusObject
ExtractStatus() const;
bool
ShouldBundleRC() const override
{
return m_BundleRC;
}
void
ResetInternalState() override;
@ -140,9 +134,6 @@ namespace llarp
llarp::SecretKey exit_key;
std::function<bool(const llarp_buffer_t&)> packet_write_func;
virtual void
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const = 0;
bool
HandleTrafficDrop(llarp::path::Path_ptr p, const llarp::PathID_t& path, uint64_t s);
@ -159,8 +150,6 @@ namespace llarp
private:
std::set<RouterID> snode_blacklist;
std::map<uint8_t, std::deque<routing::TransferTrafficMessage>> m_Upstream;
PathID_t m_CurrentPath;
using DownstreamPkt = std::pair<uint64_t, llarp::net::IPPacket>;
@ -174,12 +163,8 @@ namespace llarp
}
};
using DownstreamTrafficQueue_t =
std::priority_queue<DownstreamPkt, std::vector<DownstreamPkt>, DownstreamPktSorter>;
DownstreamTrafficQueue_t m_Downstream;
uint64_t m_Counter;
llarp_time_t m_LastUse;
uint64_t _counter;
llarp_time_t _last_use;
std::vector<SessionReadyFunc> m_PendingCallbacks;
const bool m_BundleRC;
@ -207,16 +192,7 @@ namespace llarp
Name() const override;
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:
void
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
{
// TODO: set expiration time
// msg.address_lifetime = 0;
msg.flag = 1;
}
send_packet_to_remote(std::string buf) override;
};
struct SNodeSession final : public BaseSession
@ -236,16 +212,7 @@ namespace llarp
Name() const override;
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:
void
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
{
// TODO: set expiration time
// msg.address_lifetime = 0;
msg.flag = 0;
}
send_packet_to_remote(std::string buf) override;
};
} // namespace exit

@ -114,10 +114,10 @@ namespace llarp::handlers
if (not router->PathToRouterAllowed(*rid))
return false;
ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable {
ObtainSNodeSession(*rid, [pkt = std::move(payload)](auto session) mutable {
if (session and session->IsReady())
{
session->SendPacketToRemote(std::move(pkt), type);
session->send_packet_to_remote(std::move(pkt));
}
});
}
@ -381,7 +381,7 @@ namespace llarp::handlers
maybe_pk = itr->second;
}
auto buf = const_cast<net::IPPacket&>(top).steal();
auto buf = const_cast<net::IPPacket&>(top);
inet_to_network.pop();
// we have no session for public key so drop
if (not maybe_pk)
@ -398,7 +398,7 @@ namespace llarp::handlers
auto itr = snode_sessions.find(pk);
if (itr != snode_sessions.end())
{
itr->second->SendPacketToRemote(std::move(buf), service::ProtocolType::TrafficV4);
itr->second->send_packet_to_remote(buf.to_string());
// we are in a while loop
continue;
}

@ -1,8 +1,8 @@
#pragma once
#include <llarp/link/tunnel.hpp>
#include <llarp/service/endpoint.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/quic/tunnel.hpp>
#include <llarp/router/router.hpp>
#include <llarp/ev/ev.hpp>
#include <llarp/vpn/egres_packet_router.hpp>
@ -25,7 +25,7 @@ namespace llarp::handlers
r->loop()->add_ticker([this] { Pump(Now()); });
}
virtual bool
bool
HandleInboundPacket(
const service::ConvoTag tag,
const llarp_buffer_t& buf,
@ -74,6 +74,9 @@ namespace llarp::handlers
return true;
}
void
send_packet_to_remote(std::string) override {};
std::string
GetIfName() const override
{
@ -98,9 +101,6 @@ namespace llarp::handlers
return false;
}
void
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
huint128_t
ObtainIPForAddr(std::variant<service::Address, RouterID>) override
{

@ -1254,12 +1254,12 @@ namespace llarp::handlers
EnsurePathToService(
addr,
[pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) {
[pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) mutable {
if (ctx)
{
if (extra_cb)
extra_cb();
ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit);
ctx->send_packet_to_remote(pkt.to_string());
router()->TriggerPump();
return;
}

@ -73,7 +73,7 @@ namespace llarp::handlers
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf) override;
void
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
send_packet_to_remote(std::string) override{};
std::string
GetIfName() const override;

@ -512,12 +512,6 @@ namespace llarp
// TODO: this
}
std::string
LinkManager::serialize_response(oxenc::bt_dict supplement)
{
return oxenc::bt_serialize(supplement);
}
void
LinkManager::handle_find_name(oxen::quic::message m)
{
@ -537,8 +531,7 @@ namespace llarp
_router.rpc_client()->lookup_ons_hash(
name_hash,
[this,
msg = std::move(m)]([[maybe_unused]] std::optional<service::EncryptedName> maybe) mutable {
[msg = std::move(m)]([[maybe_unused]] std::optional<service::EncryptedName> maybe) mutable {
if (maybe)
msg.respond(serialize_response({{"NAME", maybe->ciphertext}}));
else
@ -1644,4 +1637,25 @@ namespace llarp
return;
}
}
void
LinkManager::handle_path_control(oxen::quic::message m)
{
if (m.timed_out)
{
log::info(link_cat, "Path control message timed out!");
return;
}
try
{
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
return;
}
}
} // namespace llarp

@ -28,6 +28,12 @@ namespace llarp
{
struct LinkManager;
inline std::string
serialize_response(oxenc::bt_dict supplement = {})
{
return oxenc::bt_serialize(supplement);
}
namespace link
{
struct Connection;
@ -316,6 +322,9 @@ namespace llarp
{"obtain_exit", &LinkManager::handle_obtain_exit},
{"close_exit", &LinkManager::handle_close_exit}};
// Path relaying
void handle_path_control(oxen::quic::message);
// DHT responses
void handle_find_name_response(oxen::quic::message);
void handle_find_intro_response(oxen::quic::message);
@ -344,9 +353,6 @@ namespace llarp
{"obtain_exit", &LinkManager::handle_obtain_exit_response},
{"close_exit", &LinkManager::handle_close_exit_response}};
std::string
serialize_response(oxenc::bt_dict supplement = {});
public:
// Public response functions and error handling functions invoked elsehwere. These take
// r-value references s.t. that message is taken out of calling scope

@ -2,7 +2,6 @@
#include <llarp/service/convotag.hpp>
#include <llarp/service/endpoint.hpp>
#include <llarp/service/name.hpp>
#include "stream.hpp"
#include <limits>
#include <llarp/util/logging.hpp>
#include <llarp/util/logging/buffer.hpp>

@ -108,8 +108,8 @@ namespace llarp
oxenc::bt_dict_producer btdp;
btdp.append("HASH", hash);
btdp.append("FRAME", hashed_data);
btdp.append("HASH", hash);
return std::move(btdp).str();
}

@ -107,33 +107,41 @@ namespace llarp::path
Path::send_path_control_message(
std::string method, std::string body, std::function<void(oxen::quic::message m)> func)
{
oxenc::bt_dict_producer btdp;
btdp.append("METHOD", method);
btdp.append("BODY", body);
auto payload = std::move(btdp).str();
auto* payload_ptr = reinterpret_cast<unsigned char*>(payload.data());
std::string payload;
// TODO: old impl padded messages if smaller than a certain size; do we still want to?
auto crypto = CryptoManager::instance();
{
oxenc::bt_dict_producer btdp;
btdp.append("BODY", body);
btdp.append("METHOD", method);
payload = std::move(btdp).str();
}
TunnelNonce nonce;
outer_nonce.Randomize();
nonce.Randomize();
for (const auto& hop : hops)
{
// do a round of chacha for each hop and mutate the nonce with that hop's nonce
CryptoManager::instance()->xchacha20(payload_ptr, hop.shared, nonce);
CryptoManager::instance()->xchacha20(
reinterpret_cast<unsigned char*>(payload.data()), payload.size(), hop.shared, nonce);
nonce ^= hop.nonceXOR;
}
oxenc::bt_dict_producer outer_dict;
outer_dict.append("PATHID", TXID().ToView());
outer_dict.append("NONCE", nonce.ToView());
outer_dict.append("PATHID", TXID().ToView());
outer_dict.append("PAYLOAD", payload);
return router.send_control_message(
upstream(), "path_control", std::move(outer_dict.str()), std::move(func));
upstream(),
"path_control",
std::move(outer_dict).str(),
[response_cb = std::move(func)](oxen::quic::message m) {
{
// do path hop logic here
}
});
}
bool

@ -20,7 +20,7 @@ namespace llarp
{
namespace
{
auto log_path = log::Cat("path");
auto path_cat = log::Cat("path");
}
namespace path
@ -326,7 +326,7 @@ namespace llarp
const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value())
{
log::warning(log_path, "{} has no first hop candidate", Name());
log::warning(path_cat, "{} has no first hop candidate", Name());
return std::nullopt;
}
hops.emplace_back(*maybe);
@ -465,22 +465,44 @@ namespace llarp
router->path_context().AddOwnPath(self, path);
PathBuildStarted(path);
auto response_cb = [self](oxen::quic::message) {
// TODO: this (replaces handling LRSM, which also needs replacing)
// TODO: Talk to Tom about why are we using it as a response callback?
// Do you mean TransitHop::HandleLRSM?
// TODO:
// Path build fail and success are handled poorly at best and changing how we
// handle these responses as well as how we store and use Paths as a whole might
// be worth doing sooner rather than later. Leaving some TODOs below where fail
// and success live.
auto response_cb = [self](oxen::quic::message m) {
if (m)
{
std::string status;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
status = btdc.require<std::string>("STATUS");
}
catch (...)
{
log::warning(path_cat, "Error: Failed to parse path build response!", status);
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
throw;
}
// TODO: success logic
}
else
{
log::warning(path_cat, "Path build request returned failure {}");
// TODO: failure logic
}
};
if (not router->send_control_message(
path->upstream(), "path_build", std::move(frames).str(), std::move(response_cb)))
{
log::warning(log_path, "Error sending path_build control message");
log::warning(path_cat, "Error sending path_build control message");
// TODO: inform failure (what this means needs revisiting, badly)
path->EnterState(path::ePathFailed, router->now());
}
// TODO: we don't use this concept anymore?
router->persist_connection_until(path->upstream(), path->ExpireTime());
}
void

@ -73,10 +73,6 @@ namespace llarp::path
bool
ShouldBuildMore(llarp_time_t now) const override;
/// should we bundle RCs in builds?
virtual bool
ShouldBundleRC() const = 0;
void
ResetInternalState() override;

@ -140,7 +140,7 @@ namespace llarp
virtual void
HandlePathBuildFailedAt(Path_ptr path, RouterID hop);
virtual void
void
PathBuildStarted(Path_ptr path);
/// a path died now what?
@ -249,7 +249,7 @@ namespace llarp
BuildOneAlignedTo(const RouterID endpoint) = 0;
virtual void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) = 0;
send_packet_to_remote(std::string buf) = 0;
virtual std::optional<std::vector<RouterContact>>
GetHopsForBuild() = 0;

@ -110,7 +110,7 @@ namespace llarp
else
{
r.node_db()->put_rc_if_newer(result);
r.connect_to(result);
// r.connect_to(result);
}
}
else
@ -306,7 +306,8 @@ namespace llarp
{
for (const auto& rc : bootstrap_rc_list)
{
LogInfo("Doing explore via bootstrap node: ", RouterID(rc.pubkey));
log::info(link_cat, "Doing explore via bootstrap node: {}", RouterID(rc.pubkey));
// TODO: replace this concept
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
}

@ -64,7 +64,7 @@ namespace llarp
{
/// for unit tests
static bool BlockBogons;
static llarp_time_t Lifetime;
static llarp_time_t UpdateInterval;
static llarp_time_t StaleInsertionAge;

@ -779,12 +779,6 @@ namespace llarp::service
return path::Builder::GetHopsAlignedToForBuild(endpoint, SnodeBlacklist());
}
void
Endpoint::PathBuildStarted(path::Path_ptr path)
{
path::Builder::PathBuildStarted(path);
}
constexpr auto MaxOutboundContextPerRemote = 1;
void

@ -304,12 +304,6 @@ namespace llarp
/// this MUST be called if you want to call EnsurePathTo on the given address
void MarkAddressOutbound(service::Address) override;
bool
ShouldBundleRC() const override
{
return false;
}
void
BlacklistSNode(const RouterID snode) override;
@ -426,9 +420,6 @@ namespace llarp
std::optional<std::vector<RouterContact>>
GetHopsForBuildWithEndpoint(RouterID endpoint);
void
PathBuildStarted(path::Path_ptr path) override;
void
AsyncProcessAuthMessage(
std::shared_ptr<ProtocolMessage> msg, std::function<void(AuthResult)> hook);

@ -17,46 +17,27 @@ namespace llarp::service
bool
OutboundContext::Stop()
{
markedBad = true;
marked_bad = true;
return path::Builder::Stop();
}
bool
OutboundContext::IsDone(llarp_time_t now) const
OutboundContext::IsDone(std::chrono::milliseconds now) const
{
(void)now;
return AvailablePaths(path::ePathRoleAny) == 0 && ShouldRemove();
}
bool
OutboundContext::ShouldBundleRC() const
{
return service_endpoint->ShouldBundleRC();
}
bool
OutboundContext::HandleDataDrop(path::Path_ptr p, const PathID_t& dst, uint64_t seq)
{
// pick another intro
if (dst == remoteIntro.path_id && remoteIntro.router == p->Endpoint())
{
LogWarn(Name(), " message ", seq, " dropped by endpoint ", p->Endpoint(), " via ", dst);
markedBad = remoteIntro.IsExpired(Now());
MarkCurrentIntroBad(Now());
ShiftIntroRouter(p->Endpoint());
UpdateIntroSet();
}
return true;
}
constexpr auto OutboundContextNumPaths = 4;
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
: path::Builder{parent->router(), OutboundContextNumPaths, parent->numHops}
, SendContext{introset.address_keys, {}, this, parent}
, location{introset.address_keys.Addr().ToKey()}
, addr{introset.address_keys.Addr()}
, currentIntroSet{introset}
, ep{*parent}
, current_intro{introset}
, location{current_intro.address_keys.Addr().ToKey()}
, addr{current_intro.address_keys.Addr()}
, remote_identity{current_intro.address_keys}
, created_at{ep.Now()}
{
assert(not introset.intros.empty());
@ -69,29 +50,29 @@ namespace llarp::service
CSRNG rng{};
it += std::uniform_int_distribution<size_t>{0, introset.intros.size() - 1}(rng);
}
m_NextIntro = *it;
currentConvoTag.Randomize();
lastShift = Now();
next_intro = *it;
current_tag.Randomize();
last_shift = Now();
// add send and connect timeouts to the parent endpoints path alignment timeout
// this will make it so that there is less of a chance for timing races
sendTimeout += parent->PathAlignmentTimeout();
connectTimeout += parent->PathAlignmentTimeout();
send_timeout += parent->PathAlignmentTimeout();
connect_timeout += parent->PathAlignmentTimeout();
}
OutboundContext::~OutboundContext() = default;
/// actually swap intros
void
OutboundContext::SwapIntros()
OutboundContext::swap_intros()
{
if (remoteIntro != m_NextIntro)
if (remote_intro != next_intro)
{
remoteIntro = m_NextIntro;
service_endpoint->PutSenderFor(currentConvoTag, currentIntroSet.address_keys, false);
service_endpoint->PutIntroFor(currentConvoTag, remoteIntro);
ShiftIntroRouter(m_NextIntro.router);
remote_intro = next_intro;
ep.PutSenderFor(current_tag, current_intro.address_keys, false);
ep.PutIntroFor(current_tag, remote_intro);
ShiftIntroRouter(next_intro.router);
// if we have not made a handshake to the remote endpoint do so
if (not IntroGenerated())
if (not generated_intro)
{
KeepAlive();
}
@ -109,10 +90,10 @@ namespace llarp::service
const Address&,
std::optional<IntroSet> foundIntro,
const RouterID& endpoint,
llarp_time_t,
std::chrono::milliseconds,
uint64_t relayOrder)
{
if (markedBad)
if (marked_bad)
return true;
updatingIntroSet = false;
if (foundIntro)
@ -122,25 +103,25 @@ namespace llarp::service
LogWarn(Name(), " got introset with zero timestamp: ", *foundIntro);
return true;
}
if (currentIntroSet.time_signed > foundIntro->time_signed)
if (current_intro.time_signed > foundIntro->time_signed)
{
LogInfo("introset is old, dropping");
return true;
}
const llarp_time_t now = Now();
const std::chrono::milliseconds now = Now();
if (foundIntro->IsExpired(now))
{
LogError("got expired introset from lookup from ", endpoint);
return true;
}
currentIntroSet = *foundIntro;
current_intro = *foundIntro;
ShiftIntroRouter(RouterID{});
}
else if (relayOrder > 0)
{
++m_LookupFails;
LogWarn(Name(), " failed to look up introset, fails=", m_LookupFails);
++lookup_fails;
LogWarn(Name(), " failed to look up introset, fails=", lookup_fails);
}
return true;
}
@ -148,11 +129,11 @@ namespace llarp::service
bool
OutboundContext::ReadyToSend() const
{
if (markedBad)
if (marked_bad)
return false;
if (remoteIntro.router.IsZero())
if (remote_intro.router.IsZero())
return false;
return IntroSent() and GetPathByRouter(remoteIntro.router);
return sent_intro and GetPathByRouter(remote_intro.router);
}
void
@ -160,7 +141,7 @@ namespace llarp::service
{
const auto now = Now();
Introduction selectedIntro{};
for (const auto& intro : currentIntroSet.intros)
for (const auto& intro : current_intro.intros)
{
if (intro.expiry > selectedIntro.expiry and intro.router != r)
{
@ -169,8 +150,8 @@ namespace llarp::service
}
if (selectedIntro.router.IsZero() || selectedIntro.ExpiresSoon(now))
return;
m_NextIntro = selectedIntro;
lastShift = now;
next_intro = selectedIntro;
last_shift = now;
}
void
@ -195,86 +176,86 @@ namespace llarp::service
OutboundContext::HandlePathBuilt(path::Path_ptr p)
{
path::Builder::HandlePathBuilt(p);
p->SetDataHandler([self = weak_from_this()](auto path, auto frame) {
if (auto ptr = self.lock())
return ptr->HandleHiddenServiceFrame(path, frame);
return false;
});
p->SetDropHandler([self = weak_from_this()](auto path, auto id, auto seqno) {
if (auto ptr = self.lock())
return ptr->HandleDataDrop(path, id, seqno);
return false;
});
if (markedBad)
// p->SetDataHandler([self = weak_from_this()](auto path, auto frame) {
// if (auto ptr = self.lock())
// return ptr->HandleHiddenServiceFrame(path, frame);
// return false;
// });
// p->SetDropHandler([self = weak_from_this()](auto path, auto id, auto seqno) {
// if (auto ptr = self.lock())
// return ptr->HandleDataDrop(path, id, seqno);
// return false;
// });
if (marked_bad)
{
// ignore new path if we are marked dead
LogInfo(Name(), " marked bad, ignoring new path");
p->EnterState(path::ePathIgnore, Now());
}
else if (p->Endpoint() == m_NextIntro.router)
else if (p->Endpoint() == next_intro.router)
{
// we now have a path to the next intro, swap intros
SwapIntros();
swap_intros();
}
}
void
OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t)
{
if (generatedIntro)
if (generated_intro)
{
LogWarn(Name(), " dropping packet as we are not fully handshaked right now");
return;
}
if (remoteIntro.router.IsZero())
if (remote_intro.router.IsZero())
{
LogWarn(Name(), " dropping intro frame we have no intro ready yet");
return;
}
auto path = GetPathByRouter(remoteIntro.router);
auto path = GetPathByRouter(remote_intro.router);
if (path == nullptr)
{
LogError(Name(), " has no path to ", remoteIntro.router, " when we should have had one");
LogError(Name(), " has no path to ", remote_intro.router, " when we should have had one");
return;
}
auto frame = std::make_shared<ProtocolFrameMessage>();
frame->clear();
auto ex = std::make_shared<AsyncKeyExchange>(
service_endpoint->Loop(),
remoteIdent,
service_endpoint->GetIdentity(),
currentIntroSet.sntru_pubkey,
remoteIntro,
service_endpoint,
currentConvoTag,
ep.Loop(),
remote_identity,
ep.GetIdentity(),
current_intro.sntru_pubkey,
remote_intro,
ep,
current_tag,
t);
ex->hook = [self = shared_from_this(), path](auto frame) {
if (not self->Send(std::move(frame), path))
return;
self->service_endpoint->Loop()->call_later(
self->remoteIntro.latency, [self]() { self->sentIntro = true; });
self->ep.Loop()->call_later(
self->remote_intro.latency, [self]() { self->sent_intro = true; });
};
ex->msg.PutBuffer(payload);
ex->msg.introReply = path->intro;
frame->path_id = ex->msg.introReply.path_id;
frame->flag = 0;
generatedIntro = true;
generated_intro = true;
// ensure we have a sender put for this convo tag
service_endpoint->PutSenderFor(currentConvoTag, currentIntroSet.address_keys, false);
ep.PutSenderFor(current_tag, current_intro.address_keys, false);
// encrypt frame async
service_endpoint->router()->queue_work(
ep.router()->queue_work(
[ex, frame] { return AsyncKeyExchange::Encrypt(ex, frame); });
LogInfo(Name(), " send intro frame T=", currentConvoTag);
LogInfo(Name(), " send intro frame T=", current_tag);
}
std::string
OutboundContext::Name() const
{
return "OBContext:" + currentIntroSet.address_keys.Addr().ToString();
return "OBContext:" + current_intro.address_keys.Addr().ToString();
}
void
@ -282,14 +263,17 @@ namespace llarp::service
{
constexpr auto IntrosetUpdateInterval = 10s;
const auto now = Now();
if (updatingIntroSet or markedBad or now < m_LastIntrosetUpdateAt + IntrosetUpdateInterval)
if (updatingIntroSet or marked_bad or now < last_introset_update + IntrosetUpdateInterval)
return;
LogInfo(Name(), " updating introset");
m_LastIntrosetUpdateAt = now;
log::info(link_cat, "{} updating introset", Name());
last_introset_update = now;
// we want to use the parent endpoint's paths because outbound context
// does not implement path::PathSet::HandleGotIntroMessage
const auto paths = GetManyPathsWithUniqueEndpoints(service_endpoint, 2, location);
const auto paths = GetManyPathsWithUniqueEndpoints(&ep, 2, location);
[[maybe_unused]] uint64_t relayOrder = 0;
for ([[maybe_unused]] const auto& path : paths)
{
// TODO: implement this
@ -314,19 +298,17 @@ namespace llarp::service
OutboundContext::ExtractStatus() const
{
auto obj = path::Builder::ExtractStatus();
obj["estimatedRTT"] = to_json(estimatedRTT);
obj["currentConvoTag"] = currentConvoTag.ToHex();
obj["remoteIntro"] = remoteIntro.ExtractStatus();
obj["sessionCreatedAt"] = to_json(createdAt);
obj["lastGoodSend"] = to_json(lastGoodSend);
obj["lastRecv"] = to_json(m_LastInboundTraffic);
obj["lastIntrosetUpdate"] = to_json(m_LastIntrosetUpdateAt);
obj["seqno"] = sequenceNo;
obj["markedBad"] = markedBad;
obj["lastShift"] = to_json(lastShift);
obj["remoteIdentity"] = addr.ToString();
obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus();
obj["nextIntro"] = m_NextIntro.ExtractStatus();
obj["current_tag"] = current_tag.ToHex();
obj["remote_intro"] = remote_intro.ExtractStatus();
obj["session_created"] = to_json(created_at);
obj["last_send"] = to_json(last_send);
obj["lastRecv"] = to_json(last_inbound_traffic);
obj["lastIntrosetUpdate"] = to_json(last_introset_update);
obj["marked_bad"] = marked_bad;
obj["last_shift"] = to_json(last_shift);
obj["remote_identityity"] = addr.ToString();
obj["currentRemote_introset"] = current_intro.ExtractStatus();
obj["nextIntro"] = next_intro.ExtractStatus();
obj["readyToSend"] = ReadyToSend();
return obj;
}
@ -334,43 +316,45 @@ namespace llarp::service
void
OutboundContext::KeepAlive()
{
std::array<byte_t, 64> tmp;
llarp_buffer_t buf{tmp};
CryptoManager::instance()->randomize(buf);
ustring buf(64, '\0');
CryptoManager::instance()->randomize(buf.data(), buf.size());
SendPacketToRemote(buf, ProtocolType::Control);
m_LastKeepAliveAt = Now();
last_keep_alive = Now();
}
bool
OutboundContext::Pump(llarp_time_t now)
OutboundContext::Pump(std::chrono::milliseconds now)
{
if (ReadyToSend() and remoteIntro.router.IsZero())
if (ReadyToSend() and remote_intro.router.IsZero())
{
SwapIntros();
swap_intros();
}
if (ReadyToSend())
{
// if we dont have a cached session key after sending intro we are in a fugged state so
// expunge
SharedSecret discardme;
if (not service_endpoint->GetCachedSessionKeyFor(currentConvoTag, discardme))
if (not ep.GetCachedSessionKeyFor(current_tag, discardme))
{
LogError(Name(), " no cached key after sending intro, we are in a fugged state, oh no");
return true;
}
}
if (m_GotInboundTraffic and m_LastInboundTraffic + sendTimeout <= now)
if (got_inbound_traffic and last_inbound_traffic + send_timeout <= now)
{
// timeout on other side
UpdateIntroSet();
MarkCurrentIntroBad(now);
ShiftIntroRouter(remoteIntro.router);
ShiftIntroRouter(remote_intro.router);
}
// check for stale intros
// update the introset if we think we need to
if (currentIntroSet.HasStaleIntros(now, path::INTRO_PATH_SPREAD)
or remoteIntro.ExpiresSoon(now, path::INTRO_PATH_SPREAD))
if (current_intro.HasStaleIntros(now, path::INTRO_PATH_SPREAD)
or remote_intro.ExpiresSoon(now, path::INTRO_PATH_SPREAD))
{
UpdateIntroSet();
ShiftIntroduction(false);
@ -378,11 +362,11 @@ namespace llarp::service
if (ReadyToSend())
{
if (not remoteIntro.router.IsZero() and not GetPathByRouter(remoteIntro.router))
if (not remote_intro.router.IsZero() and not GetPathByRouter(remote_intro.router))
{
// pick another good intro if we have no path on our current intro
std::vector<Introduction> otherIntros;
ForEachPath([now, router = remoteIntro.router, &otherIntros](auto path) {
ForEachPath([now, router = remote_intro.router, &otherIntros](auto path) {
if (path and path->IsReady() and path->Endpoint() != router
and not path->ExpiresSoon(now, path::INTRO_PATH_SPREAD))
{
@ -392,33 +376,30 @@ namespace llarp::service
if (not otherIntros.empty())
{
std::shuffle(otherIntros.begin(), otherIntros.end(), CSRNG{});
remoteIntro = otherIntros[0];
remote_intro = otherIntros[0];
}
}
}
// lookup router in intro if set and unknown
if (not m_NextIntro.router.IsZero())
service_endpoint->EnsureRouterIsKnown(m_NextIntro.router);
if (not next_intro.router.IsZero())
ep.EnsureRouterIsKnown(next_intro.router);
if (ReadyToSend() and not m_ReadyHooks.empty())
if (ReadyToSend() and not ready_hooks.empty())
{
const auto path = GetPathByRouter(remoteIntro.router);
const auto path = GetPathByRouter(remote_intro.router);
if (not path)
{
LogWarn(Name(), " ready but no path to ", remoteIntro.router, " ???");
LogWarn(Name(), " ready but no path to ", remote_intro.router, " ???");
return true;
}
for (const auto& hook : m_ReadyHooks)
hook(this);
m_ReadyHooks.clear();
}
const auto timeout = std::max(lastGoodSend, m_LastInboundTraffic);
if (lastGoodSend > 0s and now >= timeout + (sendTimeout / 2))
const auto timeout = std::max(last_send, last_inbound_traffic);
if (last_send > 0s and now >= timeout + (send_timeout / 2))
{
// send a keep alive to keep this session alive
KeepAlive();
if (markedBad)
if (marked_bad)
{
LogWarn(Name(), " keepalive timeout hit");
return true;
@ -426,14 +407,14 @@ namespace llarp::service
}
// check for half open state where we can send but we get nothing back
if (m_LastInboundTraffic == 0s and now - createdAt > connectTimeout)
if (last_inbound_traffic == 0s and now - created_at > connect_timeout)
{
LogWarn(Name(), " half open state, we can send but we got nothing back");
return true;
}
// if we are dead return true so we are removed
const bool removeIt = timeout > 0s ? (now >= timeout && now - timeout > sendTimeout)
: (now >= createdAt && now - createdAt > connectTimeout);
const bool removeIt = timeout > 0s ? (now >= timeout && now - timeout > send_timeout)
: (now >= created_at && now - created_at > connect_timeout);
if (removeIt)
{
LogInfo(Name(), " session is stale");
@ -442,42 +423,22 @@ namespace llarp::service
return false;
}
void
OutboundContext::AddReadyHook(std::function<void(OutboundContext*)> hook, llarp_time_t timeout)
{
if (ReadyToSend())
{
hook(this);
return;
}
if (m_ReadyHooks.empty())
{
router->loop()->call_later(timeout, [this]() {
LogWarn(Name(), " did not obtain session in time");
for (const auto& hook : m_ReadyHooks)
hook(nullptr);
m_ReadyHooks.clear();
});
}
m_ReadyHooks.push_back(hook);
}
std::optional<std::vector<RouterContact>>
OutboundContext::GetHopsForBuild()
{
if (m_NextIntro.router.IsZero())
if (next_intro.router.IsZero())
{
ShiftIntroduction(false);
}
if (m_NextIntro.router.IsZero())
if (next_intro.router.IsZero())
return std::nullopt;
return GetHopsAlignedToForBuild(m_NextIntro.router, service_endpoint->SnodeBlacklist());
return GetHopsAlignedToForBuild(next_intro.router, ep.SnodeBlacklist());
}
bool
OutboundContext::ShouldBuildMore(llarp_time_t now) const
OutboundContext::ShouldBuildMore(std::chrono::milliseconds now) const
{
if (markedBad or path::Builder::BuildCooldownHit(now))
if (marked_bad or path::Builder::BuildCooldownHit(now))
return false;
if (NumInStatus(path::ePathBuilding) >= std::max(numDesiredPaths / size_t{2}, size_t{1}))
@ -491,44 +452,25 @@ namespace llarp::service
if (not path->intro.ExpiresSoon(now, path::DEFAULT_LIFETIME - path::INTRO_PATH_SPREAD))
{
numValidPaths++;
if (path->intro.router == m_NextIntro.router)
if (path->intro.router == next_intro.router)
havePathToNextIntro = true;
}
});
return numValidPaths < numDesiredPaths or not havePathToNextIntro;
}
void
OutboundContext::MarkCurrentIntroBad(llarp_time_t now)
{
MarkIntroBad(remoteIntro, now);
}
void
OutboundContext::MarkIntroBad(const Introduction&, llarp_time_t)
{}
bool
OutboundContext::IntroSent() const
{
return sentIntro;
}
bool
OutboundContext::IntroGenerated() const
{
return generatedIntro;
}
bool
OutboundContext::ShiftIntroduction(bool rebuild)
{
bool success = false;
bool success = false, shifted = false;
const auto now = Now();
if (abs(now - lastShift) < shiftTimeout)
auto shift_timeout = send_timeout * 5 / 2;
if (abs(now - last_shift) < shift_timeout)
return false;
bool shifted = false;
std::vector<Introduction> intros = currentIntroSet.intros;
std::vector<Introduction> intros = current_intro.intros;
if (intros.size() > 1)
{
std::shuffle(intros.begin(), intros.end(), CSRNG{});
@ -539,14 +481,14 @@ namespace llarp::service
{
if (intro.ExpiresSoon(now))
continue;
if (service_endpoint->SnodeBlacklist().count(intro.router))
if (ep.SnodeBlacklist().count(intro.router))
continue;
if (remoteIntro.router == intro.router)
if (remote_intro.router == intro.router)
{
if (intro.expiry > m_NextIntro.expiry)
if (intro.expiry > next_intro.expiry)
{
success = true;
m_NextIntro = intro;
next_intro = intro;
}
}
}
@ -555,28 +497,28 @@ namespace llarp::service
/// pick newer intro not on same router
for (const auto& intro : intros)
{
if (service_endpoint->SnodeBlacklist().count(intro.router))
if (ep.SnodeBlacklist().count(intro.router))
continue;
service_endpoint->EnsureRouterIsKnown(intro.router);
ep.EnsureRouterIsKnown(intro.router);
if (intro.ExpiresSoon(now))
continue;
if (m_NextIntro != intro)
if (next_intro != intro)
{
if (intro.expiry > m_NextIntro.expiry)
if (intro.expiry > next_intro.expiry)
{
shifted = intro.router != m_NextIntro.router;
m_NextIntro = intro;
shifted = intro.router != next_intro.router;
next_intro = intro;
success = true;
}
}
}
}
if (m_NextIntro.router.IsZero())
if (next_intro.router.IsZero())
return false;
if (shifted)
lastShift = now;
last_shift = now;
if (rebuild && !BuildCooldownHit(Now()))
BuildOneAlignedTo(m_NextIntro.router);
BuildOneAlignedTo(next_intro.router);
return success;
}
@ -587,7 +529,7 @@ namespace llarp::service
UpdateIntroSet();
const RouterID endpoint{path->Endpoint()};
// if a path to our current intro died...
if (endpoint == remoteIntro.router)
if (endpoint == remote_intro.router)
{
// figure out how many paths to this router we have
size_t num = 0;
@ -600,117 +542,33 @@ namespace llarp::service
// we have no more paths to this endpoint so we want to pivot off of it
MarkCurrentIntroBad(Now());
ShiftIntroRouter(endpoint);
if (m_NextIntro.router != endpoint)
BuildOneAlignedTo(m_NextIntro.router);
if (next_intro.router != endpoint)
BuildOneAlignedTo(next_intro.router);
}
}
}
bool
OutboundContext::ShouldKeepAlive(llarp_time_t now) const
OutboundContext::ShouldKeepAlive(std::chrono::milliseconds now) const
{
const auto SendKeepAliveInterval = sendTimeout / 2;
if (not m_GotInboundTraffic)
const auto SendKeepAliveInterval = send_timeout / 2;
if (not got_inbound_traffic)
return false;
if (m_LastInboundTraffic == 0s)
if (last_inbound_traffic == 0s)
return false;
return (now - m_LastKeepAliveAt) >= SendKeepAliveInterval;
return (now - last_keep_alive) >= SendKeepAliveInterval;
}
void
OutboundContext::Tick(llarp_time_t now)
OutboundContext::Tick(std::chrono::milliseconds now)
{
path::Builder::Tick(now);
if (ShouldKeepAlive(now))
KeepAlive();
}
bool
OutboundContext::HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrameMessage& frame)
{
m_LastInboundTraffic = service_endpoint->Now();
m_GotInboundTraffic = true;
if (frame.flag)
{
// handle discard
ServiceInfo si;
if (!service_endpoint->GetSenderFor(frame.convo_tag, si))
{
LogWarn("no sender for T=", frame.convo_tag);
return false;
}
// verify source
if (!frame.Verify(si))
{
LogWarn("signature verification failed, T=", frame.convo_tag);
return false;
}
// remove convotag it doesn't exist
LogWarn("remove convotag T=", frame.convo_tag, " R=", frame.flag);
AuthResult result{AuthResultCode::eAuthFailed, "unknown reason"};
if (const auto maybe = AuthResultCodeFromInt(frame.flag))
result.code = *maybe;
SharedSecret sessionKey{};
if (service_endpoint->GetCachedSessionKeyFor(frame.convo_tag, sessionKey))
{
ProtocolMessage msg{};
if (frame.DecryptPayloadInto(sessionKey, msg))
{
if (msg.proto == ProtocolType::Auth and not msg.payload.empty())
{
result.reason =
std::string{reinterpret_cast<const char*>(msg.payload.data()), msg.payload.size()};
}
}
}
service_endpoint->RemoveConvoTag(frame.convo_tag);
if (authResultListener)
{
authResultListener(result);
authResultListener = nullptr;
}
return true;
}
std::function<void(std::shared_ptr<ProtocolMessage>)> hook = nullptr;
if (authResultListener)
{
std::function<void(AuthResult)> handler = authResultListener;
authResultListener = nullptr;
hook = [handler](std::shared_ptr<ProtocolMessage> msg) {
AuthResult result{AuthResultCode::eAuthAccepted, "OK"};
if (msg->proto == ProtocolType::Auth and not msg->payload.empty())
{
result.reason =
std::string{reinterpret_cast<const char*>(msg->payload.data()), msg->payload.size()};
}
handler(result);
};
}
const auto& ident = service_endpoint->GetIdentity();
if (not frame.AsyncDecryptAndVerify(service_endpoint->Loop(), p, ident, service_endpoint, hook))
{
// send reset convo tag message
LogError("failed to decrypt and verify frame");
ProtocolFrameMessage f;
f.flag = 1;
f.convo_tag = frame.convo_tag;
f.path_id = p->intro.path_id;
f.Sign(ident);
{
LogWarn("invalidating convotag T=", frame.convo_tag);
service_endpoint->RemoveConvoTag(frame.convo_tag);
service_endpoint->_send_queue.tryPushBack(
SendEvent_t{std::make_shared<routing::PathTransferMessage>(f, frame.path_id), p});
}
}
return true;
}
void
OutboundContext::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
OutboundContext::send_packet_to_remote(std::string buf)
{
AsyncEncryptAndSendTo(buf, t);
}

@ -1,7 +1,7 @@
#pragma once
#include <llarp/path/pathbuilder.hpp>
#include "sendcontext.hpp"
#include <llarp/service/convotag.hpp>
#include <llarp/util/status.hpp>
#include <unordered_map>
@ -13,16 +13,51 @@ namespace llarp::service
struct Endpoint;
/// context needed to initiate an outbound hidden service session
struct OutboundContext : public path::Builder,
public SendContext,
struct OutboundContext : public llarp::path::Builder,
public std::enable_shared_from_this<OutboundContext>
{
OutboundContext(const IntroSet& introSet, Endpoint* parent);
private:
Endpoint& ep;
IntroSet current_intro;
Introduction next_intro;
const dht::Key_t location;
const Address addr;
ServiceInfo remote_identity;
Introduction remote_intro;
ConvoTag current_tag;
uint64_t update_introset_tx = 0;
uint16_t lookup_fails = 0;
uint16_t build_fails = 0;
bool got_inbound_traffic = false;
bool generated_intro = false;
bool sent_intro = false;
bool marked_bad = false;
const std::chrono::milliseconds created_at;
std::chrono::milliseconds last_send = 0ms;
std::chrono::milliseconds send_timeout = path::BUILD_TIMEOUT;
std::chrono::milliseconds connect_timeout = send_timeout * 2;
std::chrono::milliseconds last_shift = 0ms;
std::chrono::milliseconds last_inbound_traffic = 0ms;
std::chrono::milliseconds last_introset_update = 0ms;
std::chrono::milliseconds last_keep_alive = 0ms;
public:
OutboundContext(const IntroSet& introSet, Endpoint* parent);
~OutboundContext() override;
void
Tick(llarp_time_t now) override;
encrypt_and_send(std::string buf);
void
Tick(std::chrono::milliseconds now) override;
util::StatusObject
ExtractStatus() const;
@ -30,9 +65,6 @@ namespace llarp::service
void
BlacklistSNode(const RouterID) override{};
bool
ShouldBundleRC() const override;
path::PathSet_ptr
GetSelf() override
{
@ -51,9 +83,6 @@ namespace llarp::service
bool
Stop() override;
bool
HandleDataDrop(path::Path_ptr p, const PathID_t& dst, uint64_t s);
void
HandlePathDied(path::Path_ptr p) override;
@ -63,51 +92,41 @@ namespace llarp::service
/// update the current selected intro to be a new best introduction
/// return true if we have changed intros
bool
ShiftIntroduction(bool rebuild = true) override;
ShiftIntroduction(bool rebuild = true);
/// shift the intro off the current router it is using
void
ShiftIntroRouter(const RouterID remote) override;
/// mark the current remote intro as bad
void
MarkCurrentIntroBad(llarp_time_t now) override;
void
MarkIntroBad(const Introduction& marked, llarp_time_t now);
ShiftIntroRouter(const RouterID remote);
/// return true if we are ready to send
bool
ReadyToSend() const;
void
AddReadyHook(std::function<void(OutboundContext*)> readyHook, llarp_time_t timeout);
/// for exits
void
SendPacketToRemote(const llarp_buffer_t&, ProtocolType t) override;
send_packet_to_remote(std::string buf) override;
bool
ShouldBuildMore(llarp_time_t now) const override;
ShouldBuildMore(std::chrono::milliseconds now) const override;
/// pump internal state
/// return true to mark as dead
bool
Pump(llarp_time_t now);
Pump(std::chrono::milliseconds now);
/// return true if it's safe to remove ourselves
bool
IsDone(llarp_time_t now) const;
IsDone(std::chrono::milliseconds now) const;
bool
CheckPathIsDead(path::Path_ptr p, llarp_time_t dlt);
CheckPathIsDead(path::Path_ptr p, std::chrono::milliseconds dlt);
void
AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t) override;
AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t);
/// issues a lookup to find the current intro set of the remote service
void
UpdateIntroSet() override;
UpdateIntroSet();
void
HandlePathBuilt(path::Path_ptr path) override;
@ -121,9 +140,6 @@ namespace llarp::service
std::optional<std::vector<RouterContact>>
GetHopsForBuild() override;
bool
HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrameMessage& frame);
std::string
Name() const override;
@ -131,15 +147,15 @@ namespace llarp::service
KeepAlive();
bool
ShouldKeepAlive(llarp_time_t now) const;
ShouldKeepAlive(std::chrono::milliseconds now) const;
const IntroSet&
GetCurrentIntroSet() const
{
return currentIntroSet;
return current_intro;
}
llarp_time_t
std::chrono::milliseconds
RTT() const;
bool
@ -147,33 +163,12 @@ namespace llarp::service
const Address& addr,
std::optional<IntroSet> i,
const RouterID& endpoint,
llarp_time_t,
std::chrono::milliseconds,
uint64_t relayOrder);
private:
/// swap remoteIntro with next intro
void
SwapIntros();
bool
IntroGenerated() const override;
bool
IntroSent() const override;
const dht::Key_t location;
const Address addr;
uint64_t m_UpdateIntrosetTX = 0;
IntroSet currentIntroSet;
Introduction m_NextIntro;
llarp_time_t lastShift = 0s;
uint16_t m_LookupFails = 0;
uint16_t m_BuildFails = 0;
llarp_time_t m_LastInboundTraffic = 0s;
bool m_GotInboundTraffic = false;
bool generatedIntro = false;
bool sentIntro = false;
std::vector<std::function<void(OutboundContext*)>> m_ReadyHooks;
llarp_time_t m_LastIntrosetUpdateAt = 0s;
llarp_time_t m_LastKeepAliveAt = 0s;
swap_intros();
};
} // namespace llarp::service

Loading…
Cancel
Save