Merge pull request #1539 from majestrate/path-algorithm-flavors-2021-02-18

add option to enforce unique netblocks per path.
pull/1563/head
Jeff 3 years ago committed by GitHub
commit f2b234d6c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 == 0)
{
m_UniqueHopsNetmaskSize = arg;
}
else if (arg > 32 or arg < 4)
{
throw std::invalid_argument{"[paths]:unique-range-size must be between 4 and 32"};
}
m_UniqueHopsNetmaskSize = arg;
},
Comment{"Netmask for router path selection; each router must be from a distinct IP subnet "
"of the given size.",
"E.g. 16 ensures that all routers are using distinct /16 IP addresses."});
#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",
"e.g. exclude-country=DE",
"can be listed multiple times to exclude multiple countries"});
#endif
}
bool
PeerSelectionConfig::Acceptable(const std::set<RouterContact>& rcs) const
{
if (m_UniqueHopsNetmaskSize == 0)
return true;
const auto netmask = netmask_ipv6_bits(96 + m_UniqueHopsNetmaskSize);
std::set<IPRange> seenRanges;
for (const auto& hop : rcs)
{
for (const auto& addr : hop.addrs)
{
const auto network_addr = net::In6ToHUInt(addr.ip) & netmask;
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
{
return false;
}
}
}
return true;
}
Config::Config(fs::path datadir)
: m_DataDir(datadir.empty() ? fs::current_path() : std::move(datadir))
{}
@ -1108,6 +1171,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);
@ -1229,6 +1293,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
///
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(const 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;

@ -151,6 +151,30 @@ namespace llarp
}
};
/// rng type that uses llarp::randint(), which is cryptographically secure
struct CSRNG
{
using result_type = uint64_t;
static constexpr uint64_t
min()
{
return std::numeric_limits<uint64_t>::min();
};
static constexpr uint64_t
max()
{
return std::numeric_limits<uint64_t>::max();
};
uint64_t
operator()()
{
return llarp::randint();
};
};
} // namespace llarp
#endif

@ -14,6 +14,12 @@ namespace llarp
huint128_t addr = {0};
huint128_t netmask_bits = {0};
constexpr IPRange()
{}
constexpr IPRange(huint128_t address, huint128_t netmask)
: addr{std::move(address)}, netmask_bits{std::move(netmask)}
{}
static constexpr IPRange
FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask)
{

@ -16,6 +16,7 @@
#include <unordered_map>
#include <utility>
#include <atomic>
#include <algorithm>
namespace llarp
{
@ -89,22 +90,19 @@ namespace llarp
GetRandom(Filter visit) const
{
util::NullLock lock{m_Access};
const auto sz = m_Entries.size();
if (sz < 3)
return std::nullopt;
const auto begin = m_Entries.begin();
const auto middle = std::next(m_Entries.begin(), randint() % sz);
for (auto itr = middle; itr != m_Entries.end(); ++itr)
{
if (visit(itr->second.rc))
return itr->second.rc;
}
for (auto itr = begin; itr != middle; ++itr)
std::vector<const decltype(m_Entries)::value_type*> entries;
for (const auto& entry : m_Entries)
entries.push_back(&entry);
std::shuffle(entries.begin(), entries.end(), llarp::CSRNG{});
for (const auto entry : entries)
{
if (visit(itr->second.rc))
return itr->second.rc;
if (visit(entry->second.rc))
return entry->second.rc;
}
return std::nullopt;
}

@ -206,7 +206,7 @@ namespace llarp
}
std::optional<RouterContact>
Builder::SelectFirstHop() const
Builder::SelectFirstHop(const 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,62 @@ namespace llarp
}
std::optional<std::vector<RouterContact>>
Builder::GetHopsAlignedToForBuild(RouterID endpoint)
Builder::GetHopsAlignedToForBuild(RouterID endpoint, const 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.insert(endpointRC);
hopsSet.insert(hops.begin(), hops.end());
if (r->routerProfiling().IsBadForPath(rc.pubkey))
return false;
for (const auto& hop : hopsSet)
{
if (hop.pubkey == rc.pubkey)
return false;
}
hopsSet.insert(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, const 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(const 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

@ -25,6 +25,7 @@ namespace llarp
.def_readwrite("lokid", &Config::lokid)
.def_readwrite("bootstrap", &Config::bootstrap)
.def_readwrite("logging", &Config::logging)
.def_readwrite("paths", &Config::paths)
.def("Load", &Config::Load);
py::class_<RouterConfig>(mod, "RouterConfig")
@ -47,6 +48,10 @@ namespace llarp
.def_readwrite("numNetThreads", &RouterConfig::m_numNetThreads)
.def_readwrite("JobQueueSize", &RouterConfig::m_JobQueueSize);
py::class_<PeerSelectionConfig>(mod, "PeerSelectionConfig")
.def(py::init<>())
.def_readwrite("netmask", &PeerSelectionConfig::m_UniqueHopsNetmaskSize);
py::class_<NetworkConfig>(mod, "NetworkConfig")
.def(py::init<>())
.def_readwrite("enableProfiling", &NetworkConfig::m_enableProfiling)

@ -68,8 +68,8 @@ class RouterHive(object):
config.network.enableProfiling = False
config.network.endpointType = 'null'
config.links.addInboundLink("lo", AF_INET, port);
config.links.setOutboundLink("lo", AF_INET, port + 10000);
config.links.addInboundLink("lo", AF_INET, port)
config.links.setOutboundLink("lo", AF_INET, 0)
# config.dns.options = {"local-dns": ("127.3.2.1:%d" % port)}
if index == 0:
@ -90,9 +90,9 @@ class RouterHive(object):
config = pyllarp.Config(dirname)
config.Load(None, False);
port = index + 50000
tunname = "lokihive%d" % index
config.paths.netmask = 0
config.router.dataDir = dirname
config.router.netid = self.netid
config.router.blockBogons = False
@ -100,7 +100,7 @@ class RouterHive(object):
config.network.enableProfiling = False
config.network.endpointType = 'null'
config.links.setOutboundLink("lo", AF_INET, port + 10000);
config.links.setOutboundLink("lo", AF_INET, 0);
# config.dns.options = {"local-dns": ("127.3.2.1:%d" % port)}

Loading…
Cancel
Save