C++ version, clang-format

- C++ bumped 17 -> 20
- clang-format (plus all clangs) -> 17
pull/2212/head
dr7ana 8 months ago
parent b7b93981fe
commit 8cc152a3c0

@ -1,29 +1,19 @@
BasedOnStyle: Google
# alignment
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: 'false'
AlignConsecutiveDeclarations: 'false'
AlignEscapedNewlinesLeft: 'true'
AlignOperands: 'false'
AlignTrailingComments: 'true'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: 'false'
AllowShortLoopsOnASingleLine: 'false'
ColumnLimit: 120
KeepEmptyLinesAtTheStartOfBlocks: 'false'
NamespaceIndentation: All
PenaltyBreakString: '3'
SortIncludes: CaseInsensitive
SpaceBeforeParens: ControlStatements
SpacesInAngles: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: Cpp11
UseTab: Never
PointerAlignment: Left
QualifierAlignment: Custom
QualifierOrder: ['inline', 'static', 'constexpr', 'const', 'type']
# bracing
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
@ -46,20 +36,34 @@ AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: 'true'
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: 'true'
BreakConstructorInitializers: BeforeColon
# indent width
IndentWidth: 4
# indentation
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
IndentWidth: 4
NamespaceIndentation: All
# treat pointers and reference declarations as if part of the type
DerivePointerAlignment: false
PointerAlignment: Left
# shorties
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: 'false'
AllowShortLoopsOnASingleLine: 'false'
# spacing
KeepEmptyLinesAtTheStartOfBlocks: 'false'
PenaltyBreakString: '3'
SpaceBeforeParens: ControlStatements
SpacesInAngles: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: Cpp11
UseTab: Never
# when wrapping function calls/declarations, force each parameter to have its own line
# wrapping
PackConstructorInitializers: NextLine
BinPackParameters: 'false'
BinPackArguments: 'false'
@ -70,6 +74,7 @@ BinPackArguments: 'false'
# - Absolute path includes in angle brackets
# - External dependencies
# - System dependencies
SortIncludes: CaseInsensitive
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '".+\.h'

@ -384,7 +384,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
'echo "Building on ${DRONE_STAGE_MACHINE}"',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y git clang-format-15 jsonnet',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y git clang-format-17 jsonnet',
'./contrib/ci/drone-format-verify.sh',
],
}],

@ -104,7 +104,7 @@ option(WARN_DEPRECATED "show deprecation warnings" OFF)
include(CheckCXXSourceCompiles)
include(CheckLibraryExists)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11)

@ -1,5 +1,5 @@
CLANG_FORMAT_DESIRED_VERSION=15
CLANG_FORMAT_DESIRED_VERSION=17
CLANG_FORMAT=$(command -v clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
if [ $? -ne 0 ]; then

@ -256,8 +256,8 @@ namespace
/// will make a coredump when there is an unhandled exception
LONG GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
const auto flags =
(MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo);
const auto flags = (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData
| MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo);
const std::string fname = fmt::format("C:\\ProgramData\\lokinet\\crash-{}.dump", llarp::time_now_ms().count());

@ -38,7 +38,7 @@ namespace llarp::apple
// Called when we are ready to start reading packets
on_readable_callback _on_readable;
static inline constexpr auto PacketQueueSize = 1024;
inline static constexpr auto PacketQueueSize = 1024;
thread::Queue<IPPacket> _read_que{PacketQueueSize};

@ -288,7 +288,7 @@ namespace llarp
return other == key;
}
constexpr static char derived_key_hash_str[161] =
static constexpr char derived_key_hash_str[161] =
"just imagine what would happen if we all decided to understand. you "
"can't in the and by be or then before so just face it this text hurts "
"to read? lokinet yolo!";

@ -122,11 +122,11 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<PubKey> = true;
inline constexpr bool IsToStringFormattable<PubKey> = true;
template <>
constexpr inline bool IsToStringFormattable<SecretKey> = true;
inline constexpr bool IsToStringFormattable<SecretKey> = true;
template <>
constexpr inline bool IsToStringFormattable<PrivateKey> = true;
inline constexpr bool IsToStringFormattable<PrivateKey> = true;
using ShortHash = AlignedBuffer<SHORTHASHSIZE>;
using LongHash = AlignedBuffer<HASHSIZE>;

@ -91,5 +91,5 @@ namespace llarp
} // namespace dns
template <>
constexpr inline bool IsToStringFormattable<llarp::dns::Message> = true;
inline constexpr bool IsToStringFormattable<llarp::dns::Message> = true;
} // namespace llarp

@ -57,4 +57,4 @@ namespace llarp::dns
} // namespace llarp::dns
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::Question> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::dns::Question> = true;

@ -42,4 +42,4 @@ namespace llarp::dns
} // namespace llarp::dns
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::ResourceRecord> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::dns::ResourceRecord> = true;

@ -1203,13 +1203,17 @@ namespace llarp
log::info(link_cat, "PublishIntroMessage failed with error code: {}", payload);
if (payload == PublishIntroMessage::INVALID_INTROSET)
{}
{
}
else if (payload == PublishIntroMessage::EXPIRED)
{}
{
}
else if (payload == PublishIntroMessage::INSUFFICIENT)
{}
{
}
else if (payload == PublishIntroMessage::INVALID_ORDER)
{}
{
}
}
}
@ -1705,7 +1709,8 @@ namespace llarp
// see Path::HandleUpdateExitVerifyMessage
}
else
{}
{
}
}
}

@ -131,7 +131,7 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<Ip_address_deprecated> = true;
inline constexpr bool IsToStringFormattable<Ip_address_deprecated> = true;
} // namespace llarp

@ -51,7 +51,7 @@ namespace llarp
return IP_range_deprecated{net::ExpandV4(ipaddr_ipv4_bits(a, b, c, d)), netmask_ipv6_bits(mask + 96)};
}
static inline IP_range_deprecated FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask)
inline static IP_range_deprecated FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask)
{
return IP_range_deprecated{
net::ExpandV4(llarp::net::ToHost(addr)), netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
@ -150,7 +150,7 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<IP_range_deprecated> = true;
inline constexpr bool IsToStringFormattable<IP_range_deprecated> = true;
} // namespace llarp

@ -31,7 +31,7 @@ namespace llarp
// Initializes with upper and lower values
constexpr uint128_t(uint64_t upper, uint64_t lower)
// clang-format off
// clang-format off
#ifdef __BIG_ENDIAN__
: upper{upper}, lower{lower}
#else
@ -194,7 +194,8 @@ namespace llarp
constexpr uint128_t& operator<<=(uint64_t shift)
{
if (shift == 0)
{}
{
}
else if (shift < 64)
{
upper = upper << shift | (lower >> (64 - shift));
@ -226,7 +227,8 @@ namespace llarp
constexpr uint128_t& operator>>=(uint64_t shift)
{
if (shift == 0)
{}
{
}
else if (shift < 64)
{
lower = lower >> shift | upper << (64 - shift);

@ -15,11 +15,6 @@ namespace llarp
{
struct Router;
namespace routing
{
struct AbstractRoutingMessage;
}
namespace path
{
std::string make_onion_payload(

@ -9,9 +9,8 @@
namespace llarp::path
{
Path::Path(Router& rtr, const std::vector<RemoteRC>& h, std::weak_ptr<PathHandler> pathset, std::string shortName)
: path_set{std::move(pathset)}, _router{rtr}, _short_name{std::move(shortName)}
: handler{std::move(pathset)}, _router{rtr}, _short_name{std::move(shortName)}
{
hops.resize(h.size());
size_t hsz = h.size();
@ -39,6 +38,33 @@ namespace llarp::path
intro.path_id = hops[hsz - 1].txID;
}
bool Path::operator<(const Path& other) const
{
auto& first_hop = hops[0];
auto& other_first = other.hops[0];
return std::tie(first_hop.txID, first_hop.rxID, first_hop.upstream)
< std::tie(other_first.txID, other_first.rxID, other_first.upstream);
}
bool Path::operator==(const Path& other) const
{
bool ret = true;
size_t len = std::min(hops.size(), other.hops.size()), i = 0;
while (ret and i < len)
{
ret &= hops[i] == other.hops[i];
++i;
};
return ret;
}
bool Path::operator!=(const Path& other) const
{
return not(*this == other);
}
bool Path::obtain_exit(SecretKey sk, uint64_t flag, std::string tx_id, std::function<void(std::string)> func)
{
return send_path_control_message(
@ -250,7 +276,7 @@ namespace llarp::path
void Path::rebuild()
{
if (auto parent = path_set.lock())
if (auto parent = handler.lock())
{
std::vector<RemoteRC> new_hops;
@ -368,21 +394,6 @@ namespace llarp::path
return fmt::format("TX={} RX={}", TXID(), RXID());
}
/** Note: this is one of two places where AbstractRoutingMessage::bt_encode() is called, the
other of which is llarp/path/transit_hop.cpp in TransitHop::SendRoutingMessage(). For now,
we will default to the override of ::bt_encode() that returns an std::string. The role that
llarp_buffer_t plays here is likely superfluous, and can be replaced with either a leaner
llarp_buffer, or just handled using strings.
One important consideration is the frequency at which routing messages are sent, making
superfluous copies important to optimize out here. We have to instantiate at least one
std::string whether we pass a bt_dict_producer as a reference or create one within the
::bt_encode() call.
If we decide to stay with std::strings, the function Path::HandleUpstream (along with the
functions it calls and so on) will need to be modified to take an std::string that we can
std::move around.
*/
/* TODO: replace this with sending an onion-ed data message
bool Path::SendRoutingMessage(std::string payload, Router*)
{

@ -36,15 +36,12 @@ namespace llarp
struct TransitHopInfo;
struct PathHopConfig;
struct Endpoint_Hash;
struct endpoint_comparator;
/// A path we made
struct Path final : public AbstractHopHandler, public std::enable_shared_from_this<Path>
{
std::vector<PathHopConfig> hops;
std::weak_ptr<PathHandler> path_set;
std::weak_ptr<PathHandler> handler;
service::Introduction intro;
@ -68,11 +65,6 @@ namespace llarp
StatusObject ExtractStatus() const;
bool operator<(const Path& other) const
{
return hops < other.hops;
}
void MarkActive(llarp_time_t now)
{
last_recv_msg = std::max(now, last_recv_msg);
@ -165,6 +157,12 @@ namespace llarp
std::string name() const;
bool operator<(const Path& other) const;
bool operator==(const Path& other) const;
bool operator!=(const Path& other) const;
private:
std::string make_outer_payload(std::string payload);
@ -181,41 +179,23 @@ namespace llarp
uint64_t last_latency_test_id = 0;
const std::string _short_name;
};
struct Hash
{
size_t operator()(const Path& p) const
{
const auto& tx = p.hops[0].txID;
const auto& rx = p.hops[0].rxID;
const auto& r = p.hops[0].upstream;
const size_t rhash = std::accumulate(r.begin(), r.end(), 0, std::bit_xor{});
return std::accumulate(
rx.begin(),
rx.begin(),
std::accumulate(tx.begin(), tx.end(), rhash, std::bit_xor{}),
std::bit_xor{});
}
};
/// hash for std::shared_ptr<Path> by path endpoint
struct Endpoint_Hash
{
size_t operator()(const std::shared_ptr<Path>& p) const
{
if (p == nullptr)
return 0;
return std::hash<RouterID>{}(p->pivot_router_id());
}
};
/// comparision for equal endpoints
struct endpoint_comparator
{
bool operator()(const std::shared_ptr<Path>& left, const std::shared_ptr<Path>& right) const
{
return left && right && left->pivot_router_id() == left->pivot_router_id();
}
};
} // namespace path
} // namespace llarp
namespace std
{
template <>
struct hash<llarp::path::Path>
{
size_t operator()(const llarp::path::Path& p) const
{
auto& first_hop = p.hops[0];
llarp::AlignedBuffer<PUBKEYSIZE> b;
std::memcpy(b.data(), first_hop.txID.data(), PATHIDSIZE);
std::memcpy(&b[PATHIDSIZE], first_hop.txID.data(), PATHIDSIZE);
auto h = hash<llarp::AlignedBuffer<PUBKEYSIZE>>{}(b);
return h ^ hash<llarp::RouterID>{}(first_hop.upstream);
}
};
} // namespace std

@ -41,13 +41,24 @@ namespace llarp
llarp_time_t lifetime = DEFAULT_LIFETIME;
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);
}
bool operator<(const PathHopConfig& other) const
{
return std::tie(txID, rxID, rc, upstream, lifetime)
< std::tie(other.txID, other.rxID, other.rc, other.upstream, other.lifetime);
}
bool operator==(const PathHopConfig& other) const
{
return std::tie(txID, rxID, rc, upstream, lifetime)
== std::tie(other.txID, other.rxID, other.rc, other.upstream, other.lifetime);
}
bool operator!=(const PathHopConfig& other) const
{
return not(*this == other);
}
};
// milliseconds waiting between builds on a path per router
static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms;

@ -1,557 +0,0 @@
#include "pathbuilder.hpp"
#include "path.hpp"
#include "path_context.hpp"
#include <llarp/crypto/crypto.hpp>
#include <llarp/link/link_manager.hpp>
#include <llarp/messages/path.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/path/pathset.hpp>
#include <llarp/profiling.hpp>
#include <llarp/router/router.hpp>
#include <llarp/util/logging.hpp>
#include <functional>
namespace llarp
{
namespace
{
auto path_cat = log::Cat("path");
}
namespace path
{
bool BuildLimiter::Attempt(const RouterID& router)
{
return _edge_limiter.Insert(router);
}
void BuildLimiter::Decay(llarp_time_t now)
{
_edge_limiter.Decay(now);
}
bool BuildLimiter::Limited(const RouterID& router) const
{
return _edge_limiter.Contains(router);
}
PathBuilder::PathBuilder(Router* p_router, size_t pathNum, size_t hops)
: path::PathSet{pathNum}, _run{true}, router{p_router}, num_hops{hops}
{}
/* - For each hop:
* SetupHopKeys:
* - Generate Ed keypair for the hop. ("commkey")
* - Use that key and the hop's pubkey for DH key exchange (makes "hop.shared")
* - Note: this *was* using hop's "enckey" but we're getting rid of that
* - hop's "upstream" RouterID is next hop, or that hop's ID if it is terminal hop
* - hop's chacha nonce is hash of symmetric key (hop.shared) from DH
* - hop's "txID" and "rxID" are chosen before this step
* - txID is the path ID for messages coming *from* the client/path origin
* - rxID is the path ID for messages going *to* it.
*
* CreateHopInfoFrame:
* - bt-encode "hop info":
* - path lifetime
* - protocol version
* - txID
* - rxID
* - nonce
* - upstream hop RouterID
* - ephemeral public key (for DH)
* - generate *second* ephemeral Ed keypair... ("framekey") TODO: why?
* - generate DH symmetric key using "framekey" and hop's pubkey
* - generate nonce for second encryption
* - encrypt "hop info" using this symmetric key
* - bt-encode nonce, "framekey" pubkey, encrypted "hop info"
* - hash this bt-encoded string
* - bt-encode hash and the frame in a dict, serialize
*
*
* all of these "frames" go in a list, along with any needed dummy frames
*/
void PathBuilder::setup_hop_keys(path::PathHopConfig& hop, const RouterID& nextHop)
{
// generate key
crypto::encryption_keygen(hop.commkey);
hop.nonce.Randomize();
// do key exchange
if (!crypto::dh_client(hop.shared, hop.rc.router_id(), hop.commkey, hop.nonce))
{
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
ShortHash hash;
crypto::shorthash(hash, hop.shared.data(), hop.shared.size());
hop.nonceXOR = hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
hop.upstream = nextHop;
}
std::string PathBuilder::create_hop_info_frame(const path::PathHopConfig& hop)
{
std::string hop_info;
{
oxenc::bt_dict_producer btdp;
btdp.append("COMMKEY", hop.commkey.toPublic().ToView());
btdp.append("LIFETIME", path::DEFAULT_LIFETIME.count());
btdp.append("NONCE", hop.nonce.ToView());
btdp.append("RX", hop.rxID.ToView());
btdp.append("TX", hop.txID.ToView());
btdp.append("UPSTREAM", hop.upstream.ToView());
hop_info = std::move(btdp).str();
}
SecretKey framekey;
crypto::encryption_keygen(framekey);
SharedSecret shared;
SymmNonce outer_nonce;
outer_nonce.Randomize();
// derive (outer) shared key
if (!crypto::dh_client(shared, hop.rc.router_id(), 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("NONCE", outer_nonce.ToView());
btdp.append("PUBKEY", framekey.toPublic().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("FRAME", hashed_data);
btdp.append("HASH", hash);
return std::move(btdp).str();
}
void PathBuilder::ResetInternalState()
{
build_interval_limit = PATH_BUILD_RATE;
_last_build = 0s;
}
void PathBuilder::Tick(llarp_time_t now)
{
PathSet::Tick(now);
now = llarp::time_now_ms();
router->pathbuild_limiter().Decay(now);
ExpirePaths(now, router);
if (ShouldBuildMore(now))
BuildOne();
TickPaths(router);
if (build_stats.attempts > 50)
{
if (build_stats.SuccessRatio() <= BuildStats::MinGoodRatio && now - last_warn_time > 5s)
{
LogWarn(Name(), " has a low path build success. ", build_stats);
last_warn_time = now;
}
}
}
util::StatusObject PathBuilder::ExtractStatus() const
{
util::StatusObject obj{
{"buildStats", build_stats.ExtractStatus()},
{"numHops", uint64_t{num_hops}},
{"numPaths", uint64_t{num_paths_desired}}};
std::transform(
_paths.begin(),
_paths.end(),
std::back_inserter(obj["paths"]),
[](const auto& item) -> util::StatusObject { return item.second->ExtractStatus(); });
return obj;
}
std::optional<RemoteRC> PathBuilder::SelectFirstHop(const std::set<RouterID>& exclude) const
{
std::optional<RemoteRC> found = std::nullopt;
router->for_each_connection([&](link::Connection& conn) {
RouterID rid{conn.conn->remote_key()};
#ifndef TESTNET
if (router->is_bootstrap_node(rid))
return;
#endif
if (exclude.count(rid))
return;
if (BuildCooldownHit(rid))
return;
if (router->router_profiling().IsBadForPath(rid))
return;
found = router->node_db()->get_rc(rid);
});
return found;
}
std::optional<std::vector<RemoteRC>> PathBuilder::GetHopsForBuild()
{
auto filter = [r = router](const RemoteRC& rc) -> bool {
return not r->router_profiling().IsBadForPath(rc.router_id(), 1);
};
if (auto maybe = router->node_db()->get_random_rc_conditional(filter))
return GetHopsAlignedToForBuild(maybe->router_id());
return std::nullopt;
}
bool PathBuilder::Stop()
{
_run = false;
// tell all our paths that they are to be ignored
const auto now = Now();
for (auto& item : _paths)
{
item.second->EnterState(PathStatus::IGNORE, now);
}
return true;
}
bool PathBuilder::IsStopped() const
{
return !_run.load();
}
bool PathBuilder::ShouldRemove() const
{
return IsStopped() and NumInStatus(PathStatus::ESTABLISHED) == 0;
}
bool PathBuilder::BuildCooldownHit(RouterID edge) const
{
return router->pathbuild_limiter().Limited(edge);
}
bool PathBuilder::BuildCooldownHit(llarp_time_t now) const
{
return now < _last_build + build_interval_limit;
}
bool PathBuilder::ShouldBuildMore(llarp_time_t now) const
{
if (IsStopped())
return false;
if (BuildCooldownHit(now))
return false;
return PathSet::ShouldBuildMore(now);
}
void PathBuilder::BuildOne(PathRole roles)
{
if (const auto maybe = GetHopsForBuild())
Build(*maybe, roles);
}
bool PathBuilder::UrgentBuild(llarp_time_t) const
{
return build_interval_limit > MIN_PATH_BUILD_INTERVAL * 4;
}
std::optional<std::vector<RemoteRC>> PathBuilder::GetHopsAlignedToForBuild(
RouterID endpoint, const std::set<RouterID>& exclude)
{
const auto pathConfig = router->config()->paths;
std::vector<RemoteRC> hops;
{
const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value())
{
log::warning(path_cat, "{} has no first hop candidate", Name());
return std::nullopt;
}
hops.emplace_back(*maybe);
};
RemoteRC endpointRC;
if (const auto maybe = router->node_db()->get_rc(endpoint))
{
endpointRC = *maybe;
}
else
return std::nullopt;
for (size_t idx = hops.size(); idx < num_hops; ++idx)
{
if (idx + 1 == num_hops)
{
hops.emplace_back(endpointRC);
}
else
{
auto filter = [&hops, r = router, endpointRC, pathConfig, exclude](const RemoteRC& rc) -> bool {
const auto& rid = rc.router_id();
if (exclude.count(rid))
return false;
std::set<RemoteRC> hopsSet;
hopsSet.insert(endpointRC);
hopsSet.insert(hops.begin(), hops.end());
if (r->router_profiling().IsBadForPath(rid, 1))
return false;
for (const auto& hop : hopsSet)
{
if (hop.router_id() == rid)
return false;
}
hopsSet.insert(rc);
#ifndef TESTNET
if (not pathConfig.check_rcs(hopsSet))
return false;
#endif
return rc.router_id() != endpointRC.router_id();
};
if (auto maybe = router->node_db()->get_random_rc_conditional(filter))
hops.emplace_back(*maybe);
else
return std::nullopt;
}
}
return hops;
}
bool PathBuilder::BuildOneAlignedTo(const RouterID remote)
{
if (const auto maybe = GetHopsAlignedToForBuild(remote); maybe.has_value())
{
LogInfo(Name(), " building path to ", remote);
Build(*maybe);
return true;
}
return false;
}
llarp_time_t PathBuilder::Now() const
{
return router->now();
}
void PathBuilder::Build(std::vector<RemoteRC> hops, PathRole roles)
{
if (IsStopped())
{
log::info(path_cat, "Path builder is stopped, aborting path build...");
return;
}
_last_build = llarp::time_now_ms();
const auto& edge = hops[0].router_id();
if (not router->pathbuild_limiter().Attempt(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>(router, hops, GetWeak(), roles, std::move(path_shortName));
log::info(path_cat, "{} building path -> {} : {}", Name(), path->short_name(), path->HopsString());
oxenc::bt_list_producer frames;
std::vector<std::string> frame_str(path::MAX_LEN);
auto& path_hops = path->hops;
size_t n_hops = path_hops.size();
size_t last_len{0};
// each hop will be able to read the outer part of its frame and decrypt
// the inner part with that information. It will then do an onion step on the
// remaining frames so the next hop can read the outer part of its frame,
// and so on. As this de-onion happens from hop 1 to n, we create and onion
// the frames from hop n downto 1 (i.e. reverse order). The first frame is
// not onioned.
//
// Onion-ing the frames in this way will prevent relays controlled by
// the same entity from knowing they are part of the same path
// (unless they're adjacent in the path; nothing we can do about that obviously).
// i from n_hops downto 0
size_t i = n_hops;
while (i > 0)
{
i--;
bool lastHop = (i == (n_hops - 1));
const auto& nextHop = lastHop ? path_hops[i].rc.router_id() : path_hops[i + 1].rc.router_id();
PathBuildMessage::setup_hop_keys(path_hops[i], nextHop);
frame_str[i] = PathBuildMessage::serialize(path_hops[i]);
// all frames should be the same length...not sure what that is yet
// it may vary if path lifetime is non-default, as that is encoded as an
// integer in decimal, but it should be constant for a given path
if (last_len != 0)
assert(frame_str[i].size() == last_len);
last_len = frame_str[i].size();
// onion each previously-created frame using the established shared secret and
// onion_nonce = path_hops[i].nonce ^ path_hops[i].nonceXOR, which the transit hop
// will have recovered after decrypting its frame.
// Note: final value passed to crypto::onion is xor factor, but that's for *after*
// the onion round to compute the return value, so we don't care about it.
for (size_t j = n_hops - 1; j > i; j--)
{
auto onion_nonce = path_hops[i].nonce ^ path_hops[i].nonceXOR;
crypto::onion(
reinterpret_cast<unsigned char*>(frame_str[j].data()),
frame_str[j].size(),
path_hops[i].shared,
onion_nonce,
onion_nonce);
}
}
std::string dummy;
dummy.reserve(last_len);
// append dummy frames; path build request must always have MAX_LEN frames
for (i = n_hops; i < path::MAX_LEN; i++)
{
frame_str[i].resize(last_len);
randombytes(reinterpret_cast<uint8_t*>(frame_str[i].data()), frame_str[i].size());
}
for (auto& str : frame_str) // NOLINT
{
frames.append(std::move(str));
}
router->path_context().AddOwnPath(GetSelf(), path);
PathBuildStarted(path);
// TODO:
// Path build fail and success are handled poorly at best and changing how we
// handle these responses as well as how we store and use Paths as a whole might
// be worth doing sooner rather than later. Leaving some TODOs below where fail
// and success live.
auto response_cb = [path](oxen::quic::message m) {
if (m)
{
// TODO: inform success (what this means needs revisiting, badly)
path->EnterState(path::PathStatus::ESTABLISHED);
return;
}
try
{
// TODO: inform failure (what this means needs revisiting, badly)
if (m.timed_out)
{
log::warning(path_cat, "Path build request timed out!");
path->EnterState(path::PathStatus::TIMEOUT);
}
else
{
oxenc::bt_dict_consumer d{m.body()};
auto status = d.require<std::string_view>(messages::STATUS_KEY);
log::warning(path_cat, "Path build returned failure status: {}", status);
path->EnterState(path::PathStatus::FAILED);
}
}
catch (const std::exception& e)
{
log::warning(path_cat, "Exception caught parsing path build response: {}", e.what());
}
};
if (not router->send_control_message(
path->upstream(), "path_build", std::move(frames).str(), std::move(response_cb)))
{
log::warning(path_cat, "Error sending path_build control message");
path->EnterState(path::PathStatus::FAILED, router->now());
}
}
void PathBuilder::HandlePathBuilt(std::shared_ptr<Path> p)
{
build_interval_limit = PATH_BUILD_RATE;
router->router_profiling().MarkPathSuccess(p.get());
LogInfo(p->name(), " built latency=", to_string(p->intro.latency));
build_stats.success++;
}
void PathBuilder::HandlePathBuildFailedAt(std::shared_ptr<Path> p, RouterID edge)
{
PathSet::HandlePathBuildFailedAt(p, edge);
DoPathBuildBackoff();
}
void PathBuilder::DoPathBuildBackoff()
{
static constexpr std::chrono::milliseconds MaxBuildInterval = 30s;
// linear backoff
build_interval_limit = std::min(PATH_BUILD_RATE + build_interval_limit, MaxBuildInterval);
LogWarn(Name(), " build interval is now ", to_string(build_interval_limit));
}
void PathBuilder::HandlePathBuildTimeout(std::shared_ptr<Path> p)
{
router->router_profiling().MarkPathTimeout(p.get());
PathSet::HandlePathBuildTimeout(p);
DoPathBuildBackoff();
}
void PathBuilder::ManualRebuild(size_t num, PathRole roles)
{
LogDebug(Name(), " manual rebuild ", num);
while (num--)
BuildOne(roles);
}
} // namespace path
} // namespace llarp

@ -247,6 +247,6 @@ namespace llarp
} // namespace path
template <>
constexpr inline bool IsToStringFormattable<path::BuildStats> = true;
inline constexpr bool IsToStringFormattable<path::BuildStats> = true;
} // namespace llarp

@ -58,24 +58,9 @@ namespace llarp::path
return started + lifetime;
}
TransitHopInfo::TransitHopInfo(const RouterID& down) : downstream(down)
TransitHopInfo::TransitHopInfo(RouterID down) : downstream{std::move(down)}
{}
/** Note: this is one of two places where AbstractRoutingMessage::bt_encode() is called, the
other of which is llarp/path/path.cpp in Path::SendRoutingMessage(). For now,
we will default to the override of ::bt_encode() that returns an std::string. The role that
llarp_buffer_t plays here is likely superfluous, and can be replaced with either a leaner
llarp_buffer, or just handled using strings.
One important consideration is the frequency at which routing messages are sent, making
superfluous copies important to optimize out here. We have to instantiate at least one
std::string whether we pass a bt_dict_producer as a reference or create one within the
::bt_encode() call.
If we decide to stay with std::strings, the function Path::HandleUpstream (along with the
functions it calls and so on) will need to be modified to take an std::string that we can
std::move around.
*/
/* TODO: replace this with layer of onion + send data message
bool TransitHop::SendRoutingMessage(std::string payload, Router* r)
{

@ -14,31 +14,31 @@ namespace llarp
struct TransitHopInfo
{
TransitHopInfo() = default;
TransitHopInfo(const RouterID& down);
TransitHopInfo(RouterID down);
HopID txID, rxID;
RouterID upstream;
RouterID downstream;
std::string to_string() const;
};
inline bool operator==(const TransitHopInfo& lhs, const TransitHopInfo& rhs)
{
return std::tie(lhs.txID, lhs.rxID, lhs.upstream, lhs.downstream)
== std::tie(rhs.txID, rhs.rxID, rhs.upstream, rhs.downstream);
}
bool operator==(const TransitHopInfo& rhs) const
{
return std::tie(txID, rxID, upstream, downstream)
== std::tie(rhs.txID, rhs.rxID, rhs.upstream, rhs.downstream);
}
inline bool operator!=(const TransitHopInfo& lhs, const TransitHopInfo& rhs)
{
return !(lhs == rhs);
}
bool operator!=(const TransitHopInfo& rhs) const
{
return not(*this == rhs);
}
inline bool operator<(const TransitHopInfo& lhs, const TransitHopInfo& rhs)
{
return std::tie(lhs.txID, lhs.rxID, lhs.upstream, lhs.downstream)
< std::tie(rhs.txID, rhs.rxID, rhs.upstream, rhs.downstream);
}
bool operator<(const TransitHopInfo& rhs) const
{
return std::tie(txID, rxID, upstream, downstream)
< std::tie(rhs.txID, rhs.rxID, rhs.upstream, rhs.downstream);
}
};
struct TransitHop : public AbstractHopHandler, std::enable_shared_from_this<TransitHop>
{
@ -125,9 +125,9 @@ namespace llarp
} // namespace path
template <>
constexpr inline bool IsToStringFormattable<path::TransitHop> = true;
inline constexpr bool IsToStringFormattable<path::TransitHop> = true;
template <>
constexpr inline bool IsToStringFormattable<path::TransitHopInfo> = true;
inline constexpr bool IsToStringFormattable<path::TransitHopInfo> = true;
} // namespace llarp
@ -136,7 +136,7 @@ namespace std
template <>
struct hash<llarp::path::TransitHopInfo>
{
std::size_t operator()(llarp::path::TransitHopInfo const& a) const
std::size_t operator()(const llarp::path::TransitHopInfo& a) const
{
hash<llarp::RouterID> RHash{};
hash<llarp::HopID> PHash{};

@ -71,7 +71,7 @@ namespace llarp
return (path_success * chances) > path_fail;
}
static bool constexpr checkIsGood(uint64_t fails, uint64_t success, uint64_t chances)
static constexpr bool checkIsGood(uint64_t fails, uint64_t success, uint64_t chances)
{
if (fails > 0 && (fails + success) >= chances)
return (success / fails) > 1;

@ -20,7 +20,7 @@
namespace llarp
{
static inline constexpr size_t NETID_SIZE{8};
inline static constexpr size_t NETID_SIZE{8};
/// On the wire we encode the data as a dict containing:
/// "" -- the RC format version, which must be == RouterContact::Version for us to attempt to
@ -45,11 +45,11 @@ namespace llarp
static constexpr uint8_t RC_VERSION = 0;
/// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected.
static inline bool BLOCK_BOGONS = true;
inline static bool BLOCK_BOGONS = true;
static inline std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
inline static std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID};
static inline constexpr size_t MAX_RC_SIZE = 1024;
inline static constexpr size_t MAX_RC_SIZE = 1024;
/// How long (from its signing time) before an RC is considered "stale". Relays republish
/// their RCs slightly more frequently than this so that ideally this won't happen.
@ -112,7 +112,7 @@ namespace llarp
public:
/// should we serialize the exit info?
const static bool serializeExit = true;
static const bool serializeExit = true;
StatusObject extract_status() const;
@ -302,11 +302,11 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<RouterContact> = true;
inline constexpr bool IsToStringFormattable<RouterContact> = true;
template <>
constexpr inline bool IsToStringFormattable<RemoteRC> = true;
inline constexpr bool IsToStringFormattable<RemoteRC> = true;
template <>
constexpr inline bool IsToStringFormattable<LocalRC> = true;
inline constexpr bool IsToStringFormattable<LocalRC> = true;
using RouterLookupHandler = std::function<void(const std::vector<RemoteRC>&)>;
} // namespace llarp

@ -48,7 +48,7 @@ namespace llarp
}
template <>
constexpr inline bool IsToStringFormattable<RouterID> = true;
inline constexpr bool IsToStringFormattable<RouterID> = true;
} // namespace llarp
namespace std

@ -59,7 +59,7 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<RouterVersion> = true;
inline constexpr bool IsToStringFormattable<RouterVersion> = true;
static constexpr int64_t INVALID_VERSION = -1;
static const RouterVersion emptyRouterVersion({0, 0, 0}, INVALID_VERSION);

@ -91,7 +91,7 @@ namespace llarp
}
template <>
constexpr inline bool IsToStringFormattable<service::Address> = true;
inline constexpr bool IsToStringFormattable<service::Address> = true;
} // namespace llarp
namespace std

@ -549,7 +549,7 @@ namespace llarp::service
auto Endpoint::GetUniqueEndpointsForLookup() const
{
std::unordered_set<std::shared_ptr<path::Path>, path::Endpoint_Hash, path::endpoint_comparator> paths;
std::unordered_set<std::shared_ptr<path::Path>> paths;
for_each_path([&paths](auto path) {
if (path and path->IsReady())
@ -561,7 +561,8 @@ namespace llarp::service
bool Endpoint::ReadyForNetwork() const
{
return is_ready() and ReadyToDoLookup(GetUniqueEndpointsForLookup().size());
// return is_ready() and ReadyToDoLookup(GetUniqueEndpointsForLookup().size());
return true;
}
bool Endpoint::ReadyToDoLookup(size_t num_paths) const

@ -97,4 +97,4 @@ namespace llarp::service
} // namespace llarp::service
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::service::ServiceInfo> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::service::ServiceInfo> = true;

@ -81,7 +81,7 @@ namespace llarp::service
} // namespace llarp::service
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::service::Introduction> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::service::Introduction> = true;
namespace std
{

@ -171,6 +171,6 @@ namespace llarp::service
} // namespace llarp::service
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::service::IntroSet> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::service::IntroSet> = true;
template <>
constexpr inline bool llarp::IsToStringFormattable<llarp::service::EncryptedIntroSet> = true;
inline constexpr bool llarp::IsToStringFormattable<llarp::service::EncryptedIntroSet> = true;

@ -81,40 +81,41 @@ namespace llarp
std::set<SessionTag>& tags */);
} // namespace util
template <typename Endpoint_t>
static std::
unordered_set<std::shared_ptr<path::Path>, path::Endpoint_Hash, path::endpoint_comparator>
GetManyPathsWithUniqueEndpoints(
/* Endpoint_t* ep,
size_t N,
std::optional<dht::Key_t> maybeLocation = std::nullopt,
size_t tries = 10 */)
{
// std::unordered_set<RouterID> exclude;
std::unordered_set<std::shared_ptr<path::Path>, path::Endpoint_Hash, path::endpoint_comparator> paths;
// do
// {
// --tries;
// std::shared_ptr<path::Path> path;
// if (maybeLocation)
// {
// path = ep->GetEstablishedPathClosestTo(RouterID{maybeLocation->as_array()},
// exclude);
// }
// else
// {
// path = ep->PickRandomEstablishedPath();
// }
// if (path and path->IsReady())
// {
// paths.emplace(path);
// exclude.insert(path->Endpoint());
// }
// } while (tries > 0 and paths.size() < N);
return paths;
}
// template <typename Endpoint_t>
// static std::
// unordered_set<std::shared_ptr<path::Path>, path::Endpoint_Hash, path::endpoint_comparator>
// GetManyPathsWithUniqueEndpoints(
// /* Endpoint_t* ep,
// size_t N,
// std::optional<dht::Key_t> maybeLocation = std::nullopt,
// size_t tries = 10 */)
// {
// // std::unordered_set<RouterID> exclude;
// std::unordered_set<std::shared_ptr<path::Path>, path::Endpoint_Hash, path::endpoint_comparator>
// paths;
// // do
// // {
// // --tries;
// // std::shared_ptr<path::Path> path;
// // if (maybeLocation)
// // {
// // path = ep->GetEstablishedPathClosestTo(RouterID{maybeLocation->as_array()},
// // exclude);
// // }
// // else
// // {
// // path = ep->PickRandomEstablishedPath();
// // }
// // if (path and path->IsReady())
// // {
// // paths.emplace(path);
// // exclude.insert(path->Endpoint());
// // }
// // } while (tries > 0 and paths.size() < N);
// return paths;
// }
} // namespace service
template <>
constexpr inline bool IsToStringFormattable<service::ProtocolType> = true;
inline constexpr bool IsToStringFormattable<service::ProtocolType> = true;
} // namespace llarp

@ -282,7 +282,7 @@ namespace llarp
} // namespace detail
// True if T is or is derived from AlignedBuffer<N> for any N
template <typename T>
constexpr inline bool is_aligned_buffer = decltype(detail::is_aligned_buffer_impl(static_cast<T*>(nullptr)))::value;
inline constexpr bool is_aligned_buffer = decltype(detail::is_aligned_buffer_impl(static_cast<T*>(nullptr)))::value;
} // namespace llarp

@ -40,7 +40,7 @@ namespace llarp
template <size_t... I>
struct concat_args_fmt_impl<std::integer_sequence<size_t, I...>>
{
constexpr static std::array<char, sizeof...(I)> format{(I % 2 == 0 ? '{' : '}')...};
static constexpr std::array<char, sizeof...(I)> format{(I % 2 == 0 ? '{' : '}')...};
};
template <size_t N>
constexpr std::string_view concat_args_fmt()

@ -54,5 +54,5 @@ namespace llarp
};
template <>
constexpr inline bool IsToStringFormattable<buffer_printer> = true;
inline constexpr bool IsToStringFormattable<buffer_printer> = true;
} // namespace llarp

@ -13,7 +13,7 @@
namespace llarp
{
constexpr static char whitespace[] = " \t\n\r\f\v";
static constexpr char whitespace[] = " \t\n\r\f\v";
std::string_view TrimWhitespace(std::string_view str)
{

@ -14,9 +14,9 @@ namespace llarp
return std::chrono::duration_cast<Res>(point.time_since_epoch());
}
const static auto started_at_system = Clock_t::now();
static const auto started_at_system = Clock_t::now();
const static auto started_at_steady = std::chrono::steady_clock::now();
static const auto started_at_steady = std::chrono::steady_clock::now();
} // namespace
std::chrono::steady_clock::time_point get_time()

@ -266,7 +266,7 @@ namespace llarp::win32
std::thread _recv_thread;
std::thread _send_thread;
static inline constexpr size_t packet_queue_length = 1024;
inline static constexpr size_t packet_queue_length = 1024;
public:
WintunInterface(vpn::InterfaceInfo info, Router* router)

Loading…
Cancel
Save