network: retrieve subnet mask and gateway info
This commit is contained in:
		| @@ -11,6 +11,7 @@ | ||||
| #include "core/hle/service/nifm/nifm.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/network/network.h" | ||||
| #include "core/network/network_interface.h" | ||||
|  | ||||
| namespace Service::NIFM { | ||||
|  | ||||
| @@ -357,16 +358,10 @@ private: | ||||
|         static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | ||||
|                       "IpConfigInfo has incorrect size."); | ||||
|  | ||||
|         auto ipv4 = Network::GetHostIPv4Address(); | ||||
|         if (!ipv4) { | ||||
|             LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | ||||
|             ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||||
|         } | ||||
|  | ||||
|         const IpConfigInfo ip_config_info{ | ||||
|         IpConfigInfo ip_config_info{ | ||||
|             .ip_address_setting{ | ||||
|                 .is_automatic{true}, | ||||
|                 .current_address{*ipv4}, | ||||
|                 .current_address{0, 0, 0, 0}, | ||||
|                 .subnet_mask{255, 255, 255, 0}, | ||||
|                 .gateway{192, 168, 1, 1}, | ||||
|             }, | ||||
| @@ -377,6 +372,19 @@ private: | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         const auto iface = Network::GetSelectedNetworkInterface(); | ||||
|         if (iface) { | ||||
|             ip_config_info.ip_address_setting = | ||||
|                 IpAddressSetting{.is_automatic{true}, | ||||
|                                  .current_address{Network::TranslateIPv4(iface->ip_address)}, | ||||
|                                  .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)}, | ||||
|                                  .gateway{Network::TranslateIPv4(iface->gateway)}}; | ||||
|  | ||||
|         } else { | ||||
|             LOG_ERROR(Service_NIFM, | ||||
|                       "Couldn't get host network configuration info, using default values"); | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushRaw<IpConfigInfo>(ip_config_info); | ||||
|   | ||||
| @@ -50,11 +50,6 @@ void Finalize() { | ||||
|     WSACleanup(); | ||||
| } | ||||
|  | ||||
| constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||
|     auto& bytes = addr.S_un.S_un_b; | ||||
|     return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; | ||||
| } | ||||
|  | ||||
| sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||||
|     sockaddr_in result; | ||||
|  | ||||
| @@ -141,12 +136,6 @@ void Initialize() {} | ||||
|  | ||||
| void Finalize() {} | ||||
|  | ||||
| constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||
|     const u32 bytes = addr.s_addr; | ||||
|     return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), | ||||
|                        static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; | ||||
| } | ||||
|  | ||||
| sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||||
|     sockaddr_in result; | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,12 @@ | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <winsock2.h> | ||||
| #elif YUZU_UNIX | ||||
| #include <netinet/in.h> | ||||
| #endif | ||||
|  | ||||
| namespace Network { | ||||
|  | ||||
| class Socket; | ||||
| @@ -93,6 +99,19 @@ public: | ||||
|     ~NetworkInstance(); | ||||
| }; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||
|     auto& bytes = addr.S_un.S_un_b; | ||||
|     return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; | ||||
| } | ||||
| #elif YUZU_UNIX | ||||
| constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||
|     const u32 bytes = addr.s_addr; | ||||
|     return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), | ||||
|                        static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /// @brief Returns host's IPv4 address | ||||
| /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array | ||||
| std::optional<IPv4Address> GetHostIPv4Address(); | ||||
|   | ||||
| @@ -2,11 +2,15 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/bit_cast.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/settings.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/network/network_interface.h" | ||||
|  | ||||
| @@ -29,8 +33,9 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||
|  | ||||
|     // retry up to 5 times | ||||
|     for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { | ||||
|         ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, | ||||
|                                    nullptr, adapter_addresses.data(), &buf_size); | ||||
|         ret = GetAdaptersAddresses( | ||||
|             AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, | ||||
|             nullptr, adapter_addresses.data(), &buf_size); | ||||
|  | ||||
|         if (ret == ERROR_BUFFER_OVERFLOW) { | ||||
|             adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); | ||||
| @@ -57,9 +62,26 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||
|                                      *current_address->FirstUnicastAddress->Address.lpSockaddr) | ||||
|                                      .sin_addr; | ||||
|  | ||||
|             ULONG mask = 0; | ||||
|             if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, | ||||
|                                         &mask) != NO_ERROR) { | ||||
|                 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             struct in_addr gateway = {0}; | ||||
|             if (current_address->FirstGatewayAddress != nullptr && | ||||
|                 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { | ||||
|                 gateway = Common::BitCast<struct sockaddr_in>( | ||||
|                               *current_address->FirstGatewayAddress->Address.lpSockaddr) | ||||
|                               .sin_addr; | ||||
|             } | ||||
|  | ||||
|             result.push_back(NetworkInterface{ | ||||
|                 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, | ||||
|                 .ip_address{ip_addr}}); | ||||
|                 .ip_address{ip_addr}, | ||||
|                 .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, | ||||
|                 .gateway = gateway}); | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
| @@ -83,7 +105,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||
|     } | ||||
|  | ||||
|     for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { | ||||
|         if (ifa->ifa_addr == nullptr) { | ||||
|         if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
| @@ -95,9 +117,59 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         std::uint32_t gateway{0}; | ||||
|         std::ifstream file{"/proc/net/route"}; | ||||
|         if (file.is_open()) { | ||||
|  | ||||
|             // ignore header | ||||
|             file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | ||||
|  | ||||
|             bool gateway_found = false; | ||||
|  | ||||
|             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; | ||||
|  | ||||
|                 std::uint32_t dest{0}; | ||||
|                 iss >> dest; | ||||
|                 if (dest != 0) { | ||||
|                     // not the default route | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 iss >> gateway; | ||||
|  | ||||
|                 std::uint16_t flags{0}; | ||||
|                 iss >> flags; | ||||
|  | ||||
|                 // flag RTF_GATEWAY (defined in <linux/route.h>) | ||||
|                 if ((flags & 0x2) == 0) { | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 gateway_found = true; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (!gateway_found) { | ||||
|                 gateway = 0; | ||||
|             } | ||||
|         } else { | ||||
|             LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); | ||||
|         } | ||||
|  | ||||
|         result.push_back(NetworkInterface{ | ||||
|             .name{ifa->ifa_name}, | ||||
|             .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}}); | ||||
|             .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, | ||||
|             .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, | ||||
|             .gateway{in_addr{.s_addr = gateway}}}); | ||||
|     } | ||||
|  | ||||
|     freeifaddrs(ifaddr); | ||||
| @@ -107,4 +179,25 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||
|  | ||||
| #endif | ||||
|  | ||||
| std::optional<NetworkInterface> GetSelectedNetworkInterface() { | ||||
|     const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); | ||||
|     const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||||
|     if (network_interfaces.size() == 0) { | ||||
|         LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     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()) { | ||||
|         return *res; | ||||
|     } else { | ||||
|         LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace Network | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <optional> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| @@ -18,8 +19,11 @@ namespace Network { | ||||
| struct NetworkInterface { | ||||
|     std::string name; | ||||
|     struct in_addr ip_address; | ||||
|     struct in_addr subnet_mask; | ||||
|     struct in_addr gateway; | ||||
| }; | ||||
|  | ||||
| std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); | ||||
| std::optional<NetworkInterface> GetSelectedNetworkInterface(); | ||||
|  | ||||
| } // namespace Network | ||||
|   | ||||
		Reference in New Issue
	
	Block a user