From 9bc96893fb23b68e0f9fa522603afb4024480b53 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 14 Sep 2025 16:39:58 -0500 Subject: [PATCH 1/3] Common/Network: Allow passing std::span to DHCPPacket::AddOption. --- Source/Core/Common/Network.cpp | 2 +- Source/Core/Common/Network.h | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index aed767a85e4..2d0e17492be 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -278,7 +278,7 @@ DHCPPacket::DHCPPacket(const std::vector& data) } } -void DHCPPacket::AddOption(u8 fnc, const std::vector& params) +void DHCPPacket::AddOption(u8 fnc, std::span params) { if (params.size() > 255) return; diff --git a/Source/Core/Common/Network.h b/Source/Core/Common/Network.h index 8f446d7a381..91ed4c25133 100644 --- a/Source/Core/Common/Network.h +++ b/Source/Core/Common/Network.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -180,7 +181,15 @@ struct DHCPPacket { DHCPPacket(); DHCPPacket(const std::vector& data); - void AddOption(u8 fnc, const std::vector& params); + + void AddOption(u8 fnc, std::span params); + + template + void AddOption(u8 fnc, const u8 (&data)[N]) + { + AddOption(fnc, std::span(data)); + } + std::vector Build() const; DHCPBody body; From 930af97d950d2b00f7d63701d1ecab2eeda9c73a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 14 Sep 2025 18:41:52 -0500 Subject: [PATCH 2/3] Common/Network: Add GetSubnetMask function. --- Source/Core/Common/Network.cpp | 65 +++++++++++++++++++++++++++++++++- Source/Core/Common/Network.h | 1 + 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index 2d0e17492be..b4b50bb5a1c 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -9,17 +9,21 @@ #include #ifndef _WIN32 +#include +#include +#include #include -#include #include #include #else #include +#include #endif #include #include "Common/BitUtils.h" +#include "Common/Buffer.h" #include "Common/CommonFuncs.h" #include "Common/Random.h" #include "Common/StringUtil.h" @@ -101,6 +105,65 @@ std::optional StringToBluetoothAddress(std::string_view str) return std::bit_cast(*result); } +std::optional GetSubnetMask(const IPAddress& address) +{ + std::optional result; + + // Android apparently does not provide getifaddrs. + // TODO: Provide an android implementation. +#if !defined(ANDROID) + const auto target_in_addr = std::bit_cast(address); + +#if defined(_WIN32) + static constexpr ULONG flags = GAA_FLAG_INCLUDE_PREFIX; + + // This is basically what Microsoft recommends. + Common::UniqueBuffer buffer(15000 / sizeof(IP_ADAPTER_ADDRESSES)); + auto buffer_size = ULONG(buffer.size() * sizeof(IP_ADAPTER_ADDRESSES)); + + if (GetAdaptersAddresses(AF_INET, flags, nullptr, buffer.data(), &buffer_size) != NO_ERROR) + return result; + + for (auto* addr = buffer.data(); addr != nullptr; addr = addr->Next) + { + for (auto* ua = addr->FirstUnicastAddress; ua != nullptr; ua = ua->Next) + { + auto* const sa = reinterpret_cast(ua->Address.lpSockaddr); + if (sa->sin_addr.s_addr != target_in_addr.s_addr) + continue; + + result = std::bit_cast(htonl(u32(-1) << (32 - ua->OnLinkPrefixLength))); + break; + } + } +#else + ifaddrs* ifaddrs{}; + if (getifaddrs(&ifaddrs) == -1) + return result; + + for (auto* ifa = ifaddrs; ifa != nullptr; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr->sa_family != AF_INET || ifa->ifa_addr == nullptr || + ifa->ifa_netmask == nullptr) + { + continue; + } + + auto* const ifaddr = reinterpret_cast(ifa->ifa_addr); + if (ifaddr->sin_addr.s_addr != target_in_addr.s_addr) + continue; + + auto* const mask = reinterpret_cast(ifa->ifa_netmask); + result = std::bit_cast(mask->sin_addr.s_addr); + break; + } + + freeifaddrs(ifaddrs); +#endif +#endif + return result; +} + EthernetHeader::EthernetHeader() = default; EthernetHeader::EthernetHeader(u16 ether_type) : ethertype(htons(ether_type)) diff --git a/Source/Core/Common/Network.h b/Source/Core/Common/Network.h index 91ed4c25133..6e415a48298 100644 --- a/Source/Core/Common/Network.h +++ b/Source/Core/Common/Network.h @@ -281,6 +281,7 @@ std::optional StringToMacAddress(std::string_view mac_string); std::string BluetoothAddressToString(BluetoothAddress bdaddr); std::optional StringToBluetoothAddress(std::string_view str); +std::optional GetSubnetMask(const IPAddress& address); u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0); u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data, u16 length, u8 protocol); From aad3c87b172b680583832bf23bdb00a0b46f8c72 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 14 Sep 2025 16:42:04 -0500 Subject: [PATCH 3/3] Core/HW: Make BBA HLE detect the network prefix length (i.e. subnet mask) instead of assuming /24. --- Source/Core/Common/Network.cpp | 2 +- Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp | 63 ++++++++++++++------ Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h | 1 + 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index b4b50bb5a1c..d51ce3fc40d 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -110,7 +110,7 @@ std::optional GetSubnetMask(const IPAddress& address) std::optional result; // Android apparently does not provide getifaddrs. - // TODO: Provide an android implementation. + // TODO: Provide an Android implementation. #if !defined(ANDROID) const auto target_in_addr = std::bit_cast(address); diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp index 305ab6ee0c9..93630a19a62 100644 --- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp +++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp @@ -5,6 +5,9 @@ #include #include + +#include + #include "SFML/Network/IpAddress.hpp" #include "SFML/Network/Socket.hpp" @@ -21,7 +24,7 @@ #include "Common/MsgHandler.h" #include "Common/Network.h" #include "Common/ScopeGuard.h" -#include "Core/HW/EXI/EXI_Device.h" + #include "Core/HW/EXI/EXI_DeviceEthernet.h" namespace @@ -84,16 +87,39 @@ bool CEXIETHERNET::BuiltInBBAInterface::Activate() // Workaround to get the host IP (might not be accurate) // TODO: Fix the JNI crash and use GetSystemDefaultInterface() // - https://pastebin.com/BFpmnxby (see https://dolp.in/pr10920) - const u32 ip = sf::IpAddress::resolve(m_local_ip) - .value_or(sf::IpAddress::getLocalAddress().value_or(sf::IpAddress::Any)) - .toInteger(); + const auto ipaddr = sf::IpAddress::resolve(m_local_ip) + .value_or(sf::IpAddress::getLocalAddress().value_or(sf::IpAddress::Any)); + const u32 ip = ipaddr.toInteger(); + m_current_ip = htonl(ip); + + const auto mask_result = Common::GetSubnetMask(std::bit_cast(m_current_ip)); + if (!mask_result.has_value()) + ERROR_LOG_FMT(SP1, "Failed to determine network prefix length, assuming 24."); + + // Fallback to a /24 network prefix. + static constexpr Common::IPAddress fallback_subnet_mask = {0xff, 0xff, 0xff, 0x00}; + m_subnet_mask = mask_result.value_or(fallback_subnet_mask); + + const u32 netmask = ntohl(std::bit_cast(m_subnet_mask)); + m_current_mac = Common::BitCastPtr(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]); m_arp_table[m_current_ip] = m_current_mac; - m_router_ip = (m_current_ip & 0xFFFFFF) | 0x01000000; + + // The router IP is assumed to be the first IP on the network. + // TODO: Properly determine the router IP. + m_router_ip = htonl((ip & netmask) | 1u); m_router_mac = Common::GenerateMacAddress(Common::MACConsumer::BBA); m_arp_table[m_router_ip] = m_router_mac; + const auto network_prefix_length = std::countl_one(netmask); + INFO_LOG_FMT(SP1, "Network: {}/{} IP: {} Router: {}", + fmt::join(Common::AsU8Span(htonl(ip & netmask)), "."), network_prefix_length, + fmt::join(Common::AsU8Span(m_current_ip), "."), + fmt::join(Common::AsU8Span(m_router_ip), ".")); + INFO_LOG_FMT(SP1, "MAC: {} Router MAC: {}", Common::MacAddressToString(m_current_mac), + Common::MacAddressToString(m_router_mac)); + m_network_ref.Clear(); (void)m_upnp_httpd.listen(Common::SSDP_PORT, sf::IpAddress(ip)); @@ -219,10 +245,9 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(const Common::UDPPacket& pack to.sin_family = IPPROTO_UDP; to.sin_port = udp_header.source_port; - const u8* router_ip_ptr = reinterpret_cast(&m_router_ip); - const std::vector ip_part(router_ip_ptr, router_ip_ptr + sizeof(m_router_ip)); - - const std::vector timeout_24h = {0, 1, 0x51, 0x80}; + const auto router_ip = Common::AsU8Span(m_router_ip); + const u32 broadcast_ip = m_current_ip | ~std::bit_cast(m_subnet_mask); + const u8 timeout_24h[] = {0, 1, 0x51, 0x80}; Common::DHCPPacket reply; reply.body = Common::DHCPBody(request.transaction_id, m_current_mac, m_current_ip, m_router_ip); @@ -233,16 +258,16 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(const Common::UDPPacket& pack (dhcp.options.size() == 0 || dhcp.options[0].size() < 2 || dhcp.options[0].at(2) == 1) ? reply.AddOption(53, {2}) : // default, send a suggestion reply.AddOption(53, {5}); - reply.AddOption(54, ip_part); // dhcp server ip - reply.AddOption(51, timeout_24h); // lease time 24h - reply.AddOption(58, timeout_24h); // renewal time - reply.AddOption(59, timeout_24h); // rebind time - reply.AddOption(1, {255, 255, 255, 0}); // submask - reply.AddOption(28, {ip_part[0], ip_part[1], ip_part[2], 255}); // broadcast ip - reply.AddOption(6, ip_part); // dns server - reply.AddOption(15, {0x6c, 0x61, 0x6e}); // domain name "lan" - reply.AddOption(3, ip_part); // router ip - reply.AddOption(255, {}); // end + reply.AddOption(54, router_ip); // dhcp server ip + reply.AddOption(51, timeout_24h); // lease time 24h + reply.AddOption(58, timeout_24h); // renewal time + reply.AddOption(59, timeout_24h); // rebind time + reply.AddOption(1, m_subnet_mask); // submask + reply.AddOption(28, Common::AsU8Span(broadcast_ip)); // broadcast ip + reply.AddOption(6, router_ip); // dns server + reply.AddOption(15, {0x6c, 0x61, 0x6e}); // domain name "lan" + reply.AddOption(3, router_ip); // router ip + reply.AddOption(255, {}); // end const Common::UDPPacket response(m_current_mac, m_router_mac, from, to, reply.Build()); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index 21852f341c9..bba6b62b071 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -447,6 +447,7 @@ private: std::mutex m_mtx; std::string m_local_ip; u32 m_current_ip = 0; + Common::IPAddress m_subnet_mask{}; Common::MACAddress m_current_mac{}; u32 m_router_ip = 0; Common::MACAddress m_router_mac{};