From d46792da945cbb965e88317fa0d4679ce0018950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 25 Jul 2025 12:20:23 +0200 Subject: [PATCH] Extract netmask and default gateway from host system (#3294) * Retrieve correct netmask in netctl * Retrieve correct default route in netctl (Linux) * Retrieve default gateway on mac * added default gateway for windows --------- Co-authored-by: georgemoralis --- src/core/libraries/network/net_util.cpp | 207 +++++++++++++++++++++++- src/core/libraries/network/net_util.h | 6 + src/core/libraries/network/netctl.cpp | 24 ++- 3 files changed, 233 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/network/net_util.cpp b/src/core/libraries/network/net_util.cpp index d0f0a81da..f07ddcebe 100644 --- a/src/core/libraries/network/net_util.cpp +++ b/src/core/libraries/network/net_util.cpp @@ -11,6 +11,7 @@ typedef int socklen_t; #else #include #include +#include #include #include #include @@ -21,8 +22,13 @@ typedef int socklen_t; typedef int net_socket; #endif #if defined(__APPLE__) -#include #include +#include +#endif +#if __linux__ +#include +#include +#include #endif #include @@ -100,6 +106,8 @@ bool NetUtilInternal::RetrieveEthernetAddr() { } } + close(sock); + if (success) { memcpy(ether_address.data(), ifr.ifr_hwaddr.sa_data, 6); return true; @@ -107,4 +115,201 @@ bool NetUtilInternal::RetrieveEthernetAddr() { #endif return false; } + +const std::string& NetUtilInternal::GetDefaultGateway() const { + return default_gateway; +} + +bool NetUtilInternal::RetrieveDefaultGateway() { + std::scoped_lock lock{m_mutex}; + +#ifdef _WIN32 + ULONG flags = GAA_FLAG_INCLUDE_GATEWAYS; + ULONG family = AF_INET; // Only IPv4 + ULONG buffer_size = 15000; + + std::vector buffer(buffer_size); + PIP_ADAPTER_ADDRESSES adapter_addresses = + reinterpret_cast(buffer.data()); + + DWORD result = GetAdaptersAddresses(family, flags, nullptr, adapter_addresses, &buffer_size); + if (result != NO_ERROR) { + return false; + } + + for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses; adapter != nullptr; + adapter = adapter->Next) { + if (adapter->OperStatus != IfOperStatusUp) + continue; + + IP_ADAPTER_GATEWAY_ADDRESS_LH* gateway = adapter->FirstGatewayAddress; + while (gateway) { + sockaddr* sa = gateway->Address.lpSockaddr; + if (sa->sa_family == AF_INET) { + char str[INET_ADDRSTRLEN]; + sockaddr_in* sa_in = reinterpret_cast(sa); + if (inet_ntop(AF_INET, &sa_in->sin_addr, str, sizeof(str))) { + this->default_gateway = str; + return true; + } + } + gateway = gateway->Next; + } + } + + return false; +#elif defined(__APPLE__) + // adapted from + // https://github.com/seladb/PcapPlusPlus/blob/a49a79e0b67b402ad75ffa96c1795def36df75c8/Pcap%2B%2B/src/PcapLiveDevice.cpp#L1236 + // route message struct for communication in APPLE device + struct BSDRoutingMessage { + struct rt_msghdr header; + char messageSpace[512]; + }; + + struct BSDRoutingMessage routingMessage; + // It creates a raw socket that can be used for routing-related operations + int sockfd = socket(PF_ROUTE, SOCK_RAW, 0); + if (sockfd < 0) { + return false; + } + memset(reinterpret_cast(&routingMessage), 0, sizeof(routingMessage)); + routingMessage.header.rtm_msglen = sizeof(struct rt_msghdr); + routingMessage.header.rtm_version = RTM_VERSION; + routingMessage.header.rtm_type = RTM_GET; + routingMessage.header.rtm_addrs = RTA_DST | RTA_NETMASK; + routingMessage.header.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + routingMessage.header.rtm_msglen += 2 * sizeof(sockaddr_in); + + if (write(sockfd, reinterpret_cast(&routingMessage), routingMessage.header.rtm_msglen) < + 0) { + return false; + } + + // Read the response from the route socket + if (read(sockfd, reinterpret_cast(&routingMessage), sizeof(routingMessage)) < 0) { + return false; + } + + struct in_addr* gateAddr = nullptr; + struct sockaddr* sa = nullptr; + char* spacePtr = (reinterpret_cast(&routingMessage.header + 1)); + auto rtmAddrs = routingMessage.header.rtm_addrs; + int index = 1; + auto roundUpClosestMultiple = [](int multiple, int num) { + return ((num + multiple - 1) / multiple) * multiple; + }; + while (rtmAddrs) { + if (rtmAddrs & 1) { + sa = reinterpret_cast(spacePtr); + if (index == RTA_GATEWAY) { + gateAddr = &((sockaddr_in*)sa)->sin_addr; + break; + } + spacePtr += sa->sa_len > 0 ? roundUpClosestMultiple(sizeof(uint32_t), sa->sa_len) + : sizeof(uint32_t); + } + index++; + rtmAddrs >>= 1; + } + + if (gateAddr == nullptr) { + return false; + } + + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, gateAddr, str, sizeof(str)); + this->default_gateway = str; + return true; + +#else + std::ifstream route{"/proc/net/route"}; + std::string line; + + std::getline(route, line); + while (std::getline(route, line)) { + std::istringstream iss{line}; + std::string iface, destination, gateway; + int flags; + + iss >> iface >> destination >> gateway >> std::hex >> flags; + + if (destination == "00000000") { + u64 default_gateway{}; + std::stringstream ss; + ss << std::hex << gateway; + ss >> default_gateway; + + char str[INET_ADDRSTRLEN]; + in_addr addr; + addr.s_addr = default_gateway; + inet_ntop(AF_INET, &addr, str, sizeof(str)); + this->default_gateway = str; + return true; + } + } +#endif + return false; +} + +const std::string& NetUtilInternal::GetNetmask() const { + return netmask; +} + +bool NetUtilInternal::RetrieveNetmask() { + std::scoped_lock lock{m_mutex}; + char netmaskStr[INET_ADDRSTRLEN]; + auto success = false; + +#ifdef _WIN32 + std::vector adapter_addresses(sizeof(IP_ADAPTER_ADDRESSES)); + ULONG size_infos = sizeof(IP_ADAPTER_ADDRESSES); + + if (GetAdaptersAddresses(AF_INET, 0, NULL, + reinterpret_cast(adapter_addresses.data()), + &size_infos) == ERROR_BUFFER_OVERFLOW) + adapter_addresses.resize(size_infos); + + if (GetAdaptersAddresses(AF_INET, 0, NULL, + reinterpret_cast(adapter_addresses.data()), + &size_infos) == NO_ERROR && + size_infos) { + PIP_ADAPTER_ADDRESSES adapter; + for (adapter = reinterpret_cast(adapter_addresses.data()); adapter; + adapter = adapter->Next) { + PIP_ADAPTER_UNICAST_ADDRESS unicast = adapter->FirstUnicastAddress; + if (unicast) { + ULONG prefix_length = unicast->OnLinkPrefixLength; + ULONG mask = prefix_length == 0 ? 0 : 0xFFFFFFFF << (32 - prefix_length); + in_addr addr{}; + addr.S_un.S_addr = htonl(mask); + inet_ntop(AF_INET, &addr, netmaskStr, INET_ADDRSTRLEN); + success = true; + } + } + } + +#else + ifaddrs* ifap; + + if (getifaddrs(&ifap) == 0) { + ifaddrs* p; + for (p = ifap; p; p = p->ifa_next) { + if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(p->ifa_netmask); + inet_ntop(AF_INET, &sa->sin_addr, netmaskStr, INET_ADDRSTRLEN); + success = true; + } + } + } + + freeifaddrs(ifap); +#endif + + if (success) { + netmask = netmaskStr; + } + return success; +} + } // namespace NetUtil \ No newline at end of file diff --git a/src/core/libraries/network/net_util.h b/src/core/libraries/network/net_util.h index be9dc15a1..fce55c2ff 100644 --- a/src/core/libraries/network/net_util.h +++ b/src/core/libraries/network/net_util.h @@ -15,10 +15,16 @@ public: private: std::array ether_address{}; + std::string default_gateway{}; + std::string netmask{}; std::mutex m_mutex; public: const std::array& GetEthernetAddr() const; + const std::string& GetDefaultGateway() const; + const std::string& GetNetmask() const; bool RetrieveEthernetAddr(); + bool RetrieveDefaultGateway(); + bool RetrieveNetmask(); }; } // namespace NetUtil \ No newline at end of file diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index 14623cd85..69733fcf6 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -162,13 +162,13 @@ int PS4_SYSV_ABI sceNetCtlGetIfStat() { int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { LOG_DEBUG(Lib_NetCtl, "code = {}", code); + auto* netinfo = Common::Singleton::Instance(); switch (code) { case ORBIS_NET_CTL_INFO_DEVICE: info->device = ORBIS_NET_CTL_DEVICE_WIRED; break; case ORBIS_NET_CTL_INFO_ETHER_ADDR: { - auto* netinfo = Common::Singleton::Instance(); netinfo->RetrieveEthernetAddr(); memcpy(info->ether_addr.data, netinfo->GetEthernetAddr().data(), 6); } break; @@ -197,11 +197,29 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { } break; } - + case ORBIS_NET_CTL_INFO_NETMASK: { + auto success = netinfo->RetrieveNetmask(); + if (success) { + strncpy(info->netmask, netinfo->GetNetmask().data(), sizeof(info->netmask)); + LOG_DEBUG(Lib_NetCtl, "netmask: {}", info->netmask); + } else { + LOG_WARNING(Lib_NetCtl, "netmask: failed to retrieve"); + } + break; + } + case ORBIS_NET_CTL_INFO_DEFAULT_ROUTE: { + auto success = netinfo->RetrieveDefaultGateway(); + if (success) { + strncpy(info->netmask, netinfo->GetDefaultGateway().data(), sizeof(info->netmask)); + LOG_DEBUG(Lib_NetCtl, "default gateway: {}", info->netmask); + } else { + LOG_WARNING(Lib_NetCtl, "default gateway: failed to retrieve"); + } + break; + } default: LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); } - LOG_DEBUG(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; }