diff options
| -rw-r--r-- | src/core/hle/service/nwm/nwm_uds.cpp | 275 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_connection.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_connection.h | 5 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.cpp | 21 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.h | 28 |
5 files changed, 250 insertions, 88 deletions
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 893bbb1e7..4e2af9ae6 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp | |||
| @@ -2,8 +2,10 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <array> | 6 | #include <array> |
| 6 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include <list> | ||
| 7 | #include <mutex> | 9 | #include <mutex> |
| 8 | #include <unordered_map> | 10 | #include <unordered_map> |
| 9 | #include <vector> | 11 | #include <vector> |
| @@ -37,9 +39,12 @@ static ConnectionStatus connection_status{}; | |||
| 37 | /* Node information about the current network. | 39 | /* Node information about the current network. |
| 38 | * The amount of elements in this vector is always the maximum number | 40 | * The amount of elements in this vector is always the maximum number |
| 39 | * of nodes specified in the network configuration. | 41 | * of nodes specified in the network configuration. |
| 40 | * The first node is always the host, so this always contains at least 1 entry. | 42 | * The first node is always the host. |
| 41 | */ | 43 | */ |
| 42 | static NodeList node_info(1); | 44 | static NodeList node_info; |
| 45 | |||
| 46 | // Node information about our own system. | ||
| 47 | static NodeInfo current_node; | ||
| 43 | 48 | ||
| 44 | // Mapping of bind node ids to their respective events. | 49 | // Mapping of bind node ids to their respective events. |
| 45 | static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; | 50 | static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; |
| @@ -54,6 +59,10 @@ static NetworkInfo network_info; | |||
| 54 | // Event that will generate and send the 802.11 beacon frames. | 59 | // Event that will generate and send the 802.11 beacon frames. |
| 55 | static int beacon_broadcast_event; | 60 | static int beacon_broadcast_event; |
| 56 | 61 | ||
| 62 | // Mutex to synchronize access to the connection status between the emulation thread and the | ||
| 63 | // network thread. | ||
| 64 | static std::mutex connection_status_mutex; | ||
| 65 | |||
| 57 | // Mutex to synchronize access to the list of received beacons between the emulation thread and the | 66 | // Mutex to synchronize access to the list of received beacons between the emulation thread and the |
| 58 | // network thread. | 67 | // network thread. |
| 59 | static std::mutex beacon_mutex; | 68 | static std::mutex beacon_mutex; |
| @@ -63,14 +72,26 @@ static std::mutex beacon_mutex; | |||
| 63 | constexpr size_t MaxBeaconFrames = 15; | 72 | constexpr size_t MaxBeaconFrames = 15; |
| 64 | 73 | ||
| 65 | // List of the last <MaxBeaconFrames> beacons received from the network. | 74 | // List of the last <MaxBeaconFrames> beacons received from the network. |
| 66 | static std::deque<Network::WifiPacket> received_beacons; | 75 | static std::list<Network::WifiPacket> received_beacons; |
| 67 | 76 | ||
| 68 | /** | 77 | /** |
| 69 | * Returns a list of received 802.11 beacon frames from the specified sender since the last call. | 78 | * Returns a list of received 802.11 beacon frames from the specified sender since the last call. |
| 70 | */ | 79 | */ |
| 71 | std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { | 80 | std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { |
| 72 | std::lock_guard<std::mutex> lock(beacon_mutex); | 81 | std::lock_guard<std::mutex> lock(beacon_mutex); |
| 73 | // TODO(Subv): Filter by sender. | 82 | if (sender != Network::BroadcastMac) { |
| 83 | std::list<Network::WifiPacket> filtered_list; | ||
| 84 | const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(), | ||
| 85 | [&sender](const Network::WifiPacket& packet) { | ||
| 86 | return packet.transmitter_address == sender; | ||
| 87 | }); | ||
| 88 | if (beacon != received_beacons.end()) { | ||
| 89 | filtered_list.push_back(*beacon); | ||
| 90 | // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries | ||
| 91 | received_beacons.erase(beacon); | ||
| 92 | } | ||
| 93 | return filtered_list; | ||
| 94 | } | ||
| 74 | return std::move(received_beacons); | 95 | return std::move(received_beacons); |
| 75 | } | 96 | } |
| 76 | 97 | ||
| @@ -83,6 +104,15 @@ void SendPacket(Network::WifiPacket& packet) { | |||
| 83 | // limit is exceeded. | 104 | // limit is exceeded. |
| 84 | void HandleBeaconFrame(const Network::WifiPacket& packet) { | 105 | void HandleBeaconFrame(const Network::WifiPacket& packet) { |
| 85 | std::lock_guard<std::mutex> lock(beacon_mutex); | 106 | std::lock_guard<std::mutex> lock(beacon_mutex); |
| 107 | const auto unique_beacon = | ||
| 108 | std::find_if(received_beacons.begin(), received_beacons.end(), | ||
| 109 | [&packet](const Network::WifiPacket& new_packet) { | ||
| 110 | return new_packet.transmitter_address == packet.transmitter_address; | ||
| 111 | }); | ||
| 112 | if (unique_beacon != received_beacons.end()) { | ||
| 113 | // We already have a beacon from the same mac in the deque, remove the old one; | ||
| 114 | received_beacons.erase(unique_beacon); | ||
| 115 | } | ||
| 86 | 116 | ||
| 87 | received_beacons.emplace_back(packet); | 117 | received_beacons.emplace_back(packet); |
| 88 | 118 | ||
| @@ -91,14 +121,33 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) { | |||
| 91 | received_beacons.pop_front(); | 121 | received_beacons.pop_front(); |
| 92 | } | 122 | } |
| 93 | 123 | ||
| 124 | void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { | ||
| 125 | auto assoc_result = GetAssociationResult(packet.data); | ||
| 126 | |||
| 127 | ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful, | ||
| 128 | "Could not join network"); | ||
| 129 | { | ||
| 130 | std::lock_guard<std::mutex> lock(connection_status_mutex); | ||
| 131 | ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting)); | ||
| 132 | } | ||
| 133 | |||
| 134 | // Send the EAPoL-Start packet to the server. | ||
| 135 | using Network::WifiPacket; | ||
| 136 | WifiPacket eapol_start; | ||
| 137 | eapol_start.channel = network_channel; | ||
| 138 | eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node); | ||
| 139 | // TODO(B3N30): Encrypt the packet. | ||
| 140 | eapol_start.destination_address = packet.transmitter_address; | ||
| 141 | eapol_start.type = WifiPacket::PacketType::Data; | ||
| 142 | |||
| 143 | SendPacket(eapol_start); | ||
| 144 | } | ||
| 145 | |||
| 94 | /* | 146 | /* |
| 95 | * Returns an available index in the nodes array for the | 147 | * Returns an available index in the nodes array for the |
| 96 | * currently-hosted UDS network. | 148 | * currently-hosted UDS network. |
| 97 | */ | 149 | */ |
| 98 | static u16 GetNextAvailableNodeId() { | 150 | static u16 GetNextAvailableNodeId() { |
| 99 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||
| 100 | "Can not accept clients if we're not hosting a network"); | ||
| 101 | |||
| 102 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { | 151 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { |
| 103 | if ((connection_status.node_bitmask & (1 << index)) == 0) | 152 | if ((connection_status.node_bitmask & (1 << index)) == 0) |
| 104 | return index; | 153 | return index; |
| @@ -113,35 +162,46 @@ static u16 GetNextAvailableNodeId() { | |||
| 113 | * authentication frame with SEQ1. | 162 | * authentication frame with SEQ1. |
| 114 | */ | 163 | */ |
| 115 | void StartConnectionSequence(const MacAddress& server) { | 164 | void StartConnectionSequence(const MacAddress& server) { |
| 116 | ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); | ||
| 117 | |||
| 118 | // TODO(Subv): Handle timeout. | ||
| 119 | |||
| 120 | // Send an authentication frame with SEQ1 | ||
| 121 | using Network::WifiPacket; | 165 | using Network::WifiPacket; |
| 122 | WifiPacket auth_request; | 166 | WifiPacket auth_request; |
| 123 | auth_request.channel = network_channel; | 167 | { |
| 124 | auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); | 168 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 125 | auth_request.destination_address = server; | 169 | ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); |
| 126 | auth_request.type = WifiPacket::PacketType::Authentication; | 170 | |
| 171 | // TODO(Subv): Handle timeout. | ||
| 172 | |||
| 173 | // Send an authentication frame with SEQ1 | ||
| 174 | auth_request.channel = network_channel; | ||
| 175 | auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); | ||
| 176 | auth_request.destination_address = server; | ||
| 177 | auth_request.type = WifiPacket::PacketType::Authentication; | ||
| 178 | } | ||
| 127 | 179 | ||
| 128 | SendPacket(auth_request); | 180 | SendPacket(auth_request); |
| 129 | } | 181 | } |
| 130 | 182 | ||
| 131 | /// Sends an Association Response frame to the specified mac address | 183 | /// Sends an Association Response frame to the specified mac address |
| 132 | void SendAssociationResponseFrame(const MacAddress& address) { | 184 | void SendAssociationResponseFrame(const MacAddress& address) { |
| 133 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); | ||
| 134 | |||
| 135 | using Network::WifiPacket; | 185 | using Network::WifiPacket; |
| 136 | WifiPacket assoc_response; | 186 | WifiPacket assoc_response; |
| 137 | assoc_response.channel = network_channel; | 187 | |
| 138 | // TODO(Subv): This will cause multiple clients to end up with the same association id, but | 188 | { |
| 139 | // we're not using that for anything. | 189 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 140 | u16 association_id = 1; | 190 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { |
| 141 | assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, | 191 | LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u", |
| 142 | network_info.network_id); | 192 | connection_status.status); |
| 143 | assoc_response.destination_address = address; | 193 | return; |
| 144 | assoc_response.type = WifiPacket::PacketType::AssociationResponse; | 194 | } |
| 195 | |||
| 196 | assoc_response.channel = network_channel; | ||
| 197 | // TODO(Subv): This will cause multiple clients to end up with the same association id, but | ||
| 198 | // we're not using that for anything. | ||
| 199 | u16 association_id = 1; | ||
| 200 | assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, | ||
| 201 | network_info.network_id); | ||
| 202 | assoc_response.destination_address = address; | ||
| 203 | assoc_response.type = WifiPacket::PacketType::AssociationResponse; | ||
| 204 | } | ||
| 145 | 205 | ||
| 146 | SendPacket(assoc_response); | 206 | SendPacket(assoc_response); |
| 147 | } | 207 | } |
| @@ -155,16 +215,23 @@ void SendAssociationResponseFrame(const MacAddress& address) { | |||
| 155 | void HandleAuthenticationFrame(const Network::WifiPacket& packet) { | 215 | void HandleAuthenticationFrame(const Network::WifiPacket& packet) { |
| 156 | // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior | 216 | // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior |
| 157 | if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { | 217 | if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { |
| 158 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); | ||
| 159 | |||
| 160 | // Respond with an authentication response frame with SEQ2 | ||
| 161 | using Network::WifiPacket; | 218 | using Network::WifiPacket; |
| 162 | WifiPacket auth_request; | 219 | WifiPacket auth_request; |
| 163 | auth_request.channel = network_channel; | 220 | { |
| 164 | auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); | 221 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 165 | auth_request.destination_address = packet.transmitter_address; | 222 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { |
| 166 | auth_request.type = WifiPacket::PacketType::Authentication; | 223 | LOG_ERROR(Service_NWM, |
| 167 | 224 | "Connection sequence aborted, because connection status is %u", | |
| 225 | connection_status.status); | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | |||
| 229 | // Respond with an authentication response frame with SEQ2 | ||
| 230 | auth_request.channel = network_channel; | ||
| 231 | auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); | ||
| 232 | auth_request.destination_address = packet.transmitter_address; | ||
| 233 | auth_request.type = WifiPacket::PacketType::Authentication; | ||
| 234 | } | ||
| 168 | SendPacket(auth_request); | 235 | SendPacket(auth_request); |
| 169 | 236 | ||
| 170 | SendAssociationResponseFrame(packet.transmitter_address); | 237 | SendAssociationResponseFrame(packet.transmitter_address); |
| @@ -180,6 +247,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { | |||
| 180 | case Network::WifiPacket::PacketType::Authentication: | 247 | case Network::WifiPacket::PacketType::Authentication: |
| 181 | HandleAuthenticationFrame(packet); | 248 | HandleAuthenticationFrame(packet); |
| 182 | break; | 249 | break; |
| 250 | case Network::WifiPacket::PacketType::AssociationResponse: | ||
| 251 | HandleAssociationResponseFrame(packet); | ||
| 252 | break; | ||
| 183 | } | 253 | } |
| 184 | } | 254 | } |
| 185 | 255 | ||
| @@ -305,7 +375,7 @@ static void InitializeWithVersion(Interface* self) { | |||
| 305 | u32 sharedmem_size = rp.Pop<u32>(); | 375 | u32 sharedmem_size = rp.Pop<u32>(); |
| 306 | 376 | ||
| 307 | // Update the node information with the data the game gave us. | 377 | // Update the node information with the data the game gave us. |
| 308 | rp.PopRaw(node_info[0]); | 378 | rp.PopRaw(current_node); |
| 309 | 379 | ||
| 310 | u16 version = rp.Pop<u16>(); | 380 | u16 version = rp.Pop<u16>(); |
| 311 | 381 | ||
| @@ -315,10 +385,14 @@ static void InitializeWithVersion(Interface* self) { | |||
| 315 | 385 | ||
| 316 | ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); | 386 | ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); |
| 317 | 387 | ||
| 318 | // Reset the connection status, it contains all zeros after initialization, | 388 | { |
| 319 | // except for the actual status value. | 389 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 320 | connection_status = {}; | 390 | |
| 321 | connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | 391 | // Reset the connection status, it contains all zeros after initialization, |
| 392 | // except for the actual status value. | ||
| 393 | connection_status = {}; | ||
| 394 | connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | ||
| 395 | } | ||
| 322 | 396 | ||
| 323 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 397 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 324 | rb.Push(RESULT_SUCCESS); | 398 | rb.Push(RESULT_SUCCESS); |
| @@ -348,12 +422,16 @@ static void GetConnectionStatus(Interface* self) { | |||
| 348 | IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); | 422 | IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); |
| 349 | 423 | ||
| 350 | rb.Push(RESULT_SUCCESS); | 424 | rb.Push(RESULT_SUCCESS); |
| 351 | rb.PushRaw(connection_status); | 425 | { |
| 352 | 426 | std::lock_guard<std::mutex> lock(connection_status_mutex); | |
| 353 | // Reset the bitmask of changed nodes after each call to this | 427 | rb.PushRaw(connection_status); |
| 354 | // function to prevent falsely informing games of outstanding | 428 | |
| 355 | // changes in subsequent calls. | 429 | // Reset the bitmask of changed nodes after each call to this |
| 356 | connection_status.changed_nodes = 0; | 430 | // function to prevent falsely informing games of outstanding |
| 431 | // changes in subsequent calls. | ||
| 432 | // TODO(Subv): Find exactly where the NWM module resets this value. | ||
| 433 | connection_status.changed_nodes = 0; | ||
| 434 | } | ||
| 357 | 435 | ||
| 358 | LOG_DEBUG(Service_NWM, "called"); | 436 | LOG_DEBUG(Service_NWM, "called"); |
| 359 | } | 437 | } |
| @@ -434,31 +512,36 @@ static void BeginHostingNetwork(Interface* self) { | |||
| 434 | // The real UDS module throws a fatal error if this assert fails. | 512 | // The real UDS module throws a fatal error if this assert fails. |
| 435 | ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); | 513 | ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); |
| 436 | 514 | ||
| 437 | connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); | 515 | { |
| 438 | 516 | std::lock_guard<std::mutex> lock(connection_status_mutex); | |
| 439 | // Ensure the application data size is less than the maximum value. | 517 | connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); |
| 440 | ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); | 518 | |
| 441 | 519 | // Ensure the application data size is less than the maximum value. | |
| 442 | // Set up basic information for this network. | 520 | ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, |
| 443 | network_info.oui_value = NintendoOUI; | 521 | "Data size is too big."); |
| 444 | network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); | 522 | |
| 445 | 523 | // Set up basic information for this network. | |
| 446 | connection_status.max_nodes = network_info.max_nodes; | 524 | network_info.oui_value = NintendoOUI; |
| 447 | 525 | network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); | |
| 448 | // Resize the nodes list to hold max_nodes. | 526 | |
| 449 | node_info.resize(network_info.max_nodes); | 527 | connection_status.max_nodes = network_info.max_nodes; |
| 450 | 528 | ||
| 451 | // There's currently only one node in the network (the host). | 529 | // Resize the nodes list to hold max_nodes. |
| 452 | connection_status.total_nodes = 1; | 530 | node_info.resize(network_info.max_nodes); |
| 453 | network_info.total_nodes = 1; | 531 | |
| 454 | // The host is always the first node | 532 | // There's currently only one node in the network (the host). |
| 455 | connection_status.network_node_id = 1; | 533 | connection_status.total_nodes = 1; |
| 456 | node_info[0].network_node_id = 1; | 534 | network_info.total_nodes = 1; |
| 457 | connection_status.nodes[0] = connection_status.network_node_id; | 535 | // The host is always the first node |
| 458 | // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. | 536 | connection_status.network_node_id = 1; |
| 459 | connection_status.node_bitmask |= 1; | 537 | current_node.network_node_id = 1; |
| 460 | // Notify the application that the first node was set. | 538 | connection_status.nodes[0] = connection_status.network_node_id; |
| 461 | connection_status.changed_nodes |= 1; | 539 | // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. |
| 540 | connection_status.node_bitmask |= 1; | ||
| 541 | // Notify the application that the first node was set. | ||
| 542 | connection_status.changed_nodes |= 1; | ||
| 543 | node_info[0] = current_node; | ||
| 544 | } | ||
| 462 | 545 | ||
| 463 | // If the game has a preferred channel, use that instead. | 546 | // If the game has a preferred channel, use that instead. |
| 464 | if (network_info.channel != 0) | 547 | if (network_info.channel != 0) |
| @@ -495,9 +578,13 @@ static void DestroyNetwork(Interface* self) { | |||
| 495 | // Unschedule the beacon broadcast event. | 578 | // Unschedule the beacon broadcast event. |
| 496 | CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); | 579 | CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); |
| 497 | 580 | ||
| 498 | // TODO(Subv): Check if connection_status is indeed reset after this call. | 581 | { |
| 499 | connection_status = {}; | 582 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 500 | connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); | 583 | |
| 584 | // TODO(Subv): Check if connection_status is indeed reset after this call. | ||
| 585 | connection_status = {}; | ||
| 586 | connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); | ||
| 587 | } | ||
| 501 | connection_status_event->Signal(); | 588 | connection_status_event->Signal(); |
| 502 | 589 | ||
| 503 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | 590 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |
| @@ -540,17 +627,24 @@ static void SendTo(Interface* self) { | |||
| 540 | 627 | ||
| 541 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | 628 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |
| 542 | 629 | ||
| 543 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && | 630 | u16 network_node_id; |
| 544 | connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { | ||
| 545 | rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, | ||
| 546 | ErrorSummary::InvalidState, ErrorLevel::Status)); | ||
| 547 | return; | ||
| 548 | } | ||
| 549 | 631 | ||
| 550 | if (dest_node_id == connection_status.network_node_id) { | 632 | { |
| 551 | rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, | 633 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 552 | ErrorSummary::WrongArgument, ErrorLevel::Status)); | 634 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && |
| 553 | return; | 635 | connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { |
| 636 | rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, | ||
| 637 | ErrorSummary::InvalidState, ErrorLevel::Status)); | ||
| 638 | return; | ||
| 639 | } | ||
| 640 | |||
| 641 | if (dest_node_id == connection_status.network_node_id) { | ||
| 642 | rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, | ||
| 643 | ErrorSummary::WrongArgument, ErrorLevel::Status)); | ||
| 644 | return; | ||
| 645 | } | ||
| 646 | |||
| 647 | network_node_id = connection_status.network_node_id; | ||
| 554 | } | 648 | } |
| 555 | 649 | ||
| 556 | // TODO(Subv): Do something with the flags. | 650 | // TODO(Subv): Do something with the flags. |
| @@ -567,8 +661,8 @@ static void SendTo(Interface* self) { | |||
| 567 | 661 | ||
| 568 | // TODO(Subv): Increment the sequence number after each sent packet. | 662 | // TODO(Subv): Increment the sequence number after each sent packet. |
| 569 | u16 sequence_number = 0; | 663 | u16 sequence_number = 0; |
| 570 | std::vector<u8> data_payload = GenerateDataPayload( | 664 | std::vector<u8> data_payload = |
| 571 | data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); | 665 | GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); |
| 572 | 666 | ||
| 573 | // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt | 667 | // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt |
| 574 | // and encapsulate the payload. | 668 | // and encapsulate the payload. |
| @@ -595,6 +689,7 @@ static void GetChannel(Interface* self) { | |||
| 595 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); | 689 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); |
| 596 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | 690 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |
| 597 | 691 | ||
| 692 | std::lock_guard<std::mutex> lock(connection_status_mutex); | ||
| 598 | bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); | 693 | bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); |
| 599 | 694 | ||
| 600 | u8 channel = is_connected ? network_channel : 0; | 695 | u8 channel = is_connected ? network_channel : 0; |
| @@ -766,6 +861,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { | |||
| 766 | * @param network_node_id Network Node Id of the connecting client. | 861 | * @param network_node_id Network Node Id of the connecting client. |
| 767 | */ | 862 | */ |
| 768 | void OnClientConnected(u16 network_node_id) { | 863 | void OnClientConnected(u16 network_node_id) { |
| 864 | std::lock_guard<std::mutex> lock(connection_status_mutex); | ||
| 769 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | 865 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), |
| 770 | "Can not accept clients if we're not hosting a network"); | 866 | "Can not accept clients if we're not hosting a network"); |
| 771 | ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, | 867 | ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, |
| @@ -827,8 +923,11 @@ NWM_UDS::~NWM_UDS() { | |||
| 827 | connection_status_event = nullptr; | 923 | connection_status_event = nullptr; |
| 828 | recv_buffer_memory = nullptr; | 924 | recv_buffer_memory = nullptr; |
| 829 | 925 | ||
| 830 | connection_status = {}; | 926 | { |
| 831 | connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | 927 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 928 | connection_status = {}; | ||
| 929 | connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); | ||
| 930 | } | ||
| 832 | 931 | ||
| 833 | CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); | 932 | CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); |
| 834 | } | 933 | } |
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp index c8a76ec2a..c74f51253 100644 --- a/src/core/hle/service/nwm/uds_connection.cpp +++ b/src/core/hle/service/nwm/uds_connection.cpp | |||
| @@ -75,5 +75,14 @@ std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_i | |||
| 75 | return data; | 75 | return data; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) { | ||
| 79 | AssociationResponseFrame frame; | ||
| 80 | memcpy(&frame, body.data(), sizeof(frame)); | ||
| 81 | |||
| 82 | constexpr u16 AssociationIdMask = 0x3FFF; | ||
| 83 | return std::make_tuple(static_cast<AssocStatus>(frame.status_code), | ||
| 84 | frame.assoc_id & AssociationIdMask); | ||
| 85 | } | ||
| 86 | |||
| 78 | } // namespace NWM | 87 | } // namespace NWM |
| 79 | } // namespace Service | 88 | } // namespace Service |
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h index 73f55a4fd..a664f8471 100644 --- a/src/core/hle/service/nwm/uds_connection.h +++ b/src/core/hle/service/nwm/uds_connection.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <tuple> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| @@ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body); | |||
| 47 | /// network id, starting at the frame body. | 48 | /// network id, starting at the frame body. |
| 48 | std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); | 49 | std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); |
| 49 | 50 | ||
| 51 | /// Returns a tuple of (association status, association id) from the body of an AssociationResponse | ||
| 52 | /// frame. | ||
| 53 | std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body); | ||
| 54 | |||
| 50 | } // namespace NWM | 55 | } // namespace NWM |
| 51 | } // namespace Service | 56 | } // namespace Service |
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 8c6742dba..0fd9b8b8c 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp | |||
| @@ -274,5 +274,26 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | |||
| 274 | return buffer; | 274 | return buffer; |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { | ||
| 278 | EAPoLStartPacket eapol_start{}; | ||
| 279 | eapol_start.association_id = association_id; | ||
| 280 | eapol_start.friend_code_seed = node_info.friend_code_seed; | ||
| 281 | |||
| 282 | for (int i = 0; i < node_info.username.size(); ++i) | ||
| 283 | eapol_start.username[i] = node_info.username[i]; | ||
| 284 | |||
| 285 | // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. | ||
| 286 | // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in | ||
| 287 | // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game. | ||
| 288 | // Find out what that means. | ||
| 289 | |||
| 290 | std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket)); | ||
| 291 | std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start)); | ||
| 292 | |||
| 293 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); | ||
| 294 | buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); | ||
| 295 | return buffer; | ||
| 296 | } | ||
| 297 | |||
| 277 | } // namespace NWM | 298 | } // namespace NWM |
| 278 | } // namespace Service | 299 | } // namespace Service |
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index a23520a41..76e8f546b 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h | |||
| @@ -67,6 +67,27 @@ struct DataFrameCryptoCTR { | |||
| 67 | 67 | ||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 69 | 69 | ||
| 70 | constexpr u16 EAPoLStartMagic = 0x201; | ||
| 71 | |||
| 72 | /* | ||
| 73 | * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host | ||
| 74 | */ | ||
| 75 | struct EAPoLStartPacket { | ||
| 76 | u16_be magic = EAPoLStartMagic; | ||
| 77 | u16_be association_id; | ||
| 78 | // This value is hardcoded to 1 in the NWM module. | ||
| 79 | u16_be unknown = 1; | ||
| 80 | INSERT_PADDING_BYTES(2); | ||
| 81 | |||
| 82 | u64_be friend_code_seed; | ||
| 83 | std::array<u16_be, 10> username; | ||
| 84 | INSERT_PADDING_BYTES(4); | ||
| 85 | u16_be network_node_id; | ||
| 86 | INSERT_PADDING_BYTES(6); | ||
| 87 | }; | ||
| 88 | |||
| 89 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | ||
| 90 | |||
| 70 | /** | 91 | /** |
| 71 | * Generates an unencrypted 802.11 data payload. | 92 | * Generates an unencrypted 802.11 data payload. |
| 72 | * @returns The generated frame payload. | 93 | * @returns The generated frame payload. |
| @@ -74,5 +95,12 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron | |||
| 74 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | 95 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, |
| 75 | u16 src_node, u16 sequence_number); | 96 | u16 src_node, u16 sequence_number); |
| 76 | 97 | ||
| 98 | /* | ||
| 99 | * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS | ||
| 100 | * communication. | ||
| 101 | * @returns The generated frame body. | ||
| 102 | */ | ||
| 103 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | ||
| 104 | |||
| 77 | } // namespace NWM | 105 | } // namespace NWM |
| 78 | } // namespace Service | 106 | } // namespace Service |