Fix IPv6 support detection
Some checks are pending
Generate Translation Template / Generate Translation Template (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04 gcc (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04-arm clang (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04 clang (push) Waiting to run
Build RPCS3 / RPCS3 Windows (push) Waiting to run

This commit is contained in:
RipleyTom 2025-04-25 07:33:52 +02:00 committed by Megamouse
parent ab269f6155
commit 89dea9bd92
8 changed files with 126 additions and 80 deletions

View file

@ -18,7 +18,7 @@ bool send_packet_from_p2p_port_ipv4(const std::vector<u8>& data, const sockaddr_
{ {
auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT); 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); const auto addr6 = np::sockaddr_to_sockaddr6(addr);
@ -52,7 +52,7 @@ bool send_packet_from_p2p_port_ipv6(const std::vector<u8>& data, const sockaddr_
if (nc.list_p2p_ports.contains(SCE_NP_PORT)) if (nc.list_p2p_ports.contains(SCE_NP_PORT))
{ {
auto& def_port = ::at32(nc.list_p2p_ports, 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<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in6)) == -1) if (::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in6)) == -1)
{ {

View file

@ -40,10 +40,10 @@ namespace sys_net_helpers
nt_p2p_port::nt_p2p_port(u16 port) nt_p2p_port::nt_p2p_port(u16 port)
: port(port) : port(port)
{ {
is_ipv6 = np::is_ipv6_supported(); const bool is_ipv6 = np::is_ipv6_supported();
// Creates and bind P2P Socket // 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 #ifdef _WIN32
if (p2p_socket == INVALID_SOCKET) if (p2p_socket == INVALID_SOCKET)
#else #else
@ -145,13 +145,13 @@ bool nt_p2p_port::recv_data()
{ {
::sockaddr_storage native_addr{}; ::sockaddr_storage native_addr{};
::socklen_t native_addrlen = sizeof(native_addr); ::socklen_t native_addrlen = sizeof(native_addr);
const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast<char*>(p2p_recv_data.data()), ::size32(p2p_recv_data), 0, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen); const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast<char*>(p2p_recv_data.data()), ::size32(p2p_recv_data), 0, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen);
if (recv_res == -1) if (recv_res == -1)
{ {
auto lerr = get_last_error(false); auto lerr = get_last_error(false);
if (lerr != SYS_NET_EINPROGRESS && lerr != SYS_NET_EWOULDBLOCK) 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; return false;
} }
@ -164,7 +164,7 @@ bool nt_p2p_port::recv_data()
u16 dst_vport = reinterpret_cast<le_t<u16>&>(p2p_recv_data[0]); u16 dst_vport = reinterpret_cast<le_t<u16>&>(p2p_recv_data[0]);
if (is_ipv6) if (np::is_ipv6_supported())
{ {
const auto* addr_ipv6 = reinterpret_cast<sockaddr_in6*>(&native_addr); const auto* addr_ipv6 = reinterpret_cast<sockaddr_in6*>(&native_addr);
const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6); const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6);

View file

@ -48,8 +48,6 @@ struct nt_p2p_port
socket_type p2p_socket = 0; socket_type p2p_socket = 0;
u16 port = 0; u16 port = 0;
bool is_ipv6 = false;
shared_mutex bound_p2p_vports_mutex; shared_mutex bound_p2p_vports_mutex;
// For DGRAM_P2P sockets (vport, sock_ids) // For DGRAM_P2P sockets (vport, sock_ids)
std::map<u16, std::set<s32>> bound_p2p_vports{}; std::map<u16, std::set<s32>> bound_p2p_vports{};

View file

@ -1,4 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/system_config.h"
#include "ip_address.h" #include "ip_address.h"
#include "Utilities/StrFmt.h" #include "Utilities/StrFmt.h"
#include "Emu/IdManager.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<IPV6_SUPPORT> 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<IPV6_SUPPORT> force_state) bool is_ipv6_supported(std::optional<IPV6_SUPPORT> force_state)
{ {
static atomic_t<IPV6_SUPPORT> ipv6_status = IPV6_SUPPORT::IPV6_UNKNOWN; auto& ipv6_support = g_fxo->get<ipv6_support_state>();
if (force_state) if (force_state)
ipv6_status = *force_state; ipv6_support.status = *force_state;
if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) if (ipv6_support.status != IPV6_SUPPORT::IPV6_UNKNOWN)
return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; return ipv6_support.status == IPV6_SUPPORT::IPV6_SUPPORTED;
static shared_mutex mtx; static shared_mutex mtx;
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) if (ipv6_support.status != IPV6_SUPPORT::IPV6_UNKNOWN)
return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; 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 // IPv6 feature is only used by RPCN
if (!g_cfg_rpcn.get_ipv6_support()) if (!g_cfg_rpcn.get_ipv6_support())
{ return notice_and_disable("force-disabled through config");
IPv6_log.notice("is_ipv6_supported(): disabled through config");
ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; if (g_cfg.net.net_active != np_internet_status::enabled || g_cfg.net.psn_status != np_psn_status::psn_rpcn)
return false; return notice_and_disable("RPCN is disabled");
}
// We try to connect to ipv6.google.com:8080
addrinfo* addr_info{}; addrinfo* addr_info{};
socket_type socket_ipv6{}; socket_type socket_ipv6{};
const addrinfo hints{.ai_family = AF_INET6};
auto cleanup = [&]() auto cleanup = [&]()
{ {
@ -126,31 +148,54 @@ namespace np
freeaddrinfo(addr_info); 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_log.error("is_ipv6_supported(): disabled cause: %s", message);
ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; ipv6_support.status = IPV6_SUPPORT::IPV6_UNSUPPORTED;
cleanup(); cleanup();
return false; return false;
}; };
addrinfo hints{.ai_family = AF_INET6}; auto get_ipv6 = [](const addrinfo* addr_info) -> const addrinfo*
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)
{ {
if (found->ai_family == AF_INET6) const addrinfo* found = addr_info;
break;
found = found->ai_next; while (found != nullptr)
} {
if (found->ai_family == AF_INET6)
break;
if (found == nullptr) found = found->ai_next;
return error_and_disable("Failed to find IPv6 for ipv6.google.com"); }
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); 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!"); return error_and_disable("Failed to create IPv6 socket!");
socket_ipv6 = socket_or_err; socket_ipv6 = socket_or_err;
sockaddr_in6 ipv6_addr = *reinterpret_cast<const sockaddr_in6*>(found->ai_addr); sockaddr_in6 ipv6_addr = *reinterpret_cast<const sockaddr_in6*>(google_ipv6_addr->ai_addr);
ipv6_addr.sin6_port = std::bit_cast<u16, be_t<u16>>(443); ipv6_addr.sin6_port = std::bit_cast<u16, be_t<u16>>(443);
if (::connect(socket_ipv6, reinterpret_cast<const sockaddr*>(&ipv6_addr), sizeof(ipv6_addr)) != 0) if (::connect(socket_ipv6, reinterpret_cast<const sockaddr*>(&ipv6_addr), sizeof(ipv6_addr)) != 0)
@ -171,7 +216,7 @@ namespace np
cleanup(); cleanup();
IPv6_log.success("Successfully tested IPv6 support!"); IPv6_log.success("Successfully tested IPv6 support!");
ipv6_status = IPV6_SUPPORT::IPV6_SUPPORTED; ipv6_support.status = IPV6_SUPPORT::IPV6_SUPPORTED;
return true; return true;
} }

View file

@ -970,33 +970,15 @@ namespace rpcn
state = rpcn_state::failure_no_failure; 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; state = rpcn_state::failure_input;
return false; return false;
} }
auto splithost = fmt::split(host, {":"}); const auto [hostname, port] = *hostname_and_port;
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<u16>(std::stoul(splithost[1]));
if (port == 0)
{
rpcn_log.error("connect: RPCN port is invalid!");
state = rpcn_state::failure_input;
return false;
}
}
{ {
// Ensures both read & write threads are in waiting state // Ensures both read & write threads are in waiting state
@ -1037,14 +1019,14 @@ namespace rpcn
addrinfo* addr_info{}; 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); rpcn_log.error("connect: Failed to getaddrinfo %s", host);
state = rpcn_state::failure_resolve; state = rpcn_state::failure_resolve;
return false; return false;
} }
bool found_ipv4 = false, found_ipv6 = false; bool found_ipv4 = false;
addrinfo* found = addr_info; addrinfo* found = addr_info;
while (found != nullptr) while (found != nullptr)
@ -1059,13 +1041,9 @@ namespace rpcn
} }
case AF_INET6: 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<u16, be_t<u16>>(3657);
addr_rpcn_udp_ipv6.sin6_family = AF_INET6; addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast<sockaddr_in6*>(found->ai_addr)->sin6_addr;
addr_rpcn_udp_ipv6.sin6_port = std::bit_cast<u16, be_t<u16>>(3657);
addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast<sockaddr_in6*>(found->ai_addr)->sin6_addr;
found_ipv6 = true;
}
break; break;
} }
default: break; default: break;
@ -1081,12 +1059,6 @@ namespace rpcn
return false; 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)); memcpy(&addr_rpcn_udp_ipv4, &addr_rpcn, sizeof(addr_rpcn_udp_ipv4));
addr_rpcn_udp_ipv4.sin_port = std::bit_cast<u16, be_t<u16>>(3657); // htons addr_rpcn_udp_ipv4.sin_port = std::bit_cast<u16, be_t<u16>>(3657); // htons

View file

@ -211,3 +211,33 @@ bool cfg_rpcn::del_host(std::string_view del_description, std::string_view del_h
return false; return false;
} }
std::optional<std::pair<std::string, u16>> 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<u16>(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);
}

View file

@ -36,4 +36,6 @@ private:
void set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts); void set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts);
}; };
std::optional<std::pair<std::string, u16>> parse_rpcn_host(std::string_view host);
extern cfg_rpcn g_cfg_rpcn; extern cfg_rpcn g_cfg_rpcn;

View file

@ -200,9 +200,8 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
g_cfg_rpcn.set_host(host.toString().toStdString()); g_cfg_rpcn.set_host(host.toString().toStdString());
g_cfg_rpcn.save(); g_cfg_rpcn.save();
// Resets the state in case the support was limited by the RPCN server // Resets the ipv6 support as it depends on availability of the feature on the server
if (!np::is_ipv6_supported()) np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN);
np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN);
}); });
connect(btn_add_server, &QAbstractButton::clicked, this, [this]() connect(btn_add_server, &QAbstractButton::clicked, this, [this]()