summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Fernando S2021-08-16 17:10:00 +0200
committerGravatar GitHub2021-08-16 17:10:00 +0200
commit521e6ac17483e975e2fc731fed879e9f3edf7547 (patch)
treeca5a9b7559f475de1d461885f3df9ee2282bccdf /src/core
parentMerge pull request #6861 from yzct12345/const-mempy-is-all-the-speed (diff)
parentnetwork_interface: correct formatting (diff)
downloadyuzu-521e6ac17483e975e2fc731fed879e9f3edf7547.tar.gz
yuzu-521e6ac17483e975e2fc731fed879e9f3edf7547.tar.xz
yuzu-521e6ac17483e975e2fc731fed879e9f3edf7547.zip
Merge pull request #6863 from spholz/fix-lan-play
Fix LAN Play
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp45
-rw-r--r--src/core/network/network.cpp64
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp203
-rw-r--r--src/core/network/network_interface.h29
6 files changed, 318 insertions, 49 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5c99c00f5..f5cf5c16a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -636,6 +636,8 @@ add_library(core STATIC
636 memory.h 636 memory.h
637 network/network.cpp 637 network/network.cpp
638 network/network.h 638 network/network.h
639 network/network_interface.cpp
640 network/network_interface.h
639 network/sockets.h 641 network/sockets.h
640 perf_stats.cpp 642 perf_stats.cpp
641 perf_stats.h 643 perf_stats.h
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e742db48f..0a53c0c81 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/service/nifm/nifm.h" 11#include "core/hle/service/nifm/nifm.h"
12#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
13#include "core/network/network.h" 13#include "core/network/network.h"
14#include "core/network/network_interface.h"
14 15
15namespace Service::NIFM { 16namespace Service::NIFM {
16 17
@@ -179,10 +180,10 @@ private:
179 IPC::ResponseBuilder rb{ctx, 3}; 180 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
181 182
182 if (Settings::values.bcat_backend.GetValue() == "none") { 183 if (Network::GetHostIPv4Address().has_value()) {
183 rb.PushEnum(RequestState::NotSubmitted);
184 } else {
185 rb.PushEnum(RequestState::Connected); 184 rb.PushEnum(RequestState::Connected);
185 } else {
186 rb.PushEnum(RequestState::NotSubmitted);
186 } 187 }
187 } 188 }
188 189
@@ -322,12 +323,15 @@ private:
322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 323 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
323 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 324 LOG_WARNING(Service_NIFM, "(STUBBED) called");
324 325
325 const auto [ipv4, error] = Network::GetHostIPv4Address(); 326 auto ipv4 = Network::GetHostIPv4Address();
326 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); 327 if (!ipv4) {
328 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
329 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
330 }
327 331
328 IPC::ResponseBuilder rb{ctx, 3}; 332 IPC::ResponseBuilder rb{ctx, 3};
329 rb.Push(ResultSuccess); 333 rb.Push(ResultSuccess);
330 rb.PushRaw(ipv4); 334 rb.PushRaw(*ipv4);
331 } 335 }
332 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 336 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
333 LOG_DEBUG(Service_NIFM, "called"); 337 LOG_DEBUG(Service_NIFM, "called");
@@ -354,10 +358,10 @@ private:
354 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 358 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
355 "IpConfigInfo has incorrect size."); 359 "IpConfigInfo has incorrect size.");
356 360
357 const IpConfigInfo ip_config_info{ 361 IpConfigInfo ip_config_info{
358 .ip_address_setting{ 362 .ip_address_setting{
359 .is_automatic{true}, 363 .is_automatic{true},
360 .current_address{192, 168, 1, 100}, 364 .current_address{0, 0, 0, 0},
361 .subnet_mask{255, 255, 255, 0}, 365 .subnet_mask{255, 255, 255, 0},
362 .gateway{192, 168, 1, 1}, 366 .gateway{192, 168, 1, 1},
363 }, 367 },
@@ -368,6 +372,19 @@ private:
368 }, 372 },
369 }; 373 };
370 374
375 const auto iface = Network::GetSelectedNetworkInterface();
376 if (iface) {
377 ip_config_info.ip_address_setting =
378 IpAddressSetting{.is_automatic{true},
379 .current_address{Network::TranslateIPv4(iface->ip_address)},
380 .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
381 .gateway{Network::TranslateIPv4(iface->gateway)}};
382
383 } else {
384 LOG_ERROR(Service_NIFM,
385 "Couldn't get host network configuration info, using default values");
386 }
387
371 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 388 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
372 rb.Push(ResultSuccess); 389 rb.Push(ResultSuccess);
373 rb.PushRaw<IpConfigInfo>(ip_config_info); 390 rb.PushRaw<IpConfigInfo>(ip_config_info);
@@ -384,10 +401,10 @@ private:
384 401
385 IPC::ResponseBuilder rb{ctx, 3}; 402 IPC::ResponseBuilder rb{ctx, 3};
386 rb.Push(ResultSuccess); 403 rb.Push(ResultSuccess);
387 if (Settings::values.bcat_backend.GetValue() == "none") { 404 if (Network::GetHostIPv4Address().has_value()) {
388 rb.Push<u8>(0);
389 } else {
390 rb.Push<u8>(1); 405 rb.Push<u8>(1);
406 } else {
407 rb.Push<u8>(0);
391 } 408 }
392 } 409 }
393 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 410 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -395,10 +412,10 @@ private:
395 412
396 IPC::ResponseBuilder rb{ctx, 3}; 413 IPC::ResponseBuilder rb{ctx, 3};
397 rb.Push(ResultSuccess); 414 rb.Push(ResultSuccess);
398 if (Settings::values.bcat_backend.GetValue() == "none") { 415 if (Network::GetHostIPv4Address().has_value()) {
399 rb.Push<u8>(0);
400 } else {
401 rb.Push<u8>(1); 416 rb.Push<u8>(1);
417 } else {
418 rb.Push<u8>(0);
402 } 419 }
403 } 420 }
404}; 421};
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 375bc79ec..4732d4485 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,9 +10,10 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11 11
12#ifdef _WIN32 12#ifdef _WIN32
13#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
14#include <winsock2.h> 13#include <winsock2.h>
14#include <ws2tcpip.h>
15#elif YUZU_UNIX 15#elif YUZU_UNIX
16#include <arpa/inet.h>
16#include <errno.h> 17#include <errno.h>
17#include <fcntl.h> 18#include <fcntl.h>
18#include <netdb.h> 19#include <netdb.h>
@@ -27,7 +28,9 @@
27#include "common/assert.h" 28#include "common/assert.h"
28#include "common/common_types.h" 29#include "common/common_types.h"
29#include "common/logging/log.h" 30#include "common/logging/log.h"
31#include "common/settings.h"
30#include "core/network/network.h" 32#include "core/network/network.h"
33#include "core/network/network_interface.h"
31#include "core/network/sockets.h" 34#include "core/network/sockets.h"
32 35
33namespace Network { 36namespace Network {
@@ -47,11 +50,6 @@ void Finalize() {
47 WSACleanup(); 50 WSACleanup();
48} 51}
49 52
50constexpr IPv4Address TranslateIPv4(in_addr addr) {
51 auto& bytes = addr.S_un.S_un_b;
52 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
53}
54
55sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 53sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
56 sockaddr_in result; 54 sockaddr_in result;
57 55
@@ -138,12 +136,6 @@ void Initialize() {}
138 136
139void Finalize() {} 137void Finalize() {}
140 138
141constexpr IPv4Address TranslateIPv4(in_addr addr) {
142 const u32 bytes = addr.s_addr;
143 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
144 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
145}
146
147sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 139sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
148 sockaddr_in result; 140 sockaddr_in result;
149 141
@@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
182} 174}
183 175
184bool EnableNonBlock(int fd, bool enable) { 176bool EnableNonBlock(int fd, bool enable) {
185 int flags = fcntl(fd, F_GETFD); 177 int flags = fcntl(fd, F_GETFL);
186 if (flags == -1) { 178 if (flags == -1) {
187 return false; 179 return false;
188 } 180 }
@@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) {
191 } else { 183 } else {
192 flags &= ~O_NONBLOCK; 184 flags &= ~O_NONBLOCK;
193 } 185 }
194 return fcntl(fd, F_SETFD, flags) == 0; 186 return fcntl(fd, F_SETFL, flags) == 0;
195} 187}
196 188
197Errno TranslateNativeError(int e) { 189Errno TranslateNativeError(int e) {
@@ -227,8 +219,12 @@ Errno GetAndLogLastError() {
227#else 219#else
228 int e = errno; 220 int e = errno;
229#endif 221#endif
222 const Errno err = TranslateNativeError(e);
223 if (err == Errno::AGAIN) {
224 return err;
225 }
230 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); 226 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
231 return TranslateNativeError(e); 227 return err;
232} 228}
233 229
234int TranslateDomain(Domain domain) { 230int TranslateDomain(Domain domain) {
@@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() {
353 Finalize(); 349 Finalize();
354} 350}
355 351
356std::pair<IPv4Address, Errno> GetHostIPv4Address() { 352std::optional<IPv4Address> GetHostIPv4Address() {
357 std::array<char, 256> name{}; 353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
358 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
359 return {IPv4Address{}, GetAndLogLastError()}; 355 if (network_interfaces.size() == 0) {
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {};
360 } 358 }
361 359
362 hostent* const ent = gethostbyname(name.data()); 360 const auto res =
363 if (!ent) { 361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
364 return {IPv4Address{}, GetAndLogLastError()}; 362 return iface.name == selected_network_interface;
365 } 363 });
366 if (ent->h_addr_list == nullptr) { 364
367 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); 365 if (res != network_interfaces.end()) {
368 return {IPv4Address{}, Errno::SUCCESS}; 366 char ip_addr[16] = {};
369 } 367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
370 if (ent->h_length != sizeof(in_addr)) { 368 LOG_INFO(Network, "IP address: {}", ip_addr);
371 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
372 }
373 369
374 in_addr addr; 370 return TranslateIPv4(res->ip_address);
375 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); 371 } else {
376 return {TranslateIPv4(addr), Errno::SUCCESS}; 372 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
373 return {};
374 }
377} 375}
378 376
379std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 377std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index bd30f1899..fdd3e4655 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <utility> 9#include <utility>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
14#ifdef _WIN32
15#include <winsock2.h>
16#elif YUZU_UNIX
17#include <netinet/in.h>
18#endif
19
13namespace Network { 20namespace Network {
14 21
15class Socket; 22class Socket;
@@ -92,8 +99,21 @@ public:
92 ~NetworkInstance(); 99 ~NetworkInstance();
93}; 100};
94 101
102#ifdef _WIN32
103constexpr IPv4Address TranslateIPv4(in_addr addr) {
104 auto& bytes = addr.S_un.S_un_b;
105 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
106}
107#elif YUZU_UNIX
108constexpr IPv4Address TranslateIPv4(in_addr addr) {
109 const u32 bytes = addr.s_addr;
110 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
111 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
112}
113#endif
114
95/// @brief Returns host's IPv4 address 115/// @brief Returns host's IPv4 address
96/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code 116/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
97std::pair<IPv4Address, Errno> GetHostIPv4Address(); 117std::optional<IPv4Address> GetHostIPv4Address();
98 118
99} // namespace Network 119} // namespace Network
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..cecc9aa11
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,203 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <fstream>
7#include <sstream>
8#include <vector>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/string_util.h"
15#include "core/network/network_interface.h"
16
17#ifdef _WIN32
18#include <iphlpapi.h>
19#else
20#include <cerrno>
21#include <ifaddrs.h>
22#include <net/if.h>
23#endif
24
25namespace Network {
26
27#ifdef _WIN32
28
29std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
30 std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses;
31 DWORD ret = ERROR_BUFFER_OVERFLOW;
32 DWORD buf_size = 0;
33
34 // retry up to 5 times
35 for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
36 ret = GetAdaptersAddresses(
37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
38 nullptr, adapter_addresses.data(), &buf_size);
39
40 if (ret == ERROR_BUFFER_OVERFLOW) {
41 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
42 } else {
43 break;
44 }
45 }
46
47 if (ret == NO_ERROR) {
48 std::vector<NetworkInterface> result;
49
50 for (auto current_address = adapter_addresses.data(); current_address != nullptr;
51 current_address = current_address->Next) {
52 if (current_address->FirstUnicastAddress == nullptr ||
53 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
54 continue;
55 }
56
57 if (current_address->OperStatus != IfOperStatusUp) {
58 continue;
59 }
60
61 const auto ip_addr = Common::BitCast<struct sockaddr_in>(
62 *current_address->FirstUnicastAddress->Address.lpSockaddr)
63 .sin_addr;
64
65 ULONG mask = 0;
66 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
67 &mask) != NO_ERROR) {
68 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
69 continue;
70 }
71
72 struct in_addr gateway = {.S_un{.S_addr{0}}};
73 if (current_address->FirstGatewayAddress != nullptr &&
74 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
75 gateway = Common::BitCast<struct sockaddr_in>(
76 *current_address->FirstGatewayAddress->Address.lpSockaddr)
77 .sin_addr;
78 }
79
80 result.push_back(NetworkInterface{
81 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
82 .ip_address{ip_addr},
83 .subnet_mask = in_addr{.S_un{.S_addr{mask}}},
84 .gateway = gateway});
85 }
86
87 return result;
88 } else {
89 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
90 return {};
91 }
92}
93
94#else
95
96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
97 std::vector<NetworkInterface> result;
98
99 struct ifaddrs* ifaddr = nullptr;
100
101 if (getifaddrs(&ifaddr) != 0) {
102 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
103 std::strerror(errno));
104 return result;
105 }
106
107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
109 continue;
110 }
111
112 if (ifa->ifa_addr->sa_family != AF_INET) {
113 continue;
114 }
115
116 if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
117 continue;
118 }
119
120 std::uint32_t gateway{0};
121 std::ifstream file{"/proc/net/route"};
122 if (file.is_open()) {
123
124 // ignore header
125 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
126
127 bool gateway_found = false;
128
129 for (std::string line; std::getline(file, line);) {
130 std::istringstream iss{line};
131
132 std::string iface_name{};
133 iss >> iface_name;
134 if (iface_name != ifa->ifa_name) {
135 continue;
136 }
137
138 iss >> std::hex;
139
140 std::uint32_t dest{0};
141 iss >> dest;
142 if (dest != 0) {
143 // not the default route
144 continue;
145 }
146
147 iss >> gateway;
148
149 std::uint16_t flags{0};
150 iss >> flags;
151
152 // flag RTF_GATEWAY (defined in <linux/route.h>)
153 if ((flags & 0x2) == 0) {
154 continue;
155 }
156
157 gateway_found = true;
158 break;
159 }
160
161 if (!gateway_found) {
162 gateway = 0;
163 }
164 } else {
165 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
166 }
167
168 result.push_back(NetworkInterface{
169 .name{ifa->ifa_name},
170 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
171 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
172 .gateway{in_addr{.s_addr = gateway}}});
173 }
174
175 freeifaddrs(ifaddr);
176
177 return result;
178}
179
180#endif
181
182std::optional<NetworkInterface> GetSelectedNetworkInterface() {
183 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
184 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
185 if (network_interfaces.size() == 0) {
186 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
187 return {};
188 }
189
190 const auto res =
191 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
192 return iface.name == selected_network_interface;
193 });
194
195 if (res != network_interfaces.end()) {
196 return *res;
197 } else {
198 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
199 return {};
200 }
201}
202
203} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..980edb2f5
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,29 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8#include <string>
9#include <vector>
10
11#ifdef _WIN32
12#include <winsock2.h>
13#else
14#include <netinet/in.h>
15#endif
16
17namespace Network {
18
19struct NetworkInterface {
20 std::string name;
21 struct in_addr ip_address;
22 struct in_addr subnet_mask;
23 struct in_addr gateway;
24};
25
26std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
27std::optional<NetworkInterface> GetSelectedNetworkInterface();
28
29} // namespace Network