following up before Tom meeting

This commit is contained in:
dr7ana 2023-10-04 06:25:25 -07:00
parent f35f7fe3f2
commit 4ed6a01e02
23 changed files with 525 additions and 267 deletions

View File

@ -35,28 +35,11 @@ namespace llarp::routing
try
{
btdp.append("A", "O");
{
auto subdict = btdp.append_dict("B");
for (auto& b : blacklist_policy)
b.bt_encode(subdict);
}
btdp.append("E", flag);
btdp.append("I", pubkey.ToView());
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("V", version);
{
auto subdict = btdp.append_dict("B");
for (auto& w : whitelist_policy)
w.bt_encode(subdict);
}
btdp.append("X", address_lifetime);
btdp.append("Z", sig.ToView());
}
@ -72,8 +55,6 @@ namespace llarp::routing
ObtainExitMessage::decode_key(const llarp_buffer_t& k, llarp_buffer_t* buf)
{
bool read = false;
if (!BEncodeMaybeReadDictList("B", blacklist_policy, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("E", flag, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("I", pubkey, read, k, buf))
@ -84,8 +65,6 @@ namespace llarp::routing
return false;
if (!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
if (!BEncodeMaybeReadDictList("W", whitelist_policy, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("X", address_lifetime, read, k, buf))
return false;
if (!BEncodeMaybeReadDictEntry("Z", sig, read, k, buf))
@ -106,7 +85,6 @@ namespace llarp::routing
try
{
btdp.append("A", "G");
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("V", version);
@ -174,16 +152,7 @@ namespace llarp::routing
try
{
btdp.append("A", "J");
btdp.append("B", backoff_time);
{
auto subdict = btdp.append_dict("R");
for (auto& b : blacklist_policy)
b.bt_encode(subdict);
}
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("V", version);
@ -204,8 +173,6 @@ namespace llarp::routing
bool read = false;
if (!BEncodeMaybeReadDictInt("B", backoff_time, read, k, buf))
return false;
if (!BEncodeMaybeReadDictList("R", blacklist_policy, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("S", sequence_number, read, k, buf))
return false;
if (!BEncodeMaybeReadDictInt("T", tx_id, read, k, buf))
@ -255,7 +222,6 @@ namespace llarp::routing
try
{
btdp.append("A", "V");
btdp.append("P", path_id.ToView());
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
@ -322,7 +288,6 @@ namespace llarp::routing
try
{
btdp.append("A", "V");
btdp.append("S", sequence_number);
btdp.append("T", tx_id);
btdp.append("V", version);

View File

@ -10,11 +10,9 @@ namespace llarp::routing
{
struct ObtainExitMessage final : public AbstractRoutingMessage
{
std::vector<llarp::exit::Policy> blacklist_policy;
uint64_t flag{0}; // 0 for snode, 1 for internet access
llarp::PubKey pubkey;
uint64_t tx_id{0};
std::vector<llarp::exit::Policy> whitelist_policy;
uint64_t address_lifetime{0};
llarp::Signature sig;
@ -26,11 +24,9 @@ namespace llarp::routing
void
clear() override
{
blacklist_policy.clear();
flag = 0;
pubkey.Zero();
tx_id = 0;
whitelist_policy.clear();
address_lifetime = 0;
sig.Zero();
}
@ -88,7 +84,6 @@ namespace llarp::routing
{
using Nonce_t = llarp::AlignedBuffer<16>;
uint64_t backoff_time;
std::vector<llarp::exit::Policy> blacklist_policy;
uint64_t tx_id;
Nonce_t nonce;
llarp::Signature sig;
@ -97,7 +92,6 @@ namespace llarp::routing
clear() override
{
backoff_time = 0;
blacklist_policy.clear();
tx_id = 0;
nonce.Zero();
sig.Zero();

View File

@ -159,14 +159,14 @@ namespace llarp::exit
const static auto roles = llarp::path::ePathRoleExit | llarp::path::ePathRoleSVC;
if (p->SupportsAnyRoles(roles))
{
llarp::LogInfo(p->Name(), " closing exit path");
llarp::LogInfo(p->name(), " closing exit path");
routing::CloseExitMessage msg;
if (msg.Sign(m_ExitIdentity) && p->SendExitClose(msg, router))
{
p->ClearRoles(roles);
}
else
llarp::LogWarn(p->Name(), " failed to send exit close message");
llarp::LogWarn(p->name(), " failed to send exit close message");
}
};
ForEachPath(sendExitClose);
@ -180,10 +180,10 @@ namespace llarp::exit
auto sendExitClose = [&](const path::Path_ptr p) {
if (p->SupportsAnyRoles(path::ePathRoleExit))
{
LogInfo(p->Name(), " closing exit path");
LogInfo(p->name(), " closing exit path");
routing::CloseExitMessage msg;
if (!(msg.Sign(m_ExitIdentity) && p->SendExitClose(msg, router)))
LogWarn(p->Name(), " failed to send exit close message");
LogWarn(p->name(), " failed to send exit close message");
}
};
ForEachPath(sendExitClose);

View File

@ -210,7 +210,7 @@ namespace llarp
std::string
Name() const override;
virtual void
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:
@ -239,7 +239,7 @@ namespace llarp
std::string
Name() const override;
virtual void
void
SendPacketToRemote(const llarp_buffer_t& pkt, service::ProtocolType t) override;
protected:

View File

@ -3,6 +3,8 @@
#include "contacts.hpp"
#include <llarp/messages/dht.hpp>
#include <llarp/messages/exit.hpp>
#include <llarp/messages/path.hpp>
#include <llarp/router/router.hpp>
#include <llarp/router/rc_lookup_handler.hpp>
#include <llarp/nodedb.hpp>
@ -198,6 +200,13 @@ namespace llarp
};
}
if (func)
{
func = [this, f = std::move(func)](oxen::quic::message m) mutable {
router.loop()->call([func = std::move(f), msg = std::move(m)]() mutable { func(msg); });
};
}
return send_control_message_impl(remote, std::move(endpoint), std::move(body), std::move(func));
}
@ -211,22 +220,14 @@ namespace llarp
if (is_stopping)
return false;
// DISCUSS: uncomment this if we want to excecute two callbacks
// auto cb = [this, f = std::move(func), endpoint](oxen::quic::message m) {
// f(m);
// if (auto itr = rpc_responses.find(endpoint); itr != rpc_responses.end())
// std::invoke(itr->second, this, std::move(m));
// };
if (auto conn = ep.get_conn(remote); conn)
{
conn->control_stream->command(endpoint, body, std::move(func));
return true;
}
router.loop()->call([&]() {
auto pending = PendingControlMessage(body, endpoint, func);
router.loop()->call([this, remote, endpoint, body, f = std::move(func)]() {
auto pending = PendingControlMessage(body, endpoint, f);
auto [itr, b] = pending_conn_msg_queue.emplace(remote, MessageQueue());
itr->second.push_back(std::move(pending));
@ -547,7 +548,7 @@ namespace llarp
router.rpc_client()->lookup_ons_hash(
name_hash, [this, msg = std::move(m)](std::optional<service::EncryptedName> maybe) mutable {
if (maybe.has_value())
msg.respond(serialize_response({{"NAME", maybe->ciphertext.c_str()}}));
msg.respond(serialize_response({{"NAME", maybe->ciphertext}}));
else
msg.respond(serialize_response({{"STATUS", FindNameMessage::NOT_FOUND}}), true);
});
@ -600,14 +601,14 @@ namespace llarp
LinkManager::handle_find_router(oxen::quic::message m)
{
std::string target_key;
uint64_t is_exploratory, is_iterative;
bool is_exploratory, is_iterative;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
is_exploratory = btdc.require<uint64_t>("E");
is_iterative = btdc.require<uint64_t>("I");
is_exploratory = btdc.require<bool>("E");
is_iterative = btdc.require<bool>("I");
target_key = btdc.require<std::string>("K");
}
catch (const std::exception& e)
@ -642,8 +643,7 @@ namespace llarp
}
m.respond(
serialize_response(
{{"STATUS", FindRouterMessage::RETRY_EXP}, {"RECIPIENT", neighbors.c_str()}}),
serialize_response({{"STATUS", FindRouterMessage::RETRY_EXP}, {"RECIPIENT", neighbors}}),
true);
}
else
@ -661,7 +661,7 @@ namespace llarp
}
else
{
m.respond(serialize_response({{"RC", closest_rc.ToString().c_str()}}));
m.respond(serialize_response({{"RC", closest_rc.ToString()}}));
}
}
else if (not is_iterative)
@ -735,6 +735,7 @@ namespace llarp
}
RouterID target{reinterpret_cast<uint8_t*>(payload.data())};
if (status == FindRouterMessage::RETRY_EXP)
{
log::critical(link_cat, "FindRouterMessage failed, retrying as exploratory!");
@ -1034,6 +1035,36 @@ namespace llarp
}
}
void
LinkManager::handle_path_build(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_path_build_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_path_confirm(oxen::quic::message m)
{
@ -1049,6 +1080,21 @@ namespace llarp
}
}
void
LinkManager::handle_path_confirm_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_path_latency(oxen::quic::message m)
{
@ -1064,6 +1110,98 @@ namespace llarp
}
}
void
LinkManager::handle_path_latency_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_path_transfer(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_path_transfer_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_obtain_exit(oxen::quic::message m)
{
// TODO: implement transit_hop things like nextseqno(), info.rxID, etc
std::string copy{m.body_str()};
ustring pubkey, signature;
uint64_t flag, tx_id, seq_no;
try
{
oxenc::bt_dict_consumer btdc{copy};
flag = btdc.require<uint64_t>("E");
pubkey = btdc.require<ustring>("I");
seq_no = btdc.require<uint64_t>("S");
tx_id = btdc.require<uint64_t>("T");
signature = btdc.require<ustring>("Z");
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", ObtainExit::EXCEPTION}}), true);
return;
}
RouterID target{pubkey.data()};
if (CryptoManager::instance()->verify(
pubkey.data(), reinterpret_cast<uint8_t*>(copy.data()), copy.size(), signature.data()))
{}
}
void
LinkManager::handle_obtain_exit_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
void
LinkManager::handle_update_exit(oxen::quic::message m)
{
@ -1080,20 +1218,11 @@ namespace llarp
}
void
LinkManager::handle_obtain_exit(oxen::quic::message m)
LinkManager::handle_update_exit_response(oxen::quic::message m)
{
// TODO: implement transit_hop things like nextseqno(), info.rxID, etc
std::string payload{m.body_str()}, pubkey;
[[maybe_unused]] uint64_t flag, tx_id, seq_no;
try
{
oxenc::bt_dict_consumer btdc{payload};
flag = btdc.require<uint64_t>("E");
pubkey = btdc.require<std::string>("I");
seq_no = btdc.require<uint64_t>("S");
tx_id = btdc.require<uint64_t>("T");
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
@ -1101,11 +1230,6 @@ namespace llarp
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
RouterID target;
target.FromString(pubkey);
// auto handler = router.path_context().GetByDownstream(target, tx_id);
}
void
@ -1124,6 +1248,17 @@ namespace llarp
}
void
LinkManager::handle_path_build(oxen::quic::message)
{}
LinkManager::handle_close_exit_response(oxen::quic::message m)
{
try
{
oxenc::bt_dict_consumer btdc{m.body()};
}
catch (const std::exception& e)
{
log::warning(link_cat, "Exception: {}", e.what());
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
return;
}
}
} // namespace llarp

View File

@ -289,10 +289,11 @@ namespace llarp
// Path messages
void handle_path_build(oxen::quic::message); // relay
void handle_relay_status(oxen::quic::message); // relay
void handle_path_confirm(oxen::quic::message); // relay
void handle_path_latency(oxen::quic::message); // relay
void handle_path_transfer(oxen::quic::message); // relay
void handle_relay_commit(oxen::quic::message); // relay
void handle_relay_status(oxen::quic::message); // relay
// Exit messages
void handle_obtain_exit(oxen::quic::message); // relay
@ -319,6 +320,7 @@ namespace llarp
// Path responses
void handle_path_build_response(oxen::quic::message);
void handle_relay_commit_response(oxen::quic::message);
void handle_relay_status_response(oxen::quic::message);
void handle_path_confirm_response(oxen::quic::message);
void handle_path_latency_response(oxen::quic::message);

View File

@ -1,5 +1,6 @@
#pragma once
#include <llarp/crypto/crypto.hpp>
#include <llarp/dht/key.hpp>
#include <llarp/path/path_types.hpp>
#include <llarp/router_id.hpp>

View File

@ -6,10 +6,10 @@ namespace llarp
{
namespace FindRouterMessage
{
inline const char* EXCEPTION = "EXCEPTION";
inline const char* RETRY_EXP = "RETRY AS EXPLORATORY";
inline const char* RETRY_ITER = "RETRY AS ITERATIVE";
inline const char* RETRY_NEW = "RETRY WITH NEW RECIPIENT";
inline auto EXCEPTION = "EXCEPTION"sv;
inline auto RETRY_EXP = "RETRY AS EXPLORATORY"sv;
inline auto RETRY_ITER = "RETRY AS ITERATIVE"sv;
inline auto RETRY_NEW = "RETRY WITH NEW RECIPIENT"sv;
inline static std::string
serialize(const RouterID& rid, bool is_iterative, bool is_exploratory, uint64_t tx_id)
@ -18,7 +18,6 @@ namespace llarp
try
{
btdp.append("A", "R");
btdp.append("E", is_exploratory);
btdp.append("I", is_iterative);
btdp.append("K", rid.ToView());
@ -35,10 +34,10 @@ namespace llarp
namespace FindIntroMessage
{
inline const char* EXCEPTION = "EXCEPTION";
inline const char* NOT_FOUND = "NOT FOUND";
inline const char* INVALID_ORDER = "INVALID ORDER";
inline const char* INSUFFICIENT_NODES = "INSUFFICIENT NODES";
inline auto EXCEPTION = "EXCEPTION"sv;
inline auto NOT_FOUND = "NOT FOUND"sv;
inline auto INVALID_ORDER = "INVALID ORDER"sv;
inline auto INSUFFICIENT_NODES = "INSUFFICIENT NODES"sv;
inline static std::string
serialize(
@ -48,7 +47,6 @@ namespace llarp
try
{
btdp.append("A", "F");
btdp.append("N", tag);
btdp.append("O", order);
btdp.append("R", is_relayed ? 1 : 0);
@ -66,8 +64,8 @@ namespace llarp
namespace FindNameMessage
{
inline const char* EXCEPTION = "EXCEPTION";
inline const char* NOT_FOUND = "NOT FOUND";
inline auto EXCEPTION = "EXCEPTION"sv;
inline auto NOT_FOUND = "NOT FOUND"sv;
inline static std::string
serialize([[maybe_unused]] const dht::Key_t& from, dht::Key_t name_hash, uint64_t tx_id)
@ -76,7 +74,6 @@ namespace llarp
try
{
btdp.append("A", "N");
btdp.append("H", name_hash.ToView());
btdp.append("T", tx_id);
}
@ -91,11 +88,11 @@ namespace llarp
namespace PublishIntroMessage
{
inline const char* EXCEPTION = "EXCEPTION";
inline const char* INVALID_INTROSET = "INVALID INTROSET";
inline const char* EXPIRED = "EXPIRED INTROSET";
inline const char* INSUFFICIENT = "INSUFFICIENT NODES";
inline const char* INVALID_ORDER = "INVALID ORDER";
inline auto EXCEPTION = "EXCEPTION"sv;
inline auto INVALID_INTROSET = "INVALID INTROSET"sv;
inline auto EXPIRED = "EXPIRED INTROSET"sv;
inline auto INSUFFICIENT = "INSUFFICIENT NODES"sv;
inline auto INVALID_ORDER = "INVALID ORDER"sv;
inline static std::string
serialize(std::string introset, uint64_t relay_order, uint64_t is_relayed, uint64_t tx_id)
@ -104,7 +101,6 @@ namespace llarp
try
{
btdp.append("A", "I");
btdp.append("I", introset);
btdp.append("O", relay_order);
btdp.append("R", is_relayed);

25
llarp/messages/exit.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "common.hpp"
namespace llarp
{
namespace ObtainExit
{
inline auto EXCEPTION = "EXCEPTION"sv;
}
namespace UpdateExit
{
inline auto EXCEPTION = "EXCEPTION"sv;
}
namespace CloseExit
{
inline auto EXCEPTION = "EXCEPTION"sv;
}
} // namespace llarp

View File

@ -4,22 +4,119 @@
namespace llarp
{
struct PathMessage : public AbstractSerializable
{};
namespace PathBuildMessage
{
inline static void
setup_hop_keys(path::PathHopConfig& hop, const RouterID& nextHop)
{
auto crypto = CryptoManager::instance();
struct RelayCommitMessage : public PathMessage
{};
// generate key
crypto->encryption_keygen(hop.commkey);
struct RelayStatusMessage : public PathMessage
{};
hop.nonce.Randomize();
// do key exchange
if (!crypto->dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce))
{
auto err = fmt::format("Failed to generate shared key for path build!");
log::error(path_cat, err);
throw std::runtime_error{std::move(err)};
}
// generate nonceXOR value self->hop->pathKey
crypto->shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
struct PathConfirmMessage : public PathMessage
{};
hop.upstream = nextHop;
}
struct PathLatencyMessage : public PathMessage
{};
inline static std::string
serialize(const path::PathHopConfig& hop)
{
auto crypto = CryptoManager::instance();
struct PathTransferMessage : public PathMessage
{};
std::string hop_info;
{
oxenc::bt_dict_producer btdp;
btdp.append("lifetime", path::DEFAULT_LIFETIME.count());
btdp.append("txid", hop.txID.ToView());
btdp.append("rxid", hop.rxID.ToView());
btdp.append("nonce", hop.nonce.ToView());
btdp.append("next", hop.upstream.ToView());
btdp.append("commkey", hop.commkey.toPublic().ToView());
hop_info = std::move(btdp).str();
}
SecretKey framekey;
crypto->encryption_keygen(framekey);
SharedSecret shared;
TunnelNonce outer_nonce;
outer_nonce.Randomize();
// derive (outer) shared key
if (!crypto->dh_client(shared, hop.rc.pubkey, framekey, outer_nonce))
{
log::error(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"};
}
// encrypt hop_info (mutates in-place)
if (!crypto->xchacha20(
reinterpret_cast<uint8_t*>(hop_info.data()), hop_info.size(), shared, outer_nonce))
{
log::error(path_cat, "Hop info encryption failed!");
throw std::runtime_error{"Hop info encrypttion failed"};
}
std::string hashed_data;
{
oxenc::bt_dict_producer btdp;
btdp.append("encrypted", hop_info);
btdp.append("pubkey", framekey.toPublic().ToView());
btdp.append("nonce", outer_nonce.ToView());
hashed_data = std::move(btdp).str();
}
std::string hash;
hash.reserve(SHORTHASHSIZE);
if (!crypto->hmac(
reinterpret_cast<uint8_t*>(hash.data()),
reinterpret_cast<uint8_t*>(hashed_data.data()),
hashed_data.size(),
shared))
{
log::error(path_cat, "Failed to generate HMAC for hop info");
throw std::runtime_error{"Failed to generate HMAC for hop info"};
}
oxenc::bt_dict_producer btdp;
btdp.append("hash", hash);
btdp.append("frame", hashed_data);
return std::move(btdp).str();
}
} // namespace PathBuildMessage
namespace RelayCommitMessage
{}
namespace RelayStatusMessage
{}
namespace PathConfirmMessage
{}
namespace PathLatencyMessage
{}
namespace PathTransferMessage
{}
} // namespace llarp

View File

@ -308,9 +308,9 @@ namespace llarp
}
// persist sessions to upstream and downstream routers until the commit
// ends
self->context->router()->PersistSessionUntil(
self->context->router()->persist_connection_until(
self->hop->info.downstream, self->hop->ExpireTime() + 10s);
self->context->router()->PersistSessionUntil(
self->context->router()->persist_connection_until(
self->hop->info.upstream, self->hop->ExpireTime() + 10s);
// put hop
self->context->PutTransitHop(self->hop);
@ -345,7 +345,7 @@ namespace llarp
else
{
// persist session to downstream until path expiration
self->context->router()->PersistSessionUntil(
self->context->router()->persist_connection_until(
self->hop->info.downstream, self->hop->ExpireTime() + 10s);
// put hop
self->context->PutTransitHop(self->hop);

View File

@ -1,6 +1,5 @@
#include "path.hpp"
#include <llarp/dht/context.hpp>
#include <llarp/exit/exit_messages.hpp>
#include <llarp/link/link_manager.hpp>
#include <llarp/messages/discard.hpp>
@ -114,13 +113,13 @@ namespace llarp::path
}
bool
Path::IsEndpoint(const RouterID& r, const PathID_t& id) const
Path::is_endpoint(const RouterID& r, const PathID_t& id) const
{
return hops[hops.size() - 1].rc.pubkey == r && hops[hops.size() - 1].txID == id;
}
RouterID
Path::Upstream() const
Path::upstream() const
{
return hops[0].rc.pubkey;
}
@ -206,7 +205,7 @@ namespace llarp::path
{
r->notify_router_event<tooling::PathBuildRejectedEvent>(Endpoint(), RXID(), *failedAt);
LogWarn(
Name(),
name(),
" build failed at ",
*failedAt,
" status=",
@ -302,16 +301,16 @@ namespace llarp::path
}
else if (st == ePathBuilding)
{
LogInfo("path ", Name(), " is building");
LogInfo("path ", name(), " is building");
buildStarted = now;
}
else if (st == ePathEstablished && _status == ePathBuilding)
{
LogInfo("path ", Name(), " is built, took ", ToString(now - buildStarted));
LogInfo("path ", name(), " is built, took ", ToString(now - buildStarted));
}
else if (st == ePathTimeout && _status == ePathEstablished)
{
LogInfo("path ", Name(), " died");
LogInfo("path ", name(), " died");
_status = st;
if (auto parent = m_PathSet.lock())
{
@ -320,11 +319,11 @@ namespace llarp::path
}
else if (st == ePathEstablished && _status == ePathTimeout)
{
LogInfo("path ", Name(), " reanimated");
LogInfo("path ", name(), " reanimated");
}
else if (st == ePathIgnore)
{
LogInfo("path ", Name(), " ignored");
LogInfo("path ", name(), " ignored");
}
_status = st;
}
@ -404,7 +403,7 @@ namespace llarp::path
std::vector<RouterContact> newHops;
for (const auto& hop : hops)
newHops.emplace_back(hop.rc);
LogInfo(Name(), " rebuilding on ", ShortName());
LogInfo(name(), " rebuilding on ", ShortName());
parent->Build(newHops);
}
}
@ -419,7 +418,7 @@ namespace llarp::path
latency.sequence_number = NextSeqNo();
m_LastLatencyTestID = latency.sent_time;
m_LastLatencyTestTime = now;
LogDebug(Name(), " send latency test id=", latency.sent_time);
LogDebug(name(), " send latency test id=", latency.sent_time);
if (not SendRoutingMessage(latency, r))
return false;
FlushUpstream(r);
@ -447,7 +446,7 @@ namespace llarp::path
const auto dlt = now - buildStarted;
if (dlt >= path::BUILD_TIMEOUT)
{
LogWarn(Name(), " waited for ", ToString(dlt), " and no path was built");
LogWarn(name(), " waited for ", ToString(dlt), " and no path was built");
r->router_profiling().MarkPathFail(this);
EnterState(ePathExpired, now);
return;
@ -471,7 +470,7 @@ namespace llarp::path
dlt = now - m_LastRecvMessage;
if (dlt >= path::ALIVE_TIMEOUT)
{
LogWarn(Name(), " waited for ", ToString(dlt), " and path looks dead");
LogWarn(name(), " waited for ", ToString(dlt), " and path looks dead");
r->router_profiling().MarkPathFail(this);
EnterState(ePathTimeout, now);
}
@ -488,13 +487,13 @@ namespace llarp::path
{
for (const auto& msg : msgs)
{
if (r->send_data_message(Upstream(), msg))
if (r->send_data_message(upstream(), msg))
{
m_TXRate += msg.enc.size();
}
else
{
LogDebug("failed to send upstream to ", Upstream());
LogDebug("failed to send upstream to ", upstream());
}
}
r->TriggerPump();
@ -572,7 +571,7 @@ namespace llarp::path
}
std::string
Path::Name() const
Path::name() const
{
return fmt::format("TX={} RX={}", TXID(), RXID());
}
@ -725,7 +724,7 @@ namespace llarp::path
r->router_profiling().MarkPathSuccess(this);
// persist session with upstream router until the path is done
r->PersistSessionUntil(Upstream(), intro.expiry);
r->persist_connection_until(upstream(), intro.expiry);
MarkActive(now);
return SendLatencyMessage(r);
}
@ -808,22 +807,22 @@ namespace llarp::path
{
if (msg.Verify(EndpointPubKey()))
{
LogInfo(Name(), " had its exit closed");
LogInfo(name(), " had its exit closed");
_role &= ~ePathRoleExit;
return true;
}
LogError(Name(), " CXM from exit with bad signature");
LogError(name(), " CXM from exit with bad signature");
}
else
LogError(Name(), " unwarranted CXM");
LogError(name(), " unwarranted CXM");
return false;
}
bool
Path::SendExitRequest(const routing::ObtainExitMessage& msg, Router* r)
{
LogInfo(Name(), " sending exit request to ", Endpoint());
LogInfo(name(), " sending exit request to ", Endpoint());
m_ExitObtainTX = msg.tx_id;
return SendRoutingMessage(msg, r);
}
@ -831,7 +830,7 @@ namespace llarp::path
bool
Path::SendExitClose(const routing::CloseExitMessage& msg, Router* r)
{
LogInfo(Name(), " closing exit to ", Endpoint());
LogInfo(name(), " closing exit to ", Endpoint());
// mark as not exit anymore
_role &= ~ePathRoleExit;
return SendRoutingMessage(msg, r);
@ -842,7 +841,7 @@ namespace llarp::path
{
(void)msg;
(void)r;
LogError(Name(), " got unwarranted OXM");
LogError(name(), " got unwarranted OXM");
return false;
}
@ -851,7 +850,7 @@ namespace llarp::path
{
(void)msg;
(void)r;
LogError(Name(), " got unwarranted UXM");
LogError(name(), " got unwarranted UXM");
return false;
}
@ -862,14 +861,14 @@ namespace llarp::path
{
if (!msg.Verify(EndpointPubKey()))
{
LogError(Name(), "RXM invalid signature");
LogError(name(), "RXM invalid signature");
return false;
}
LogInfo(Name(), " ", Endpoint(), " Rejected exit");
LogInfo(name(), " ", Endpoint(), " Rejected exit");
MarkActive(r->now());
return InformExitResult(llarp_time_t(msg.backoff_time));
}
LogError(Name(), " got unwarranted RXM");
LogError(name(), " got unwarranted RXM");
return false;
}
@ -880,16 +879,16 @@ namespace llarp::path
{
if (!msg.Verify(EndpointPubKey()))
{
LogError(Name(), " GXM signature failed");
LogError(name(), " GXM signature failed");
return false;
}
// we now can send exit traffic
_role |= ePathRoleExit;
LogInfo(Name(), " ", Endpoint(), " Granted exit");
LogInfo(name(), " ", Endpoint(), " Granted exit");
MarkActive(r->now());
return InformExitResult(0s);
}
LogError(Name(), " got unwarranted GXM");
LogError(name(), " got unwarranted GXM");
return false;
}

View File

@ -34,16 +34,10 @@ namespace llarp
{
struct TransitHop;
struct TransitHopInfo;
struct PathHopConfig;
using TransitHop_ptr = std::shared_ptr<TransitHop>;
inline bool
operator<(const PathHopConfig& lhs, const PathHopConfig& rhs)
{
return std::tie(lhs.txID, lhs.rxID, lhs.rc, lhs.upstream, lhs.lifetime)
< std::tie(rhs.txID, rhs.rxID, rhs.rc, rhs.upstream, rhs.lifetime);
}
/// A path we made
struct Path final : public AbstractHopHandler,
public routing::AbstractRoutingMessageHandler,
@ -330,16 +324,16 @@ namespace llarp
EndpointPubKey() const;
bool
IsEndpoint(const RouterID& router, const PathID_t& path) const;
is_endpoint(const RouterID& router, const PathID_t& path) const;
PathID_t
RXID() const override;
RouterID
Upstream() const;
upstream() const;
std::string
Name() const;
name() const;
void
AddObtainExitHandler(ObtainedExitHandler handler)

View File

@ -1,7 +1,15 @@
#pragma once
#include <llarp/constants/path.hpp>
#include <llarp/crypto/constants.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/crypto/types.hpp>
#include <llarp/router_contact.hpp>
namespace
{
static auto path_cat = llarp::log::Cat("lokinet.path");
} // namespace
namespace llarp
{
@ -10,6 +18,43 @@ namespace llarp
using AlignedBuffer<PATHIDSIZE>::AlignedBuffer;
};
namespace path
{
/// configuration for a single hop when building a path
struct PathHopConfig
{
/// path id
PathID_t txID, rxID;
// router contact of router
RouterContact rc;
// temp public encryption key
SecretKey commkey;
/// shared secret at this hop
SharedSecret shared;
/// hash of shared secret used for nonce mutation
ShortHash nonceXOR;
/// next hop's router id
RouterID upstream;
/// nonce for key exchange
TunnelNonce nonce;
// lifetime
llarp_time_t lifetime = DEFAULT_LIFETIME;
util::StatusObject
ExtractStatus() const;
};
inline bool
operator<(const PathHopConfig& lhs, const PathHopConfig& rhs)
{
return std::tie(lhs.txID, lhs.rxID, lhs.rc, lhs.upstream, lhs.lifetime)
< std::tie(rhs.txID, rhs.rxID, rhs.rc, rhs.upstream, rhs.lifetime);
}
// milliseconds waiting between builds on a path per router
static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms;
static constexpr auto PATH_BUILD_RATE = 100ms;
} // namespace path
} // namespace llarp
namespace std

View File

@ -2,15 +2,16 @@
#include "path_context.hpp"
#include <llarp/crypto/crypto.hpp>
#include <llarp/link/link_manager.hpp>
#include <llarp/messages/path.hpp>
#include <llarp/messages/relay_commit.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/profiling.hpp>
#include <llarp/router/router.hpp>
#include <llarp/router/rc_lookup_handler.hpp>
#include <llarp/util/buffer.hpp>
#include <llarp/tooling/path_event.hpp>
#include <llarp/link/link_manager.hpp>
#include <llarp/util/buffer.hpp>
#include <llarp/util/logging.hpp>
#include <functional>
@ -78,7 +79,7 @@ namespace llarp
*/
void
Builder::SetupHopKeys(path::PathHopConfig& hop, const RouterID& nextHop)
Builder::setup_hop_keys(path::PathHopConfig& hop, const RouterID& nextHop)
{
auto crypto = CryptoManager::instance();
@ -89,41 +90,47 @@ namespace llarp
// do key exchange
if (!crypto->dh_client(hop.shared, hop.rc.pubkey, hop.commkey, hop.nonce))
{
LogError(Name(), " Failed to generate shared key for path build");
throw std::runtime_error{"Failed to generate shared key for path build"};
auto err = fmt::format("{} failed to generate shared key for path build!", Name());
log::error(path_cat, err);
throw std::runtime_error{std::move(err)};
}
// generate nonceXOR value self->hop->pathKey
crypto->shorthash(hop.nonceXOR, llarp_buffer_t(hop.shared));
crypto->shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
hop.upstream = nextHop;
}
// FIXME: this is definitely not optimal using bt_dict instead of bt_dict_producer, among other
// things
std::string
Builder::CreateHopInfoFrame(const path::PathHopConfig& hop)
Builder::create_hop_info_frame(const path::PathHopConfig& hop)
{
auto crypto = CryptoManager::instance();
oxenc::bt_dict hop_info_dict;
hop_info_dict["lifetime"] = path::DEFAULT_LIFETIME.count(); // milliseconds
hop_info_dict["txid"] = hop.txID;
hop_info_dict["rxid"] = hop.rxID;
hop_info_dict["nonce"] = hop.nonce;
hop_info_dict["next"] = hop.upstream;
hop_info_dict["commkey"] = hop.commkey.toPublic(); // pubkey of ephemeral Ed key for DH
std::string hop_info;
{
oxenc::bt_dict_producer btdp;
btdp.append("lifetime", path::DEFAULT_LIFETIME.count());
btdp.append("txid", hop.txID.ToView());
btdp.append("rxid", hop.rxID.ToView());
btdp.append("nonce", hop.nonce.ToView());
btdp.append("next", hop.upstream.ToView());
btdp.append("commkey", hop.commkey.toPublic().ToView());
hop_info = std::move(btdp).str();
}
auto hop_info = oxenc::bt_serialize(hop_info_dict);
SecretKey framekey;
crypto->encryption_keygen(framekey);
SharedSecret shared;
TunnelNonce outer_nonce;
outer_nonce.Randomize();
// derive (outer) shared key
if (!crypto->dh_client(shared, hop.rc.pubkey, framekey, outer_nonce))
{
llarp::LogError("DH failed during hop info encryption");
log::error(path_cat, "DH client failed during hop info encryption!");
throw std::runtime_error{"DH failed during hop info encryption"};
}
@ -131,31 +138,41 @@ namespace llarp
if (!crypto->xchacha20(
reinterpret_cast<uint8_t*>(hop_info.data()), hop_info.size(), shared, outer_nonce))
{
llarp::LogError("hop info encrypt failed");
throw std::runtime_error{"hop info encrypt failed"};
log::error(path_cat, "Hop info encryption failed!");
throw std::runtime_error{"Hop info encrypttion failed"};
}
oxenc::bt_dict hashed_dict;
hashed_dict["encrypted"] = hop_info;
hashed_dict["pubkey"] = framekey.toPublic();
hashed_dict["nonce"] = nonce;
auto hashed_data = oxenc::bt_serialize(hashed_dict);
std::basic_string<uint8_t> hash{SHORTHASHSIZE, '\0'};
std::string hashed_data;
{
oxenc::bt_dict_producer btdp;
btdp.append("encrypted", hop_info);
btdp.append("pubkey", framekey.toPublic().ToView());
btdp.append("nonce", outer_nonce.ToView());
hashed_data = std::move(btdp).str();
}
std::string hash;
hash.reserve(SHORTHASHSIZE);
if (!crypto->hmac(
hash.data(),
reinterpret_cast<uint8_t*>(hash.data()),
reinterpret_cast<uint8_t*>(hashed_data.data()),
hashed_data.size(),
shared))
{
llarp::LogError("Failed to generate HMAC for hop info");
log::error(path_cat, "Failed to generate HMAC for hop info");
throw std::runtime_error{"Failed to generate HMAC for hop info"};
}
oxenc::bt_dict final_frame;
final_frame["hash"] = hash;
final_frame["frame"] = hashed_data;
oxenc::bt_dict_producer btdp;
return oxenc::bt_serialize(final_frame);
btdp.append("hash", hash);
btdp.append("frame", hashed_data);
return std::move(btdp).str();
}
void
@ -386,63 +403,83 @@ namespace llarp
Builder::Build(std::vector<RouterContact> hops, PathRole roles)
{
if (IsStopped())
{
log::info(path_cat, "Path builder is stopped, aborting path build...");
return;
lastBuild = Now();
}
lastBuild = llarp::time_now_ms();
const RouterID edge{hops[0].pubkey};
if (not router->pathbuild_limiter().Attempt(edge))
{
LogWarn(Name(), " building too fast to edge router ", edge);
log::warning(path_cat, "{} building too quickly to edge router {}", Name(), edge);
return;
}
std::string path_shortName = "[path " + router->ShortName() + "-";
path_shortName = path_shortName + std::to_string(router->NextPathBuildNumber()) + "]";
auto path = std::make_shared<path::Path>(hops, GetWeak(), roles, std::move(path_shortName));
LogInfo(Name(), " build ", path->ShortName(), ": ", path->HopsString());
log::info(
path_cat, "{} building path -> {} : {}", Name(), path->ShortName(), path->HopsString());
oxenc::bt_list_producer frames;
auto& path_hops = path->hops;
size_t n_hops = path_hops.size();
size_t last_len{0};
for (size_t i = 0; i < path_hops.size(); i++)
for (size_t i = 0; i < n_hops; i++)
{
bool lastHop = (i == (path_hops.size() - 1));
bool lastHop = (i == (n_hops - 1));
const auto& nextHop = lastHop ? path_hops[i].rc.pubkey : path_hops[i + 1].rc.pubkey;
SetupHopKeys(path_hops[i], nextHop);
auto frame_str = CreateHopInfoFrame(path_hops[i]);
// TODO: talk to Tom about what he thinks about this
PathBuildMessage::setup_hop_keys(path_hops[i], nextHop);
auto frame_str = PathBuildMessage::serialize(path_hops[i]);
// all frames should be the same length...not sure what that is yet
if (last_len != 0)
assert(
frame_str.size()
== last_len); // all frames should be the same length...not sure what that is yet
assert(frame_str.size() == last_len);
last_len = frame_str.size();
frames.append(frame_str);
frames.append(std::move(frame_str));
}
std::string dummy{last_len, '\0'};
std::string dummy;
dummy.reserve(last_len);
// append dummy frames; path build request must always have MAX_LEN frames
for (size_t i = 0; i < path::MAX_LEN - path_hops.size(); i++)
for (size_t i = 0; i < path::MAX_LEN - n_hops; i++)
{
randombytes(reinterpret_cast<uint8_t*>(dummy.data()), dummy.size());
frames.append(dummy);
}
m_router->notify_router_event<tooling::PathAttemptEvent>(m_router->pubkey(), path);
// TODO: talk to Tom about whether we do still this or not
// router->notify_router_event<tooling::PathAttemptEvent>(router->pubkey(), path);
auto self = GetSelf();
m_router->path_context().AddOwnPath(self, path);
router->path_context().AddOwnPath(self, path);
PathBuildStarted(path);
auto response_cb = [self](oxen::quic::message m) {
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?
};
if (not m_router->send_control_message(
path->Upstream(), "path_build", std::move(frames.str()), std::move(response_cb)))
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");
path->EnterState(path::ePathFailed, m_router->now());
path->EnterState(path::ePathFailed, router->now());
}
m_router->PersistSessionUntil(path->Upstream(), path->ExpireTime());
router->persist_connection_until(path->upstream(), path->ExpireTime());
}
void
@ -451,7 +488,7 @@ namespace llarp
buildIntervalLimit = PATH_BUILD_RATE;
router->router_profiling().MarkPathSuccess(p.get());
LogInfo(p->Name(), " built latency=", ToString(p->intro.latency));
LogInfo(p->name(), " built latency=", ToString(p->intro.latency));
m_BuildStats.success++;
}
@ -479,24 +516,25 @@ namespace llarp
DoPathBuildBackoff();
for (const auto& hop : p->hops)
{
const RouterID router{hop.rc.pubkey};
const RouterID rid{hop.rc.pubkey};
// look up router and see if it's still on the network
router->loop()->call_soon([router, r = router]() {
LogInfo("looking up ", router, " because of path build timeout");
router->loop()->call_soon([rid, r = router]() {
log::info(path_cat, "Looking up RouterID {} due to path build timeout", rid);
r->rc_lookup_handler().get_rc(
router,
[r](const auto& router, const auto* rc, auto result) {
rid,
[r](const auto& rid, const auto* rc, auto result) {
if (result == RCRequestResult::Success && rc != nullptr)
{
LogInfo("refreshed rc for ", router);
log::info(path_cat, "Refreshed RouterContact for {}", rid);
;
r->node_db()->PutIfNewer(*rc);
}
else
{
// remove all connections to this router as it's probably not registered anymore
LogWarn("removing router ", router, " because of path build timeout");
r->link_manager().deregister_peer(router);
r->node_db()->Remove(router);
log::warning(path_cat, "Removing router {} due to path build timeout", rid);
r->link_manager().deregister_peer(rid);
r->node_db()->Remove(rid);
}
},
true);

View File

@ -10,35 +10,6 @@
namespace llarp::path
{
/// configuration for a single hop when building a path
struct PathHopConfig
{
/// path id
PathID_t txID, rxID;
// router contact of router
RouterContact rc;
// temp public encryption key
SecretKey commkey;
/// shared secret at this hop
SharedSecret shared;
/// hash of shared secret used for nonce mutation
ShortHash nonceXOR;
/// next hop's router id
RouterID upstream;
/// nonce for key exchange
TunnelNonce nonce;
// lifetime
llarp_time_t lifetime = DEFAULT_LIFETIME;
util::StatusObject
ExtractStatus() const;
};
// milliseconds waiting between builds on a path per router
static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms;
static constexpr auto PATH_BUILD_RATE = 100ms;
/// limiter for path builds
/// prevents overload and such
class BuildLimiter
@ -81,10 +52,10 @@ namespace llarp::path
DoPathBuildBackoff();
void
SetupHopKeys(path::PathHopConfig& hop, const RouterID& nextHop);
setup_hop_keys(path::PathHopConfig& hop, const RouterID& nextHop);
std::string
CreateHopInfoFrame(const path::PathHopConfig& hop);
create_hop_info_frame(const path::PathHopConfig& hop);
public:
Router* const router;

View File

@ -233,7 +233,7 @@ namespace llarp::path
auto itr = m_Paths.begin();
while (itr != m_Paths.end())
{
if (itr->second->IsEndpoint(ep, id))
if (itr->second->is_endpoint(ep, id))
{
return itr->second;
}
@ -290,7 +290,7 @@ namespace llarp::path
PathSet::AddPath(Path_ptr path)
{
Lock_t l(m_PathsMutex);
const auto upstream = path->Upstream(); // RouterID
const auto upstream = path->upstream(); // RouterID
const auto RXID = path->RXID(); // PathID
if (not m_Paths.emplace(std::make_pair(upstream, RXID), path).second)
{

View File

@ -90,25 +90,20 @@ namespace llarp::path
if (!IsEndpoint(r->pubkey()))
return false;
std::array<byte_t, MAX_LINK_MSG_SIZE - 128> tmp;
llarp_buffer_t buf(tmp);
auto bte = msg.bt_encode();
buf.write(bte.begin(), bte.end());
auto buf = msg.bt_encode();
TunnelNonce N;
N.Randomize();
buf.sz = buf.cur - buf.base;
// pad to nearest MESSAGE_PAD_SIZE bytes
auto dlt = buf.sz % PAD_SIZE;
auto dlt = buf.size() % PAD_SIZE;
if (dlt)
{
dlt = PAD_SIZE - dlt;
// randomize padding
CryptoManager::instance()->randbytes(buf.cur, dlt);
buf.sz += dlt;
CryptoManager::instance()->randbytes(reinterpret_cast<uint8_t*>(buf.data()), dlt);
}
buf.cur = buf.base;
return HandleDownstream(buf, N, r);
}

View File

@ -229,7 +229,7 @@ namespace llarp
}
void
Router::PersistSessionUntil(const RouterID& remote, llarp_time_t until)
Router::persist_connection_until(const RouterID& remote, llarp_time_t until)
{
_link_manager.set_conn_persist(remote, until);
}

View File

@ -466,7 +466,7 @@ namespace llarp
StopLinks();
void
PersistSessionUntil(const RouterID& remote, llarp_time_t until);
persist_connection_until(const RouterID& remote, llarp_time_t until);
bool
EnsureIdentity();

View File

@ -711,7 +711,7 @@ namespace llarp
{
for (size_t i = 0; i < INTROSET_REQS_PER_RELAY; ++i)
{
router()->send_control_message(path->Upstream(), "publish_intro", introset.bt_encode());
router()->send_control_message(path->upstream(), "publish_intro", introset.bt_encode());
}
}

View File

@ -24,6 +24,7 @@ namespace llarp::service
llarp_time_t expiry = 0s;
uint64_t version = llarp::constants::proto_version;
Introduction() = default;
Introduction(std::string buf);
util::StatusObject

View File

@ -32,7 +32,7 @@ namespace llarp::service
path::Path_ptr path, PathID_t from, std::shared_ptr<ProtocolMessage> self)
{
if (!self->handler->HandleDataMessage(path, from, self))
LogWarn("failed to handle data message from ", path->Name());
LogWarn("failed to handle data message from ", path->name());
}
bool