From 0301bc816448aacbf89e3f3e6255da8403df7346 Mon Sep 17 00:00:00 2001 From: dr7ana Date: Tue, 19 Mar 2024 07:16:36 -0700 Subject: [PATCH] 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) --- llarp/address/address.cpp | 77 +++++++++++++- llarp/address/address.hpp | 210 ++++++++++++++++++++++++------------- llarp/address/ip_range.hpp | 5 +- llarp/address/types.hpp | 5 + llarp/config/config.cpp | 59 ++++++++--- llarp/config/config.hpp | 13 ++- llarp/handlers/remote.cpp | 2 +- llarp/handlers/tun.cpp | 11 +- llarp/service/name.hpp | 4 +- 9 files changed, 287 insertions(+), 99 deletions(-) diff --git a/llarp/address/address.cpp b/llarp/address/address.cpp index b1400f82a..c9903a860 100644 --- a/llarp/address/address.cpp +++ b/llarp/address/address.cpp @@ -3,4 +3,79 @@ #include "utils.hpp" namespace llarp -{} // namespace llarp +{ + static auto logcat = log::Cat("address"); + + std::optional ClientAddress::from_client_addr(std::string arg) + { + std::optional 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::from_relay_addr(std::string arg) + { + std::optional 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 diff --git a/llarp/address/address.hpp b/llarp/address/address.hpp index 7b4302de8..aebcb1714 100644 --- a/llarp/address/address.hpp +++ b/llarp/address/address.hpp @@ -13,117 +13,187 @@ namespace llarp { - template , int> = 0> struct RemoteAddress { - pubkey_t _pubkey; - std::optional _name = std::nullopt; + protected: std::string _tld; - bool is_ons{false}; + explicit RemoteAddress(std::string_view tld) : _tld{std::move(tld)} + {} + + public: RemoteAddress() = default; + virtual ~RemoteAddress() = default; - explicit RemoteAddress(std::string addr, bool _is_ons = false) : _name{std::move(addr)}, is_ons{_is_ons} + std::string to_string() const { - if (not is_ons) - _pubkey.from_string(*_name); - - if constexpr (std::is_same_v) - _tld = std::string{TLD::CLIENT}; - else if constexpr (std::is_same_v) - _tld = std::string{TLD::RELAY}; - else - throw std::invalid_argument{"Something seriously weird just happened."}; + return remote_name(); } - explicit RemoteAddress(PubKey pk, std::string_view tld, std::optional 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)} - {} + virtual std::string name() const = 0; - RemoteAddress& operator=(const RemoteAddress& other) = default; - - RemoteAddress& operator=(RemoteAddress&& other) + std::string tld() const { - _pubkey = std::move(other._pubkey); - _name = std::move(other._name); - _tld = std::move(other._tld); - return *this; + return _tld; } - 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 + std::string remote_name() const { - return not(*this == other); + return name() + tld(); } + }; - ~RemoteAddress() = default; + struct ClientAddress final : public RemoteAddress + { + private: + std::optional _pubkey{std::nullopt}; + std::optional _name{std::nullopt}; + bool _is_ons{false}; - std::string to_string() const - { - return remote_name(); - } + explicit ClientAddress(std::string_view addr, bool is_ons); + + public: + ClientAddress() = default; + ~ClientAddress() override = default; + + explicit ClientAddress(ClientPubKey cpk, std::optional 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; - std::string name() const + bool operator<(const ClientAddress& other) const; + bool operator==(const ClientAddress& other) const; + bool operator!=(const ClientAddress& other) const; + + static std::optional from_client_addr(std::string arg); + + std::optional pubkey() { - return _name.value_or(_pubkey.to_string()); + return _pubkey; } - std::string tld() const + bool is_ons() const { - return _tld; + return _is_ons; } - std::string remote_name() const + std::string name() const override { - return name() + tld(); + return _name.value_or(_pubkey->to_string()); } }; - template , int> = 0> - std::optional> from_pubkey_addr(const std::string& arg) + struct RelayAddress final : public RemoteAddress { - if constexpr (std::is_same_v || std::is_same_v) + 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 from_relay_addr(std::string arg); + + const RelayPubKey& pubkey() { - if (service::is_valid_ons(arg)) - { - return std::make_optional(RemoteAddress(arg, true)); - } - if (auto maybe_addr = parse_addr_string(arg, TLD::CLIENT)) - { - return std::make_optional(RemoteAddress(*maybe_addr)); - } + return _pubkey; } - if (auto maybe_addr = parse_addr_string(arg, TLD::RELAY)) + + std::string name() const override { - return std::make_optional(RemoteAddress(*maybe_addr)); + return _pubkey.to_string(); } + }; - return std::nullopt; - } - template - inline constexpr bool IsToStringFormattable> = true; + // template , int> = 0> + // std::optional> from_pubkey_addr(const std::string& arg) + // { + // if constexpr (std::is_same_v || std::is_same_v) + // { + // if (service::is_valid_ons(arg)) + // { + // return std::make_optional(RemoteAddress(arg, true)); + // } + // if (auto maybe_addr = parse_addr_string(arg, TLD::CLIENT)) + // { + // return std::make_optional(RemoteAddress(*maybe_addr)); + // } + // } + // if (auto maybe_addr = parse_addr_string(arg, TLD::RELAY)) + // { + // return std::make_optional(RemoteAddress(*maybe_addr)); + // } + + // return std::nullopt; + // } + + // template , int> = 0> + // std::optional> from_pubkey_addr(const std::string& arg) + // // auto from_pubkey_addr(const std::string& arg) + // { + // if (arg.ends_with(".loki")) + // { + // return RemoteAddress(arg, service::is_valid_ons(arg)); + // } + // if (arg.ends_with(".snode")) + // { + // return RemoteAddress(arg); + // } + + // return std::nullopt; + // } + + template <> + inline constexpr bool IsToStringFormattable = true; + + template + inline constexpr bool IsToStringFormattable>> = true; } // namespace llarp namespace std { - template - struct hash> + template <> + struct hash { - virtual size_t operator()(const llarp::RemoteAddress& r) const + virtual size_t operator()(const llarp::RemoteAddress& r) const { return std::hash{}(r.to_string()); } }; + + template <> + struct hash : public hash + {}; + + template <> + struct hash : public hash + {}; } // namespace std diff --git a/llarp/address/ip_range.hpp b/llarp/address/ip_range.hpp index 49bfed4da..619d93f55 100644 --- a/llarp/address/ip_range.hpp +++ b/llarp/address/ip_range.hpp @@ -22,9 +22,8 @@ namespace llarp public: IPRange() = default; - explicit IPRange(std::string a) : IPRange{a, 0} - {} - explicit IPRange(std::string a, uint8_t m) + + explicit IPRange(std::string a, uint8_t m = 0) : _addr{std::move(a), 0}, _mask{m}, _is_ipv4{_addr.is_ipv4()}, _ip{init_ip()} {} explicit IPRange(oxen::quic::Address a, uint8_t m) diff --git a/llarp/address/types.hpp b/llarp/address/types.hpp index 3af42c4a8..895c3e436 100644 --- a/llarp/address/types.hpp +++ b/llarp/address/types.hpp @@ -18,6 +18,11 @@ namespace llarp using ipv6_net = oxen::quic::ipv6_net; using ip_net = std::variant; + template <> + inline constexpr bool IsToStringFormattable = true; + template <> + inline constexpr bool IsToStringFormattable = true; + inline constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; inline constexpr size_t ICMP_HEADER_SIZE{8}; diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index ce7eec71f..1e3804d4a 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -471,7 +471,6 @@ namespace llarp return; std::optional range; - RemoteAddress remote; const auto pos = arg.find(":"); @@ -485,8 +484,8 @@ namespace llarp if (pos != std::string::npos) arg = arg.substr(0, pos); - if (auto maybe_raddr = from_pubkey_addr(arg); maybe_raddr) - _range_map.emplace(std::move(*maybe_raddr), std::move(*range)); + if (auto maybe_raddr = ClientAddress::from_client_addr(arg); maybe_raddr) + _client_ranges.emplace(std::move(*maybe_raddr), std::move(*range)); else 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.", }, [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( @@ -633,10 +636,10 @@ namespace llarp auto addr_arg = arg.substr(0, pos); auto ip_arg = arg.substr(pos + 1); - if (auto maybe_raddr = from_pubkey_addr(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}; - _addr_map.emplace(std::move(*maybe_raddr), std::move(addr)); + _client_addrs.emplace(std::move(*maybe_raddr), std::move(addr)); } else throw std::invalid_argument{"[endpoint]:mapaddr invalid entry: {}"_format(arg)}; @@ -747,7 +750,7 @@ namespace llarp } 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"); @@ -768,17 +771,47 @@ namespace llarp for (const auto& [key, value] : parsed) { - oxen::quic::Address addr; - 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(*_local_ip); + } + else + { + auto addr_v6 = addr.to_ipv6(); + in_range = _local_ip_range->contains(addr.to_ipv6()); + is_local = addr_v6 == std::get(*_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(&value)) { - if (auto maybe_raddr = from_pubkey_addr(*str); maybe_raddr) + if (auto maybe_raddr = ClientAddress::from_client_addr(*str); maybe_raddr) + { + _persisting_clients.emplace(std::move(*maybe_raddr), std::move(addr)); + continue; + } + if (auto maybe_raddr = RelayAddress::from_relay_addr(*str); maybe_raddr) { - _persisting_addrs.emplace(std::move(*maybe_raddr), std::move(addr)); + _persisting_relays.emplace(std::move(*maybe_raddr), std::move(addr)); continue; } diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index df454466c..e8756c00b 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -141,20 +141,25 @@ namespace llarp /* TESTNET: Under modification */ std::optional addr_map_persist_file; - std::unordered_map, oxen::quic::Address> _persisting_addrs; + + std::unordered_map _persisting_clients; + std::unordered_map _persisting_relays; // only member that refers to an actual interface std::string _if_name; - IPRange _local_ip_range; + std::optional _local_ip_range; + std::optional _local_addr; + std::optional _local_ip; + std::optional _base_ipv6_range = std::nullopt; // Remote client exit addresses mapped to fixed local IP addresses - std::unordered_map, oxen::quic::Address> _addr_map; + std::unordered_map _client_addrs; // Remote client exit addresses mapped to local IP ranges. Addresses can be populated via client // PubKey or their ONS name - std::unordered_map, IPRange> _range_map; + std::unordered_map _client_ranges; std::set _owned_ranges; diff --git a/llarp/handlers/remote.cpp b/llarp/handlers/remote.cpp index 08e84c891..b7ad295ac 100644 --- a/llarp/handlers/remote.cpp +++ b/llarp/handlers/remote.cpp @@ -158,7 +158,7 @@ namespace llarp::handlers 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; // TODO: here is where we should map remote services and exits, but first we need diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 333593ef3..cd55faa8b 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -312,7 +312,7 @@ namespace llarp::handlers else _path_alignment_timeout = service::DEFAULT_PATH_ALIGN_TIMEOUT; - for (const auto& item : conf._addr_map) + for (const auto& item : conf._client_addrs) { (void)item; // if (not map_address(item.second, item.first, false)) @@ -354,11 +354,12 @@ namespace llarp::handlers else _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; + if (conf.addr_map_persist_file and not conf._persisting_clients.empty()) + { + } + if (_persisting_addr_file) { const auto& file = *_persisting_addr_file; @@ -427,7 +428,7 @@ namespace llarp::handlers continue; 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; } diff --git a/llarp/service/name.hpp b/llarp/service/name.hpp index 5164055f0..245a233ca 100644 --- a/llarp/service/name.hpp +++ b/llarp/service/name.hpp @@ -18,11 +18,11 @@ namespace llarp::service inline bool is_valid_ons(std::string_view ons_name) { // 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; // strip off .loki suffix - ons_name = ons_name.substr(0, ons_name.find_last_of('.')); + ons_name.remove_suffix(5); // ensure chars are sane for (const auto ch : ons_name)