introset and message transmission underway

- message handling through classes that inherit from PathSet
- cleanups around link_manager
- etc etc
pull/2213/head
dr7ana 8 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; return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(), seed.data()) != -1;
} }
void 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 void

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

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

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

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

@ -114,10 +114,10 @@ namespace llarp::handlers
if (not router->PathToRouterAllowed(*rid)) if (not router->PathToRouterAllowed(*rid))
return false; 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()) 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; maybe_pk = itr->second;
} }
auto buf = const_cast<net::IPPacket&>(top).steal(); auto buf = const_cast<net::IPPacket&>(top);
inet_to_network.pop(); inet_to_network.pop();
// we have no session for public key so drop // we have no session for public key so drop
if (not maybe_pk) if (not maybe_pk)
@ -398,7 +398,7 @@ namespace llarp::handlers
auto itr = snode_sessions.find(pk); auto itr = snode_sessions.find(pk);
if (itr != snode_sessions.end()) 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 // we are in a while loop
continue; continue;
} }

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

@ -1254,12 +1254,12 @@ namespace llarp::handlers
EnsurePathToService( EnsurePathToService(
addr, 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 (ctx)
{ {
if (extra_cb) if (extra_cb)
extra_cb(); extra_cb();
ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit); ctx->send_packet_to_remote(pkt.to_string());
router()->TriggerPump(); router()->TriggerPump();
return; return;
} }

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

@ -512,12 +512,6 @@ namespace llarp
// TODO: this // TODO: this
} }
std::string
LinkManager::serialize_response(oxenc::bt_dict supplement)
{
return oxenc::bt_serialize(supplement);
}
void void
LinkManager::handle_find_name(oxen::quic::message m) LinkManager::handle_find_name(oxen::quic::message m)
{ {
@ -537,8 +531,7 @@ namespace llarp
_router.rpc_client()->lookup_ons_hash( _router.rpc_client()->lookup_ons_hash(
name_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) if (maybe)
msg.respond(serialize_response({{"NAME", maybe->ciphertext}})); msg.respond(serialize_response({{"NAME", maybe->ciphertext}}));
else else
@ -1644,4 +1637,25 @@ namespace llarp
return; 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 } // namespace llarp

@ -28,6 +28,12 @@ namespace llarp
{ {
struct LinkManager; struct LinkManager;
inline std::string
serialize_response(oxenc::bt_dict supplement = {})
{
return oxenc::bt_serialize(supplement);
}
namespace link namespace link
{ {
struct Connection; struct Connection;
@ -316,6 +322,9 @@ namespace llarp
{"obtain_exit", &LinkManager::handle_obtain_exit}, {"obtain_exit", &LinkManager::handle_obtain_exit},
{"close_exit", &LinkManager::handle_close_exit}}; {"close_exit", &LinkManager::handle_close_exit}};
// Path relaying
void handle_path_control(oxen::quic::message);
// DHT responses // DHT responses
void handle_find_name_response(oxen::quic::message); void handle_find_name_response(oxen::quic::message);
void handle_find_intro_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}, {"obtain_exit", &LinkManager::handle_obtain_exit_response},
{"close_exit", &LinkManager::handle_close_exit_response}}; {"close_exit", &LinkManager::handle_close_exit_response}};
std::string
serialize_response(oxenc::bt_dict supplement = {});
public: public:
// Public response functions and error handling functions invoked elsehwere. These take // Public response functions and error handling functions invoked elsehwere. These take
// r-value references s.t. that message is taken out of calling scope // 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/convotag.hpp>
#include <llarp/service/endpoint.hpp> #include <llarp/service/endpoint.hpp>
#include <llarp/service/name.hpp> #include <llarp/service/name.hpp>
#include "stream.hpp"
#include <limits> #include <limits>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <llarp/util/logging/buffer.hpp> #include <llarp/util/logging/buffer.hpp>

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

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

@ -20,7 +20,7 @@ namespace llarp
{ {
namespace namespace
{ {
auto log_path = log::Cat("path"); auto path_cat = log::Cat("path");
} }
namespace path namespace path
@ -326,7 +326,7 @@ namespace llarp
const auto maybe = SelectFirstHop(exclude); const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value()) 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; return std::nullopt;
} }
hops.emplace_back(*maybe); hops.emplace_back(*maybe);
@ -465,22 +465,44 @@ namespace llarp
router->path_context().AddOwnPath(self, path); router->path_context().AddOwnPath(self, path);
PathBuildStarted(path); PathBuildStarted(path);
auto response_cb = [self](oxen::quic::message) { // TODO:
// TODO: this (replaces handling LRSM, which also needs replacing) // 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
// TODO: Talk to Tom about why are we using it as a response callback? // be worth doing sooner rather than later. Leaving some TODOs below where fail
// Do you mean TransitHop::HandleLRSM? // 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( if (not router->send_control_message(
path->upstream(), "path_build", std::move(frames).str(), std::move(response_cb))) 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()); path->EnterState(path::ePathFailed, router->now());
} }
// TODO: we don't use this concept anymore?
router->persist_connection_until(path->upstream(), path->ExpireTime());
} }
void void

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

@ -140,7 +140,7 @@ namespace llarp
virtual void virtual void
HandlePathBuildFailedAt(Path_ptr path, RouterID hop); HandlePathBuildFailedAt(Path_ptr path, RouterID hop);
virtual void void
PathBuildStarted(Path_ptr path); PathBuildStarted(Path_ptr path);
/// a path died now what? /// a path died now what?
@ -249,7 +249,7 @@ namespace llarp
BuildOneAlignedTo(const RouterID endpoint) = 0; BuildOneAlignedTo(const RouterID endpoint) = 0;
virtual void 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>> virtual std::optional<std::vector<RouterContact>>
GetHopsForBuild() = 0; GetHopsForBuild() = 0;

@ -110,7 +110,7 @@ namespace llarp
else else
{ {
r.node_db()->put_rc_if_newer(result); r.node_db()->put_rc_if_newer(result);
r.connect_to(result); // r.connect_to(result);
} }
} }
else else
@ -306,7 +306,8 @@ namespace llarp
{ {
for (const auto& rc : bootstrap_rc_list) 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 // TODO: replace this concept
// dht->ExploreNetworkVia(dht::Key_t{rc.pubkey}); // dht->ExploreNetworkVia(dht::Key_t{rc.pubkey});
} }

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

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

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

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

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <llarp/path/pathbuilder.hpp> #include <llarp/path/pathbuilder.hpp>
#include "sendcontext.hpp" #include <llarp/service/convotag.hpp>
#include <llarp/util/status.hpp> #include <llarp/util/status.hpp>
#include <unordered_map> #include <unordered_map>
@ -13,16 +13,51 @@ namespace llarp::service
struct Endpoint; struct Endpoint;
/// context needed to initiate an outbound hidden service session /// context needed to initiate an outbound hidden service session
struct OutboundContext : public path::Builder, struct OutboundContext : public llarp::path::Builder,
public SendContext,
public std::enable_shared_from_this<OutboundContext> 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; ~OutboundContext() override;
void void
Tick(llarp_time_t now) override; encrypt_and_send(std::string buf);
void
Tick(std::chrono::milliseconds now) override;
util::StatusObject util::StatusObject
ExtractStatus() const; ExtractStatus() const;
@ -30,9 +65,6 @@ namespace llarp::service
void void
BlacklistSNode(const RouterID) override{}; BlacklistSNode(const RouterID) override{};
bool
ShouldBundleRC() const override;
path::PathSet_ptr path::PathSet_ptr
GetSelf() override GetSelf() override
{ {
@ -51,9 +83,6 @@ namespace llarp::service
bool bool
Stop() override; Stop() override;
bool
HandleDataDrop(path::Path_ptr p, const PathID_t& dst, uint64_t s);
void void
HandlePathDied(path::Path_ptr p) override; HandlePathDied(path::Path_ptr p) override;
@ -63,51 +92,41 @@ namespace llarp::service
/// update the current selected intro to be a new best introduction /// update the current selected intro to be a new best introduction
/// return true if we have changed intros /// return true if we have changed intros
bool bool
ShiftIntroduction(bool rebuild = true) override; ShiftIntroduction(bool rebuild = true);
/// shift the intro off the current router it is using /// shift the intro off the current router it is using
void void
ShiftIntroRouter(const RouterID remote) override; ShiftIntroRouter(const RouterID remote);
/// mark the current remote intro as bad
void
MarkCurrentIntroBad(llarp_time_t now) override;
void
MarkIntroBad(const Introduction& marked, llarp_time_t now);
/// return true if we are ready to send /// return true if we are ready to send
bool bool
ReadyToSend() const; ReadyToSend() const;
void
AddReadyHook(std::function<void(OutboundContext*)> readyHook, llarp_time_t timeout);
/// for exits /// for exits
void void
SendPacketToRemote(const llarp_buffer_t&, ProtocolType t) override; send_packet_to_remote(std::string buf) override;
bool bool
ShouldBuildMore(llarp_time_t now) const override; ShouldBuildMore(std::chrono::milliseconds now) const override;
/// pump internal state /// pump internal state
/// return true to mark as dead /// return true to mark as dead
bool bool
Pump(llarp_time_t now); Pump(std::chrono::milliseconds now);
/// return true if it's safe to remove ourselves /// return true if it's safe to remove ourselves
bool bool
IsDone(llarp_time_t now) const; IsDone(std::chrono::milliseconds now) const;
bool bool
CheckPathIsDead(path::Path_ptr p, llarp_time_t dlt); CheckPathIsDead(path::Path_ptr p, std::chrono::milliseconds dlt);
void 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 /// issues a lookup to find the current intro set of the remote service
void void
UpdateIntroSet() override; UpdateIntroSet();
void void
HandlePathBuilt(path::Path_ptr path) override; HandlePathBuilt(path::Path_ptr path) override;
@ -121,9 +140,6 @@ namespace llarp::service
std::optional<std::vector<RouterContact>> std::optional<std::vector<RouterContact>>
GetHopsForBuild() override; GetHopsForBuild() override;
bool
HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrameMessage& frame);
std::string std::string
Name() const override; Name() const override;
@ -131,15 +147,15 @@ namespace llarp::service
KeepAlive(); KeepAlive();
bool bool
ShouldKeepAlive(llarp_time_t now) const; ShouldKeepAlive(std::chrono::milliseconds now) const;
const IntroSet& const IntroSet&
GetCurrentIntroSet() const GetCurrentIntroSet() const
{ {
return currentIntroSet; return current_intro;
} }
llarp_time_t std::chrono::milliseconds
RTT() const; RTT() const;
bool bool
@ -147,33 +163,12 @@ namespace llarp::service
const Address& addr, const Address& addr,
std::optional<IntroSet> i, std::optional<IntroSet> i,
const RouterID& endpoint, const RouterID& endpoint,
llarp_time_t, std::chrono::milliseconds,
uint64_t relayOrder); uint64_t relayOrder);
private: private:
/// swap remoteIntro with next intro /// swap remoteIntro with next intro
void void
SwapIntros(); swap_intros();
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;
}; };
} // namespace llarp::service } // namespace llarp::service

Loading…
Cancel
Save