aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-02-10 22:17:24 +0100
committercrueter <crueter@eden-emu.dev>2026-02-10 22:17:24 +0100
commit6b3f8e1b62f17650ed60a83dd30bedbe86268169 (patch)
tree1e0b6bae896e9496440a449d1e37567457952c41 /src
parentaacc40254c6e0b93f66c1189cd09a01b9203a0b9 (diff)
[core/internal_network] cleanup network interface code a bit (#2905)
Signed-off-by: lizzie <lizzie@eden-emu.dev> Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2905 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: DraVee <dravee@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/internal_network/network_interface.cpp278
-rw-r--r--src/core/internal_network/wifi_scanner.cpp109
-rw-r--r--src/core/internal_network/wifi_scanner_dummy.cpp11
4 files changed, 203 insertions, 205 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 14eb331d24..8bcb66ac35 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1131,7 +1131,6 @@ add_library(core STATIC
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
internal_network/sockets.h
- internal_network/wifi_scanner.cpp
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
launch_timestamp_cache.h
@@ -1170,9 +1169,12 @@ add_library(core STATIC
tools/renderdoc.h)
if (ENABLE_WIFI_SCAN)
- # find_package(libiw REQUIRED)
- target_compile_definitions(core PRIVATE ENABLE_WIFI_SCAN)
- target_link_libraries(core PRIVATE iw)
+ target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
+ if (PLATFORM_LINUX)
+ target_link_libraries(core PRIVATE iw)
+ endif()
+else()
+ target_sources(core PRIVATE internal_network/wifi_scanner_dummy.cpp)
endif()
if (WIN32)
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 8e8f099be8..48be1e2dc4 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
@@ -9,21 +9,39 @@
#include <ranges>
#include <bit>
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "common/settings.h"
-#include "common/string_util.h"
-#include "core/internal_network/emu_net_state.h"
-#include "core/internal_network/network_interface.h"
-
#ifdef _WIN32
+#include <winsock2.h>
+#include <windows.h>
#include <iphlpapi.h>
-#else
+#elif defined(__linux__) || defined(__ANDROID__)
#include <cerrno>
#include <ifaddrs.h>
#include <net/if.h>
+#elif defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netdb.h>
#endif
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "common/string_util.h"
+#include "core/internal_network/emu_net_state.h"
+#include "core/internal_network/network_interface.h"
+
namespace Network {
#ifdef _WIN32
@@ -71,22 +89,12 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(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)
});
}
@@ -96,158 +104,146 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#else
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
+#if defined(__ANDROID__) || defined(__linux__)
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<Network::NetworkInterface> 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;
- }
-
-#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<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
- .subnet_mask{std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
- .gateway{in_addr{.s_addr = 0}}
- });
+ // TODO: This is still horrible, it was worse before (somehow)
+ struct RoutingEntry {
+ std::string iface_name;
+ u32 dest;
+ u32 gateway;
+ u32 flags;
+ };
+ std::vector<RoutingEntry> routes{};
+#ifdef __ANDROID__
+ // 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<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
- .subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
- .gateway = gateway_0
- });
- continue;
- }
-
- // ignore header
- file.ignore((std::numeric_limits<std::streamsize>::max)(), '\n');
-
- bool gateway_found = false;
-
+ if (std::ifstream file("/proc/net/route"); file.is_open()) {
+ file.ignore((std::numeric_limits<std::streamsize>::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 <linux/route.h>)
- 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<Network::NetworkInterface> 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 <linux/route.h>)
+ });
+ 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<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
- .gateway = gateway_0
+ .gateway = gw
});
-#endif // ANDROID
}
-
freeifaddrs(ifaddr);
- return result;
-}
-
-#endif // _WIN32
-
-std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
+ return ifaces;
+#elif defined(__FreeBSD__)
+ std::vector<Network::NetworkInterface> ifaces;
+ int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
+ if (fd < 0) {
+ LOG_ERROR(Network, "socket: {}", std::strerror(errno));
+ return {};
+ }
- 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;
+ size_t bufsz = 0;
+ int mib[6] = {
+ CTL_NET, PF_ROUTE, 0,
+ AF_UNSPEC, NET_RT_IFLIST, 0
+ };
+ if (::sysctl(mib, sizeof(mib) / sizeof(mib[0]), nullptr, &bufsz, nullptr, 0) < 0) {
+ LOG_ERROR(Network, "sysctl.1: {}", std::strerror(errno));
+ ::close(fd);
+ return {};
+ }
+ std::vector<char> buf(bufsz);
+ if (::sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf.data(), &bufsz, nullptr, 0) < 0) {
+ LOG_ERROR(Network, "sysctl.2: {}", std::strerror(errno));
+ ::close(fd);
+ return {};
}
- #ifdef __ANDROID__
- if (selected_network_interface.empty()) {
- return network_interfaces[0];
+ struct rt_msghdr const *rtm = NULL;
+ for (char *next = buf.data(); next < buf.data() + bufsz; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr const *)next;
+ if (rtm->rtm_type == RTM_IFINFO) {
+ struct if_msghdr const* ifm = (struct if_msghdr const *)rtm;
+ size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
+ char const* p = (char const*)(ifm + 1);
+
+ Network::NetworkInterface iface{};
+ for (size_t i = 0; i < RTAX_MAX; i++)
+ if ((ifm->ifm_addrs & (1 << i)) != 0) {
+ struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
+ if (msglen == 0 || msglen < SA_SIZE(sa))
+ break;
+ if (i == RTA_NETMASK && sa->sa_family == AF_LINK) {
+ size_t namelen = 0;
+ struct sockaddr_dl const* sdl = reinterpret_cast<struct sockaddr_dl const*>(sa);
+ ::link_ntoa_r(sdl, nullptr, &namelen);
+ iface.name = std::string(namelen, ' ');
+ ::link_ntoa_r(sdl, iface.name.data(), &namelen);
+ std::memcpy(&iface.ip_address, sa, sizeof(struct sockaddr_in));
+ }
+ msglen -= SA_SIZE(sa);
+ p += SA_SIZE(sa);
+ }
+ ifaces.push_back(iface);
}
- #endif
+ }
+ ::close(fd);
+ return ifaces;
+#else
+ return {};
+#endif
+}
- const auto res =
- std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
- return iface.name == selected_network_interface;
- });
+#endif // _WIN32
- if (res == network_interfaces.end()) {
+std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
+ 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
diff --git a/src/core/internal_network/wifi_scanner.cpp b/src/core/internal_network/wifi_scanner.cpp
index 127221099f..1b97f69177 100644
--- a/src/core/internal_network/wifi_scanner.cpp
+++ b/src/core/internal_network/wifi_scanner.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <chrono>
@@ -6,11 +6,6 @@
#include <thread>
#include <vector>
-#include "common/logging/log.h"
-#include "core/internal_network/wifi_scanner.h"
-
-using namespace std::chrono_literals;
-
#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
@@ -18,16 +13,30 @@ using namespace std::chrono_literals;
#ifdef _MSC_VER
#pragma comment(lib, "wlanapi.lib")
#endif
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <iwlib.h>
+#elif defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net80211/ieee80211_ioctl.h>
#endif
+#include "common/logging/log.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/wifi_scanner.h"
+
+using namespace std::chrono_literals;
+
namespace Network {
-#ifdef ENABLE_WIFI_SCAN
#ifdef _WIN32
static u8 QualityToPercent(DWORD q) {
- return static_cast<u8>(q);
+ return u8(q);
}
-static std::vector<Network::ScanData> ScanWifiWin(std::chrono::milliseconds deadline) {
+std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) {
std::vector<Network::ScanData> out;
HANDLE hClient{};
@@ -85,38 +94,16 @@ static std::vector<Network::ScanData> ScanWifiWin(std::chrono::milliseconds dead
WlanCloseHandle(hClient, nullptr);
return out;
}
-#endif /* _WIN32 */
-
-#if defined(__linux__) && !defined(_WIN32) && !defined(ANDROID)
-#include <iwlib.h>
-
+#elif defined(__linux__) && !defined(__ANDROID__)
static u8 QualityToPercent(const iwrange& r, const wireless_scan* ws) {
const iw_quality qual = ws->stats.qual;
const int lvl = qual.level;
const int max = r.max_qual.level ? r.max_qual.level : 100;
- return static_cast<u8>(std::clamp(100 * lvl / max, 0, 100));
-}
-
-static int wifi_callback(int skfd, char* ifname, char* args[], int count)
-{
- iwrange range;
-
- int res = iw_get_range_info(skfd, ifname, &range);
-
- LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res);
-
- if (res >= 0) {
- strncpy(args[0], ifname, IFNAMSIZ - 1);
- args[0][IFNAMSIZ - 1] = 0;
-
- return 1;
- }
-
- return 0;
+ return u8(std::clamp(100 * lvl / max, 0, 100));
}
// TODO(crueter, Maufeat): Check if driver supports wireless extensions, fallback to nl80211 if not
-static std::vector<Network::ScanData> ScanWifiLinux(std::chrono::milliseconds deadline) {
+std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) {
std::vector<Network::ScanData> out;
int sock = iw_sockets_open();
if (sock < 0) {
@@ -127,7 +114,17 @@ static std::vector<Network::ScanData> ScanWifiLinux(std::chrono::milliseconds de
char ifname[IFNAMSIZ] = {0};
char *args[1] = {ifname};
- iw_enum_devices(sock, &wifi_callback, args, 0);
+ iw_enum_devices(sock, [](int skfd, char* ifname, char* args[], int count) -> int {
+ iwrange range;
+ int res = iw_get_range_info(skfd, ifname, &range);
+ LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res);
+ if (res >= 0) {
+ strncpy(args[0], ifname, IFNAMSIZ - 1);
+ args[0][IFNAMSIZ - 1] = 0;
+ return 1;
+ }
+ return 0;
+ }, args, 0);
if (strlen(ifname) == 0) {
LOG_WARNING(Network, "No wireless interface found");
@@ -153,20 +150,19 @@ static std::vector<Network::ScanData> ScanWifiLinux(std::chrono::milliseconds de
out.clear();
for (auto* ws = head.result; ws; ws = ws->next) {
- if (!ws->b.has_essid)
- continue;
+ if (ws->b.has_essid) {
+ Network::ScanData sd{};
+ sd.ssid_len = static_cast<u8>(std::min<int>(ws->b.essid_len, 0x20));
+ std::memcpy(sd.ssid, ws->b.essid, sd.ssid_len);
+ sd.quality = QualityToPercent(range, ws);
+ sd.flags |= 1;
+ if (ws->b.has_key)
+ sd.flags |= 2;
- Network::ScanData sd{};
- sd.ssid_len = static_cast<u8>(std::min<int>(ws->b.essid_len, 0x20));
- std::memcpy(sd.ssid, ws->b.essid, sd.ssid_len);
- sd.quality = QualityToPercent(range, ws);
- sd.flags |= 1;
- if (ws->b.has_key)
- sd.flags |= 2;
-
- out.emplace_back(sd);
- char tmp[0x22]{};
- std::memcpy(tmp, sd.ssid, sd.ssid_len);
+ out.emplace_back(sd);
+ char tmp[0x22]{};
+ std::memcpy(tmp, sd.ssid, sd.ssid_len);
+ }
}
have = !out.empty();
}
@@ -174,21 +170,14 @@ static std::vector<Network::ScanData> ScanWifiLinux(std::chrono::milliseconds de
iw_sockets_close(sock);
return out;
}
-#endif /* linux */
-#endif
-
+#elif defined(__FreeBSD__)
std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) {
-#ifdef ENABLE_WIFI_SCAN
-#if defined(_WIN32)
- return ScanWifiWin(deadline);
-#elif defined(__linux__) && !defined(ANDROID)
- return ScanWifiLinux(deadline);
-#else
- return {}; // unsupported host, pretend no results
-#endif
+ return {}; // disabled, pretend no results
+}
#else
+std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) {
return {}; // disabled, pretend no results
-#endif
}
+#endif
} // namespace Network
diff --git a/src/core/internal_network/wifi_scanner_dummy.cpp b/src/core/internal_network/wifi_scanner_dummy.cpp
new file mode 100644
index 0000000000..ebc66b2e41
--- /dev/null
+++ b/src/core/internal_network/wifi_scanner_dummy.cpp
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <chrono>
+#include "core/internal_network/wifi_scanner.h"
+
+namespace Network {
+std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) {
+ return {}; // disabled, pretend no results
+}
+} // namespace Network