mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
Merge pull request #1539 from majestrate/path-algorithm-flavors-2021-02-18
add option to enforce unique netblocks per path.
This commit is contained in:
commit
f2b234d6c3
|
@ -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)
|
||||
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;
|
||||
}
|
||||
for (auto itr = begin; itr != middle; ++itr)
|
||||
{
|
||||
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;
|
||||
});
|
||||
auto filter =
|
||||
[&hops, r = m_router, endpointRC, pathConfig, exclude](const auto& rc) -> bool {
|
||||
if (exclude.count(rc.pubkey))
|
||||
return false;
|
||||
|
||||
if (not maybe.has_value())
|
||||
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…
Reference in a new issue