mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Services/UDS: Added a function to send EAPoL-Start packets (#2920)
* Services/UDS: Added a function to generate the EAPoL-Start packet body. * Services/UDS: Added filter for beacons. * Services/UDS: Lock a mutex when accessing connection_status from both the emulation and network thread. * Services/UDS: Handle the Association Response frame and respond with the EAPoL-Start frame. * fixup: make use of current_node, changed received_beacons into a list, mutex and assert corrections * fixup: fix damn clang-format
This commit is contained in:
		
							parent
							
								
									a81536f53f
								
							
						
					
					
						commit
						d673d508dd
					
				
					 5 changed files with 243 additions and 81 deletions
				
			
		|  | @ -2,8 +2,10 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include <list> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | @ -37,9 +39,12 @@ static ConnectionStatus connection_status{}; | ||||||
| /* Node information about the current network.
 | /* Node information about the current network.
 | ||||||
|  * The amount of elements in this vector is always the maximum number |  * The amount of elements in this vector is always the maximum number | ||||||
|  * of nodes specified in the network configuration. |  * of nodes specified in the network configuration. | ||||||
|  * The first node is always the host, so this always contains at least 1 entry. |  * The first node is always the host. | ||||||
|  */ |  */ | ||||||
| static NodeList node_info(1); | static NodeList node_info; | ||||||
|  | 
 | ||||||
|  | // Node information about our own system.
 | ||||||
|  | static NodeInfo current_node; | ||||||
| 
 | 
 | ||||||
| // Mapping of bind node ids to their respective events.
 | // Mapping of bind node ids to their respective events.
 | ||||||
| static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; | static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; | ||||||
|  | @ -54,6 +59,10 @@ static NetworkInfo network_info; | ||||||
| // Event that will generate and send the 802.11 beacon frames.
 | // Event that will generate and send the 802.11 beacon frames.
 | ||||||
| static int beacon_broadcast_event; | 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
 | // Mutex to synchronize access to the list of received beacons between the emulation thread and the
 | ||||||
| // network thread.
 | // network thread.
 | ||||||
| static std::mutex beacon_mutex; | static std::mutex beacon_mutex; | ||||||
|  | @ -63,14 +72,26 @@ static std::mutex beacon_mutex; | ||||||
| constexpr size_t MaxBeaconFrames = 15; | constexpr size_t MaxBeaconFrames = 15; | ||||||
| 
 | 
 | ||||||
| // List of the last <MaxBeaconFrames> beacons received from the network.
 | // List of the last <MaxBeaconFrames> beacons received from the network.
 | ||||||
| static std::deque<Network::WifiPacket> received_beacons; | static std::list<Network::WifiPacket> received_beacons; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Returns a list of received 802.11 beacon frames from the specified sender since the last call. |  * 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::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { | ||||||
|     std::lock_guard<std::mutex> lock(beacon_mutex); |     std::lock_guard<std::mutex> lock(beacon_mutex); | ||||||
|     // TODO(Subv): Filter by sender.
 |     if (sender != Network::BroadcastMac) { | ||||||
|  |         std::list<Network::WifiPacket> 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); |     return std::move(received_beacons); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -83,6 +104,15 @@ void SendPacket(Network::WifiPacket& packet) { | ||||||
| // limit is exceeded.
 | // limit is exceeded.
 | ||||||
| void HandleBeaconFrame(const Network::WifiPacket& packet) { | void HandleBeaconFrame(const Network::WifiPacket& packet) { | ||||||
|     std::lock_guard<std::mutex> lock(beacon_mutex); |     std::lock_guard<std::mutex> 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); |     received_beacons.emplace_back(packet); | ||||||
| 
 | 
 | ||||||
|  | @ -91,14 +121,33 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) { | ||||||
|         received_beacons.pop_front(); |         received_beacons.pop_front(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { | ||||||
|  |     auto assoc_result = GetAssociationResult(packet.data); | ||||||
|  | 
 | ||||||
|  |     ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful, | ||||||
|  |                "Could not join network"); | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|  |         ASSERT(connection_status.status == static_cast<u32>(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<u16>(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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Returns an available index in the nodes array for the |  * Returns an available index in the nodes array for the | ||||||
|  * currently-hosted UDS network. |  * currently-hosted UDS network. | ||||||
|  */ |  */ | ||||||
| static u16 GetNextAvailableNodeId() { | 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) { |     for (u16 index = 0; index < connection_status.max_nodes; ++index) { | ||||||
|         if ((connection_status.node_bitmask & (1 << index)) == 0) |         if ((connection_status.node_bitmask & (1 << index)) == 0) | ||||||
|             return index; |             return index; | ||||||
|  | @ -113,35 +162,46 @@ static u16 GetNextAvailableNodeId() { | ||||||
|  * authentication frame with SEQ1. |  * authentication frame with SEQ1. | ||||||
|  */ |  */ | ||||||
| void StartConnectionSequence(const MacAddress& server) { | 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; |     using Network::WifiPacket; | ||||||
|     WifiPacket auth_request; |     WifiPacket auth_request; | ||||||
|     auth_request.channel = network_channel; |     { | ||||||
|     auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     auth_request.destination_address = server; |         ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); | ||||||
|     auth_request.type = WifiPacket::PacketType::Authentication; | 
 | ||||||
|  |         // 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); |     SendPacket(auth_request); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Sends an Association Response frame to the specified mac address
 | /// Sends an Association Response frame to the specified mac address
 | ||||||
| void SendAssociationResponseFrame(const MacAddress& address) { | void SendAssociationResponseFrame(const MacAddress& address) { | ||||||
|     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); |  | ||||||
| 
 |  | ||||||
|     using Network::WifiPacket; |     using Network::WifiPacket; | ||||||
|     WifiPacket assoc_response; |     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.
 |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     u16 association_id = 1; |         if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { | ||||||
|     assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, |             LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u", | ||||||
|                                                      network_info.network_id); |                       connection_status.status); | ||||||
|     assoc_response.destination_address = address; |             return; | ||||||
|     assoc_response.type = WifiPacket::PacketType::AssociationResponse; |         } | ||||||
|  | 
 | ||||||
|  |         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); |     SendPacket(assoc_response); | ||||||
| } | } | ||||||
|  | @ -155,16 +215,23 @@ void SendAssociationResponseFrame(const MacAddress& address) { | ||||||
| void HandleAuthenticationFrame(const Network::WifiPacket& packet) { | void HandleAuthenticationFrame(const Network::WifiPacket& packet) { | ||||||
|     // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
 |     // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
 | ||||||
|     if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { |     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; |         using Network::WifiPacket; | ||||||
|         WifiPacket auth_request; |         WifiPacket auth_request; | ||||||
|         auth_request.channel = network_channel; |         { | ||||||
|         auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); |             std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|         auth_request.destination_address = packet.transmitter_address; |             if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { | ||||||
|         auth_request.type = WifiPacket::PacketType::Authentication; |                 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); |         SendPacket(auth_request); | ||||||
| 
 | 
 | ||||||
|         SendAssociationResponseFrame(packet.transmitter_address); |         SendAssociationResponseFrame(packet.transmitter_address); | ||||||
|  | @ -180,6 +247,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { | ||||||
|     case Network::WifiPacket::PacketType::Authentication: |     case Network::WifiPacket::PacketType::Authentication: | ||||||
|         HandleAuthenticationFrame(packet); |         HandleAuthenticationFrame(packet); | ||||||
|         break; |         break; | ||||||
|  |     case Network::WifiPacket::PacketType::AssociationResponse: | ||||||
|  |         HandleAssociationResponseFrame(packet); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -305,7 +375,7 @@ static void InitializeWithVersion(Interface* self) { | ||||||
|     u32 sharedmem_size = rp.Pop<u32>(); |     u32 sharedmem_size = rp.Pop<u32>(); | ||||||
| 
 | 
 | ||||||
|     // Update the node information with the data the game gave us.
 |     // Update the node information with the data the game gave us.
 | ||||||
|     rp.PopRaw(node_info[0]); |     rp.PopRaw(current_node); | ||||||
| 
 | 
 | ||||||
|     u16 version = rp.Pop<u16>(); |     u16 version = rp.Pop<u16>(); | ||||||
| 
 | 
 | ||||||
|  | @ -315,10 +385,14 @@ static void InitializeWithVersion(Interface* self) { | ||||||
| 
 | 
 | ||||||
|     ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); |     ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); | ||||||
| 
 | 
 | ||||||
|     // Reset the connection status, it contains all zeros after initialization,
 |     { | ||||||
|     // except for the actual status value.
 |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     connection_status = {}; | 
 | ||||||
|     connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); |         // Reset the connection status, it contains all zeros after initialization,
 | ||||||
|  |         // except for the actual status value.
 | ||||||
|  |         connection_status = {}; | ||||||
|  |         connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|  | @ -348,12 +422,16 @@ static void GetConnectionStatus(Interface* self) { | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); | ||||||
| 
 | 
 | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushRaw(connection_status); |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|  |         rb.PushRaw(connection_status); | ||||||
| 
 | 
 | ||||||
|     // Reset the bitmask of changed nodes after each call to this
 |         // Reset the bitmask of changed nodes after each call to this
 | ||||||
|     // function to prevent falsely informing games of outstanding
 |         // function to prevent falsely informing games of outstanding
 | ||||||
|     // changes in subsequent calls.
 |         // changes in subsequent calls.
 | ||||||
|     connection_status.changed_nodes = 0; |         // TODO(Subv): Find exactly where the NWM module resets this value.
 | ||||||
|  |         connection_status.changed_nodes = 0; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_NWM, "called"); |     LOG_DEBUG(Service_NWM, "called"); | ||||||
| } | } | ||||||
|  | @ -434,31 +512,36 @@ static void BeginHostingNetwork(Interface* self) { | ||||||
|     // The real UDS module throws a fatal error if this assert fails.
 |     // 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."); |     ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); | ||||||
| 
 | 
 | ||||||
|     connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|  |         connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); | ||||||
| 
 | 
 | ||||||
|     // Ensure the application data size is less than the maximum value.
 |         // Ensure the application data size is less than the maximum value.
 | ||||||
|     ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); |         ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, | ||||||
|  |                    "Data size is too big."); | ||||||
| 
 | 
 | ||||||
|     // Set up basic information for this network.
 |         // Set up basic information for this network.
 | ||||||
|     network_info.oui_value = NintendoOUI; |         network_info.oui_value = NintendoOUI; | ||||||
|     network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); |         network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); | ||||||
| 
 | 
 | ||||||
|     connection_status.max_nodes = network_info.max_nodes; |         connection_status.max_nodes = network_info.max_nodes; | ||||||
| 
 | 
 | ||||||
|     // Resize the nodes list to hold max_nodes.
 |         // Resize the nodes list to hold max_nodes.
 | ||||||
|     node_info.resize(network_info.max_nodes); |         node_info.resize(network_info.max_nodes); | ||||||
| 
 | 
 | ||||||
|     // There's currently only one node in the network (the host).
 |         // There's currently only one node in the network (the host).
 | ||||||
|     connection_status.total_nodes = 1; |         connection_status.total_nodes = 1; | ||||||
|     network_info.total_nodes = 1; |         network_info.total_nodes = 1; | ||||||
|     // The host is always the first node
 |         // The host is always the first node
 | ||||||
|     connection_status.network_node_id = 1; |         connection_status.network_node_id = 1; | ||||||
|     node_info[0].network_node_id = 1; |         current_node.network_node_id = 1; | ||||||
|     connection_status.nodes[0] = connection_status.network_node_id; |         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.
 |         // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
 | ||||||
|     connection_status.node_bitmask |= 1; |         connection_status.node_bitmask |= 1; | ||||||
|     // Notify the application that the first node was set.
 |         // Notify the application that the first node was set.
 | ||||||
|     connection_status.changed_nodes |= 1; |         connection_status.changed_nodes |= 1; | ||||||
|  |         node_info[0] = current_node; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // If the game has a preferred channel, use that instead.
 |     // If the game has a preferred channel, use that instead.
 | ||||||
|     if (network_info.channel != 0) |     if (network_info.channel != 0) | ||||||
|  | @ -495,9 +578,13 @@ static void DestroyNetwork(Interface* self) { | ||||||
|     // Unschedule the beacon broadcast event.
 |     // Unschedule the beacon broadcast event.
 | ||||||
|     CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); |     CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); | ||||||
| 
 | 
 | ||||||
|     // TODO(Subv): Check if connection_status is indeed reset after this call.
 |     { | ||||||
|     connection_status = {}; |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); | 
 | ||||||
|  |         // TODO(Subv): Check if connection_status is indeed reset after this call.
 | ||||||
|  |         connection_status = {}; | ||||||
|  |         connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); | ||||||
|  |     } | ||||||
|     connection_status_event->Signal(); |     connection_status_event->Signal(); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  | @ -540,17 +627,24 @@ static void SendTo(Interface* self) { | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
| 
 | 
 | ||||||
|     if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && |     u16 network_node_id; | ||||||
|         connection_status.status != static_cast<u32>(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, |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|                            ErrorSummary::WrongArgument, ErrorLevel::Status)); |         if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && | ||||||
|         return; |             connection_status.status != static_cast<u32>(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.
 |     // TODO(Subv): Do something with the flags.
 | ||||||
|  | @ -567,8 +661,8 @@ static void SendTo(Interface* self) { | ||||||
| 
 | 
 | ||||||
|     // TODO(Subv): Increment the sequence number after each sent packet.
 |     // TODO(Subv): Increment the sequence number after each sent packet.
 | ||||||
|     u16 sequence_number = 0; |     u16 sequence_number = 0; | ||||||
|     std::vector<u8> data_payload = GenerateDataPayload( |     std::vector<u8> data_payload = | ||||||
|         data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); |         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
 |     // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
 | ||||||
|     // and encapsulate the payload.
 |     // and encapsulate the payload.
 | ||||||
|  | @ -595,6 +689,7 @@ static void GetChannel(Interface* self) { | ||||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); |     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
| 
 | 
 | ||||||
|  |     std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); |     bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); | ||||||
| 
 | 
 | ||||||
|     u8 channel = is_connected ? network_channel : 0; |     u8 channel = is_connected ? network_channel : 0; | ||||||
|  | @ -766,6 +861,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { | ||||||
|  * @param network_node_id Network Node Id of the connecting client. |  * @param network_node_id Network Node Id of the connecting client. | ||||||
|  */ |  */ | ||||||
| void OnClientConnected(u16 network_node_id) { | void OnClientConnected(u16 network_node_id) { | ||||||
|  |     std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), |     ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||||||
|                "Can not accept clients if we're not hosting a network"); |                "Can not accept clients if we're not hosting a network"); | ||||||
|     ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, |     ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, | ||||||
|  | @ -827,8 +923,11 @@ NWM_UDS::~NWM_UDS() { | ||||||
|     connection_status_event = nullptr; |     connection_status_event = nullptr; | ||||||
|     recv_buffer_memory = nullptr; |     recv_buffer_memory = nullptr; | ||||||
| 
 | 
 | ||||||
|     connection_status = {}; |     { | ||||||
|     connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); |         std::lock_guard<std::mutex> lock(connection_status_mutex); | ||||||
|  |         connection_status = {}; | ||||||
|  |         connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); |     CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -75,5 +75,14 @@ std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_i | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) { | ||||||
|  |     AssociationResponseFrame frame; | ||||||
|  |     memcpy(&frame, body.data(), sizeof(frame)); | ||||||
|  | 
 | ||||||
|  |     constexpr u16 AssociationIdMask = 0x3FFF; | ||||||
|  |     return std::make_tuple(static_cast<AssocStatus>(frame.status_code), | ||||||
|  |                            frame.assoc_id & AssociationIdMask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace NWM
 | } // namespace NWM
 | ||||||
| } // namespace Service
 | } // namespace Service
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <tuple> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
|  | @ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body); | ||||||
| /// network id, starting at the frame body.
 | /// network id, starting at the frame body.
 | ||||||
| std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); | std::vector<u8> 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<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body); | ||||||
|  | 
 | ||||||
| } // namespace NWM
 | } // namespace NWM
 | ||||||
| } // namespace Service
 | } // namespace Service
 | ||||||
|  |  | ||||||
|  | @ -274,5 +274,26 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | ||||||
|     return buffer; |     return buffer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { | ||||||
|  |     EAPoLStartPacket eapol_start{}; | ||||||
|  |     eapol_start.association_id = association_id; | ||||||
|  |     eapol_start.friend_code_seed = node_info.friend_code_seed; | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < node_info.username.size(); ++i) | ||||||
|  |         eapol_start.username[i] = node_info.username[i]; | ||||||
|  | 
 | ||||||
|  |     // 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<u8> eapol_buffer(sizeof(EAPoLStartPacket)); | ||||||
|  |     std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start)); | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); | ||||||
|  |     buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); | ||||||
|  |     return buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace NWM
 | } // namespace NWM
 | ||||||
| } // namespace Service
 | } // namespace Service
 | ||||||
|  |  | ||||||
|  | @ -67,6 +67,27 @@ struct DataFrameCryptoCTR { | ||||||
| 
 | 
 | ||||||
| static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR 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); | ||||||
|  | 
 | ||||||
|  |     u64_be friend_code_seed; | ||||||
|  |     std::array<u16_be, 10> username; | ||||||
|  |     INSERT_PADDING_BYTES(4); | ||||||
|  |     u16_be network_node_id; | ||||||
|  |     INSERT_PADDING_BYTES(6); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Generates an unencrypted 802.11 data payload. |  * Generates an unencrypted 802.11 data payload. | ||||||
|  * @returns The generated frame payload. |  * @returns The generated frame payload. | ||||||
|  | @ -74,5 +95,12 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron | ||||||
| std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | ||||||
|                                     u16 src_node, u16 sequence_number); |                                     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<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | ||||||
|  | 
 | ||||||
| } // namespace NWM
 | } // namespace NWM
 | ||||||
| } // namespace Service
 | } // namespace Service
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue