From b77d6bf87d010d679438006388407bcdc3868100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dcaro=20Arag=C3=A3o?= Date: Thu, 13 Aug 2020 18:29:47 -0300 Subject: [PATCH] [GCS] Improve fallback for getting local valid IP for GCS server (#10004) --- src/ray/common/network_util.cc | 162 ++++++++++++++++++++++++++++----- src/ray/common/network_util.h | 59 +++++++++++- 2 files changed, 197 insertions(+), 24 deletions(-) diff --git a/src/ray/common/network_util.cc b/src/ray/common/network_util.cc index 44a52f6fb..9494b9ebc 100644 --- a/src/ray/common/network_util.cc +++ b/src/ray/common/network_util.cc @@ -20,53 +20,169 @@ std::string GetValidLocalIp(int port, int64_t timeout_ms) { AsyncClient async_client; boost::system::error_code error_code; std::string address; + const std::string localhost_ip = "127.0.0.1"; bool is_timeout; + if (async_client.Connect(kPublicDNSServerIp, kPublicDNSServerPort, timeout_ms, &is_timeout, &error_code)) { address = async_client.GetLocalIPAddress(); } else { - address = "127.0.0.1"; + address = localhost_ip; if (is_timeout || error_code == boost::system::errc::host_unreachable) { boost::asio::ip::detail::endpoint primary_endpoint; - boost::asio::io_context io_context; - boost::asio::ip::tcp::resolver resolver(io_context); - boost::asio::ip::tcp::resolver::query query( - boost::asio::ip::host_name(), "", - boost::asio::ip::resolver_query_base::flags::v4_mapped); - boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query, error_code); - boost::asio::ip::tcp::resolver::iterator end; // End marker. - if (!error_code) { - while (iter != end) { - boost::asio::ip::tcp::endpoint ep = *iter; - if (ep.address().is_v4() && !ep.address().is_loopback() && - !ep.address().is_multicast()) { - primary_endpoint.address(ep.address()); - primary_endpoint.port(ep.port()); + bool success = false; - AsyncClient client; - if (client.Connect(primary_endpoint.address().to_string(), port, timeout_ms, - &is_timeout)) { - break; - } + if (!error_code) { + std::vector ip_candidates = + NetIf::GetValidLocalIpCandidates(); + + for (const auto &ip_candidate : ip_candidates) { + primary_endpoint.address(ip_candidate); + + AsyncClient client; + if (client.Connect(primary_endpoint.address().to_string(), port, timeout_ms, + &is_timeout)) { + success = true; + break; } - iter++; } } else { RAY_LOG(WARNING) << "Failed to resolve ip address, error = " << strerror(error_code.value()); - iter = end; } - if (iter != end) { + if (success) { address = primary_endpoint.address().to_string(); } } } + if (address == localhost_ip) { + RAY_LOG(ERROR) << "Failed to find other valid local IP. Using " << localhost_ip + << ", not possible to go distributed!"; + } + return address; } +namespace NetIf { +std::vector prefixes_and_priorities = { + {"e", Priority::kVeryHigh}, // (Ethernet) Ex: eth0, enp7s0f1, ens160, en0 + + {"w", Priority::kHigh}, // (WiFi) Ex: wlp0s20f3, wlp2s0 + + {"br", Priority::kNormal}, // (Manual bridge) Ex: br0 + {"ap", Priority::kNormal}, // (Access point) Ex: ap0 + + {"tun", Priority::kExclude}, // (VPN) Ex: tun0 + {"tap", Priority::kExclude}, // (VPN) Ex: tap0 + {"virbr", Priority::kExclude}, // (VM bridge) Ex: virbr0 + {"vm", Priority::kExclude}, // (VM) Ex: vmnet1 + {"vbox", Priority::kExclude}, // (VM) Ex: vboxnet1 + {"ppp", Priority::kExclude}, // (P2P) Ex: ppp0 + {"pan", Priority::kExclude}, // (Bluetooth) Ex: pan0 + {"br-", Priority::kExclude}, // (Docker bridge) Ex: br-3681c4c3d645 + {"veth", Priority::kExclude}, // (Docker virtual interface) Ex: veth7175b67 + {"docker", Priority::kExclude}, // (Docker virtual interface) Ex: docker0 +}; + +#ifndef _WIN32 + +std::vector GetValidLocalIpCandidates() { + struct ifaddrs *ifs_info = nullptr; + getifaddrs(&ifs_info); + + std::vector ifnames_and_ips; + + struct ifaddrs *if_info = nullptr; + for (if_info = ifs_info; if_info != nullptr; if_info = if_info->ifa_next) { + if (if_info->ifa_addr->sa_family == AF_INET) { + void *addr = &((struct sockaddr_in *)if_info->ifa_addr)->sin_addr; + + char ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, addr, ip, INET_ADDRSTRLEN); + + ifnames_and_ips.push_back( + std::make_pair(std::string(if_info->ifa_name), std::string(ip))); + } + } + freeifaddrs(ifs_info); + + // Bigger prefixes must be tested first in CompNameAndIps + std::sort(prefixes_and_priorities.begin(), prefixes_and_priorities.end(), + CompPrefixLen); + + // Filter out interfaces with small possibility of being desired to be used to serve + std::sort(ifnames_and_ips.begin(), ifnames_and_ips.end(), CompNamesAndIps); + while (GetPriority(ifnames_and_ips.back().first) == Priority::kExclude) { + ifnames_and_ips.pop_back(); + } + + std::vector candidates; + for (const auto &ifname_and_ip : ifnames_and_ips) { + boost::asio::ip::address boost_addr = + boost::asio::ip::make_address(ifname_and_ip.second); + + if (!boost_addr.is_loopback()) { + candidates.push_back(boost_addr); + } + } + + return candidates; +} + +#else + +std::vector GetValidLocalIpCandidates() { + boost::asio::ip::detail::endpoint primary_endpoint; + boost::asio::io_context io_context; + boost::asio::ip::tcp::resolver resolver(io_context); + boost::asio::ip::tcp::resolver::query query( + boost::asio::ip::host_name(), "", + boost::asio::ip::resolver_query_base::flags::v4_mapped); + + boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; // End marker. + + std::vector candidates; + for (; iter != end; iter++) { + boost::asio::ip::tcp::endpoint ep = iter->endpoint(); + + if (ep.address().is_v4() && !ep.address().is_loopback() && + !ep.address().is_multicast()) { + candidates.push_back(ep.address()); + } + } + + return candidates; +} + +#endif + +Priority GetPriority(const std::string &if_name) { + for (const auto &prefix_and_priority : prefixes_and_priorities) { + if (NameStartsWith(if_name, prefix_and_priority.first)) { + return prefix_and_priority.second; + } + } + + return Priority::kExclude; +} + +bool CompNamesAndIps(const NameAndIp &left, const NameAndIp &right) { + return GetPriority(left.first) < GetPriority(right.first); +} + +bool CompPrefixLen(const PrefixAndPriority &left, const PrefixAndPriority &right) { + return left.first.size() > right.first.size(); +} + +bool NameStartsWith(const std::string &name, const std::string &prefix) { + return name.compare(0, prefix.size(), prefix) == 0; +} +} // namespace NetIf + bool Ping(const std::string &ip, int port, int64_t timeout_ms) { AsyncClient client; bool is_timeout; diff --git a/src/ray/common/network_util.h b/src/ray/common/network_util.h index 8e035d057..f7dcc7c3b 100644 --- a/src/ray/common/network_util.h +++ b/src/ray/common/network_util.h @@ -19,6 +19,15 @@ #include #include #include +#include + +#ifndef _WIN32 + +#include +#include +#include + +#endif #include "ray/common/constants.h" @@ -112,7 +121,7 @@ class AsyncClient { /// /// \param port The port that the local ip is listening on. /// \param timeout_ms The maximum wait time in milliseconds. -/// \return A valid local ip. +/// \return a valid local ip. std::string GetValidLocalIp(int port, int64_t timeout_ms); /// A helper function to test whether target rpc server is valid. @@ -124,3 +133,51 @@ std::string GetValidLocalIp(int port, int64_t timeout_ms); bool Ping(const std::string &ip, int port, int64_t timeout_ms); bool CheckFree(int port); + +/// \namespace NetIf +/// +/// Namespace with implementations of helper function to get valid IPs +/// from network interfaces +namespace NetIf { +/// Priority of a IPs from a given interface to be chosen to serve something +enum class Priority { kVeryHigh, kHigh, kNormal, kExclude }; + +/// To keep one IP and its interface +typedef std::pair NameAndIp; + +/// To keep interface names prefixes and its priorities +typedef std::pair PrefixAndPriority; + +/// Interface name prefix and its priority +extern std::vector prefixes_and_priorities; + +/// A helper function to get IPs from local interfaces. +/// It also filters out IPs from interfaces with priority Priority::kExclude +/// and sort the IPs based on its interfaces priority. +/// If running on Windows, uses boost to try to resolve hostname +/// and don't filter candidates. +/// +/// \return a vector with valid local IP candidates that were not filtered out +std::vector GetValidLocalIpCandidates(); + +/// Based on the prefix of the interface name, returns a level of priority. +/// +/// \param if_name the name of the interface to be tested. +/// \return the priority of the interface. +Priority GetPriority(const std::string &if_name); + +/// Helper function to be used with std::sort. +/// Lowest priority comes first +bool CompNamesAndIps(const NameAndIp &left, const NameAndIp &right); + +/// Helper function to be used with std::sort. +/// Biggest prefix comes first +bool CompPrefixLen(const PrefixAndPriority &left, const PrefixAndPriority &right); + +/// A helper tiny function to check if the interface name has a given prefix. +/// +/// \param name the interface name to be checked. +/// \param prefix the prefix that will be looked in 'name'. +/// \return true if 'name' starts with 'prefix'. +bool NameStartsWith(const std::string &name, const std::string &prefix); +} // namespace NetIf