From c9b4ca85b208b937d03749a1736923ca075866fe Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 17 Jun 2021 12:05:50 -0400 Subject: [PATCH] [feature] optionally keep inbound convos mapped to same IP on restart (#1672) * add option to persist address mappings between restarts using [network]:persist-addrmap-file * make it work * only persist address map for inbound convos * turn persisting address map on by default * dont load addrmap file if it has been modified last over a minute ago to prevent foot cannons fired from loading a really old version of it --- llarp/config/config.cpp | 16 +++++ llarp/config/config.hpp | 5 +- llarp/handlers/tun.cpp | 125 ++++++++++++++++++++++++++++++++++++++++ llarp/handlers/tun.hpp | 3 + 4 files changed, 145 insertions(+), 4 deletions(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 0fee00179..ec2f30c09 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -674,6 +674,22 @@ namespace llarp m_PathAlignmentTimeout = std::chrono::seconds{val}; }); + conf.defineOption( + "network", + "persist-addrmap-file", + ClientOnly, + Default{fs::path{params.defaultDataDir / "addrmap.dat"}}, + Comment{ + "persist mapped ephemeral addresses to a file", + "on restart the mappings will be loaded so that ip addresses will not be mapped to a " + "different address", + }, + [this](fs::path arg) { + if (arg.empty()) + throw std::invalid_argument("persist-addrmap-file cannot be empty"); + m_AddrMapPersistFile = arg; + }); + // Deprecated options: conf.defineOption("network", "enabled", Deprecated); } diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 7fb212df4..a3b683d4c 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -127,10 +127,7 @@ namespace llarp std::optional m_PathAlignmentTimeout; - // TODO: - // on-up - // on-down - // on-ready + std::optional m_AddrMapPersistFile; void defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index c816297f3..8c499c448 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -208,6 +208,110 @@ namespace llarp m_OurIP = m_OurRange.addr; m_UseV6 = false; + m_PersistAddrMapFile = conf.m_AddrMapPersistFile; + if (m_PersistAddrMapFile) + { + const auto& file = *m_PersistAddrMapFile; + if (fs::exists(file)) + { + bool shouldLoadFile = true; + { + constexpr auto LastModifiedWindow = 1min; + const auto lastmodified = fs::last_write_time(file); + const auto now = decltype(lastmodified)::clock::now(); + if (now < lastmodified or now - lastmodified > LastModifiedWindow) + { + shouldLoadFile = false; + } + } + std::vector data; + if (auto maybe = util::OpenFileStream(file, std::ios_base::binary); + maybe and shouldLoadFile) + { + LogInfo(Name(), " loading address map file from ", file); + maybe->seekg(0, std::ios_base::end); + const size_t len = maybe->tellg(); + maybe->seekg(0, std::ios_base::beg); + data.resize(len); + LogInfo(Name(), " reading ", len, " bytes"); + maybe->read(data.data(), data.size()); + } + else + { + if (shouldLoadFile) + { + LogInfo(Name(), " address map file ", file, " does not exist, so we won't load it"); + } + else + LogInfo(Name(), " address map file ", file, " not loaded because it's stale"); + } + if (not data.empty()) + { + std::string_view bdata{data.data(), data.size()}; + LogInfo(Name(), " parsing address map data: ", bdata); + const auto parsed = oxenmq::bt_deserialize(bdata); + for (const auto& [key, value] : parsed) + { + huint128_t ip{}; + if (not ip.FromString(key)) + { + LogWarn(Name(), " malformed IP in addr map data: ", key); + continue; + } + if (m_OurIP == ip) + continue; + if (not m_OurRange.Contains(ip)) + { + LogWarn(Name(), " out of range IP in addr map data: ", ip); + continue; + } + EndpointBase::AddressVariant_t addr; + + if (const auto* str = std::get_if(&value)) + { + if (auto maybe = service::ParseAddress(*str)) + { + addr = *maybe; + } + else + { + LogWarn(Name(), " invalid address in addr map: ", *str); + continue; + } + } + else + { + LogWarn(Name(), " invalid first entry in addr map, not a string"); + continue; + } + if (const auto* loki = std::get_if(&addr)) + { + m_IPToAddr.emplace(ip, loki->data()); + m_AddrToIP.emplace(loki->data(), ip); + m_SNodes[*loki] = false; + LogInfo(Name(), " remapped ", ip, " to ", *loki); + } + if (const auto* snode = std::get_if(&addr)) + { + m_IPToAddr.emplace(ip, snode->data()); + m_AddrToIP.emplace(snode->data(), ip); + m_SNodes[*snode] = true; + LogInfo(Name(), " remapped ", ip, " to ", *snode); + } + if (m_NextIP < ip) + m_NextIP = ip; + // make sure we dont unmap this guy + MarkIPActive(ip); + } + } + } + else + { + LogInfo( + Name(), " skipping loading addr map at ", file, " as it does not currently exist"); + } + } + if (auto* quic = GetQUICTunnel()) { quic->listen([this](std::string_view, uint16_t port) { @@ -858,6 +962,27 @@ namespace llarp bool TunEndpoint::Stop() { + // save address map if applicable + if (m_PersistAddrMapFile) + { + const auto& file = *m_PersistAddrMapFile; + LogInfo(Name(), " saving address map to ", file); + if (auto maybe = util::OpenFileStream(file, std::ios_base::binary)) + { + std::map addrmap; + for (const auto& [ip, addr] : m_IPToAddr) + { + if (not m_SNodes.at(addr)) + { + const service::Address a{addr.as_array()}; + if (HasInboundConvo(a)) + addrmap[ip.ToString()] = a.ToString(); + } + } + const auto data = oxenmq::bt_serialize(addrmap); + maybe->write(data.data(), data.size()); + } + } if (m_Resolver) m_Resolver->Stop(); return llarp::service::Endpoint::Stop(); diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index 29daf8fcc..e420ace4c 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -290,6 +290,9 @@ namespace llarp /// idempotent wakeup for writing messages to network std::shared_ptr m_MessageSendWaker; + + /// a file to load / store the ephemeral address map to + std::optional m_PersistAddrMapFile; }; } // namespace handlers