From 89dea9bd921bf335b464e83556b40ed7b788d5d4 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Fri, 25 Apr 2025 07:33:52 +0200 Subject: [PATCH] Fix IPv6 support detection --- .../Emu/Cell/lv2/sys_net/network_context.cpp | 4 +- rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp | 10 +- rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h | 2 - rpcs3/Emu/NP/ip_address.cpp | 107 +++++++++++++----- rpcs3/Emu/NP/rpcn_client.cpp | 46 ++------ rpcs3/Emu/NP/rpcn_config.cpp | 30 +++++ rpcs3/Emu/NP/rpcn_config.h | 2 + rpcs3/rpcs3qt/rpcn_settings_dialog.cpp | 5 +- 8 files changed, 126 insertions(+), 80 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp index 61bd5a4045..377da5bf31 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -18,7 +18,7 @@ bool send_packet_from_p2p_port_ipv4(const std::vector& data, const sockaddr_ { auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT); - if (def_port.is_ipv6) + if (np::is_ipv6_supported()) { const auto addr6 = np::sockaddr_to_sockaddr6(addr); @@ -52,7 +52,7 @@ bool send_packet_from_p2p_port_ipv6(const std::vector& data, const sockaddr_ if (nc.list_p2p_ports.contains(SCE_NP_PORT)) { auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT); - ensure(def_port.is_ipv6); + ensure(np::is_ipv6_supported()); if (::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(&addr), sizeof(sockaddr_in6)) == -1) { diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index 5f48d273dd..0c472043c1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -40,10 +40,10 @@ namespace sys_net_helpers nt_p2p_port::nt_p2p_port(u16 port) : port(port) { - is_ipv6 = np::is_ipv6_supported(); + const bool is_ipv6 = np::is_ipv6_supported(); // Creates and bind P2P Socket - p2p_socket = is_ipv6 ? ::socket(AF_INET6, SOCK_DGRAM, 0) : ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + p2p_socket = ::socket(is_ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); #ifdef _WIN32 if (p2p_socket == INVALID_SOCKET) #else @@ -145,13 +145,13 @@ bool nt_p2p_port::recv_data() { ::sockaddr_storage native_addr{}; ::socklen_t native_addrlen = sizeof(native_addr); - const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast(p2p_recv_data.data()), ::size32(p2p_recv_data), 0, reinterpret_cast(&native_addr), &native_addrlen); + const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast(p2p_recv_data.data()), ::size32(p2p_recv_data), 0, reinterpret_cast(&native_addr), &native_addrlen); if (recv_res == -1) { auto lerr = get_last_error(false); if (lerr != SYS_NET_EINPROGRESS && lerr != SYS_NET_EWOULDBLOCK) - sys_net.error("Error recvfrom on %s P2P socket: %d", is_ipv6 ? "IPv6" : "IPv4", lerr); + sys_net.error("Error recvfrom on %s P2P socket: %d", np::is_ipv6_supported() ? "IPv6" : "IPv4", lerr); return false; } @@ -164,7 +164,7 @@ bool nt_p2p_port::recv_data() u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); - if (is_ipv6) + if (np::is_ipv6_supported()) { const auto* addr_ipv6 = reinterpret_cast(&native_addr); const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h index 3ffc3df54d..2d57bc8461 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h @@ -48,8 +48,6 @@ struct nt_p2p_port socket_type p2p_socket = 0; u16 port = 0; - bool is_ipv6 = false; - shared_mutex bound_p2p_vports_mutex; // For DGRAM_P2P sockets (vport, sock_ids) std::map> bound_p2p_vports{}; diff --git a/rpcs3/Emu/NP/ip_address.cpp b/rpcs3/Emu/NP/ip_address.cpp index 705f4782e2..312212973f 100644 --- a/rpcs3/Emu/NP/ip_address.cpp +++ b/rpcs3/Emu/NP/ip_address.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "Emu/system_config.h" #include "ip_address.h" #include "Utilities/StrFmt.h" #include "Emu/IdManager.h" @@ -89,33 +90,54 @@ namespace np } } + struct ipv6_support_state + { + ipv6_support_state() = default; + ipv6_support_state(const ipv6_support_state&) = delete; + ipv6_support_state& operator=(const ipv6_support_state&) = delete; + + atomic_t status = IPV6_SUPPORT::IPV6_UNKNOWN; + }; + + // The conditions for IPv6 to be enabled are, in order: + // -IPv6 is not disabled in config + // -Internet config is Connected + // -PSN config is RPCN + // -RPCN host has an IPv6 + // -Can connect to ipv6.google.com:413 bool is_ipv6_supported(std::optional force_state) { - static atomic_t ipv6_status = IPV6_SUPPORT::IPV6_UNKNOWN; + auto& ipv6_support = g_fxo->get(); if (force_state) - ipv6_status = *force_state; + ipv6_support.status = *force_state; - if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) - return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; + if (ipv6_support.status != IPV6_SUPPORT::IPV6_UNKNOWN) + return ipv6_support.status == IPV6_SUPPORT::IPV6_SUPPORTED; static shared_mutex mtx; std::lock_guard lock(mtx); - if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) - return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; + if (ipv6_support.status != IPV6_SUPPORT::IPV6_UNKNOWN) + return ipv6_support.status == IPV6_SUPPORT::IPV6_SUPPORTED; + + auto notice_and_disable = [&](std::string_view reason) -> bool + { + IPv6_log.notice("is_ipv6_supported(): disabled cause: %s", reason); + ipv6_support.status = IPV6_SUPPORT::IPV6_UNSUPPORTED; + return false; + }; // IPv6 feature is only used by RPCN if (!g_cfg_rpcn.get_ipv6_support()) - { - IPv6_log.notice("is_ipv6_supported(): disabled through config"); - ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; - return false; - } + return notice_and_disable("force-disabled through config"); + + if (g_cfg.net.net_active != np_internet_status::enabled || g_cfg.net.psn_status != np_psn_status::psn_rpcn) + return notice_and_disable("RPCN is disabled"); - // We try to connect to ipv6.google.com:8080 addrinfo* addr_info{}; socket_type socket_ipv6{}; + const addrinfo hints{.ai_family = AF_INET6}; auto cleanup = [&]() { @@ -126,31 +148,54 @@ namespace np freeaddrinfo(addr_info); }; - auto error_and_disable = [&](const char* message) -> bool + auto error_and_disable = [&](std::string_view message) -> bool { - IPv6_log.error("is_ipv6_supported(): %s", message); - ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; + IPv6_log.error("is_ipv6_supported(): disabled cause: %s", message); + ipv6_support.status = IPV6_SUPPORT::IPV6_UNSUPPORTED; cleanup(); return false; }; - addrinfo hints{.ai_family = AF_INET6}; - - if (getaddrinfo("ipv6.google.com", nullptr, &hints, &addr_info)) - return error_and_disable("Failed to resolve ipv6.google.com!"); - - addrinfo* found = addr_info; - - while (found != nullptr) + auto get_ipv6 = [](const addrinfo* addr_info) -> const addrinfo* { - if (found->ai_family == AF_INET6) - break; + const addrinfo* found = addr_info; - found = found->ai_next; - } + while (found != nullptr) + { + if (found->ai_family == AF_INET6) + break; - if (found == nullptr) - return error_and_disable("Failed to find IPv6 for ipv6.google.com"); + found = found->ai_next; + } + + return found; + }; + + // Check if RPCN has an IPv6 address + const auto parsed_host = parse_rpcn_host(g_cfg_rpcn.get_host()); + + if (!parsed_host) + return error_and_disable("failed to parse RPCN host"); + + const auto [hostname, _] = *parsed_host; + + if (getaddrinfo(hostname.c_str(), nullptr, &hints, &addr_info)) + return error_and_disable("failed to resolve RPCN host"); + + if (!get_ipv6(addr_info)) + return error_and_disable("RPCN host doesn't support IPv6"); + + freeaddrinfo(addr_info); + addr_info = {}; + + // We try to connect to ipv6.google.com:8080 + if (getaddrinfo("ipv6.google.com", nullptr, &hints, &addr_info)) + return error_and_disable("failed to resolve ipv6.google.com"); + + const auto* google_ipv6_addr = get_ipv6(addr_info); + + if (!google_ipv6_addr) + return error_and_disable("failed to find IPv6 for ipv6.google.com"); socket_type socket_or_err = ::socket(AF_INET6, SOCK_STREAM, 0); @@ -162,7 +207,7 @@ namespace np return error_and_disable("Failed to create IPv6 socket!"); socket_ipv6 = socket_or_err; - sockaddr_in6 ipv6_addr = *reinterpret_cast(found->ai_addr); + sockaddr_in6 ipv6_addr = *reinterpret_cast(google_ipv6_addr->ai_addr); ipv6_addr.sin6_port = std::bit_cast>(443); if (::connect(socket_ipv6, reinterpret_cast(&ipv6_addr), sizeof(ipv6_addr)) != 0) @@ -171,7 +216,7 @@ namespace np cleanup(); IPv6_log.success("Successfully tested IPv6 support!"); - ipv6_status = IPV6_SUPPORT::IPV6_SUPPORTED; + ipv6_support.status = IPV6_SUPPORT::IPV6_SUPPORTED; return true; } diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 3658a471d6..c963e85ab7 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -970,33 +970,15 @@ namespace rpcn state = rpcn_state::failure_no_failure; - if (host.empty()) + const auto hostname_and_port = parse_rpcn_host(host); + + if (!hostname_and_port) { - rpcn_log.error("connect: RPCN host is empty!"); state = rpcn_state::failure_input; return false; } - auto splithost = fmt::split(host, {":"}); - if (splithost.size() != 1 && splithost.size() != 2) - { - rpcn_log.error("connect: RPCN host is invalid!"); - state = rpcn_state::failure_input; - return false; - } - - u16 port = 31313; - - if (splithost.size() == 2) - { - port = ::narrow(std::stoul(splithost[1])); - if (port == 0) - { - rpcn_log.error("connect: RPCN port is invalid!"); - state = rpcn_state::failure_input; - return false; - } - } + const auto [hostname, port] = *hostname_and_port; { // Ensures both read & write threads are in waiting state @@ -1037,14 +1019,14 @@ namespace rpcn addrinfo* addr_info{}; - if (getaddrinfo(splithost[0].c_str(), nullptr, nullptr, &addr_info)) + if (getaddrinfo(hostname.c_str(), nullptr, nullptr, &addr_info)) { rpcn_log.error("connect: Failed to getaddrinfo %s", host); state = rpcn_state::failure_resolve; return false; } - bool found_ipv4 = false, found_ipv6 = false; + bool found_ipv4 = false; addrinfo* found = addr_info; while (found != nullptr) @@ -1059,13 +1041,9 @@ namespace rpcn } case AF_INET6: { - if (np::is_ipv6_supported()) - { - addr_rpcn_udp_ipv6.sin6_family = AF_INET6; - addr_rpcn_udp_ipv6.sin6_port = std::bit_cast>(3657); - addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast(found->ai_addr)->sin6_addr; - found_ipv6 = true; - } + addr_rpcn_udp_ipv6.sin6_family = AF_INET6; + addr_rpcn_udp_ipv6.sin6_port = std::bit_cast>(3657); + addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast(found->ai_addr)->sin6_addr; break; } default: break; @@ -1081,12 +1059,6 @@ namespace rpcn return false; } - if (np::is_ipv6_supported() && !found_ipv6) - { - rpcn_log.warning("IPv6 seems supported but no IPv6 could be found for the RPCN server, IPv6 is disabled!"); - is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNSUPPORTED); - } - memcpy(&addr_rpcn_udp_ipv4, &addr_rpcn, sizeof(addr_rpcn_udp_ipv4)); addr_rpcn_udp_ipv4.sin_port = std::bit_cast>(3657); // htons diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index a112084da7..b489d7d924 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -211,3 +211,33 @@ bool cfg_rpcn::del_host(std::string_view del_description, std::string_view del_h return false; } + +std::optional> parse_rpcn_host(std::string_view host) +{ + if (host.empty()) + { + rpcn_log.error("RPCN host is empty!"); + return std::nullopt; + } + + auto splithost = fmt::split(host, {":"}); + if (splithost.size() != 1 && splithost.size() != 2) + { + rpcn_log.error("RPCN host is invalid!"); + return std::nullopt; + } + + u16 port = 31313; + + if (splithost.size() == 2) + { + port = ::narrow(std::stoul(splithost[1])); + if (port == 0) + { + rpcn_log.error("RPCN port is invalid!"); + return std::nullopt; + } + } + + return std::make_pair(std::move(splithost[0]), port); +} diff --git a/rpcs3/Emu/NP/rpcn_config.h b/rpcs3/Emu/NP/rpcn_config.h index 7c1317fbe0..a94eb053b7 100644 --- a/rpcs3/Emu/NP/rpcn_config.h +++ b/rpcs3/Emu/NP/rpcn_config.h @@ -36,4 +36,6 @@ private: void set_hosts(const std::vector>& vec_hosts); }; +std::optional> parse_rpcn_host(std::string_view host); + extern cfg_rpcn g_cfg_rpcn; diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 673dc53c52..5046435008 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -200,9 +200,8 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) g_cfg_rpcn.set_host(host.toString().toStdString()); g_cfg_rpcn.save(); - // Resets the state in case the support was limited by the RPCN server - if (!np::is_ipv6_supported()) - np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN); + // Resets the ipv6 support as it depends on availability of the feature on the server + np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN); }); connect(btn_add_server, &QAbstractButton::clicked, this, [this]()