re-separated address classes

- there is one key difference between the address classes: ClientAddress can be constructed with an ONS name, and can therefore exist in one of 2 dual states (pubkey vs ONS)
This commit is contained in:
dr7ana 2024-03-19 07:16:36 -07:00
parent b037c04285
commit 0301bc8164
9 changed files with 294 additions and 106 deletions

View File

@ -3,4 +3,79 @@
#include "utils.hpp" #include "utils.hpp"
namespace llarp namespace llarp
{} // namespace llarp {
static auto logcat = log::Cat("address");
std::optional<ClientAddress> ClientAddress::from_client_addr(std::string arg)
{
std::optional<ClientAddress> ret = std::nullopt;
if (not arg.ends_with(".loki"))
log::warning(logcat, "Invalid ClientAddress constructor input lacking '.loki' (input:{})", arg);
else
ret = ClientAddress{arg, service::is_valid_ons(arg)};
return ret;
}
ClientAddress::ClientAddress(std::string_view arg, bool is_ons) : RemoteAddress{TLD::CLIENT}, _is_ons{is_ons}
{
// This private constructor is only called after checking for a '.loki' suffix; only Santa checks twice
arg.remove_suffix(5);
// If this was constructed using an ONS name, we don't fill in the pubkey
if (not _is_ons)
_pubkey->from_string(arg);
else
_name = arg;
}
bool ClientAddress::operator<(const ClientAddress& other) const
{
return std::tie(_pubkey, _name, _is_ons) < std::tie(other._pubkey, other._name, other._is_ons);
}
bool ClientAddress::operator==(const ClientAddress& other) const
{
return std::tie(_pubkey, _name, _is_ons) == std::tie(other._pubkey, other._name, other._is_ons);
}
bool ClientAddress::operator!=(const ClientAddress& other) const
{
return !(*this == other);
}
std::optional<RelayAddress> RelayAddress::from_relay_addr(std::string arg)
{
std::optional<RelayAddress> ret = std::nullopt;
if (not arg.ends_with(".snode"))
log::warning(logcat, "Invalid RelayAddress constructor input lacking '.loki' (input:{})", arg);
else
ret = RelayAddress{arg};
return ret;
}
RelayAddress::RelayAddress(std::string_view arg) : RemoteAddress{TLD::RELAY}
{
// This private constructor is only called after checking for a '.loki' suffix; only Santa checks twice
arg.remove_suffix(6);
_pubkey.from_string(arg);
}
bool RelayAddress::operator<(const RelayAddress& other) const
{
return _pubkey < other._pubkey;
}
bool RelayAddress::operator==(const RelayAddress& other) const
{
return _pubkey == other._pubkey;
}
bool RelayAddress::operator!=(const RelayAddress& other) const
{
return !(*this == other);
}
} // namespace llarp

View File

@ -13,72 +13,24 @@
namespace llarp namespace llarp
{ {
template <typename pubkey_t = PubKey, std::enable_if_t<std::is_base_of_v<PubKey, pubkey_t>, int> = 0>
struct RemoteAddress struct RemoteAddress
{ {
pubkey_t _pubkey; protected:
std::optional<std::string> _name = std::nullopt;
std::string _tld; std::string _tld;
bool is_ons{false};
explicit RemoteAddress(std::string_view tld) : _tld{std::move(tld)}
{}
public:
RemoteAddress() = default; RemoteAddress() = default;
virtual ~RemoteAddress() = default;
explicit RemoteAddress(std::string addr, bool _is_ons = false) : _name{std::move(addr)}, is_ons{_is_ons}
{
if (not is_ons)
_pubkey.from_string(*_name);
if constexpr (std::is_same_v<pubkey_t, ClientPubKey>)
_tld = std::string{TLD::CLIENT};
else if constexpr (std::is_same_v<pubkey_t, RelayPubKey>)
_tld = std::string{TLD::RELAY};
else
throw std::invalid_argument{"Something seriously weird just happened."};
}
explicit RemoteAddress(PubKey pk, std::string_view tld, std::optional<std::string> n = std::nullopt)
: _pubkey{pk.data()}, _name{std::move(n)}, _tld{tld}
{}
RemoteAddress(const RemoteAddress& other) : RemoteAddress{other._pubkey, other._tld, other._name}
{}
RemoteAddress(RemoteAddress&& other)
: RemoteAddress{std::move(other._pubkey), std::move(other._tld), std::move(other._name)}
{}
RemoteAddress& operator=(const RemoteAddress& other) = default;
RemoteAddress& operator=(RemoteAddress&& other)
{
_pubkey = std::move(other._pubkey);
_name = std::move(other._name);
_tld = std::move(other._tld);
return *this;
}
bool operator<(const RemoteAddress& other) const
{
return std::tie(_pubkey, _name, _tld) < std::tie(other._pubkey, other._name, other._tld);
}
bool operator==(const RemoteAddress& other) const
{
return _pubkey == other._pubkey and _name == other._name && _tld == other._tld;
}
bool operator!=(const RemoteAddress& other) const
{
return not(*this == other);
}
~RemoteAddress() = default;
std::string to_string() const std::string to_string() const
{ {
return remote_name(); return remote_name();
} }
std::string name() const virtual std::string name() const = 0;
{
return _name.value_or(_pubkey.to_string());
}
std::string tld() const std::string tld() const
{ {
@ -91,39 +43,157 @@ namespace llarp
} }
}; };
template <typename pubkey_t = PubKey, std::enable_if_t<std::is_base_of_v<PubKey, pubkey_t>, int> = 0> struct ClientAddress final : public RemoteAddress
std::optional<RemoteAddress<pubkey_t>> from_pubkey_addr(const std::string& arg)
{ {
if constexpr (std::is_same_v<pubkey_t, ClientPubKey> || std::is_same_v<pubkey_t, PubKey>) private:
std::optional<ClientPubKey> _pubkey{std::nullopt};
std::optional<std::string> _name{std::nullopt};
bool _is_ons{false};
explicit ClientAddress(std::string_view addr, bool is_ons);
public:
ClientAddress() = default;
~ClientAddress() override = default;
explicit ClientAddress(ClientPubKey cpk, std::optional<std::string> n = std::nullopt)
: RemoteAddress{TLD::CLIENT}, _pubkey{std::move(cpk)}, _name{std::move(n)}
{}
ClientAddress(const ClientAddress& other)
: RemoteAddress{TLD::RELAY}, _pubkey{other._pubkey}, _name{other._name}
{}
ClientAddress(ClientAddress&& other)
: RemoteAddress{TLD::RELAY}, _pubkey{std::move(other._pubkey)}, _name{std::move(other._name)}
{}
ClientAddress& operator=(const ClientAddress& other) = default;
ClientAddress& operator=(ClientAddress&& other) = default;
bool operator<(const ClientAddress& other) const;
bool operator==(const ClientAddress& other) const;
bool operator!=(const ClientAddress& other) const;
static std::optional<ClientAddress> from_client_addr(std::string arg);
std::optional<ClientPubKey> pubkey()
{ {
if (service::is_valid_ons(arg)) return _pubkey;
{
return std::make_optional(RemoteAddress<ClientPubKey>(arg, true));
}
if (auto maybe_addr = parse_addr_string(arg, TLD::CLIENT))
{
return std::make_optional(RemoteAddress<ClientPubKey>(*maybe_addr));
}
}
if (auto maybe_addr = parse_addr_string(arg, TLD::RELAY))
{
return std::make_optional(RemoteAddress<RelayPubKey>(*maybe_addr));
} }
return std::nullopt; bool is_ons() const
{
return _is_ons;
} }
template <typename pk_t>
inline constexpr bool IsToStringFormattable<RemoteAddress<pk_t>> = true; std::string name() const override
{
return _name.value_or(_pubkey->to_string());
}
};
struct RelayAddress final : public RemoteAddress
{
private:
RelayPubKey _pubkey;
explicit RelayAddress(std::string_view addr);
public:
RelayAddress() = default;
~RelayAddress() override = default;
explicit RelayAddress(RelayPubKey cpk) : RemoteAddress{TLD::CLIENT}, _pubkey{std::move(cpk)}
{}
RelayAddress(const RelayAddress& other) : RemoteAddress{TLD::RELAY}, _pubkey{other._pubkey}
{}
RelayAddress(RelayAddress&& other) : RemoteAddress{TLD::RELAY}, _pubkey{std::move(other._pubkey)}
{}
RelayAddress& operator=(const RelayAddress& other) = default;
RelayAddress& operator=(RelayAddress&& other) = default;
bool operator<(const RelayAddress& other) const;
bool operator==(const RelayAddress& other) const;
bool operator!=(const RelayAddress& other) const;
static std::optional<RelayAddress> from_relay_addr(std::string arg);
const RelayPubKey& pubkey()
{
return _pubkey;
}
std::string name() const override
{
return _pubkey.to_string();
}
};
// template <typename pubkey_t = PubKey, std::enable_if_t<std::is_base_of_v<PubKey, pubkey_t>, int> = 0>
// std::optional<RemoteAddress<pubkey_t>> from_pubkey_addr(const std::string& arg)
// {
// if constexpr (std::is_same_v<pubkey_t, ClientPubKey> || std::is_same_v<pubkey_t, PubKey>)
// {
// if (service::is_valid_ons(arg))
// {
// return std::make_optional(RemoteAddress<ClientPubKey>(arg, true));
// }
// if (auto maybe_addr = parse_addr_string(arg, TLD::CLIENT))
// {
// return std::make_optional(RemoteAddress<ClientPubKey>(*maybe_addr));
// }
// }
// if (auto maybe_addr = parse_addr_string(arg, TLD::RELAY))
// {
// return std::make_optional(RemoteAddress<RelayPubKey>(*maybe_addr));
// }
// return std::nullopt;
// }
// template <typename pubkey_t, std::enable_if_t<std::is_base_of_v<PubKey, pubkey_t>, int> = 0>
// std::optional<RemoteAddress<pubkey_t>> from_pubkey_addr(const std::string& arg)
// // auto from_pubkey_addr(const std::string& arg)
// {
// if (arg.ends_with(".loki"))
// {
// return RemoteAddress<ClientPubKey>(arg, service::is_valid_ons(arg));
// }
// if (arg.ends_with(".snode"))
// {
// return RemoteAddress<RelayPubKey>(arg);
// }
// return std::nullopt;
// }
template <>
inline constexpr bool IsToStringFormattable<RemoteAddress> = true;
template <typename T>
inline constexpr bool IsToStringFormattable<T, std::enable_if_t<std::is_base_of_v<RemoteAddress, T>>> = true;
} // namespace llarp } // namespace llarp
namespace std namespace std
{ {
template <typename pk_t> template <>
struct hash<llarp::RemoteAddress<pk_t>> struct hash<llarp::RemoteAddress>
{ {
virtual size_t operator()(const llarp::RemoteAddress<pk_t>& r) const virtual size_t operator()(const llarp::RemoteAddress& r) const
{ {
return std::hash<std::string>{}(r.to_string()); return std::hash<std::string>{}(r.to_string());
} }
}; };
template <>
struct hash<llarp::RelayAddress> : public hash<llarp::RemoteAddress>
{};
template <>
struct hash<llarp::ClientAddress> : public hash<llarp::RemoteAddress>
{};
} // namespace std } // namespace std

View File

@ -22,9 +22,8 @@ namespace llarp
public: public:
IPRange() = default; IPRange() = default;
explicit IPRange(std::string a) : IPRange{a, 0}
{} explicit IPRange(std::string a, uint8_t m = 0)
explicit IPRange(std::string a, uint8_t m)
: _addr{std::move(a), 0}, _mask{m}, _is_ipv4{_addr.is_ipv4()}, _ip{init_ip()} : _addr{std::move(a), 0}, _mask{m}, _is_ipv4{_addr.is_ipv4()}, _ip{init_ip()}
{} {}
explicit IPRange(oxen::quic::Address a, uint8_t m) explicit IPRange(oxen::quic::Address a, uint8_t m)

View File

@ -18,6 +18,11 @@ namespace llarp
using ipv6_net = oxen::quic::ipv6_net; using ipv6_net = oxen::quic::ipv6_net;
using ip_net = std::variant<ipv4_net, ipv6_net>; using ip_net = std::variant<ipv4_net, ipv6_net>;
template <>
inline constexpr bool IsToStringFormattable<ip> = true;
template <>
inline constexpr bool IsToStringFormattable<ip_net> = true;
inline constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; inline constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111;
inline constexpr size_t ICMP_HEADER_SIZE{8}; inline constexpr size_t ICMP_HEADER_SIZE{8};

View File

@ -471,7 +471,6 @@ namespace llarp
return; return;
std::optional<IPRange> range; std::optional<IPRange> range;
RemoteAddress remote;
const auto pos = arg.find(":"); const auto pos = arg.find(":");
@ -485,8 +484,8 @@ namespace llarp
if (pos != std::string::npos) if (pos != std::string::npos)
arg = arg.substr(0, pos); arg = arg.substr(0, pos);
if (auto maybe_raddr = from_pubkey_addr<ClientPubKey>(arg); maybe_raddr) if (auto maybe_raddr = ClientAddress::from_client_addr(arg); maybe_raddr)
_range_map.emplace(std::move(*maybe_raddr), std::move(*range)); _client_ranges.emplace(std::move(*maybe_raddr), std::move(*range));
else else
throw std::invalid_argument{"[network]:exit-node bad address: {}"_format(arg)}; throw std::invalid_argument{"[network]:exit-node bad address: {}"_format(arg)};
}); });
@ -575,10 +574,14 @@ namespace llarp
"lokinet will attempt to find an unused private range.", "lokinet will attempt to find an unused private range.",
}, },
[this](std::string arg) { [this](std::string arg) {
if (not _local_ip_range.from_string(arg)) if (auto maybe_range = IPRange::from_string(arg); maybe_range)
{ {
throw std::invalid_argument{fmt::format("[network]:ifaddr invalid value: '{}'", arg)}; _local_ip_range = *maybe_range;
_local_addr = _local_ip_range->address();
_local_ip = _local_ip_range->get_ip();
} }
else
throw std::invalid_argument{fmt::format("[network]:ifaddr invalid value: '{}'", arg)};
}); });
conf.define_option<std::string>( conf.define_option<std::string>(
@ -633,10 +636,10 @@ namespace llarp
auto addr_arg = arg.substr(0, pos); auto addr_arg = arg.substr(0, pos);
auto ip_arg = arg.substr(pos + 1); auto ip_arg = arg.substr(pos + 1);
if (auto maybe_raddr = from_pubkey_addr<ClientPubKey>(std::move(addr_arg)); maybe_raddr) if (auto maybe_raddr = ClientAddress::from_client_addr(std::move(addr_arg)); maybe_raddr)
{ {
oxen::quic::Address addr{std::move(ip_arg), 0}; oxen::quic::Address addr{std::move(ip_arg), 0};
_addr_map.emplace(std::move(*maybe_raddr), std::move(addr)); _client_addrs.emplace(std::move(*maybe_raddr), std::move(addr));
} }
else else
throw std::invalid_argument{"[endpoint]:mapaddr invalid entry: {}"_format(arg)}; throw std::invalid_argument{"[endpoint]:mapaddr invalid entry: {}"_format(arg)};
@ -747,7 +750,7 @@ namespace llarp
} }
else else
{ {
auto err = "Config could not load persisting address map file from path:{} --"_format(file); auto err = "Config could not load persisting address map file from path:{}"_format(file);
log::info(logcat, "{} {}", err, load_file ? "NOT FOUND" : "STALE"); log::info(logcat, "{} {}", err, load_file ? "NOT FOUND" : "STALE");
@ -768,17 +771,47 @@ namespace llarp
for (const auto& [key, value] : parsed) for (const auto& [key, value] : parsed)
{ {
oxen::quic::Address addr;
try try
{ {
addr = oxen::quic::Address{key, 0}; oxen::quic::Address addr{key, 0};
bool in_range{false}, is_local{false};
if (addr.is_ipv4())
{
auto addr_v4 = addr.to_ipv4();
in_range = _local_ip_range->contains(addr_v4);
is_local = addr_v4 == std::get<ipv4>(*_local_ip);
}
else
{
auto addr_v6 = addr.to_ipv6();
in_range = _local_ip_range->contains(addr.to_ipv6());
is_local = addr_v6 == std::get<ipv6>(*_local_ip);
}
if (is_local)
continue;
if (not in_range)
{
log::warning(
logcat,
"Out of range IP in addr map data: IP:{}, local range{}",
addr.host(),
_local_ip_range);
continue;
}
if (const auto* str = std::get_if<std::string>(&value)) if (const auto* str = std::get_if<std::string>(&value))
{ {
if (auto maybe_raddr = from_pubkey_addr(*str); maybe_raddr) if (auto maybe_raddr = ClientAddress::from_client_addr(*str); maybe_raddr)
{ {
_persisting_addrs.emplace(std::move(*maybe_raddr), std::move(addr)); _persisting_clients.emplace(std::move(*maybe_raddr), std::move(addr));
continue;
}
if (auto maybe_raddr = RelayAddress::from_relay_addr(*str); maybe_raddr)
{
_persisting_relays.emplace(std::move(*maybe_raddr), std::move(addr));
continue; continue;
} }

View File

@ -141,20 +141,25 @@ namespace llarp
/* TESTNET: Under modification */ /* TESTNET: Under modification */
std::optional<fs::path> addr_map_persist_file; std::optional<fs::path> addr_map_persist_file;
std::unordered_map<RemoteAddress<PubKey>, oxen::quic::Address> _persisting_addrs;
std::unordered_map<ClientAddress, oxen::quic::Address> _persisting_clients;
std::unordered_map<RelayAddress, oxen::quic::Address> _persisting_relays;
// only member that refers to an actual interface // only member that refers to an actual interface
std::string _if_name; std::string _if_name;
IPRange _local_ip_range; std::optional<IPRange> _local_ip_range;
std::optional<oxen::quic::Address> _local_addr;
std::optional<ip> _local_ip;
std::optional<IPRange> _base_ipv6_range = std::nullopt; std::optional<IPRange> _base_ipv6_range = std::nullopt;
// Remote client exit addresses mapped to fixed local IP addresses // Remote client exit addresses mapped to fixed local IP addresses
std::unordered_map<RemoteAddress<ClientPubKey>, oxen::quic::Address> _addr_map; std::unordered_map<ClientAddress, oxen::quic::Address> _client_addrs;
// Remote client exit addresses mapped to local IP ranges. Addresses can be populated via client // Remote client exit addresses mapped to local IP ranges. Addresses can be populated via client
// PubKey or their ONS name // PubKey or their ONS name
std::unordered_map<RemoteAddress<ClientPubKey>, IPRange> _range_map; std::unordered_map<ClientAddress, IPRange> _client_ranges;
std::set<IPRange> _owned_ranges; std::set<IPRange> _owned_ranges;

View File

@ -158,7 +158,7 @@ namespace llarp::handlers
log::info(logcat, "{} set ifname to {}", name(), _if_name); log::info(logcat, "{} set ifname to {}", name(), _if_name);
for (const auto& addr : _net_config._addr_map) for (const auto& addr : _net_config._client_addrs)
{ {
(void)addr; (void)addr;
// TODO: here is where we should map remote services and exits, but first we need // TODO: here is where we should map remote services and exits, but first we need

View File

@ -312,7 +312,7 @@ namespace llarp::handlers
else else
_path_alignment_timeout = service::DEFAULT_PATH_ALIGN_TIMEOUT; _path_alignment_timeout = service::DEFAULT_PATH_ALIGN_TIMEOUT;
for (const auto& item : conf._addr_map) for (const auto& item : conf._client_addrs)
{ {
(void)item; (void)item;
// if (not map_address(item.second, item.first, false)) // if (not map_address(item.second, item.first, false))
@ -354,11 +354,12 @@ namespace llarp::handlers
else else
_local_ip = _local_addr.to_ipv4(); _local_ip = _local_addr.to_ipv4();
// TODO: move all this parsing to the config
// DISCUSS: what format do we expect these to be bt-encoded in? If strings, then must make
// string ctors for ipv4/ipv6 types to load directly into and pass to IPRange::contains(...)
_persisting_addr_file = conf.addr_map_persist_file; _persisting_addr_file = conf.addr_map_persist_file;
if (conf.addr_map_persist_file and not conf._persisting_clients.empty())
{
}
if (_persisting_addr_file) if (_persisting_addr_file)
{ {
const auto& file = *_persisting_addr_file; const auto& file = *_persisting_addr_file;
@ -427,7 +428,7 @@ namespace llarp::handlers
continue; continue;
if (not _local_range.contains(ip)) if (not _local_range.contains(ip))
{ {
log::warning(logcat, "{} out of range IP in addr map data", name(), ip); // log::warning(logcat, "{} out of range IP in addr map data", name(), ip);
continue; continue;
} }

View File

@ -18,11 +18,11 @@ namespace llarp::service
inline bool is_valid_ons(std::string_view ons_name) inline bool is_valid_ons(std::string_view ons_name)
{ {
// make sure it ends with .loki because no fucking shit right? // make sure it ends with .loki because no fucking shit right?
if (not ends_with(ons_name, ".loki")) if (not ons_name.ends_with(".loki"))
return false; return false;
// strip off .loki suffix // strip off .loki suffix
ons_name = ons_name.substr(0, ons_name.find_last_of('.')); ons_name.remove_suffix(5);
// ensure chars are sane // ensure chars are sane
for (const auto ch : ons_name) for (const auto ch : ons_name)