// 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 #include #include #include #include #include #include #include #include #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/service.h" #include "network/network.h" namespace Core { class System; } namespace Kernel { class Event; class SharedMemory; } // namespace Kernel // Local-WLAN service namespace Service::NWM { using MacAddress = std::array; const std::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); void Reset() { friend_code_seed = 0; username.fill(0); network_node_id = 0; } }; static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); using NodeList = std::vector; enum class NetworkStatus : u32 { NotConnected = 3, ConnectedAsHost = 6, Connecting = 7, ConnectedAsClient = 9, ConnectedAsSpectator = 10, }; enum class NetworkStatusChangeReason : u32 { None = 0, ConnectionEstablished = 1, ConnectionLost = 4, }; struct ConnectionStatus { enum_le status; enum_le status_change_reason; 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 ServiceFramework { public: explicit NWM_UDS(Core::System& system); ~NWM_UDS(); class ThreadCallback; private: Core::System& system; void UpdateNetworkAttribute(Kernel::HLERequestContext& ctx); /** * NWM_UDS::Shutdown service function * Inputs: * 1 : None * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void Shutdown(Kernel::HLERequestContext& ctx); /** * 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 */ void DestroyNetwork(Kernel::HLERequestContext& ctx); /** * NWM_UDS::DisconnectNetwork service function. * This disconnects this device from the network. * Inputs: * 0 : Command header. * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void DisconnectNetwork(Kernel::HLERequestContext& ctx); /** * 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. */ void GetConnectionStatus(Kernel::HLERequestContext& ctx); /** * NWM_UDS::GetNodeInformation service function. * Returns the node inforamtion structure for the currently connected node. * Inputs: * 0 : Command header. * 1 : Node ID. * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code * 2-11 : NodeInfo structure. */ void GetNodeInformation(Kernel::HLERequestContext& ctx); /** * 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 * 2, 3: output buffer return descriptor & ptr */ void RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx); /** * 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. */ void SetApplicationData(Kernel::HLERequestContext& ctx); /** * NWM_UDS::GetApplicationData service function. * Loads the application data from the current beacon. * Inputs: * 1 : Data size. * Outputs: * 0 : Return header * 1 : Result of function, always 0 * 2 : Actual data size */ void GetApplicationData(Kernel::HLERequestContext& ctx); /** * 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. */ void Bind(Kernel::HLERequestContext& ctx); /** * NWM_UDS::Unbind service function. * Unbinds a BindNodeId from a data channel. * Inputs: * 1 : BindNodeId * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void Unbind(Kernel::HLERequestContext& ctx); /** * NWM_UDS::PullPacket service function. * Receives a data frame from the specified bind node id * Inputs: * 0 : Command header. * 1 : Bind node id. * 2 : Max out buff size >> 2. * 3 : Max out buff size. * 64 : Output buffer descriptor * 65 : Output buffer address * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code * 2 : Received data size * 3 : u16 Source network node id * 4 : Buffer descriptor * 5 : Buffer address */ void PullPacket(Kernel::HLERequestContext& ctx); /** * 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 */ void SendTo(Kernel::HLERequestContext& ctx); /** * 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. */ void GetChannel(Kernel::HLERequestContext& ctx); /** * 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 */ void InitializeWithVersion(Kernel::HLERequestContext& ctx); /** * NWM_UDS::InitializeDeprecated service function * Inputs: * 1 : Shared memory size * 2-11 : Input NodeInfo Structure * 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 */ void InitializeDeprecated(Kernel::HLERequestContext& ctx); /** * 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 */ void BeginHostingNetwork(Kernel::HLERequestContext& ctx); /** * NWM_UDS::BeginHostingNetworkDeprecated service function. * Creates a network and starts broadcasting its presence. * Inputs: * 1 - 15 : the NetworkInfo structure, excluding application data * 16 : passphrase size * 18 : VAddr of the passphrase. * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void BeginHostingNetworkDeprecated(Kernel::HLERequestContext& ctx); /** * NWM_UDS::ConnectToNetwork service function. * This connects to the specified network * Inputs: * 0 : Command header * 1 : Connection type: 0x1 = Client, 0x2 = Spectator. * 2 : Passphrase buffer size * 3 : (NetworkStructSize<<12) | 0x402 * 4 : Network struct buffer ptr * 5 : (PassphraseSize<<12) | 2 * 6 : Input passphrase buffer ptr * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void ConnectToNetwork(Kernel::HLERequestContext& ctx); /** * NWM_UDS::ConnectToNetwork Deprecatedservice function. * This connects to the specified network * Inputs: * 0 : Command header * 1 - 15 : the NetworkInfo structure, excluding application data * 16 : Connection type: 0x1 = Client, 0x2 = Spectator. * 17 : Passphrase buffer size * 18 : (PassphraseSize<<12) | 2 * 19 : Input passphrase buffer ptr * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void ConnectToNetworkDeprecated(Kernel::HLERequestContext& ctx); /** * NWM_UDS::EjectClient Disconnects clients. * Inputs: * 0 : Command header * 1 : Network node id * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code */ void EjectClient(Kernel::HLERequestContext& ctx); /** * 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 * 2, 3: output buffer return descriptor & ptr */ void DecryptBeaconData(Kernel::HLERequestContext& ctx); ResultVal> Initialize( u32 sharedmem_size, const NodeInfo& node, u16 version, std::shared_ptr sharedmem); Result BeginHostingNetwork(std::span network_info_buffer, std::vector passphrase); void ConnectToNetwork(Kernel::HLERequestContext& ctx, u16 command_id, std::span network_info_buffer, u8 connection_type, std::vector passphrase); void BeaconBroadcastCallback(std::uintptr_t user_data, s64 cycles_late); /** * Returns a list of received 802.11 beacon frames from the specified sender since the last * call. */ std::list GetReceivedBeacons(const MacAddress& sender); /* * Returns an available index in the nodes array for the * currently-hosted UDS network. */ u16 GetNextAvailableNodeId(); void BroadcastNodeMap(); void HandleNodeMapPacket(const Network::WifiPacket& packet); void HandleBeaconFrame(const Network::WifiPacket& packet); void HandleAssociationResponseFrame(const Network::WifiPacket& packet); void HandleEAPoLPacket(const Network::WifiPacket& packet); void HandleSecureDataPacket(const Network::WifiPacket& packet); /* * 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); /// Sends an Association Response frame to the specified mac address void SendAssociationResponseFrame(const MacAddress& address); /* * 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); /// Handles the deauthentication frames sent from clients to hosts, when they leave a session void HandleDeauthenticationFrame(const Network::WifiPacket& packet); void HandleDataFrame(const Network::WifiPacket& packet); /// Callback to parse and handle a received wifi packet. void OnWifiPacketReceived(const Network::WifiPacket& packet); boost::optional GetNodeMacAddress(u16 dest_node_id, u8 flags); // Event that is signaled every time the connection status changes. std::shared_ptr connection_status_event; // Shared memory provided by the application to store the receive buffer. // This is not currently used. std::shared_ptr recv_buffer_memory; // Connection status of this 3DS. ConnectionStatus connection_status{}; std::atomic initialized{false}; /* 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. */ NodeList node_info; // Node information about our own system. NodeInfo current_node; struct BindNodeData { u32 bind_node_id; ///< Id of the bind node associated with this data. u8 channel; ///< Channel that this bind node was bound to. u16 network_node_id; ///< Node id this bind node is associated with, only packets from this /// network node will be received. std::shared_ptr event; ///< Receive event for this bind node. std::deque> received_packets; ///< List of packets received on this channel. }; // Mapping of data channels to their internal data. std::unordered_map channel_data; // 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. u8 network_channel = DefaultNetworkChannel; // Information about the network that we're currently connected to. NetworkInfo network_info; // Mapping of mac addresses to their respective node_ids. struct Node { bool connected; u16 node_id; private: template void serialize(Archive& ar, const unsigned int) { ar& connected; ar& node_id; } friend class boost::serialization::access; }; std::map node_map; // Event that will generate and send the 802.11 beacon frames. Core::TimingEventType* beacon_broadcast_event; // Callback identifier for the OnWifiPacketReceived event. Network::RoomMember::CallbackHandle wifi_packet_received; // Mutex to synchronize access to the connection status between the emulation thread and the // network thread. std::mutex connection_status_mutex; std::shared_ptr connection_event; // Mutex to synchronize access to the list of received beacons between the emulation thread and // the network thread. std::mutex beacon_mutex; // List of the last beacons received from the network. std::list received_beacons; template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; }; } // namespace Service::NWM SERVICE_CONSTRUCT(Service::NWM::NWM_UDS) BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_UDS) BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_UDS::ThreadCallback)