add option to enforce unique netblocks per path.

pull/1539/head
Jeff Becker 3 years ago
parent f2f0486f13
commit 9457da27d9
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -983,6 +983,69 @@ namespace llarp
});
}
void
PeerSelectionConfig::defineConfigOptions(
ConfigDefinition& conf, const ConfigGenParameters& params)
{
(void)params;
constexpr Default DefaultUniqueCIDR{32};
conf.defineOption<int>(
"paths",
"unique-range-size",
DefaultUniqueCIDR,
ClientOnly,
[=](int arg) {
if (arg > 32 or arg < 4)
throw std::invalid_argument{"[paths]:unique-range-size must be between 4 and 32"};
m_UniqueHopsNetmaskSize = arg;
},
Comment{"In path hop selection use this as the default netmkask for testing if we should "
"include a relay in our hop list",
"i.e. 32 for uniuqe ip addresses for every hop, 24 for all hops are in a different "
"/24, 16 for all hops are in a different /16, etc..."});
#ifdef WITH_GEOIP
conf.defineOption<std::string>(
"paths",
"exclude-country",
ClientOnly,
MultiValue,
[=](std::string arg) {
m_ExcludeCountries.emplace(lowercase_ascii_string(std::move(arg)));
},
Comment{"exclude a country given its 2 letter country code from being used in path builds",
"i.e. exlcude-country=DE",
"can be listed multiple times to exlcude multiple countries"});
#endif
}
bool
PeerSelectionConfig::Acceptable(std::set<RouterContact> rcs) const
{
if (m_UniqueHopsNetmaskSize)
{
auto makeRange =
[netmask = netmask_ipv6_bits(96 + *m_UniqueHopsNetmaskSize)](IpAddress addr) -> IPRange {
return IPRange{net::ExpandV4(addr.toIP()) & netmask, netmask};
};
std::set<IPRange> seenRanges;
for (const auto& hop : rcs)
{
for (const auto& addr : hop.addrs)
{
const auto range = makeRange(addr.toIpAddress());
if (seenRanges.count(range))
{
return false;
}
seenRanges.emplace(range);
}
}
}
return true;
}
Config::Config(fs::path datadir)
: m_DataDir(datadir.empty() ? fs::current_path() : std::move(datadir))
{}
@ -1096,6 +1159,7 @@ namespace llarp
{
router.defineConfigOptions(conf, params);
network.defineConfigOptions(conf, params);
paths.defineConfigOptions(conf, params);
connect.defineConfigOptions(conf, params);
dns.defineConfigOptions(conf, params);
links.defineConfigOptions(conf, params);
@ -1217,6 +1281,11 @@ namespace llarp
llarp::ConfigDefinition def{false};
initializeConfig(def, params);
generateCommonConfigComments(def);
def.addSectionComments(
"paths",
{
"path selection algorithm options",
});
def.addSectionComments(
"network",

@ -16,6 +16,8 @@
#include <service/auth.hpp>
#include <dns/srv_data.hpp>
#include <router_contact.hpp>
#include <cstdlib>
#include <functional>
#include <string>
@ -69,6 +71,25 @@ namespace llarp
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
};
/// config for path hop selection
struct PeerSelectionConfig
{
/// in our hops what netmask will we use for unique ips for hops
/// i.e. 32 for every hop unique ip, 24 unique /24 per hop, etc
///
std::optional<int> m_UniqueHopsNetmaskSize;
/// set of countrys to exclude from path building (2 char country code)
std::unordered_set<std::string> m_ExcludeCountries;
void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
/// return true if this set of router contacts is acceptable against this config
bool
Acceptable(std::set<RouterContact> hops) const;
};
struct NetworkConfig
{
std::optional<bool> m_enableProfiling;
@ -189,6 +210,7 @@ namespace llarp
RouterConfig router;
NetworkConfig network;
PeerSelectionConfig paths;
ConnectConfig connect;
DnsConfig dns;
LinksConfig links;

@ -206,7 +206,7 @@ namespace llarp
}
std::optional<RouterContact>
Builder::SelectFirstHop() const
Builder::SelectFirstHop(std::set<RouterID> exclude) const
{
std::optional<RouterContact> found = std::nullopt;
m_router->ForEachPeer(
@ -218,6 +218,9 @@ namespace llarp
if (m_router->IsBootstrapNode(rc.pubkey))
return;
#endif
if (exclude.count(rc.pubkey))
return;
found = rc;
}
},
@ -292,44 +295,63 @@ namespace llarp
}
std::optional<std::vector<RouterContact>>
Builder::GetHopsAlignedToForBuild(RouterID endpoint)
Builder::GetHopsAlignedToForBuild(RouterID endpoint, std::set<RouterID> exclude)
{
const auto pathConfig = m_router->GetConfig()->paths;
std::vector<RouterContact> hops;
{
const auto maybe = SelectFirstHop();
const auto maybe = SelectFirstHop(exclude);
if (not maybe.has_value())
return std::nullopt;
hops.emplace_back(*maybe);
};
RouterContact endpointRC;
if (const auto maybe = m_router->nodedb()->Get(endpoint))
{
endpointRC = *maybe;
}
else
return std::nullopt;
for (size_t idx = hops.size(); idx < numHops; ++idx)
{
if (idx + 1 == numHops)
{
const auto maybe = m_router->nodedb()->Get(endpoint);
if (maybe.has_value())
{
hops.emplace_back(*maybe);
}
else
return std::nullopt;
hops.emplace_back(endpointRC);
}
else
{
const auto maybe = m_router->nodedb()->GetRandom(
[&hops, r = m_router, endpoint](const auto& rc) -> bool {
if (r->routerProfiling().IsBadForPath(rc.pubkey))
return false;
for (const auto& hop : hops)
{
if (hop.pubkey == rc.pubkey)
return false;
}
return rc.pubkey != endpoint;
});
if (not maybe.has_value())
auto filter =
[&hops, r = m_router, endpointRC, pathConfig, exclude](const auto& rc) -> bool {
if (exclude.count(rc.pubkey))
return false;
std::set<RouterContact> hopsSet;
hopsSet.emplace(endpointRC);
for (const auto& hop : hops)
hopsSet.emplace(hop);
if (r->routerProfiling().IsBadForPath(rc.pubkey))
return false;
for (const auto& hop : hopsSet)
{
if (hop.pubkey == rc.pubkey)
return false;
}
hopsSet.emplace(rc);
if (not pathConfig.Acceptable(hopsSet))
return false;
return rc.pubkey != endpointRC.pubkey;
};
if (const auto maybe = m_router->nodedb()->GetRandom(filter))
hops.emplace_back(*maybe);
else
return std::nullopt;
hops.emplace_back(*maybe);
}
}
return hops;

@ -93,17 +93,17 @@ namespace llarp
bool
BuildOneAlignedTo(const RouterID endpoint) override;
virtual std::optional<std::vector<RouterContact>>
GetHopsAlignedToForBuild(RouterID endpoint);
std::optional<std::vector<RouterContact>>
GetHopsAlignedToForBuild(RouterID endpoint, std::set<RouterID> exclude = {});
void
Build(std::vector<RouterContact> hops, PathRole roles = ePathRoleAny) override;
/// pick a first hop
virtual std::optional<RouterContact>
SelectFirstHop() const;
std::optional<RouterContact>
SelectFirstHop(std::set<RouterID> exclude = {}) const;
std::optional<std::vector<RouterContact>>
virtual std::optional<std::vector<RouterContact>>
GetHopsForBuild() override;
void

@ -681,50 +681,7 @@ namespace llarp
std::optional<std::vector<RouterContact>>
Endpoint::GetHopsForBuildWithEndpoint(RouterID endpoint)
{
std::vector<RouterContact> hops;
// get first hop
if (const auto maybe = SelectFirstHop(); maybe.has_value())
{
hops.emplace_back(*maybe);
}
else
return std::nullopt;
auto filter =
[endpoint, &hops, blacklist = SnodeBlacklist(), r = m_router](const auto& rc) -> bool {
if (blacklist.count(rc.pubkey) > 0)
return false;
if (r->routerProfiling().IsBadForPath(rc.pubkey))
return false;
for (const auto& hop : hops)
{
if (hop.pubkey == rc.pubkey)
return false;
}
return endpoint != rc.pubkey;
};
for (size_t idx = hops.size(); idx < numHops; ++idx)
{
if (idx + 1 == numHops)
{
if (const auto maybe = m_router->nodedb()->Get(endpoint))
{
hops.emplace_back(*maybe);
}
else
return std::nullopt;
}
else if (const auto maybe = m_router->nodedb()->GetRandom(filter))
{
hops.emplace_back(*maybe);
}
else
return std::nullopt;
}
return hops;
return path::Builder::GetHopsAlignedToForBuild(endpoint, SnodeBlacklist());
}
void

@ -343,7 +343,7 @@ namespace llarp
}
if (m_NextIntro.router.IsZero())
return std::nullopt;
return GetHopsAlignedToForBuild(m_NextIntro.router);
return GetHopsAlignedToForBuild(m_NextIntro.router, m_Endpoint->SnodeBlacklist());
}
bool

Loading…
Cancel
Save