diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 8e8f099be8..be780d3127 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -71,22 +71,12 @@ std::vector GetAvailableNetworkInterfaces() { gw = reinterpret_cast(a->FirstGatewayAddress->Address.lpSockaddr) ->sin_addr; - HostAdapterKind kind = HostAdapterKind::Ethernet; - switch (a->IfType) { - case IF_TYPE_IEEE80211: // 802.11 Wi-Fi - kind = HostAdapterKind::Wifi; - break; - default: - kind = HostAdapterKind::Ethernet; - break; - } - result.emplace_back(Network::NetworkInterface{ .name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}), .ip_address = ip, .subnet_mask = mask, .gateway = gw, - .kind = kind + .kind = (a->IfType == IF_TYPE_IEEE80211 ? HostAdapterKind::Wifi : HostAdapterKind::Ethernet) }); } @@ -99,155 +89,85 @@ std::vector GetAvailableNetworkInterfaces() { struct ifaddrs* ifaddr = nullptr; if (getifaddrs(&ifaddr) != 0) { - LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", - std::strerror(errno)); + LOG_ERROR(Network, "getifaddrs: {}", std::strerror(errno)); return {}; } - std::vector result; - - for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { - continue; - } - - if (ifa->ifa_addr->sa_family != AF_INET) { - continue; - } - - if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { - continue; - } - + // TODO: This is still horrible, it was worse before (somehow) + struct RoutingEntry { + std::string iface_name; + u32 dest; + u32 gateway; + u32 flags; + }; + std::vector routes{}; #ifdef ANDROID - // On Android, we can't reliably get gateway info from /proc/net/route - // Just use 0 as the gateway address - result.emplace_back(Network::NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{std::bit_cast(*ifa->ifa_addr).sin_addr}, - .subnet_mask{std::bit_cast(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = 0}} - }); + // Even through Linux based, we can't reliably obtain routing information from there :( #else - u32 gateway{}; - - std::ifstream file{"/proc/net/route"}; - if (!file.is_open()) { - LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); - - // Solaris defines s_addr as a macro, can't use special C++ shenanigans here - in_addr gateway_0; - gateway_0.s_addr = gateway; - result.emplace_back(Network::NetworkInterface{ - .name = ifa->ifa_name, - .ip_address = std::bit_cast(*ifa->ifa_addr).sin_addr, - .subnet_mask = std::bit_cast(*ifa->ifa_netmask).sin_addr, - .gateway = gateway_0 - }); - continue; - } - - // ignore header - file.ignore((std::numeric_limits::max)(), '\n'); - - bool gateway_found = false; - + if (std::ifstream file("/proc/net/route"); file.is_open()) { + file.ignore((std::numeric_limits::max)(), '\n'); //ignore header for (std::string line; std::getline(file, line);) { std::istringstream iss{line}; - - std::string iface_name; - iss >> iface_name; - if (iface_name != ifa->ifa_name) { - continue; - } - - iss >> std::hex; - - u32 dest{}; - iss >> dest; - if (dest != 0) { - // not the default route - continue; - } - - iss >> gateway; - - u16 flags{}; - iss >> flags; - - // flag RTF_GATEWAY (defined in ) - if ((flags & 0x2) == 0) { - continue; - } - - gateway_found = true; - break; - } - - if (!gateway_found) { - gateway = 0; + RoutingEntry info{}; + iss >> info.iface_name >> std::hex + >> info.dest >> info.gateway >> info.flags; + routes.emplace_back(info); } - - in_addr gateway_0; - gateway_0.s_addr = gateway; - result.emplace_back(Network::NetworkInterface{ + } else { + LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0"); + } +#endif + std::vector ifaces; + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */ + || ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */ + || (ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) /* Not loopback */ + continue; + // Just use 0 as the gateway address if not found OR routes are empty :) + auto const it = std::ranges::find_if(routes, [&ifa](auto const& e) { + return e.iface_name == ifa->ifa_name + && e.dest == 0 // not the default route + && (e.flags & 0x02) != 0; // flag RTF_GATEWAY (defined in ) + }); + in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here + gw.s_addr = it != routes.end() ? it->gateway : 0; + ifaces.emplace_back(Network::NetworkInterface{ .name = ifa->ifa_name, .ip_address = std::bit_cast(*ifa->ifa_addr).sin_addr, .subnet_mask = std::bit_cast(*ifa->ifa_netmask).sin_addr, - .gateway = gateway_0 + .gateway = gw }); -#endif // ANDROID } - freeifaddrs(ifaddr); - return result; + return ifaces; } #endif // _WIN32 std::optional GetSelectedNetworkInterface() { - - const auto& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.empty()) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); - return std::nullopt; - } - - #ifdef __ANDROID__ - if (selected_network_interface.empty()) { - return network_interfaces[0]; - } - #endif - - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res == network_interfaces.end()) { + auto const& sel_if = Settings::values.network_interface.GetValue(); + if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) { + if (sel_if.empty()) + return ifaces[0]; + if (auto const res = std::ranges::find_if(ifaces, [&sel_if](const auto& iface) { + return iface.name == sel_if; + }); res != ifaces.end()) + return *res; // Only print the error once to avoid log spam static bool print_error = true; if (print_error) { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", - selected_network_interface); + LOG_WARNING(Network, "Couldn't find interface \"{}\"", sel_if); print_error = false; } - return std::nullopt; } - - return *res; + LOG_WARNING(Network, "No interfaces"); + return std::nullopt; } void SelectFirstNetworkInterface() { - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - - if (network_interfaces.empty()) { - return; - } - - Settings::values.network_interface.SetValue(network_interfaces[0].name); + if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) + Settings::values.network_interface.SetValue(ifaces[0].name); } } // namespace Network