diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 5e6a53115..6bff96112 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -126,32 +126,23 @@ namespace llarp "provided the public-port option must also be specified.", }, [this](std::string arg) { - if (not arg.empty()) - { - llarp::LogInfo("public ip ", arg, " size ", arg.size()); + if (arg.empty()) + return; + nuint32_t addr{}; + if (not addr.FromString(arg)) + throw std::invalid_argument{stringify(arg, " is not a valid IPv4 address")}; - if (arg.size() > 15) - throw std::invalid_argument(stringify("Not a valid IPv4 addr: ", arg)); + if (IsIPv4Bogon(addr)) + throw std::invalid_argument{ + stringify(addr, " looks like it is not a publicly routable ip address")}; - m_publicAddress.setAddress(arg); - } + m_PublicIP = addr; }); - conf.defineOption("router", "public-address", Hidden, [this](std::string arg) { - if (not arg.empty()) - { - llarp::LogWarn( - "*** WARNING: The config option [router]:public-address=", - arg, - " is deprecated, use public-ip=", - arg, - " instead to avoid this warning and avoid future configuration problems."); - - if (arg.size() > 15) - throw std::invalid_argument(stringify("Not a valid IPv4 addr: ", arg)); - - m_publicAddress.setAddress(arg); - } + conf.defineOption("router", "public-address", Hidden, [](std::string) { + throw std::invalid_argument{ + "[router]:public-address option no longer supported, use [router]:public-ip and " + "[router]:public-port instead"}; }); conf.defineOption( @@ -166,8 +157,7 @@ namespace llarp [this](int arg) { if (arg <= 0 || arg > std::numeric_limits::max()) throw std::invalid_argument("public-port must be >= 0 and <= 65536"); - - m_publicAddress.setPort(arg); + m_PublicPort = ToNet(huint16_t{static_cast(arg)}); }); conf.defineOption( diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 09eb1febb..975a8ac99 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -54,7 +54,8 @@ namespace llarp bool m_blockBogons = false; - IpAddress m_publicAddress; + std::optional m_PublicIP; + nuint16_t m_PublicPort; int m_workerThreads = -1; int m_numNetThreads = -1; diff --git a/llarp/link/server.cpp b/llarp/link/server.cpp index 0abe80eaa..44ac8b4a3 100644 --- a/llarp/link/server.cpp +++ b/llarp/link/server.cpp @@ -8,6 +8,7 @@ #include #include #include +#include static constexpr auto LINK_LAYER_TICK_INTERVAL = 100ms; @@ -129,7 +130,7 @@ namespace llarp } bool - ILinkLayer::Configure(AbstractRouter* router, const std::string& ifname, int af, uint16_t port) + ILinkLayer::Configure(AbstractRouter* router, std::string ifname, int af, uint16_t port) { m_Router = router; m_udp = m_Router->loop()->make_udp( @@ -142,11 +143,42 @@ namespace llarp if (ifname == "*") { - if (!AllInterfaces(af, m_ourAddr)) + if (router->IsServiceNode()) + { + if (auto maybe = router->OurPublicIP()) + { + auto addr = var::visit([](auto&& addr) { return SockAddr{addr}; }, *maybe); + // service node outbound link + if (HasInterfaceAddress(addr.getIP())) + { + // we have our ip claimed on a local net interface + m_ourAddr = addr; + } + else if (auto maybe = net::AllInterfaces(addr)) + { + // we do not have our claimed ip, nat or something? + m_ourAddr = *maybe; + } + else + return false; // the ultimate failure case + } + else + return false; + } + else if (auto maybe = net::AllInterfaces(SockAddr{"0.0.0.0"})) + { + // client outbound link + m_ourAddr = *maybe; + } + else return false; } else { + if (ifname == "0.0.0.0" and not GetBestNetIF(ifname)) + throw std::invalid_argument{ + "0.0.0.0 provided and we cannot find a valid ip to use, please set one " + "explicitly instead in the bind section instead of 0.0.0.0"}; if (const auto maybe = GetInterfaceAddr(ifname, af)) { m_ourAddr = *maybe; @@ -157,10 +189,11 @@ namespace llarp { m_ourAddr = SockAddr{ifname + ":0"}; } - catch (const std::exception& e) + catch (const std::exception& ex) { - LogError(stringify("Could not use ifname ", ifname, " to configure ILinkLayer")); - throw e; + LogError( + stringify("Could not use ifname ", ifname, " to configure ILinkLayer: ", ex.what())); + throw ex; } } } diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index 8aa25928f..02cc17f83 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -108,7 +108,7 @@ namespace llarp SendTo_LL(const SockAddr& to, const llarp_buffer_t& pkt); virtual bool - Configure(AbstractRouter* loop, const std::string& ifname, int af, uint16_t port); + Configure(AbstractRouter* loop, std::string ifname, int af, uint16_t port); virtual std::shared_ptr NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) = 0; diff --git a/llarp/net/address_info.cpp b/llarp/net/address_info.cpp index c4aad84fe..8daf8f8de 100644 --- a/llarp/net/address_info.cpp +++ b/llarp/net/address_info.cpp @@ -27,6 +27,12 @@ namespace llarp return lhs.rank < rhs.rank || lhs.ip < rhs.ip || lhs.port < rhs.port; } + std::variant + AddressInfo::IP() const + { + return SockAddr{ip}.getIP(); + } + bool AddressInfo::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { diff --git a/llarp/net/address_info.hpp b/llarp/net/address_info.hpp index 5851ad07c..a3bcd02e7 100644 --- a/llarp/net/address_info.hpp +++ b/llarp/net/address_info.hpp @@ -9,6 +9,8 @@ #include #include +#include + /** * address_info.hpp * @@ -47,6 +49,10 @@ namespace llarp void fromSockAddr(const SockAddr& address); + /// get this as an explicit v4 or explicit v6 + std::variant + IP() const; + std::ostream& print(std::ostream& stream, int level, int spaces) const; }; diff --git a/llarp/net/net.cpp b/llarp/net/net.cpp index fe2b883c1..83e62cff5 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/net.cpp @@ -576,29 +576,56 @@ namespace llarp return addr.asIPv6(); } - bool - AllInterfaces(int af, SockAddr& result) + namespace net { - if (af == AF_INET) + namespace { - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - result = SockAddr{addr}; - return true; - } - if (af == AF_INET6) + SockAddr + All(int af) + { + if (af == AF_INET) + { + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + return SockAddr{addr}; + } + sockaddr_in6 addr6{}; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(0); + addr6.sin6_addr = IN6ADDR_ANY_INIT; + return SockAddr{addr6}; + } + } // namespace + + std::optional + AllInterfaces(SockAddr pub) { - sockaddr_in6 addr6; - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(0); - addr6.sin6_addr = IN6ADDR_ANY_INIT; - result = SockAddr{addr6}; - return true; + std::optional found; + IterAllNetworkInterfaces([pub, &found](auto* ifa) { + if (found) + return; + if (auto ifa_addr = ifa->ifa_addr) + { + if (ifa_addr->sa_family != pub.Family()) + return; + + SockAddr addr{*ifa->ifa_addr}; + + if (addr == pub) + found = addr; + } + }); + + // 0.0.0.0 is used in our compat shim as our public ip so we check for that special case + const auto zero = IPRange::FromIPv4(0, 0, 0, 0, 8); + // when we cannot find an address but we are looking for 0.0.0.0 just default to the old style + if (not found and (pub.isIPv4() and zero.Contains(pub.asIPv4()))) + found = All(pub.Family()); + return found; } - return false; - } + } // namespace net #if !defined(TESTNET) static constexpr std::array bogonRanges_v6 = { @@ -692,5 +719,20 @@ namespace llarp return false; } #endif + bool + HasInterfaceAddress(std::variant ip) + { + bool found{false}; + IterAllNetworkInterfaces([ip, &found](const auto* iface) { + if (found or iface == nullptr) + return; + if (auto addr = iface->ifa_addr; + addr and (addr->sa_family == AF_INET or addr->sa_family == AF_INET6)) + { + found = SockAddr{*iface->ifa_addr}.getIP() == ip; + } + }); + return found; + } } // namespace llarp diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index 95141f3a9..7c40617bc 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -52,6 +52,12 @@ namespace llarp bool IsIPv4Bogon(const huint32_t& addr); + inline bool + IsIPv4Bogon(const nuint32_t& addr) + { + return IsIPv4Bogon(ToHost(addr)); + } + bool IsBogon(const in6_addr& addr); @@ -61,8 +67,25 @@ namespace llarp bool IsBogonRange(const in6_addr& host, const in6_addr& mask); - bool - AllInterfaces(int af, SockAddr& addr); + /// get a sock addr we can use for all interfaces given our public address + namespace net + { + std::optional + AllInterfaces(SockAddr pubaddr); + } + + /// compat shim + // TODO: remove me + inline bool + AllInterfaces(int af, SockAddr& addr) + { + if (auto maybe = net::AllInterfaces(SockAddr{af == AF_INET ? "0.0.0.0" : "::"})) + { + addr = *maybe; + return true; + } + return false; + } /// get first network interface with public address bool @@ -92,4 +115,8 @@ namespace llarp } #endif + /// return true if we have a network interface with this ip + bool + HasInterfaceAddress(std::variant ip); + } // namespace llarp diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index a09a88527..dbb1734a2 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -351,6 +351,14 @@ namespace llarp return a; } + std::variant + SockAddr::getIP() const + { + if (isIPv4()) + return getIPv4(); + return getIPv6(); + } + void SockAddr::setIPv4(nuint32_t ip) { diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 3c25e8914..a51379b13 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -12,6 +12,7 @@ #include #include #include "net_int.hpp" +#include namespace llarp { @@ -81,6 +82,14 @@ namespace llarp std::string hostString() const; + inline int + Family() const + { + if (isIPv6()) + return AF_INET6; + return AF_INET; + } + /// Returns true if this is an empty SockAddr, defined by having no IP address set. An empty IP /// address with a valid port is still considered empty. /// @@ -133,6 +142,8 @@ namespace llarp getIPv6() const; nuint32_t getIPv4() const; + std::variant + getIP() const; /// in host order huint128_t diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 3d0d1e747..a0e761b78 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -220,6 +220,10 @@ namespace llarp virtual const byte_t* pubkey() const = 0; + /// get what our real public ip is if we can know it + virtual std::optional> + OurPublicIP() const = 0; + /// connect to N random routers virtual void ConnectToRandomRouters(int N) = 0; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 5c04b6799..2b2a4131c 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -415,9 +415,6 @@ namespace llarp if (!FromConfig(conf)) throw std::runtime_error("FromConfig() failed"); - if (!InitOutboundLinks()) - throw std::runtime_error("InitOutboundLinks() failed"); - if (not EnsureIdentity()) throw std::runtime_error("EnsureIdentity() failed"); @@ -596,8 +593,8 @@ namespace llarp transport_keyfile = m_keyManager->m_transportKeyPath; ident_keyfile = m_keyManager->m_idKeyPath; - if (not conf.router.m_publicAddress.isEmpty()) - _ourAddress = conf.router.m_publicAddress.createSockAddr(); + if (auto maybe = conf.router.m_PublicIP) + _ourAddress = SockAddr{*maybe, conf.router.m_PublicPort}; RouterContact::BlockBogons = conf.router.m_blockBogons; @@ -728,14 +725,15 @@ namespace llarp if (inboundLinks.empty() and m_isServiceNode) { - const auto& publicAddr = conf.router.m_publicAddress; - if (publicAddr.isEmpty() or not publicAddr.hasPort()) + if (_ourAddress) { - throw std::runtime_error( - "service node enabled but could not find a public IP to bind to; you need to set the " - "public-ip= and public-port= options"); + inboundLinks.push_back(LinksConfig::LinkInfo{ + _ourAddress->hostString(), _ourAddress->Family(), _ourAddress->getPort()}); } - inboundLinks.push_back(LinksConfig::LinkInfo{"0.0.0.0", AF_INET, *publicAddr.getPort()}); + else + throw std::runtime_error{ + "service node enabled but could not find a public IP to bind to; you need to set the " + "public-ip= and public-port= options"}; } // create inbound links, if we are a service node @@ -1253,6 +1251,12 @@ namespace llarp return false; } + if (not InitOutboundLinks()) + { + LogError("failed to init outbound links"); + return false; + } + if (IsServiceNode()) { if (!SaveRC()) @@ -1563,6 +1567,22 @@ namespace llarp return ep and ep->HasExit(); } + std::optional> + Router::OurPublicIP() const + { + if (_ourAddress) + return _ourAddress->getIP(); + std::optional> found; + _linkManager.ForEachInboundLink([&found](const auto& link) { + if (found) + return; + AddressInfo ai; + if (link->GetOurAddressInfo(ai)) + found = ai.IP(); + }); + return found; + } + bool Router::InitOutboundLinks() { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index afe9ec163..f8578c024 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -102,6 +102,9 @@ namespace llarp return _dht; } + std::optional> + OurPublicIP() const override; + util::StatusObject ExtractStatus() const override; diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 0675f98e5..9083f213d 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -39,11 +39,6 @@ namespace llarp [](RouterConfig& self) { return self.m_dataDir.c_str(); }, [](RouterConfig& self, std::string dir) { self.m_dataDir = dir; }) .def_readwrite("blockBogons", &RouterConfig::m_blockBogons) - .def( - "overrideAddress", - [](RouterConfig& self, std::string addr) { - self.m_publicAddress = llarp::IpAddress(addr); - }) .def_readwrite("workerThreads", &RouterConfig::m_workerThreads) .def_readwrite("numNetThreads", &RouterConfig::m_numNetThreads) .def_readwrite("JobQueueSize", &RouterConfig::m_JobQueueSize);