From 0906de9a14b735d1d409290ca050eb7d2c2d3d84 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 10 Oct 2017 17:32:14 -0400 Subject: hle: Remove a large amount of 3ds-specific service code. --- src/core/hle/service/nwm/nwm.cpp | 28 - src/core/hle/service/nwm/nwm.h | 14 - src/core/hle/service/nwm/nwm_cec.cpp | 19 - src/core/hle/service/nwm/nwm_cec.h | 22 - src/core/hle/service/nwm/nwm_ext.cpp | 19 - src/core/hle/service/nwm/nwm_ext.h | 22 - src/core/hle/service/nwm/nwm_inf.cpp | 21 - src/core/hle/service/nwm/nwm_inf.h | 22 - src/core/hle/service/nwm/nwm_sap.cpp | 20 - src/core/hle/service/nwm/nwm_sap.h | 22 - src/core/hle/service/nwm/nwm_soc.cpp | 20 - src/core/hle/service/nwm/nwm_soc.h | 22 - src/core/hle/service/nwm/nwm_tst.cpp | 20 - src/core/hle/service/nwm/nwm_tst.h | 22 - src/core/hle/service/nwm/nwm_uds.cpp | 1035 --------------------------- src/core/hle/service/nwm/nwm_uds.h | 111 --- src/core/hle/service/nwm/uds_beacon.cpp | 329 --------- src/core/hle/service/nwm/uds_beacon.h | 140 ---- src/core/hle/service/nwm/uds_connection.cpp | 88 --- src/core/hle/service/nwm/uds_connection.h | 56 -- src/core/hle/service/nwm/uds_data.cpp | 373 ---------- src/core/hle/service/nwm/uds_data.h | 164 ----- 22 files changed, 2589 deletions(-) delete mode 100644 src/core/hle/service/nwm/nwm.cpp delete mode 100644 src/core/hle/service/nwm/nwm.h delete mode 100644 src/core/hle/service/nwm/nwm_cec.cpp delete mode 100644 src/core/hle/service/nwm/nwm_cec.h delete mode 100644 src/core/hle/service/nwm/nwm_ext.cpp delete mode 100644 src/core/hle/service/nwm/nwm_ext.h delete mode 100644 src/core/hle/service/nwm/nwm_inf.cpp delete mode 100644 src/core/hle/service/nwm/nwm_inf.h delete mode 100644 src/core/hle/service/nwm/nwm_sap.cpp delete mode 100644 src/core/hle/service/nwm/nwm_sap.h delete mode 100644 src/core/hle/service/nwm/nwm_soc.cpp delete mode 100644 src/core/hle/service/nwm/nwm_soc.h delete mode 100644 src/core/hle/service/nwm/nwm_tst.cpp delete mode 100644 src/core/hle/service/nwm/nwm_tst.h delete mode 100644 src/core/hle/service/nwm/nwm_uds.cpp delete mode 100644 src/core/hle/service/nwm/nwm_uds.h delete mode 100644 src/core/hle/service/nwm/uds_beacon.cpp delete mode 100644 src/core/hle/service/nwm/uds_beacon.h delete mode 100644 src/core/hle/service/nwm/uds_connection.cpp delete mode 100644 src/core/hle/service/nwm/uds_connection.h delete mode 100644 src/core/hle/service/nwm/uds_data.cpp delete mode 100644 src/core/hle/service/nwm/uds_data.h (limited to 'src/core/hle/service/nwm') diff --git a/src/core/hle/service/nwm/nwm.cpp b/src/core/hle/service/nwm/nwm.cpp deleted file mode 100644 index 9f1994dc3..000000000 --- a/src/core/hle/service/nwm/nwm.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm.h" -#include "core/hle/service/nwm/nwm_cec.h" -#include "core/hle/service/nwm/nwm_ext.h" -#include "core/hle/service/nwm/nwm_inf.h" -#include "core/hle/service/nwm/nwm_sap.h" -#include "core/hle/service/nwm/nwm_soc.h" -#include "core/hle/service/nwm/nwm_tst.h" -#include "core/hle/service/nwm/nwm_uds.h" - -namespace Service { -namespace NWM { - -void Init() { - AddService(new NWM_CEC); - AddService(new NWM_EXT); - AddService(new NWM_INF); - AddService(new NWM_SAP); - AddService(new NWM_SOC); - AddService(new NWM_TST); - AddService(new NWM_UDS); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm.h b/src/core/hle/service/nwm/nwm.h deleted file mode 100644 index 6926b29a6..000000000 --- a/src/core/hle/service/nwm/nwm.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -namespace Service { -namespace NWM { - -/// Initialize all NWM services -void Init(); - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_cec.cpp b/src/core/hle/service/nwm/nwm_cec.cpp deleted file mode 100644 index 7f03987df..000000000 --- a/src/core/hle/service/nwm/nwm_cec.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_cec.h" - -namespace Service { -namespace NWM { - -const Interface::FunctionInfo FunctionTable[] = { - {0x000D0082, nullptr, "SendProbeRequest"}, -}; - -NWM_CEC::NWM_CEC() { - Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_cec.h b/src/core/hle/service/nwm/nwm_cec.h deleted file mode 100644 index 07b6addb5..000000000 --- a/src/core/hle/service/nwm/nwm_cec.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_CEC final : public Interface { -public: - NWM_CEC(); - - std::string GetPortName() const override { - return "nwm::CEC"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_ext.cpp b/src/core/hle/service/nwm/nwm_ext.cpp deleted file mode 100644 index 605640a13..000000000 --- a/src/core/hle/service/nwm/nwm_ext.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_ext.h" - -namespace Service { -namespace NWM { - -const Interface::FunctionInfo FunctionTable[] = { - {0x00080040, nullptr, "ControlWirelessEnabled"}, -}; - -NWM_EXT::NWM_EXT() { - Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_ext.h b/src/core/hle/service/nwm/nwm_ext.h deleted file mode 100644 index 51d39d9ea..000000000 --- a/src/core/hle/service/nwm/nwm_ext.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_EXT final : public Interface { -public: - NWM_EXT(); - - std::string GetPortName() const override { - return "nwm::EXT"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_inf.cpp b/src/core/hle/service/nwm/nwm_inf.cpp deleted file mode 100644 index c8470589b..000000000 --- a/src/core/hle/service/nwm/nwm_inf.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_inf.h" - -namespace Service { -namespace NWM { - -const Interface::FunctionInfo FunctionTable[] = { - {0x000603C4, nullptr, "RecvBeaconBroadcastData"}, - {0x00070742, nullptr, "ConnectToEncryptedAP"}, - {0x00080302, nullptr, "ConnectToAP"}, -}; - -NWM_INF::NWM_INF() { - Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_inf.h b/src/core/hle/service/nwm/nwm_inf.h deleted file mode 100644 index 0043d769c..000000000 --- a/src/core/hle/service/nwm/nwm_inf.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_INF final : public Interface { -public: - NWM_INF(); - - std::string GetPortName() const override { - return "nwm::INF"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_sap.cpp b/src/core/hle/service/nwm/nwm_sap.cpp deleted file mode 100644 index fd29ed761..000000000 --- a/src/core/hle/service/nwm/nwm_sap.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_sap.h" - -namespace Service { -namespace NWM { - -/* -const Interface::FunctionInfo FunctionTable[] = { -}; -*/ - -NWM_SAP::NWM_SAP() { - // Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_sap.h b/src/core/hle/service/nwm/nwm_sap.h deleted file mode 100644 index f692e06d4..000000000 --- a/src/core/hle/service/nwm/nwm_sap.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_SAP final : public Interface { -public: - NWM_SAP(); - - std::string GetPortName() const override { - return "nwm::SAP"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_soc.cpp b/src/core/hle/service/nwm/nwm_soc.cpp deleted file mode 100644 index fdffcb925..000000000 --- a/src/core/hle/service/nwm/nwm_soc.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_soc.h" - -namespace Service { -namespace NWM { - -/* -const Interface::FunctionInfo FunctionTable[] = { -}; -*/ - -NWM_SOC::NWM_SOC() { - // Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_soc.h b/src/core/hle/service/nwm/nwm_soc.h deleted file mode 100644 index 594941d7e..000000000 --- a/src/core/hle/service/nwm/nwm_soc.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_SOC final : public Interface { -public: - NWM_SOC(); - - std::string GetPortName() const override { - return "nwm::SOC"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_tst.cpp b/src/core/hle/service/nwm/nwm_tst.cpp deleted file mode 100644 index 5f292e5db..000000000 --- a/src/core/hle/service/nwm/nwm_tst.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_tst.h" - -namespace Service { -namespace NWM { - -/* -const Interface::FunctionInfo FunctionTable[] = { -}; -*/ - -NWM_TST::NWM_TST() { - // Register(FunctionTable); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_tst.h b/src/core/hle/service/nwm/nwm_tst.h deleted file mode 100644 index 8deca3216..000000000 --- a/src/core/hle/service/nwm/nwm_tst.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -class NWM_TST final : public Interface { -public: - NWM_TST(); - - std::string GetPortName() const override { - return "nwm::TST"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp deleted file mode 100644 index 87a6b0eca..000000000 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ /dev/null @@ -1,1035 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/core_timing.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/lock.h" -#include "core/hle/result.h" -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_beacon.h" -#include "core/hle/service/nwm/uds_connection.h" -#include "core/hle/service/nwm/uds_data.h" -#include "core/memory.h" -#include "network/network.h" - -namespace Service { -namespace NWM { - -// Event that is signaled every time the connection status changes. -static Kernel::SharedPtr connection_status_event; - -// Shared memory provided by the application to store the receive buffer. -// This is not currently used. -static Kernel::SharedPtr recv_buffer_memory; - -// Connection status of this 3DS. -static ConnectionStatus connection_status{}; - -/* Node information about the current network. - * The amount of elements in this vector is always the maximum number - * of nodes specified in the network configuration. - * The first node is always the host. - */ -static NodeList node_info; - -// Node information about our own system. -static NodeInfo current_node; - -// Mapping of bind node ids to their respective events. -static std::unordered_map> bind_node_events; - -// The WiFi network channel that the network is currently on. -// Since we're not actually interacting with physical radio waves, this is just a dummy value. -static u8 network_channel = DefaultNetworkChannel; - -// Information about the network that we're currently connected to. -static NetworkInfo network_info; - -// Event that will generate and send the 802.11 beacon frames. -static int beacon_broadcast_event; - -// Mutex to synchronize access to the connection status between the emulation thread and the -// network thread. -static std::mutex connection_status_mutex; - -// Mutex to synchronize access to the list of received beacons between the emulation thread and the -// network thread. -static std::mutex beacon_mutex; - -// Number of beacons to store before we start dropping the old ones. -// TODO(Subv): Find a more accurate value for this limit. -constexpr size_t MaxBeaconFrames = 15; - -// List of the last beacons received from the network. -static std::list received_beacons; - -/** - * Returns a list of received 802.11 beacon frames from the specified sender since the last call. - */ -std::list GetReceivedBeacons(const MacAddress& sender) { - std::lock_guard lock(beacon_mutex); - if (sender != Network::BroadcastMac) { - std::list filtered_list; - const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(), - [&sender](const Network::WifiPacket& packet) { - return packet.transmitter_address == sender; - }); - if (beacon != received_beacons.end()) { - filtered_list.push_back(*beacon); - // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries - received_beacons.erase(beacon); - } - return filtered_list; - } - return std::move(received_beacons); -} - -/// Sends a WifiPacket to the room we're currently connected to. -void SendPacket(Network::WifiPacket& packet) { - // TODO(Subv): Implement. -} - -/* - * Returns an available index in the nodes array for the - * currently-hosted UDS network. - */ -static u16 GetNextAvailableNodeId() { - for (u16 index = 0; index < connection_status.max_nodes; ++index) { - if ((connection_status.node_bitmask & (1 << index)) == 0) - return index; - } - - // Any connection attempts to an already full network should have been refused. - ASSERT_MSG(false, "No available connection slots in the network"); -} - -// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size -// limit is exceeded. -void HandleBeaconFrame(const Network::WifiPacket& packet) { - std::lock_guard lock(beacon_mutex); - const auto unique_beacon = - std::find_if(received_beacons.begin(), received_beacons.end(), - [&packet](const Network::WifiPacket& new_packet) { - return new_packet.transmitter_address == packet.transmitter_address; - }); - if (unique_beacon != received_beacons.end()) { - // We already have a beacon from the same mac in the deque, remove the old one; - received_beacons.erase(unique_beacon); - } - - received_beacons.emplace_back(packet); - - // Discard old beacons if the buffer is full. - if (received_beacons.size() > MaxBeaconFrames) - received_beacons.pop_front(); -} - -void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { - auto assoc_result = GetAssociationResult(packet.data); - - ASSERT_MSG(std::get(assoc_result) == AssocStatus::Successful, - "Could not join network"); - { - std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::Connecting)); - } - - // Send the EAPoL-Start packet to the server. - using Network::WifiPacket; - WifiPacket eapol_start; - eapol_start.channel = network_channel; - eapol_start.data = GenerateEAPoLStartFrame(std::get(assoc_result), current_node); - // TODO(B3N30): Encrypt the packet. - eapol_start.destination_address = packet.transmitter_address; - eapol_start.type = WifiPacket::PacketType::Data; - - SendPacket(eapol_start); -} - -static void HandleEAPoLPacket(const Network::WifiPacket& packet) { - std::lock_guard lock(connection_status_mutex); - - if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", - connection_status.status); - return; - } - - auto node = DeserializeNodeInfoFromFrame(packet.data); - - if (connection_status.max_nodes == connection_status.total_nodes) { - // Reject connection attempt - LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent."); - // TODO(B3N30): Figure out what packet is sent here - return; - } - - // Get an unused network node id - u16 node_id = GetNextAvailableNodeId(); - node.network_node_id = node_id + 1; - - connection_status.node_bitmask |= 1 << node_id; - connection_status.changed_nodes |= 1 << node_id; - connection_status.nodes[node_id] = node.network_node_id; - connection_status.total_nodes++; - - u8 current_nodes = network_info.total_nodes; - node_info[current_nodes] = node; - - network_info.total_nodes++; - - // Send the EAPoL-Logoff packet. - using Network::WifiPacket; - WifiPacket eapol_logoff; - eapol_logoff.channel = network_channel; - eapol_logoff.data = - GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info, - network_info.max_nodes, network_info.total_nodes); - // TODO(Subv): Encrypt the packet. - eapol_logoff.destination_address = packet.transmitter_address; - eapol_logoff.type = WifiPacket::PacketType::Data; - - SendPacket(eapol_logoff); - // TODO(B3N30): Broadcast updated node list - // The 3ds does this presumably to support spectators. - std::lock_guard lock(HLE::g_hle_lock); - connection_status_event->Signal(); - } else { - if (connection_status.status != static_cast(NetworkStatus::NotConnected)) { - LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", - connection_status.status); - return; - } - auto logoff = ParseEAPoLLogoffFrame(packet.data); - - network_info.total_nodes = logoff.connected_nodes; - network_info.max_nodes = logoff.max_nodes; - - connection_status.network_node_id = logoff.assigned_node_id; - connection_status.total_nodes = logoff.connected_nodes; - connection_status.max_nodes = logoff.max_nodes; - - node_info.clear(); - node_info.reserve(network_info.max_nodes); - for (size_t index = 0; index < logoff.connected_nodes; ++index) { - connection_status.node_bitmask |= 1 << index; - connection_status.changed_nodes |= 1 << index; - connection_status.nodes[index] = logoff.nodes[index].network_node_id; - - node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index])); - } - - // We're now connected, signal the application - connection_status.status = static_cast(NetworkStatus::ConnectedAsClient); - // Some games require ConnectToNetwork to block, for now it doesn't - // If blocking is implemented this lock needs to be changed, - // otherwise it might cause deadlocks - std::lock_guard lock(HLE::g_hle_lock); - connection_status_event->Signal(); - } -} - -/* - * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11 - * authentication frame with SEQ1. - */ -void StartConnectionSequence(const MacAddress& server) { - using Network::WifiPacket; - WifiPacket auth_request; - { - std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); - - // TODO(Subv): Handle timeout. - - // Send an authentication frame with SEQ1 - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); - auth_request.destination_address = server; - auth_request.type = WifiPacket::PacketType::Authentication; - } - - SendPacket(auth_request); -} - -/// Sends an Association Response frame to the specified mac address -void SendAssociationResponseFrame(const MacAddress& address) { - using Network::WifiPacket; - WifiPacket assoc_response; - - { - std::lock_guard lock(connection_status_mutex); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u", - connection_status.status); - return; - } - - assoc_response.channel = network_channel; - // TODO(Subv): This will cause multiple clients to end up with the same association id, but - // we're not using that for anything. - u16 association_id = 1; - assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, - network_info.network_id); - assoc_response.destination_address = address; - assoc_response.type = WifiPacket::PacketType::AssociationResponse; - } - - SendPacket(assoc_response); -} - -/* - * Handles the authentication request frame and sends the authentication response and association - * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds - * with an Authentication frame containing SEQ2, and immediately sends an Association response frame - * containing the details of the access point and the assigned association id for the new client. - */ -void HandleAuthenticationFrame(const Network::WifiPacket& packet) { - // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior - if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { - using Network::WifiPacket; - WifiPacket auth_request; - { - std::lock_guard lock(connection_status_mutex); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - LOG_ERROR(Service_NWM, - "Connection sequence aborted, because connection status is %u", - connection_status.status); - return; - } - - // Respond with an authentication response frame with SEQ2 - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); - auth_request.destination_address = packet.transmitter_address; - auth_request.type = WifiPacket::PacketType::Authentication; - } - SendPacket(auth_request); - - SendAssociationResponseFrame(packet.transmitter_address); - } -} - -static void HandleDataFrame(const Network::WifiPacket& packet) { - switch (GetFrameEtherType(packet.data)) { - case EtherType::EAPoL: - HandleEAPoLPacket(packet); - break; - case EtherType::SecureData: - // TODO(B3N30): Handle SecureData packets - break; - } -} - -/// Callback to parse and handle a received wifi packet. -void OnWifiPacketReceived(const Network::WifiPacket& packet) { - switch (packet.type) { - case Network::WifiPacket::PacketType::Beacon: - HandleBeaconFrame(packet); - break; - case Network::WifiPacket::PacketType::Authentication: - HandleAuthenticationFrame(packet); - break; - case Network::WifiPacket::PacketType::AssociationResponse: - HandleAssociationResponseFrame(packet); - break; - case Network::WifiPacket::PacketType::Data: - HandleDataFrame(packet); - break; - } -} - -/** - * NWM_UDS::Shutdown service function - * Inputs: - * 1 : None - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void Shutdown(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(purpasmart): Verify return header on HW - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_NWM, "(STUBBED) called"); -} - -/** - * NWM_UDS::RecvBeaconBroadcastData service function - * Returns the raw beacon data for nearby networks that match the supplied WlanCommId. - * Inputs: - * 1 : Output buffer max size - * 2-3 : Unknown - * 4-5 : Host MAC address. - * 6-14 : Unused - * 15 : WLan Comm Id - * 16 : Id - * 17 : Value 0 - * 18 : Input handle - * 19 : (Size<<4) | 12 - * 20 : Output buffer ptr - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void RecvBeaconBroadcastData(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 16, 4); - - u32 out_buffer_size = rp.Pop(); - u32 unk1 = rp.Pop(); - u32 unk2 = rp.Pop(); - - MacAddress mac_address; - rp.PopRaw(mac_address); - - rp.Skip(9, false); - - u32 wlan_comm_id = rp.Pop(); - u32 id = rp.Pop(); - Kernel::Handle input_handle = rp.PopHandle(); - - size_t desc_size; - const VAddr out_buffer_ptr = rp.PopMappedBuffer(&desc_size); - ASSERT(desc_size == out_buffer_size); - - VAddr current_buffer_pos = out_buffer_ptr; - u32 total_size = sizeof(BeaconDataReplyHeader); - - // Retrieve all beacon frames that were received from the desired mac address. - auto beacons = GetReceivedBeacons(mac_address); - - BeaconDataReplyHeader data_reply_header{}; - data_reply_header.total_entries = static_cast(beacons.size()); - data_reply_header.max_output_size = out_buffer_size; - - Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); - current_buffer_pos += sizeof(BeaconDataReplyHeader); - - // Write each of the received beacons into the buffer - for (const auto& beacon : beacons) { - BeaconEntryHeader entry{}; - // TODO(Subv): Figure out what this size is used for. - entry.unk_size = static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); - entry.total_size = static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); - entry.wifi_channel = beacon.channel; - entry.header_size = sizeof(BeaconEntryHeader); - entry.mac_address = beacon.transmitter_address; - - ASSERT(current_buffer_pos < out_buffer_ptr + out_buffer_size); - - Memory::WriteBlock(current_buffer_pos, &entry, sizeof(BeaconEntryHeader)); - current_buffer_pos += sizeof(BeaconEntryHeader); - - Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); - current_buffer_pos += static_cast(beacon.data.size()); - - total_size += static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); - } - - // Update the total size in the structure and write it to the buffer again. - data_reply_header.total_size = total_size; - Memory::WriteBlock(out_buffer_ptr, &data_reply_header, sizeof(BeaconDataReplyHeader)); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); - - LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X," - "input_handle=0x%08X, out_buffer_ptr=0x%08X, unk1=0x%08X, unk2=0x%08X", - out_buffer_size, wlan_comm_id, id, input_handle, out_buffer_ptr, unk1, unk2); -} - -/** - * NWM_UDS::Initialize service function - * Inputs: - * 1 : Shared memory size - * 2-11 : Input NodeInfo Structure - * 12 : 2-byte Version - * 13 : Value 0 - * 14 : Shared memory handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Value 0 - * 3 : Output event handle - */ -static void InitializeWithVersion(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 12, 2); - - u32 sharedmem_size = rp.Pop(); - - // Update the node information with the data the game gave us. - rp.PopRaw(current_node); - - u16 version = rp.Pop(); - - Kernel::Handle sharedmem_handle = rp.PopHandle(); - - recv_buffer_memory = Kernel::g_handle_table.Get(sharedmem_handle); - - ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); - - { - std::lock_guard lock(connection_status_mutex); - - // Reset the connection status, it contains all zeros after initialization, - // except for the actual status value. - connection_status = {}; - connection_status.status = static_cast(NetworkStatus::NotConnected); - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); - - // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of - // the room we're currently in. - - LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", - sharedmem_size, version, sharedmem_handle); -} - -/** - * NWM_UDS::GetConnectionStatus service function. - * Returns the connection status structure for the currently open network connection. - * This structure contains information about the connection, - * like the number of connected nodes, etc. - * Inputs: - * 0 : Command header. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2-13 : Channel of the current WiFi network connection. - */ -static void GetConnectionStatus(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 0, 0); - IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); - - rb.Push(RESULT_SUCCESS); - { - std::lock_guard lock(connection_status_mutex); - rb.PushRaw(connection_status); - - // Reset the bitmask of changed nodes after each call to this - // function to prevent falsely informing games of outstanding - // changes in subsequent calls. - // TODO(Subv): Find exactly where the NWM module resets this value. - connection_status.changed_nodes = 0; - } - - LOG_DEBUG(Service_NWM, "called"); -} - -/** - * NWM_UDS::Bind service function. - * Binds a BindNodeId to a data channel and retrieves a data event. - * Inputs: - * 1 : BindNodeId - * 2 : Receive buffer size. - * 3 : u8 Data channel to bind to. - * 4 : Network node id. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Copy handle descriptor. - * 3 : Data available event handle. - */ -static void Bind(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 0); - - u32 bind_node_id = rp.Pop(); - u32 recv_buffer_size = rp.Pop(); - u8 data_channel = rp.Pop(); - u16 network_node_id = rp.Pop(); - - // TODO(Subv): Store the data channel and verify it when receiving data frames. - - LOG_DEBUG(Service_NWM, "called"); - - if (data_channel == 0) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Usage)); - return; - } - - // Create a new event for this bind node. - // TODO(Subv): Signal this event when new data is received on this data channel. - auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, - "NWM::BindNodeEvent" + std::to_string(bind_node_id)); - bind_node_events[bind_node_id] = event; - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - - rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); -} - -/** - * NWM_UDS::BeginHostingNetwork service function. - * Creates a network and starts broadcasting its presence. - * Inputs: - * 1 : Passphrase buffer size. - * 3 : VAddr of the NetworkInfo structure. - * 5 : VAddr of the passphrase. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void BeginHostingNetwork(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 1, 4); - - const u32 passphrase_size = rp.Pop(); - - size_t desc_size; - const VAddr network_info_address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == sizeof(NetworkInfo)); - const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == passphrase_size); - - // TODO(Subv): Store the passphrase and verify it when attempting a connection. - - LOG_DEBUG(Service_NWM, "called"); - - Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); - - // The real UDS module throws a fatal error if this assert fails. - ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); - - { - std::lock_guard lock(connection_status_mutex); - connection_status.status = static_cast(NetworkStatus::ConnectedAsHost); - - // Ensure the application data size is less than the maximum value. - ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, - "Data size is too big."); - - // Set up basic information for this network. - network_info.oui_value = NintendoOUI; - network_info.oui_type = static_cast(NintendoTagId::NetworkInfo); - - connection_status.max_nodes = network_info.max_nodes; - - // Resize the nodes list to hold max_nodes. - node_info.resize(network_info.max_nodes); - - // There's currently only one node in the network (the host). - connection_status.total_nodes = 1; - network_info.total_nodes = 1; - // The host is always the first node - connection_status.network_node_id = 1; - current_node.network_node_id = 1; - connection_status.nodes[0] = connection_status.network_node_id; - // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. - connection_status.node_bitmask |= 1; - // Notify the application that the first node was set. - connection_status.changed_nodes |= 1; - node_info[0] = current_node; - } - - // If the game has a preferred channel, use that instead. - if (network_info.channel != 0) - network_channel = network_info.channel; - - connection_status_event->Signal(); - - // Start broadcasting the network, send a beacon frame every 102.4ms. - CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), - beacon_broadcast_event, 0); - - LOG_WARNING(Service_NWM, - "An UDS network has been created, but broadcasting it is unimplemented."); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - -/** - * NWM_UDS::DestroyNetwork service function. - * Closes the network that we're currently hosting. - * Inputs: - * 0 : Command header. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void DestroyNetwork(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 0, 0); - - // TODO(Subv): Find out what happens if this is called while - // no network is being hosted. - - // Unschedule the beacon broadcast event. - CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); - - { - std::lock_guard lock(connection_status_mutex); - - // TODO(Subv): Check if connection_status is indeed reset after this call. - connection_status = {}; - connection_status.status = static_cast(NetworkStatus::NotConnected); - } - connection_status_event->Signal(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_NWM, "called"); -} - -/** - * NWM_UDS::SendTo service function. - * Sends a data frame to the UDS network we're connected to. - * Inputs: - * 0 : Command header. - * 1 : Unknown. - * 2 : u16 Destination network node id. - * 3 : u8 Data channel. - * 4 : Buffer size >> 2 - * 5 : Data size - * 6 : Flags - * 7 : Input buffer descriptor - * 8 : Input buffer address - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void SendTo(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2); - - rp.Skip(1, false); - u16 dest_node_id = rp.Pop(); - u8 data_channel = rp.Pop(); - rp.Skip(1, false); - u32 data_size = rp.Pop(); - u32 flags = rp.Pop(); - - size_t desc_size; - const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == data_size); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - - u16 network_node_id; - - { - std::lock_guard lock(connection_status_mutex); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && - connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::InvalidState, ErrorLevel::Status)); - return; - } - - if (dest_node_id == connection_status.network_node_id) { - rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Status)); - return; - } - - network_node_id = connection_status.network_node_id; - } - - // TODO(Subv): Do something with the flags. - - constexpr size_t MaxSize = 0x5C6; - if (data_size > MaxSize) { - rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Usage)); - return; - } - - std::vector data(data_size); - Memory::ReadBlock(input_address, data.data(), data.size()); - - // TODO(Subv): Increment the sequence number after each sent packet. - u16 sequence_number = 0; - std::vector data_payload = - GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); - - // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt - // and encapsulate the payload. - - // TODO(Subv): Send the frame. - - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", - static_cast(dest_node_id), data_size, flags, static_cast(data_channel)); -} - -/** - * NWM_UDS::GetChannel service function. - * Returns the WiFi channel in which the network we're connected to is transmitting. - * Inputs: - * 0 : Command header. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Channel of the current WiFi network connection. - */ -static void GetChannel(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - - std::lock_guard lock(connection_status_mutex); - bool is_connected = connection_status.status != static_cast(NetworkStatus::NotConnected); - - u8 channel = is_connected ? network_channel : 0; - - rb.Push(RESULT_SUCCESS); - rb.Push(channel); - - LOG_DEBUG(Service_NWM, "called"); -} - -/** - * NWM_UDS::SetApplicationData service function. - * Updates the application data that is being broadcast in the beacon frames - * for the network that we're hosting. - * Inputs: - * 1 : Data size. - * 3 : VAddr of the data. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Channel of the current WiFi network connection. - */ -static void SetApplicationData(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 2); - - u32 size = rp.Pop(); - - size_t desc_size; - const VAddr address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == size); - - LOG_DEBUG(Service_NWM, "called"); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - - if (size > ApplicationDataSize) { - rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Usage)); - return; - } - - network_info.application_data_size = size; - Memory::ReadBlock(address, network_info.application_data.data(), size); - - rb.Push(RESULT_SUCCESS); -} - -/** - * NWM_UDS::DecryptBeaconData service function. - * Decrypts the encrypted data tags contained in the 802.11 beacons. - * Inputs: - * 1 : Input network struct buffer descriptor. - * 2 : Input network struct buffer ptr. - * 3 : Input tag0 encrypted buffer descriptor. - * 4 : Input tag0 encrypted buffer ptr. - * 5 : Input tag1 encrypted buffer descriptor. - * 6 : Input tag1 encrypted buffer ptr. - * 64 : Output buffer descriptor. - * 65 : Output buffer ptr. - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void DecryptBeaconData(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 0, 6); - - size_t desc_size; - const VAddr network_struct_addr = rp.PopStaticBuffer(&desc_size); - ASSERT(desc_size == sizeof(NetworkInfo)); - - size_t data0_size; - const VAddr encrypted_data0_addr = rp.PopStaticBuffer(&data0_size); - - size_t data1_size; - const VAddr encrypted_data1_addr = rp.PopStaticBuffer(&data1_size); - - size_t output_buffer_size; - const VAddr output_buffer_addr = rp.PeekStaticBuffer(0, &output_buffer_size); - - // This size is hardcoded in the 3DS UDS code. - ASSERT(output_buffer_size == sizeof(NodeInfo) * UDSMaxNodes); - - LOG_WARNING(Service_NWM, "called in0=%08X in1=%08X out=%08X", encrypted_data0_addr, - encrypted_data1_addr, output_buffer_addr); - - NetworkInfo net_info; - Memory::ReadBlock(network_struct_addr, &net_info, sizeof(net_info)); - - // Read the encrypted data. - // The first 4 bytes should be the OUI and the OUI Type of the tags. - std::array oui; - Memory::ReadBlock(encrypted_data0_addr, oui.data(), oui.size()); - ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI"); - Memory::ReadBlock(encrypted_data1_addr, oui.data(), oui.size()); - ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI"); - - ASSERT_MSG(Memory::Read8(encrypted_data0_addr + 3) == - static_cast(NintendoTagId::EncryptedData0), - "Unexpected tag id"); - ASSERT_MSG(Memory::Read8(encrypted_data1_addr + 3) == - static_cast(NintendoTagId::EncryptedData1), - "Unexpected tag id"); - - std::vector beacon_data(data0_size + data1_size); - Memory::ReadBlock(encrypted_data0_addr + 4, beacon_data.data(), data0_size); - Memory::ReadBlock(encrypted_data1_addr + 4, beacon_data.data() + data0_size, data1_size); - - // Decrypt the data - DecryptBeaconData(net_info, beacon_data); - - // The beacon data header contains the MD5 hash of the data. - BeaconData beacon_header; - std::memcpy(&beacon_header, beacon_data.data(), sizeof(beacon_header)); - - // TODO(Subv): Verify the MD5 hash of the data and return 0xE1211005 if invalid. - - u8 num_nodes = net_info.max_nodes; - - std::vector nodes; - - for (int i = 0; i < num_nodes; ++i) { - BeaconNodeInfo info; - std::memcpy(&info, beacon_data.data() + sizeof(beacon_header) + i * sizeof(info), - sizeof(info)); - - // Deserialize the node information. - NodeInfo node{}; - node.friend_code_seed = info.friend_code_seed; - node.network_node_id = info.network_node_id; - for (int i = 0; i < info.username.size(); ++i) - node.username[i] = info.username[i]; - - nodes.push_back(node); - } - - Memory::ZeroBlock(output_buffer_addr, sizeof(NodeInfo) * UDSMaxNodes); - Memory::WriteBlock(output_buffer_addr, nodes.data(), sizeof(NodeInfo) * nodes.size()); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.PushStaticBuffer(output_buffer_addr, output_buffer_size, 0); - rb.Push(RESULT_SUCCESS); -} - -// Sends a 802.11 beacon frame with information about the current network. -static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { - // Don't do anything if we're not actually hosting a network - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) - return; - - std::vector frame = GenerateBeaconFrame(network_info, node_info); - - using Network::WifiPacket; - WifiPacket packet; - packet.type = WifiPacket::PacketType::Beacon; - packet.data = std::move(frame); - packet.destination_address = Network::BroadcastMac; - packet.channel = network_channel; - - SendPacket(packet); - - // Start broadcasting the network, send a beacon frame every 102.4ms. - CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, - beacon_broadcast_event, 0); -} - -/* - * Called when a client connects to an UDS network we're hosting, - * updates the connection status and signals the update event. - * @param network_node_id Network Node Id of the connecting client. - */ -void OnClientConnected(u16 network_node_id) { - std::lock_guard lock(connection_status_mutex); - ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), - "Can not accept clients if we're not hosting a network"); - ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, - "Can not accept connections on a full network"); - - u32 node_id = GetNextAvailableNodeId(); - connection_status.node_bitmask |= 1 << node_id; - connection_status.changed_nodes |= 1 << node_id; - connection_status.nodes[node_id] = network_node_id; - connection_status.total_nodes++; - connection_status_event->Signal(); -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x000102C2, nullptr, "Initialize (deprecated)"}, - {0x00020000, nullptr, "Scrap"}, - {0x00030000, Shutdown, "Shutdown"}, - {0x00040402, nullptr, "CreateNetwork (deprecated)"}, - {0x00050040, nullptr, "EjectClient"}, - {0x00060000, nullptr, "EjectSpectator"}, - {0x00070080, nullptr, "UpdateNetworkAttribute"}, - {0x00080000, DestroyNetwork, "DestroyNetwork"}, - {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, - {0x000A0000, nullptr, "DisconnectNetwork"}, - {0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, - {0x000D0040, nullptr, "GetNodeInformation"}, - {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, - {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, - {0x00100042, SetApplicationData, "SetApplicationData"}, - {0x00110040, nullptr, "GetApplicationData"}, - {0x00120100, Bind, "Bind"}, - {0x00130040, nullptr, "Unbind"}, - {0x001400C0, nullptr, "PullPacket"}, - {0x00150080, nullptr, "SetMaxSendDelay"}, - {0x00170182, SendTo, "SendTo"}, - {0x001A0000, GetChannel, "GetChannel"}, - {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, - {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, - {0x001E0084, nullptr, "ConnectToNetwork"}, - {0x001F0006, DecryptBeaconData, "DecryptBeaconData"}, - {0x00200040, nullptr, "Flush"}, - {0x00210080, nullptr, "SetProbeResponseParam"}, - {0x00220402, nullptr, "ScanOnConnection"}, -}; - -NWM_UDS::NWM_UDS() { - connection_status_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event"); - - Register(FunctionTable); - - beacon_broadcast_event = - CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback); -} - -NWM_UDS::~NWM_UDS() { - network_info = {}; - bind_node_events.clear(); - connection_status_event = nullptr; - recv_buffer_memory = nullptr; - - { - std::lock_guard lock(connection_status_mutex); - connection_status = {}; - connection_status.status = static_cast(NetworkStatus::NotConnected); - } - - CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h deleted file mode 100644 index f1caaf974..000000000 --- a/src/core/hle/service/nwm/nwm_uds.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/service.h" - -// Local-WLAN service - -namespace Service { -namespace NWM { - -const size_t ApplicationDataSize = 0xC8; -const u8 DefaultNetworkChannel = 11; - -// Number of milliseconds in a TU. -const double MillisecondsPerTU = 1.024; -// Interval measured in TU, the default value is 100TU = 102.4ms -const u16 DefaultBeaconInterval = 100; - -/// The maximum number of nodes that can exist in an UDS session. -constexpr u32 UDSMaxNodes = 16; - -struct NodeInfo { - u64_le friend_code_seed; - std::array username; - INSERT_PADDING_BYTES(4); - u16_le network_node_id; - INSERT_PADDING_BYTES(6); -}; - -static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); - -using NodeList = std::vector; - -enum class NetworkStatus { - NotConnected = 3, - ConnectedAsHost = 6, - Connecting = 7, - ConnectedAsClient = 9, - ConnectedAsSpectator = 10, -}; - -struct ConnectionStatus { - u32_le status; - INSERT_PADDING_WORDS(1); - u16_le network_node_id; - u16_le changed_nodes; - u16_le nodes[UDSMaxNodes]; - u8 total_nodes; - u8 max_nodes; - u16_le node_bitmask; -}; - -static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect size."); - -struct NetworkInfo { - std::array host_mac_address; - u8 channel; - INSERT_PADDING_BYTES(1); - u8 initialized; - INSERT_PADDING_BYTES(3); - std::array oui_value; - u8 oui_type; - // This field is received as BigEndian from the game. - u32_be wlan_comm_id; - u8 id; - INSERT_PADDING_BYTES(1); - u16_be attributes; - u32_be network_id; - u8 total_nodes; - u8 max_nodes; - INSERT_PADDING_BYTES(2); - INSERT_PADDING_BYTES(0x1F); - u8 application_data_size; - std::array application_data; -}; - -static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wrong offset."); -static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); -static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); - -/// Additional block tag ids in the Beacon and Association Response frames -enum class TagId : u8 { - SSID = 0, - SupportedRates = 1, - DSParameterSet = 2, - TrafficIndicationMap = 5, - CountryInformation = 7, - ERPInformation = 42, - VendorSpecific = 221 -}; - -class NWM_UDS final : public Interface { -public: - NWM_UDS(); - ~NWM_UDS() override; - - std::string GetPortName() const override { - return "nwm::UDS"; - } -}; - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp deleted file mode 100644 index 73a80d940..000000000 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include "common/assert.h" -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_beacon.h" - -namespace Service { -namespace NWM { - -// 802.11 broadcast MAC address -constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - -constexpr u64 DefaultNetworkUptime = 900000000; // 15 minutes in microseconds. - -// Note: These values were taken from a packet capture of an o3DS XL -// broadcasting a Super Smash Bros. 4 lobby. -constexpr u16 DefaultExtraCapabilities = 0x0431; - -// Size of the SSID broadcast by an UDS beacon frame. -constexpr u8 UDSBeaconSSIDSize = 8; - -// The maximum size of the data stored in the EncryptedData0 tag (24). -constexpr u32 EncryptedDataSizeCutoff = 0xFA; - -/** - * NWM Beacon data encryption key, taken from the NWM module code. - * We stub this with an all-zeros key as that is enough for Citra's purpose. - * The real key can be used here to generate beacons that will be accepted by - * a real 3ds. - */ -constexpr std::array nwm_beacon_key = {}; - -/** - * Generates a buffer with the fixed parameters of an 802.11 Beacon frame - * using dummy values. - * @returns A buffer with the fixed parameters of the beacon frame. - */ -std::vector GenerateFixedParameters() { - std::vector buffer(sizeof(BeaconFrameHeader)); - - BeaconFrameHeader header{}; - // Use a fixed default time for now. - // TODO(Subv): Perhaps use the difference between now and the time the network was started? - header.timestamp = DefaultNetworkUptime; - header.beacon_interval = DefaultBeaconInterval; - header.capabilities = DefaultExtraCapabilities; - - std::memcpy(buffer.data(), &header, sizeof(header)); - - return buffer; -} - -/** - * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte all-zero SSID value. - * @returns A buffer with the SSID tag. - */ -std::vector GenerateSSIDTag() { - std::vector buffer(sizeof(TagHeader) + UDSBeaconSSIDSize); - - TagHeader tag_header{}; - tag_header.tag_id = static_cast(TagId::SSID); - tag_header.length = UDSBeaconSSIDSize; - - std::memcpy(buffer.data(), &tag_header, sizeof(TagHeader)); - - // The rest of the buffer is already filled with zeros. - - return buffer; -} - -/** - * Generates a buffer with the basic tagged parameters of an 802.11 Beacon frame - * such as SSID, Rate Information, Country Information, etc. - * @returns A buffer with the tagged parameters of the beacon frame. - */ -std::vector GenerateBasicTaggedParameters() { - // Append the SSID tag - std::vector buffer = GenerateSSIDTag(); - - // TODO(Subv): Add the SupportedRates tag. - // TODO(Subv): Add the DSParameterSet tag. - // TODO(Subv): Add the TrafficIndicationMap tag. - // TODO(Subv): Add the CountryInformation tag. - // TODO(Subv): Add the ERPInformation tag. - - return buffer; -} - -/** - * Generates a buffer with the Dummy Nintendo tag. - * It is currently unknown what this tag does. - * TODO(Subv): Figure out if this is needed and what it does. - * @returns A buffer with the Nintendo tagged parameters of the beacon frame. - */ -std::vector GenerateNintendoDummyTag() { - // Note: These values were taken from a packet capture of an o3DS XL - // broadcasting a Super Smash Bros. 4 lobby. - constexpr std::array dummy_data = {0x0A, 0x00, 0x00}; - - DummyTag tag{}; - tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = sizeof(DummyTag) - sizeof(TagHeader); - tag.oui_type = static_cast(NintendoTagId::Dummy); - tag.oui = NintendoOUI; - tag.data = dummy_data; - - std::vector buffer(sizeof(DummyTag)); - std::memcpy(buffer.data(), &tag, sizeof(DummyTag)); - - return buffer; -} - -/** - * Generates a buffer with the Network Info Nintendo tag. - * This tag contains the network information of the network that is being broadcast. - * It also contains the application data provided by the application that opened the network. - * @returns A buffer with the Nintendo network info parameter of the beacon frame. - */ -std::vector GenerateNintendoNetworkInfoTag(const NetworkInfo& network_info) { - NetworkInfoTag tag{}; - tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = - sizeof(NetworkInfoTag) - sizeof(TagHeader) + network_info.application_data_size; - tag.appdata_size = network_info.application_data_size; - // Set the hash to zero initially, it will be updated once we calculate it. - tag.sha_hash = {}; - - // Ensure the network structure has the correct OUI and OUI type. - ASSERT(network_info.oui_type == static_cast(NintendoTagId::NetworkInfo)); - ASSERT(network_info.oui_value == NintendoOUI); - - // Ensure the application data size is less than the maximum value. - ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); - - // This tag contains the network info structure starting at the OUI. - std::memcpy(tag.network_info.data(), &network_info.oui_value, tag.network_info.size()); - - // Copy the tag and the data so we can calculate the SHA1 over it. - std::vector buffer(sizeof(tag) + network_info.application_data_size); - std::memcpy(buffer.data(), &tag, sizeof(tag)); - std::memcpy(buffer.data() + sizeof(tag), network_info.application_data.data(), - network_info.application_data_size); - - // Calculate the SHA1 of the contents of the tag. - std::array hash; - CryptoPP::SHA1().CalculateDigest(hash.data(), - buffer.data() + offsetof(NetworkInfoTag, network_info), - buffer.size() - sizeof(TagHeader)); - - // Copy it directly into the buffer, overwriting the zeros that we had previously placed there. - std::memcpy(buffer.data() + offsetof(NetworkInfoTag, sha_hash), hash.data(), hash.size()); - - return buffer; -} - -/* - * Calculates the CTR used for the AES-CTR encryption of the data stored in the - * EncryptedDataTags. - * @returns The CTR used for beacon crypto. - */ -std::array GetBeaconCryptoCTR(const NetworkInfo& network_info) { - BeaconDataCryptoCTR data{}; - - data.host_mac = network_info.host_mac_address; - data.wlan_comm_id = network_info.wlan_comm_id; - data.id = network_info.id; - data.network_id = network_info.network_id; - - std::array hash; - std::memcpy(hash.data(), &data, sizeof(data)); - - return hash; -} - -/* - * Serializes the node information into the format needed for network transfer, - * and then encrypts it with the NWM key. - * @returns The serialized and encrypted node information. - */ -std::vector GeneratedEncryptedData(const NetworkInfo& network_info, const NodeList& nodes) { - std::vector buffer(sizeof(BeaconData)); - - BeaconData data{}; - std::memcpy(buffer.data(), &data, sizeof(BeaconData)); - - for (const NodeInfo& node : nodes) { - // Serialize each node and convert the data from - // host byte-order to Big Endian. - BeaconNodeInfo info{}; - info.friend_code_seed = node.friend_code_seed; - info.network_node_id = node.network_node_id; - for (int i = 0; i < info.username.size(); ++i) - info.username[i] = node.username[i]; - - buffer.insert(buffer.end(), reinterpret_cast(&info), - reinterpret_cast(&info) + sizeof(info)); - } - - // Calculate the MD5 hash of the data in the buffer, not including the hash field. - std::array hash; - CryptoPP::MD5().CalculateDigest(hash.data(), buffer.data() + offsetof(BeaconData, bitmask), - buffer.size() - sizeof(data.md5_hash)); - - // Copy the hash into the buffer. - std::memcpy(buffer.data(), hash.data(), hash.size()); - - // Encrypt the data using AES-CTR and the NWM beacon key. - using CryptoPP::AES; - std::array counter = GetBeaconCryptoCTR(network_info); - CryptoPP::CTR_Mode::Encryption aes; - aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data()); - aes.ProcessData(buffer.data(), buffer.data(), buffer.size()); - - return buffer; -} - -void DecryptBeaconData(const NetworkInfo& network_info, std::vector& buffer) { - // Decrypt the data using AES-CTR and the NWM beacon key. - using CryptoPP::AES; - std::array counter = GetBeaconCryptoCTR(network_info); - CryptoPP::CTR_Mode::Decryption aes; - aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data()); - aes.ProcessData(buffer.data(), buffer.data(), buffer.size()); -} - -/** - * Generates a buffer with the Network Info Nintendo tag. - * This tag contains the first portion of the encrypted payload in the 802.11 beacon frame. - * The encrypted payload contains information about the nodes currently connected to the network. - * @returns A buffer with the first Nintendo encrypted data parameters of the beacon frame. - */ -std::vector GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network_info, - const NodeList& nodes) { - const size_t payload_size = - std::min(EncryptedDataSizeCutoff, nodes.size() * sizeof(NodeInfo)); - - EncryptedDataTag tag{}; - tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = static_cast(sizeof(tag) - sizeof(TagHeader) + payload_size); - tag.oui_type = static_cast(NintendoTagId::EncryptedData0); - tag.oui = NintendoOUI; - - std::vector buffer(sizeof(tag) + payload_size); - std::memcpy(buffer.data(), &tag, sizeof(tag)); - - std::vector encrypted_data = GeneratedEncryptedData(network_info, nodes); - std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data(), payload_size); - - return buffer; -} - -/** - * Generates a buffer with the Network Info Nintendo tag. - * This tag contains the second portion of the encrypted payload in the 802.11 beacon frame. - * The encrypted payload contains information about the nodes currently connected to the network. - * This tag is only present if the payload size is greater than EncryptedDataSizeCutoff (0xFA) - * bytes. - * @returns A buffer with the second Nintendo encrypted data parameters of the beacon frame. - */ -std::vector GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& network_info, - const NodeList& nodes) { - // This tag is only present if the payload is larger than EncryptedDataSizeCutoff (0xFA). - if (nodes.size() * sizeof(NodeInfo) <= EncryptedDataSizeCutoff) - return {}; - - const size_t payload_size = nodes.size() * sizeof(NodeInfo) - EncryptedDataSizeCutoff; - - const size_t tag_length = sizeof(EncryptedDataTag) - sizeof(TagHeader) + payload_size; - - // TODO(Subv): What does the 3DS do when a game has too much data to fit into the tag? - ASSERT_MSG(tag_length <= 255, "Data is too big."); - - EncryptedDataTag tag{}; - tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = static_cast(tag_length); - tag.oui_type = static_cast(NintendoTagId::EncryptedData1); - tag.oui = NintendoOUI; - - std::vector buffer(sizeof(tag) + payload_size); - std::memcpy(buffer.data(), &tag, sizeof(tag)); - - std::vector encrypted_data = GeneratedEncryptedData(network_info, nodes); - std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data() + EncryptedDataSizeCutoff, - payload_size); - - return buffer; -} - -/** - * Generates a buffer with the Nintendo tagged parameters of an 802.11 Beacon frame - * for UDS communication. - * @returns A buffer with the Nintendo tagged parameters of the beacon frame. - */ -std::vector GenerateNintendoTaggedParameters(const NetworkInfo& network_info, - const NodeList& nodes) { - ASSERT_MSG(network_info.max_nodes == nodes.size(), "Inconsistent network state."); - - std::vector buffer = GenerateNintendoDummyTag(); - std::vector network_info_tag = GenerateNintendoNetworkInfoTag(network_info); - std::vector first_data_tag = GenerateNintendoFirstEncryptedDataTag(network_info, nodes); - std::vector second_data_tag = GenerateNintendoSecondEncryptedDataTag(network_info, nodes); - - buffer.insert(buffer.end(), network_info_tag.begin(), network_info_tag.end()); - buffer.insert(buffer.end(), first_data_tag.begin(), first_data_tag.end()); - buffer.insert(buffer.end(), second_data_tag.begin(), second_data_tag.end()); - - return buffer; -} - -std::vector GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes) { - std::vector buffer = GenerateFixedParameters(); - std::vector basic_tags = GenerateBasicTaggedParameters(); - std::vector nintendo_tags = GenerateNintendoTaggedParameters(network_info, nodes); - - buffer.insert(buffer.end(), basic_tags.begin(), basic_tags.end()); - buffer.insert(buffer.end(), nintendo_tags.begin(), nintendo_tags.end()); - - return buffer; -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h deleted file mode 100644 index 50cc76da2..000000000 --- a/src/core/hle/service/nwm/uds_beacon.h +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -using MacAddress = std::array; -constexpr std::array NintendoOUI = {0x00, 0x1F, 0x32}; - -/** - * Internal vendor-specific tag ids as stored inside - * VendorSpecific blocks in the Beacon frames. - */ -enum class NintendoTagId : u8 { - Dummy = 20, - NetworkInfo = 21, - EncryptedData0 = 24, - EncryptedData1 = 25, -}; - -struct BeaconEntryHeader { - u32_le total_size; - INSERT_PADDING_BYTES(1); - u8 wifi_channel; - INSERT_PADDING_BYTES(2); - MacAddress mac_address; - INSERT_PADDING_BYTES(6); - u32_le unk_size; - u32_le header_size; -}; - -static_assert(sizeof(BeaconEntryHeader) == 0x1C, "BeaconEntryHeader has incorrect size."); - -struct BeaconDataReplyHeader { - u32_le max_output_size; - u32_le total_size; - u32_le total_entries; -}; - -static_assert(sizeof(BeaconDataReplyHeader) == 12, "BeaconDataReplyHeader has incorrect size."); - -#pragma pack(push, 1) -struct BeaconFrameHeader { - // Number of microseconds the AP has been active. - u64_le timestamp; - // Interval between beacon transmissions, expressed in TU. - u16_le beacon_interval; - // Indicates the presence of optional capabilities. - u16_le capabilities; -}; -#pragma pack(pop) - -static_assert(sizeof(BeaconFrameHeader) == 12, "BeaconFrameHeader has incorrect size."); - -struct TagHeader { - u8 tag_id; - u8 length; -}; - -static_assert(sizeof(TagHeader) == 2, "TagHeader has incorrect size."); - -struct DummyTag { - TagHeader header; - std::array oui; - u8 oui_type; - std::array data; -}; - -static_assert(sizeof(DummyTag) == 9, "DummyTag has incorrect size."); - -struct NetworkInfoTag { - TagHeader header; - std::array network_info; - std::array sha_hash; - u8 appdata_size; -}; - -static_assert(sizeof(NetworkInfoTag) == 54, "NetworkInfoTag has incorrect size."); - -struct EncryptedDataTag { - TagHeader header; - std::array oui; - u8 oui_type; -}; - -static_assert(sizeof(EncryptedDataTag) == 6, "EncryptedDataTag has incorrect size."); - -#pragma pack(push, 1) -// The raw bytes of this structure are the CTR used in the encryption (AES-CTR) -// of the beacon data stored in the EncryptedDataTags. -struct BeaconDataCryptoCTR { - MacAddress host_mac; - u32_le wlan_comm_id; - u8 id; - INSERT_PADDING_BYTES(1); - u32_le network_id; -}; - -static_assert(sizeof(BeaconDataCryptoCTR) == 0x10, "BeaconDataCryptoCTR has incorrect size."); - -struct BeaconNodeInfo { - u64_be friend_code_seed; - std::array username; - u16_be network_node_id; -}; - -static_assert(sizeof(BeaconNodeInfo) == 0x1E, "BeaconNodeInfo has incorrect size."); - -struct BeaconData { - std::array md5_hash; - u16_be bitmask; -}; -#pragma pack(pop) - -static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); - -/** - * Decrypts the beacon data buffer for the network described by `network_info`. - */ -void DecryptBeaconData(const NetworkInfo& network_info, std::vector& buffer); - -/** - * Generates an 802.11 beacon frame starting at the management frame header. - * This frame contains information about the network and its connected clients. - * @returns The generated frame. - */ -std::vector GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp deleted file mode 100644 index c74f51253..000000000 --- a/src/core/hle/service/nwm/uds_connection.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_connection.h" -#include "fmt/format.h" - -namespace Service { -namespace NWM { - -// Note: These values were taken from a packet capture of an o3DS XL -// broadcasting a Super Smash Bros. 4 lobby. -constexpr u16 DefaultExtraCapabilities = 0x0431; - -std::vector GenerateAuthenticationFrame(AuthenticationSeq seq) { - AuthenticationFrame frame{}; - frame.auth_seq = static_cast(seq); - - std::vector data(sizeof(frame)); - std::memcpy(data.data(), &frame, sizeof(frame)); - - return data; -} - -AuthenticationSeq GetAuthenticationSeqNumber(const std::vector& body) { - AuthenticationFrame frame; - std::memcpy(&frame, body.data(), sizeof(frame)); - - return static_cast(frame.auth_seq); -} - -/** - * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the - * specified network id as the SSID value. - * @param network_id The network id to use. - * @returns A buffer with the SSID tag. - */ -static std::vector GenerateSSIDTag(u32 network_id) { - constexpr u8 SSIDSize = 8; - - struct { - u8 id = static_cast(TagId::SSID); - u8 size = SSIDSize; - } tag_header; - - std::vector buffer(sizeof(tag_header) + SSIDSize); - - std::memcpy(buffer.data(), &tag_header, sizeof(tag_header)); - - std::string network_name = fmt::format("{0:08X}", network_id); - - std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize); - - return buffer; -} - -std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) { - AssociationResponseFrame frame{}; - frame.capabilities = DefaultExtraCapabilities; - frame.status_code = static_cast(status); - // The association id is ORed with this magic value (0xC000) - constexpr u16 AssociationIdMagic = 0xC000; - frame.assoc_id = association_id | AssociationIdMagic; - - std::vector data(sizeof(frame)); - std::memcpy(data.data(), &frame, sizeof(frame)); - - auto ssid_tag = GenerateSSIDTag(network_id); - data.insert(data.end(), ssid_tag.begin(), ssid_tag.end()); - - // TODO(Subv): Add the SupportedRates tag. - // TODO(Subv): Add the DSParameterSet tag. - // TODO(Subv): Add the ERPInformation tag. - return data; -} - -std::tuple GetAssociationResult(const std::vector& body) { - AssociationResponseFrame frame; - memcpy(&frame, body.data(), sizeof(frame)); - - constexpr u16 AssociationIdMask = 0x3FFF; - return std::make_tuple(static_cast(frame.status_code), - frame.assoc_id & AssociationIdMask); -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h deleted file mode 100644 index a664f8471..000000000 --- a/src/core/hle/service/nwm/uds_connection.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -/// Sequence number of the 802.11 authentication frames. -enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 }; - -enum class AuthAlgorithm : u16 { OpenSystem = 0 }; - -enum class AuthStatus : u16 { Successful = 0 }; - -enum class AssocStatus : u16 { Successful = 0 }; - -struct AuthenticationFrame { - u16_le auth_algorithm = static_cast(AuthAlgorithm::OpenSystem); - u16_le auth_seq; - u16_le status_code = static_cast(AuthStatus::Successful); -}; - -static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size"); - -struct AssociationResponseFrame { - u16_le capabilities; - u16_le status_code; - u16_le assoc_id; -}; - -static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size"); - -/// Generates an 802.11 authentication frame, starting at the frame body. -std::vector GenerateAuthenticationFrame(AuthenticationSeq seq); - -/// Returns the sequence number from the body of an Authentication frame. -AuthenticationSeq GetAuthenticationSeqNumber(const std::vector& body); - -/// Generates an 802.11 association response frame with the specified status, association id and -/// network id, starting at the frame body. -std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); - -/// Returns a tuple of (association status, association id) from the body of an AssociationResponse -/// frame. -std::tuple GetAssociationResult(const std::vector& body); - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp deleted file mode 100644 index 4b389710f..000000000 --- a/src/core/hle/service/nwm/uds_data.cpp +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_data.h" -#include "core/hw/aes/key.h" - -namespace Service { -namespace NWM { - -using MacAddress = std::array; - -/* - * Generates a SNAP-enabled 802.2 LLC header for the specified protocol. - * @returns a buffer with the bytes of the generated header. - */ -static std::vector GenerateLLCHeader(EtherType protocol) { - LLCHeader header{}; - header.protocol = static_cast(protocol); - - std::vector buffer(sizeof(header)); - memcpy(buffer.data(), &header, sizeof(header)); - - return buffer; -} - -/* - * Generates a Nintendo UDS SecureData header with the specified parameters. - * @returns a buffer with the bytes of the generated header. - */ -static std::vector GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id, - u16 src_node_id, u16 sequence_number) { - SecureDataHeader header{}; - header.protocol_size = data_size + sizeof(SecureDataHeader); - // Note: This size includes everything except the first 4 bytes of the structure, - // reinforcing the hypotheses that the first 4 bytes are actually the header of - // another container protocol. - header.securedata_size = data_size + sizeof(SecureDataHeader) - 4; - // Frames sent by the emulated application are never UDS management frames - header.is_management = 0; - header.data_channel = channel; - header.sequence_number = sequence_number; - header.dest_node_id = dest_node_id; - header.src_node_id = src_node_id; - - std::vector buffer(sizeof(header)); - memcpy(buffer.data(), &header, sizeof(header)); - - return buffer; -} - -/* - * Calculates the CTR used for the AES-CTR process that calculates - * the CCMP crypto key for data frames. - * @returns The CTR used for data frames crypto key generation. - */ -static std::array GetDataCryptoCTR(const NetworkInfo& network_info) { - DataFrameCryptoCTR data{}; - - data.host_mac = network_info.host_mac_address; - data.wlan_comm_id = network_info.wlan_comm_id; - data.id = network_info.id; - data.network_id = network_info.network_id; - - std::array hash; - CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast(&data), sizeof(data)); - - return hash; -} - -/* - * Generates the key used for encrypting the 802.11 data frames generated by UDS. - * @returns The key used for data frames crypto. - */ -static std::array GenerateDataCCMPKey( - const std::vector& passphrase, const NetworkInfo& network_info) { - // Calculate the MD5 hash of the input passphrase. - std::array passphrase_hash; - CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size()); - - std::array ccmp_key; - - // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using - // keyslot 0x2D. - using CryptoPP::AES; - std::array counter = GetDataCryptoCTR(network_info); - std::array key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey); - CryptoPP::CTR_Mode::Encryption aes; - aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data()); - aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size()); - - return ccmp_key; -} - -/* - * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame. - * @returns a buffer with the bytes of the AAD. - */ -static std::vector GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver, - const MacAddress& bssid, u16 frame_control) { - // Reference: IEEE 802.11-2007 - - // 8.3.3.3.2 Construct AAD (22-30 bytes) - // The AAD is constructed from the MPDU header. The AAD does not include the header Duration - // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g., - // a rate change during retransmission). For similar reasons, several subfields in the Frame - // Control field are masked to 0. - struct { - u16_be FC; // MPDU Frame Control field - MacAddress A1; - MacAddress A2; - MacAddress A3; - u16_be SC; // MPDU Sequence Control field - } aad_struct{}; - - constexpr u16 AADFrameControlMask = 0x8FC7; - aad_struct.FC = frame_control & AADFrameControlMask; - aad_struct.SC = 0; - - bool to_ds = (frame_control & (1 << 0)) != 0; - bool from_ds = (frame_control & (1 << 1)) != 0; - // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration, - // however, the 3DS doesn't seem to transmit frames with such combination. - ASSERT_MSG(to_ds != from_ds, "Invalid combination"); - - // The meaning of the address fields depends on the ToDS and FromDS fields. - if (from_ds) { - aad_struct.A1 = receiver; - aad_struct.A2 = bssid; - aad_struct.A3 = sender; - } - - if (to_ds) { - aad_struct.A1 = bssid; - aad_struct.A2 = sender; - aad_struct.A3 = receiver; - } - - std::vector aad(sizeof(aad_struct)); - std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct)); - - return aad; -} - -/* - * Decrypts the payload of an encrypted 802.11 data frame using the specified key. - * @returns The decrypted payload. - */ -static std::vector DecryptDataFrame(const std::vector& encrypted_payload, - const std::array& ccmp_key, - const MacAddress& sender, const MacAddress& receiver, - const MacAddress& bssid, u16 sequence_number, - u16 frame_control) { - - // Reference: IEEE 802.11-2007 - - std::vector aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); - - std::vector packet_number{0, - 0, - 0, - 0, - static_cast((sequence_number >> 8) & 0xFF), - static_cast(sequence_number & 0xFF)}; - - // 8.3.3.3.3 Construct CCM nonce (13 bytes) - std::vector nonce; - nonce.push_back(0); // priority - nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 - nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN - - try { - CryptoPP::CCM::Decryption d; - d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); - d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0); - - CryptoPP::AuthenticatedDecryptionFilter df( - d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END | - CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION); - // put aad - df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); - - // put cipher with mac - df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(), - encrypted_payload.size() - 8); - df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, - encrypted_payload.data() + encrypted_payload.size() - 8, 8); - - df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); - df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); - df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - - size_t size = df.MaxRetrievable(); - - std::vector pdata(size); - df.Get(pdata.data(), size); - return pdata; - } catch (CryptoPP::Exception&) { - LOG_ERROR(Service_NWM, "failed to decrypt"); - } - - return {}; -} - -/* - * Encrypts the payload of an 802.11 data frame using the specified key. - * @returns The encrypted payload. - */ -static std::vector EncryptDataFrame(const std::vector& payload, - const std::array& ccmp_key, - const MacAddress& sender, const MacAddress& receiver, - const MacAddress& bssid, u16 sequence_number, - u16 frame_control) { - // Reference: IEEE 802.11-2007 - - std::vector aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); - - std::vector packet_number{0, - 0, - 0, - 0, - static_cast((sequence_number >> 8) & 0xFF), - static_cast(sequence_number & 0xFF)}; - - // 8.3.3.3.3 Construct CCM nonce (13 bytes) - std::vector nonce; - nonce.push_back(0); // priority - nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 - nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN - - try { - CryptoPP::CCM::Encryption d; - d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); - d.SpecifyDataLengths(aad.size(), payload.size(), 0); - - CryptoPP::AuthenticatedEncryptionFilter df(d); - // put aad - df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); - df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); - - // put plaintext - df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size()); - df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); - - df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - - size_t size = df.MaxRetrievable(); - - std::vector cipher(size); - df.Get(cipher.data(), size); - return cipher; - } catch (CryptoPP::Exception&) { - LOG_ERROR(Service_NWM, "failed to encrypt"); - } - - return {}; -} - -std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, - u16 src_node, u16 sequence_number) { - std::vector buffer = GenerateLLCHeader(EtherType::SecureData); - std::vector securedata_header = GenerateSecureDataHeader( - static_cast(data.size()), channel, dest_node, src_node, sequence_number); - - buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); - buffer.insert(buffer.end(), data.begin(), data.end()); - return buffer; -} - -std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { - EAPoLStartPacket eapol_start{}; - eapol_start.association_id = association_id; - eapol_start.node.friend_code_seed = node_info.friend_code_seed; - - std::copy(node_info.username.begin(), node_info.username.end(), - eapol_start.node.username.begin()); - - // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. - // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in - // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game. - // Find out what that means. - - std::vector eapol_buffer(sizeof(EAPoLStartPacket)); - std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start)); - - std::vector buffer = GenerateLLCHeader(EtherType::EAPoL); - buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); - return buffer; -} - -EtherType GetFrameEtherType(const std::vector& frame) { - LLCHeader header; - std::memcpy(&header, frame.data(), sizeof(header)); - - u16 ethertype = header.protocol; - return static_cast(ethertype); -} - -u16 GetEAPoLFrameType(const std::vector& frame) { - // Ignore the LLC header - u16_be eapol_type; - std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type)); - return eapol_type; -} - -NodeInfo DeserializeNodeInfoFromFrame(const std::vector& frame) { - EAPoLStartPacket eapol_start; - - // Skip the LLC header - std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start)); - - NodeInfo node{}; - node.friend_code_seed = eapol_start.node.friend_code_seed; - - std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(), - node.username.begin()); - - return node; -} - -NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) { - NodeInfo node_info{}; - node_info.friend_code_seed = node.friend_code_seed; - node_info.network_node_id = node.network_node_id; - - std::copy(node.username.begin(), node.username.end(), node_info.username.begin()); - - return node_info; -} - -std::vector GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, - const NodeList& nodes, u8 max_nodes, u8 total_nodes) { - EAPoLLogoffPacket eapol_logoff{}; - eapol_logoff.assigned_node_id = network_node_id; - eapol_logoff.connected_nodes = total_nodes; - eapol_logoff.max_nodes = max_nodes; - - for (size_t index = 0; index < total_nodes; ++index) { - const auto& node_info = nodes[index]; - auto& node = eapol_logoff.nodes[index]; - - node.friend_code_seed = node_info.friend_code_seed; - node.network_node_id = node_info.network_node_id; - - std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin()); - } - - std::vector eapol_buffer(sizeof(EAPoLLogoffPacket)); - std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff)); - - std::vector buffer = GenerateLLCHeader(EtherType::EAPoL); - buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); - return buffer; -} - -EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector& frame) { - EAPoLLogoffPacket eapol_logoff; - - // Skip the LLC header - std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff)); - return eapol_logoff; -} - -} // namespace NWM -} // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h deleted file mode 100644 index 76bccb1bf..000000000 --- a/src/core/hle/service/nwm/uds_data.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/nwm/uds_beacon.h" -#include "core/hle/service/service.h" - -namespace Service { -namespace NWM { - -enum class SAP : u8 { SNAPExtensionUsed = 0xAA }; - -enum class PDUControl : u8 { UnnumberedInformation = 3 }; - -enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E }; - -/* - * 802.2 header, UDS packets always use SNAP for these headers, - * which means the dsap and ssap are always SNAPExtensionUsed (0xAA) - * and the OUI is always 0. - */ -struct LLCHeader { - u8 dsap = static_cast(SAP::SNAPExtensionUsed); - u8 ssap = static_cast(SAP::SNAPExtensionUsed); - u8 control = static_cast(PDUControl::UnnumberedInformation); - std::array OUI = {}; - u16_be protocol; -}; - -static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size"); - -/* - * Nintendo SecureData header, every UDS packet contains one, - * it is used to store metadata about the transmission such as - * the source and destination network node ids. - */ -struct SecureDataHeader { - // TODO(Subv): It is likely that the first 4 bytes of this header are - // actually part of another container protocol. - u16_be protocol_size; - INSERT_PADDING_BYTES(2); - u16_be securedata_size; - u8 is_management; - u8 data_channel; - u16_be sequence_number; - u16_be dest_node_id; - u16_be src_node_id; -}; - -static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size"); - -/* - * The raw bytes of this structure are the CTR used in the encryption (AES-CTR) - * process used to generate the CCMP key for data frame encryption. - */ -struct DataFrameCryptoCTR { - u32_le wlan_comm_id; - u32_le network_id; - std::array host_mac; - u16_le id; -}; - -static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); - -struct EAPoLNodeInfo { - u64_be friend_code_seed; - std::array username; - INSERT_PADDING_BYTES(4); - u16_be network_node_id; - INSERT_PADDING_BYTES(6); -}; - -static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); - -constexpr u16 EAPoLStartMagic = 0x201; - -/* - * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host - */ -struct EAPoLStartPacket { - u16_be magic = EAPoLStartMagic; - u16_be association_id; - // This value is hardcoded to 1 in the NWM module. - u16_be unknown = 1; - INSERT_PADDING_BYTES(2); - EAPoLNodeInfo node; -}; - -static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); - -constexpr u16 EAPoLLogoffMagic = 0x202; - -struct EAPoLLogoffPacket { - u16_be magic = EAPoLLogoffMagic; - INSERT_PADDING_BYTES(2); - u16_be assigned_node_id; - MacAddress client_mac_address; - INSERT_PADDING_BYTES(6); - u8 connected_nodes; - u8 max_nodes; - INSERT_PADDING_BYTES(4); - - std::array nodes; -}; - -static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size"); - -/** - * Generates an unencrypted 802.11 data payload. - * @returns The generated frame payload. - */ -std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, - u16 src_node, u16 sequence_number); - -/* - * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS - * communication. - * @returns The generated frame body. - */ -std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); - -/* - * Returns the EtherType of the specified 802.11 frame. - */ -EtherType GetFrameEtherType(const std::vector& frame); - -/* - * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame. - * Note: The frame *must* be an EAPoL frame. - */ -u16 GetEAPoLFrameType(const std::vector& frame); - -/* - * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet - * encapsulated in an 802.11 data frame. - */ -NodeInfo DeserializeNodeInfoFromFrame(const std::vector& frame); - -/* - * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. - */ -NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node); - -/* - * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS - * communication. - * @returns The generated frame body. - */ -std::vector GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, - const NodeList& nodes, u8 max_nodes, u8 total_nodes); - -/* - * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame. - */ -EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector& frame); - -} // namespace NWM -} // namespace Service -- cgit v1.2.3