aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/nifm/nifm.cpp37
-rw-r--r--src/core/hle/service/sockets/bsd.cpp71
-rw-r--r--src/core/hle/service/sockets/bsd.h12
-rw-r--r--src/core/internal_network/network.cpp11
-rw-r--r--src/core/internal_network/network.h4
-rw-r--r--src/core/internal_network/socket_proxy.cpp29
-rw-r--r--src/core/internal_network/socket_proxy.h3
7 files changed, 111 insertions, 56 deletions
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e0a23f7a63..8714597f89 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -73,13 +73,8 @@ static u128 MakeUuidFromName(std::string_view name) {
// A joined room is the emulated network, even in airplane mode. Its sockets only reach room
// members, and LAN games (e.g. Mario Kart World LAN Play) need their connection request accepted.
static bool IsNetworkAvailable() {
- if (const auto room_member = Network::GetRoomMember().lock()) {
- // The room host and moderators are in the Moderator state instead of Joined
- const auto state = room_member->GetState();
- if (state == Network::RoomMember::State::Joined ||
- state == Network::RoomMember::State::Moderator) {
- return true;
- }
+ if (Network::GetRoomIPv4Address()) {
+ return true;
}
return Network::GetHostIPv4Address().has_value() && !Settings::values.airplane_mode.GetValue();
}
@@ -128,9 +123,9 @@ static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect s
// A host /24 mask would then put room members on different subnets, so LAN games ignore each other.
constexpr Network::IPv4Address RoomSubnetMask{255, 255, 0, 0};
-static void ApplyRoomAddress(const Network::RoomMember& room_member, IpAddressSetting& setting) {
- if (room_member.IsConnected()) {
- setting.ip_address = room_member.GetFakeIpAddress();
+static void ApplyRoomAddress(IpAddressSetting& setting) {
+ if (const auto room_ip = Network::GetRoomIPv4Address()) {
+ setting.ip_address = *room_ip;
setting.subnet_mask = RoomSubnetMask;
}
}
@@ -596,7 +591,7 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
std::strncpy(profile.network_name.data(), st.ssid, sizeof(profile.network_name) - 1);
if (auto room_member = Network::GetRoomMember().lock()) {
- ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting);
+ ApplyRoomAddress(profile.ip_setting_data.ip_address_setting);
profile.ip_setting_data.dns_setting.secondary_dns = {8, 8, 8, 8};
}
@@ -618,6 +613,8 @@ void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) {
using Network::HostAdapterKind;
const auto adapters = Network::GetAvailableNetworkInterfaces();
+ const auto selected = Network::GetSelectedNetworkInterface();
+ const auto room_ip = Network::GetRoomIPv4Address();
constexpr size_t kEntry = 0x3F0;
std::vector<u8> blob(adapters.size() * kEntry, 0);
@@ -633,6 +630,12 @@ void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) {
*reinterpret_cast<in_addr*>(base + 0x1C) = host.subnet_mask;
*reinterpret_cast<in_addr*>(base + 0x20) = host.gateway;
+ // The room traffic goes through the selected adapter, so report it like the other calls
+ if (room_ip && selected && host.name == selected->name) {
+ std::memcpy(base + 0x18, room_ip->data(), room_ip->size());
+ std::memcpy(base + 0x1C, RoomSubnetMask.data(), RoomSubnetMask.size());
+ }
+
std::string name_utf8 = host.name;
name_utf8.resize(0x110, '\0');
std::memcpy(base + 0x2E0, name_utf8.data(), 0x110);
@@ -732,9 +735,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
.wireless_setting_data{wifi}};
}();
- if (auto room_member = Network::GetRoomMember().lock()) {
- ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting);
- }
+ ApplyRoomAddress(profile.ip_setting_data.ip_address_setting);
ctx.WriteBuffer(profile);
@@ -871,10 +872,8 @@ void IGeneralService::GetCurrentIpAddress(HLERequestContext& ctx) {
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
- if (auto room_member = Network::GetRoomMember().lock()) {
- if (room_member->IsConnected()) {
- ipv4 = room_member->GetFakeIpAddress();
- }
+ if (const auto room_ip = Network::GetRoomIPv4Address()) {
+ ipv4 = room_ip;
}
IPC::ResponseBuilder rb{ctx, 3};
@@ -921,7 +920,7 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
}
if (auto room_member = Network::GetRoomMember().lock()) {
- ApplyRoomAddress(*room_member, info.ip);
+ ApplyRoomAddress(info.ip);
info.dns.is_automatic = true;
info.dns.primary_dns = {1, 1, 1, 1};
info.dns.secondary_dns = {8, 8, 8, 8};
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 02c6bb7762..1b6b132769 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -535,33 +535,36 @@ std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol pro
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
- const s32 fd = FindFreeFileDescriptorHandle();
- if (fd < 0) {
- LOG_ERROR(Service, "No more file descriptors available");
- return {-1, Errno::MFILE};
+ // Checked before taking an fd. A joined room does not lift it: room TCP is only stubbed.
+ if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
+ LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
+ return {-1, Errno::NOTCONN};
}
- file_descriptors[fd] = FileDescriptor{};
- FileDescriptor& descriptor = *file_descriptors[fd];
- // ENONMEM might be thrown here
-
- LOG_INFO(Service, "New socket fd={}", fd);
-
+ std::shared_ptr<Network::SocketBase> socket;
auto room_member = Network::GetRoomMember().lock();
if (room_member && room_member->IsConnected()) {
- descriptor.socket = std::make_shared<Network::ProxySocket>();
+ socket = std::make_shared<Network::ProxySocket>();
} else {
- descriptor.socket = std::make_shared<Network::Socket>();
+ socket = std::make_shared<Network::Socket>();
}
+ socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
- descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
- descriptor.is_connection_based = IsConnectionBased(type);
-
- if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
- LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
- return {-1, Errno::NOTCONN};
+ // Publish the socket only once it is set up, since the room thread delivers packets to it
+ std::scoped_lock lock{file_descriptors_mutex};
+ const s32 fd = FindFreeFileDescriptorHandle();
+ if (fd < 0) {
+ LOG_ERROR(Service, "No more file descriptors available");
+ return {-1, Errno::MFILE};
}
+ file_descriptors[fd] = FileDescriptor{
+ .socket = std::move(socket),
+ .is_connection_based = IsConnectionBased(type),
+ };
+
+ LOG_INFO(Service, "New socket fd={}", fd);
+
return {fd, Errno::SUCCESS};
}
@@ -649,10 +652,13 @@ std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer)
return {-1, Translate(bsd_errno)};
}
- file_descriptors[new_fd] = FileDescriptor{};
- FileDescriptor& new_descriptor = *file_descriptors[new_fd];
- new_descriptor.socket = std::move(result.socket);
- new_descriptor.is_connection_based = descriptor.is_connection_based;
+ {
+ std::scoped_lock lock{file_descriptors_mutex};
+ file_descriptors[new_fd] = FileDescriptor{
+ .socket = std::move(result.socket),
+ .is_connection_based = descriptor.is_connection_based,
+ };
+ }
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
PutValue(write_buffer, guest_addr_in);
@@ -1007,6 +1013,7 @@ Errno BSD_USA::CloseImpl(s32 fd) {
LOG_INFO(Service, "Close socket fd={}", fd);
+ std::scoped_lock lock{file_descriptors_mutex};
file_descriptors[fd].reset();
return bsd_errno;
}
@@ -1016,6 +1023,7 @@ std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
return Errno::BADF;
}
+ std::scoped_lock lock{file_descriptors_mutex};
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
@@ -1071,12 +1079,14 @@ void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const
}
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
+ // Runs on the room thread. The lock keeps a socket alive while a bsd thread closes it.
+ std::scoped_lock lock{file_descriptors_mutex};
for (auto& optional_descriptor : file_descriptors) {
- if (!optional_descriptor.has_value()) {
+ if (!optional_descriptor.has_value() || !optional_descriptor->socket) {
continue;
}
FileDescriptor& descriptor = *optional_descriptor;
- descriptor.socket.get()->HandleProxyPacket(packet);
+ descriptor.socket->HandleProxyPacket(packet);
}
}
@@ -1140,18 +1150,27 @@ BSD_USA::BSD_USA(Core::System& system_, const char* name, bool is_user_)
RegisterHandlers(functions);
+ std::scoped_lock lock{proxy_binding_mutex};
+ if (proxy_binding_count++ != 0) {
+ return;
+ }
if (auto room_member = Network::GetRoomMember().lock()) {
- proxy_packet_received = room_member->BindOnProxyPacketReceived(
- [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
+ proxy_packet_received =
+ room_member->BindOnProxyPacketReceived(&BSD_USA::OnProxyPacketReceived);
} else {
LOG_ERROR(Service, "Network isn't initialized");
}
}
BSD_USA::~BSD_USA() {
+ std::scoped_lock lock{proxy_binding_mutex};
+ if (--proxy_binding_count != 0) {
+ return;
+ }
if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Unbind(proxy_packet_received);
}
+ proxy_packet_received.reset();
}
std::unique_lock<std::mutex> BSD_USA::LockService() noexcept {
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 5cc3eb5b81..0df5c58945 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,6 +7,7 @@
#pragma once
#include <memory>
+#include <mutex>
#include <span>
#include <vector>
#include <variant>
@@ -181,12 +182,17 @@ private:
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+ /// Guards file_descriptors against the room thread, which delivers proxy packets to them
+ static inline std::mutex file_descriptors_mutex;
/// Callback to parse and handle a received wifi packet.
- void OnProxyPacketReceived(const Network::ProxyPacket& packet);
+ static void OnProxyPacketReceived(const Network::ProxyPacket& packet);
- // Callback identifier for the OnProxyPacketReceived event.
- Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
+ // Callback identifier for the OnProxyPacketReceived event. bsd:s, bsd:u and bsd:a share
+ // file_descriptors, so they bind once; otherwise each packet is queued once per service.
+ static inline Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
+ static inline std::mutex proxy_binding_mutex;
+ static inline size_t proxy_binding_count = 0;
protected:
std::unique_lock<std::mutex> LockService() noexcept override;
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 01d37fb35b..d1c436aa08 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -722,6 +722,17 @@ std::optional<IPv4Address> GetHostIPv4Address() {
return TranslateIPv4(network_interface->ip_address);
}
+std::optional<IPv4Address> GetRoomIPv4Address() {
+ if (const auto room_member = GetRoomMember().lock()) {
+ // The room host and moderators are in the Moderator state instead of Joined
+ const auto state = room_member->GetState();
+ if (state == RoomMember::State::Joined || state == RoomMember::State::Moderator) {
+ return room_member->GetFakeIpAddress();
+ }
+ }
+ return std::nullopt;
+}
+
std::string IPv4AddressToString(IPv4Address ip_addr) {
std::array<char, INET_ADDRSTRLEN> buf = {};
ASSERT(inet_ntop(AF_INET, &ip_addr, buf.data(), sizeof(buf)) == buf.data());
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index 2c9ae523c5..a598f75348 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -121,6 +121,10 @@ constexpr IPv4Address TranslateIPv4(in_addr addr) {
/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
std::optional<IPv4Address> GetHostIPv4Address();
+/// @brief Returns the fake IPv4 address of the joined room, the emulated network while in a room
+/// @return nullopt outside a room and while still joining it
+std::optional<IPv4Address> GetRoomIPv4Address();
+
std::string IPv4AddressToString(IPv4Address ip_addr);
u32 IPv4AddressToInteger(IPv4Address ip_addr);
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 83eb2ce9d0..6c2f79da6e 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -43,9 +43,21 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode");
return;
}
+ }
- auto decompressed = packet;
- decompressed.data = Common::Compression::DecompressDataZSTD(packet.data);
+ // Decompress without the lock, so the game thread does not wait for it
+ ProxyPacket decompressed{
+ .local_endpoint = packet.local_endpoint,
+ .remote_endpoint = packet.remote_endpoint,
+ .protocol = packet.protocol,
+ .broadcast = packet.broadcast,
+ .data = Common::Compression::DecompressDataZSTD(packet.data),
+ };
+ {
+ std::lock_guard guard(packets_mutex);
+ if (closed) {
+ return;
+ }
received_packets.push(std::move(decompressed));
}
packets_cv.notify_one();
@@ -152,26 +164,28 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length) {
- ProxyPacket& packet = received_packets.front();
+ const ProxyPacket& packet = received_packets.front();
if (addr) {
addr->family = Domain::INET;
addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number
}
- const std::size_t read_bytes = (std::min)(packet.data.size(), max_length);
- std::memcpy(message.data(), packet.data.data(), read_bytes);
+ const std::span<const u8> unread = std::span{packet.data}.subspan(front_read_offset);
+ const std::size_t read_bytes = (std::min)(unread.size(), max_length);
+ std::memcpy(message.data(), unread.data(), read_bytes);
if ((flags & FLAG_MSG_PEEK) != 0) {
return {static_cast<s32>(read_bytes), Errno::SUCCESS};
}
- if (protocol == Protocol::TCP && read_bytes < packet.data.size()) {
+ if (protocol == Protocol::TCP && read_bytes < unread.size()) {
// Keep the unread part of the stream for the next call
- packet.data.erase(packet.data.begin(), packet.data.begin() + read_bytes);
+ front_read_offset += read_bytes;
} else {
// Like BSD recv(), the rest of a datagram that does not fit is discarded
received_packets.pop();
+ front_read_offset = 0;
}
return {static_cast<s32>(read_bytes), Errno::SUCCESS};
@@ -240,6 +254,7 @@ Errno ProxySocket::Close() {
fd = INVALID_SOCKET;
closed = true;
received_packets = {};
+ front_read_offset = 0;
}
packets_cv.notify_all();
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
index 8343fcbae1..d2bd7aae77 100644
--- a/src/core/internal_network/socket_proxy.h
+++ b/src/core/internal_network/socket_proxy.h
@@ -90,7 +90,8 @@ private:
SockAddrIn local_endpoint{};
bool blocking = true;
std::queue<ProxyPacket> received_packets;
- Protocol protocol;
+ std::size_t front_read_offset = 0; ///< Bytes of the front TCP packet that were already read
+ Protocol protocol{};
std::mutex packets_mutex;
std::condition_variable packets_cv;