mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #2831 from Subv/uds_auth
Services/UDS: Handle beacon frames and the basic AP connection sequence frames.
This commit is contained in:
		
						commit
						617b6974b9
					
				
					 7 changed files with 289 additions and 53 deletions
				
			
		|  | @ -145,6 +145,7 @@ set(SRCS | |||
|             hle/service/nwm/nwm_tst.cpp | ||||
|             hle/service/nwm/nwm_uds.cpp | ||||
|             hle/service/nwm/uds_beacon.cpp | ||||
|             hle/service/nwm/uds_connection.cpp | ||||
|             hle/service/nwm/uds_data.cpp | ||||
|             hle/service/pm_app.cpp | ||||
|             hle/service/ptm/ptm.cpp | ||||
|  | @ -344,6 +345,7 @@ set(HEADERS | |||
|             hle/service/nwm/nwm_tst.h | ||||
|             hle/service/nwm/nwm_uds.h | ||||
|             hle/service/nwm/uds_beacon.h | ||||
|             hle/service/nwm/uds_connection.h | ||||
|             hle/service/nwm/uds_data.h | ||||
|             hle/service/pm_app.h | ||||
|             hle/service/ptm/ptm.h | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #include <array> | ||||
| #include <cstring> | ||||
| #include <mutex> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
|  | @ -15,8 +16,10 @@ | |||
| #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 { | ||||
|  | @ -51,6 +54,135 @@ 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 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 <MaxBeaconFrames> beacons received from the network.
 | ||||
| static std::deque<Network::WifiPacket> received_beacons; | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns a list of received 802.11 beacon frames from the specified sender since the last call. | ||||
|  */ | ||||
| std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { | ||||
|     std::lock_guard<std::mutex> lock(beacon_mutex); | ||||
|     // TODO(Subv): Filter by sender.
 | ||||
|     return std::move(received_beacons); | ||||
| } | ||||
| 
 | ||||
| /// Sends a WifiPacket to the room we're currently connected to.
 | ||||
| void SendPacket(Network::WifiPacket& packet) { | ||||
|     // TODO(Subv): Implement.
 | ||||
| } | ||||
| 
 | ||||
| // 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<std::mutex> lock(beacon_mutex); | ||||
| 
 | ||||
|     received_beacons.emplace_back(packet); | ||||
| 
 | ||||
|     // Discard old beacons if the buffer is full.
 | ||||
|     if (received_beacons.size() > MaxBeaconFrames) | ||||
|         received_beacons.pop_front(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Returns an available index in the nodes array for the | ||||
|  * currently-hosted UDS network. | ||||
|  */ | ||||
| static u16 GetNextAvailableNodeId() { | ||||
|     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||||
|                "Can not accept clients if we're not hosting a network"); | ||||
| 
 | ||||
|     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"); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * 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) { | ||||
|     ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); | ||||
| 
 | ||||
|     // TODO(Subv): Handle timeout.
 | ||||
| 
 | ||||
|     // Send an authentication frame with SEQ1
 | ||||
|     using Network::WifiPacket; | ||||
|     WifiPacket auth_request; | ||||
|     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) { | ||||
|     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); | ||||
| 
 | ||||
|     using Network::WifiPacket; | ||||
|     WifiPacket assoc_response; | ||||
|     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) { | ||||
|         ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); | ||||
| 
 | ||||
|         // Respond with an authentication response frame with SEQ2
 | ||||
|         using Network::WifiPacket; | ||||
|         WifiPacket auth_request; | ||||
|         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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * NWM_UDS::Shutdown service function | ||||
|  *  Inputs: | ||||
|  | @ -111,8 +243,7 @@ static void RecvBeaconBroadcastData(Interface* self) { | |||
|     u32 total_size = sizeof(BeaconDataReplyHeader); | ||||
| 
 | ||||
|     // Retrieve all beacon frames that were received from the desired mac address.
 | ||||
|     std::deque<WifiPacket> beacons = | ||||
|         GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address); | ||||
|     auto beacons = GetReceivedBeacons(mac_address); | ||||
| 
 | ||||
|     BeaconDataReplyHeader data_reply_header{}; | ||||
|     data_reply_header.total_entries = beacons.size(); | ||||
|  | @ -193,6 +324,9 @@ static void InitializeWithVersion(Interface* self) { | |||
|     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); | ||||
| } | ||||
|  | @ -610,31 +744,22 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { | |||
|     if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) | ||||
|         return; | ||||
| 
 | ||||
|     // TODO(Subv): Actually send the beacon.
 | ||||
|     std::vector<u8> 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); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Returns an available index in the nodes array for the | ||||
|  * currently-hosted UDS network. | ||||
|  */ | ||||
| static u32 GetNextAvailableNodeId() { | ||||
|     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||||
|                "Can not accept clients if we're not hosting a network"); | ||||
| 
 | ||||
|     for (unsigned 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"); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Called when a client connects to an UDS network we're hosting, | ||||
|  * updates the connection status and signals the update event. | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>; | |||
| enum class NetworkStatus { | ||||
|     NotConnected = 3, | ||||
|     ConnectedAsHost = 6, | ||||
|     Connecting = 7, | ||||
|     ConnectedAsClient = 9, | ||||
|     ConnectedAsSpectator = 10, | ||||
| }; | ||||
|  | @ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron | |||
| 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(); | ||||
|  |  | |||
|  | @ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL | |||
|     return buffer; | ||||
| } | ||||
| 
 | ||||
| std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) { | ||||
|     return {}; | ||||
| } | ||||
| } // namespace NWM
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
|  | @ -17,17 +17,6 @@ namespace NWM { | |||
| using MacAddress = std::array<u8, 6>; | ||||
| constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; | ||||
| 
 | ||||
| /// Additional block tag ids in the Beacon frames
 | ||||
| enum class TagId : u8 { | ||||
|     SSID = 0, | ||||
|     SupportedRates = 1, | ||||
|     DSParameterSet = 2, | ||||
|     TrafficIndicationMap = 5, | ||||
|     CountryInformation = 7, | ||||
|     ERPInformation = 42, | ||||
|     VendorSpecific = 221 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Internal vendor-specific tag ids as stored inside | ||||
|  * VendorSpecific blocks in the Beacon frames. | ||||
|  | @ -135,20 +124,6 @@ struct BeaconData { | |||
| 
 | ||||
| static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); | ||||
| 
 | ||||
| /// Information about a received WiFi packet.
 | ||||
| /// Acts as our own 802.11 header.
 | ||||
| struct WifiPacket { | ||||
|     enum class PacketType { Beacon, Data }; | ||||
| 
 | ||||
|     PacketType type; ///< The type of 802.11 frame, Beacon / Data.
 | ||||
| 
 | ||||
|     /// Raw 802.11 frame data, starting at the management frame header for management frames.
 | ||||
|     std::vector<u8> data; | ||||
|     MacAddress transmitter_address; ///< Mac address of the transmitter.
 | ||||
|     MacAddress destination_address; ///< Mac address of the receiver.
 | ||||
|     u8 channel;                     ///< WiFi channel where this frame was transmitted.
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Decrypts the beacon data buffer for the network described by `network_info`. | ||||
|  */ | ||||
|  | @ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) | |||
|  */ | ||||
| std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns a list of received 802.11 frames from the specified sender | ||||
|  * matching the type since the last call. | ||||
|  */ | ||||
| std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender); | ||||
| } // namespace NWM
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
							
								
								
									
										79
									
								
								src/core/hle/service/nwm/uds_connection.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/core/hle/service/nwm/uds_connection.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| // 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<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) { | ||||
|     AuthenticationFrame frame{}; | ||||
|     frame.auth_seq = static_cast<u16>(seq); | ||||
| 
 | ||||
|     std::vector<u8> data(sizeof(frame)); | ||||
|     std::memcpy(data.data(), &frame, sizeof(frame)); | ||||
| 
 | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) { | ||||
|     AuthenticationFrame frame; | ||||
|     std::memcpy(&frame, body.data(), sizeof(frame)); | ||||
| 
 | ||||
|     return static_cast<AuthenticationSeq>(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<u8> GenerateSSIDTag(u32 network_id) { | ||||
|     constexpr u8 SSIDSize = 8; | ||||
| 
 | ||||
|     struct { | ||||
|         u8 id = static_cast<u8>(TagId::SSID); | ||||
|         u8 size = SSIDSize; | ||||
|     } tag_header; | ||||
| 
 | ||||
|     std::vector<u8> 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<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) { | ||||
|     AssociationResponseFrame frame{}; | ||||
|     frame.capabilities = DefaultExtraCapabilities; | ||||
|     frame.status_code = static_cast<u16>(status); | ||||
|     // The association id is ORed with this magic value (0xC000)
 | ||||
|     constexpr u16 AssociationIdMagic = 0xC000; | ||||
|     frame.assoc_id = association_id | AssociationIdMagic; | ||||
| 
 | ||||
|     std::vector<u8> 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; | ||||
| } | ||||
| 
 | ||||
| } // namespace NWM
 | ||||
| } // namespace Service
 | ||||
							
								
								
									
										51
									
								
								src/core/hle/service/nwm/uds_connection.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/core/hle/service/nwm/uds_connection.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| // Copyright 2017 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| #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<u16>(AuthAlgorithm::OpenSystem); | ||||
|     u16_le auth_seq; | ||||
|     u16_le status_code = static_cast<u16>(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<u8> GenerateAuthenticationFrame(AuthenticationSeq seq); | ||||
| 
 | ||||
| /// Returns the sequence number from the body of an Authentication frame.
 | ||||
| AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body); | ||||
| 
 | ||||
| /// Generates an 802.11 association response frame with the specified status, association id and
 | ||||
| /// network id, starting at the frame body.
 | ||||
| std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); | ||||
| 
 | ||||
| } // namespace NWM
 | ||||
| } // namespace Service
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue