diff options
| author | 2017-09-30 18:18:45 +0200 | |
|---|---|---|
| committer | 2017-09-30 18:18:45 +0200 | |
| commit | afb1012bcd7e7aea2428aadb195b04ef72fcf861 (patch) | |
| tree | de72174b68437a348469ba50f41a77c656feef49 | |
| parent | Merge pull request #2972 from Subv/ignore_.vs (diff) | |
| download | yuzu-afb1012bcd7e7aea2428aadb195b04ef72fcf861.tar.gz yuzu-afb1012bcd7e7aea2428aadb195b04ef72fcf861.tar.xz yuzu-afb1012bcd7e7aea2428aadb195b04ef72fcf861.zip | |
Services/UDS: Handle the rest of the connection sequence. (#2963)
Services/UDS: Handle the rest of the connection sequence.
| -rw-r--r-- | src/core/hle/service/nwm/nwm_uds.cpp | 121 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.cpp | 80 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.h | 68 |
3 files changed, 250 insertions, 19 deletions
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 8ef0cda09..0aa63cc1e 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/event.h" | 16 | #include "core/hle/kernel/event.h" |
| 17 | #include "core/hle/kernel/shared_memory.h" | 17 | #include "core/hle/kernel/shared_memory.h" |
| 18 | #include "core/hle/lock.h" | ||
| 18 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| 19 | #include "core/hle/service/nwm/nwm_uds.h" | 20 | #include "core/hle/service/nwm/nwm_uds.h" |
| 20 | #include "core/hle/service/nwm/uds_beacon.h" | 21 | #include "core/hle/service/nwm/uds_beacon.h" |
| @@ -100,6 +101,20 @@ void SendPacket(Network::WifiPacket& packet) { | |||
| 100 | // TODO(Subv): Implement. | 101 | // TODO(Subv): Implement. |
| 101 | } | 102 | } |
| 102 | 103 | ||
| 104 | /* | ||
| 105 | * Returns an available index in the nodes array for the | ||
| 106 | * currently-hosted UDS network. | ||
| 107 | */ | ||
| 108 | static u16 GetNextAvailableNodeId() { | ||
| 109 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { | ||
| 110 | if ((connection_status.node_bitmask & (1 << index)) == 0) | ||
| 111 | return index; | ||
| 112 | } | ||
| 113 | |||
| 114 | // Any connection attempts to an already full network should have been refused. | ||
| 115 | ASSERT_MSG(false, "No available connection slots in the network"); | ||
| 116 | } | ||
| 117 | |||
| 103 | // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size | 118 | // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size |
| 104 | // limit is exceeded. | 119 | // limit is exceeded. |
| 105 | void HandleBeaconFrame(const Network::WifiPacket& packet) { | 120 | void HandleBeaconFrame(const Network::WifiPacket& packet) { |
| @@ -143,18 +158,88 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { | |||
| 143 | SendPacket(eapol_start); | 158 | SendPacket(eapol_start); |
| 144 | } | 159 | } |
| 145 | 160 | ||
| 146 | /* | 161 | static void HandleEAPoLPacket(const Network::WifiPacket& packet) { |
| 147 | * Returns an available index in the nodes array for the | 162 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 148 | * currently-hosted UDS network. | ||
| 149 | */ | ||
| 150 | static u16 GetNextAvailableNodeId() { | ||
| 151 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { | ||
| 152 | if ((connection_status.node_bitmask & (1 << index)) == 0) | ||
| 153 | return index; | ||
| 154 | } | ||
| 155 | 163 | ||
| 156 | // Any connection attempts to an already full network should have been refused. | 164 | if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { |
| 157 | ASSERT_MSG(false, "No available connection slots in the network"); | 165 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { |
| 166 | LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", | ||
| 167 | connection_status.status); | ||
| 168 | return; | ||
| 169 | } | ||
| 170 | |||
| 171 | auto node = DeserializeNodeInfoFromFrame(packet.data); | ||
| 172 | |||
| 173 | if (connection_status.max_nodes == connection_status.total_nodes) { | ||
| 174 | // Reject connection attempt | ||
| 175 | LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent."); | ||
| 176 | // TODO(B3N30): Figure out what packet is sent here | ||
| 177 | return; | ||
| 178 | } | ||
| 179 | |||
| 180 | // Get an unused network node id | ||
| 181 | u16 node_id = GetNextAvailableNodeId(); | ||
| 182 | node.network_node_id = node_id + 1; | ||
| 183 | |||
| 184 | connection_status.node_bitmask |= 1 << node_id; | ||
| 185 | connection_status.changed_nodes |= 1 << node_id; | ||
| 186 | connection_status.nodes[node_id] = node.network_node_id; | ||
| 187 | connection_status.total_nodes++; | ||
| 188 | |||
| 189 | u8 current_nodes = network_info.total_nodes; | ||
| 190 | node_info[current_nodes] = node; | ||
| 191 | |||
| 192 | network_info.total_nodes++; | ||
| 193 | |||
| 194 | // Send the EAPoL-Logoff packet. | ||
| 195 | using Network::WifiPacket; | ||
| 196 | WifiPacket eapol_logoff; | ||
| 197 | eapol_logoff.channel = network_channel; | ||
| 198 | eapol_logoff.data = | ||
| 199 | GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info, | ||
| 200 | network_info.max_nodes, network_info.total_nodes); | ||
| 201 | // TODO(Subv): Encrypt the packet. | ||
| 202 | eapol_logoff.destination_address = packet.transmitter_address; | ||
| 203 | eapol_logoff.type = WifiPacket::PacketType::Data; | ||
| 204 | |||
| 205 | SendPacket(eapol_logoff); | ||
| 206 | // TODO(B3N30): Broadcast updated node list | ||
| 207 | // The 3ds does this presumably to support spectators. | ||
| 208 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 209 | connection_status_event->Signal(); | ||
| 210 | } else { | ||
| 211 | if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) { | ||
| 212 | LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", | ||
| 213 | connection_status.status); | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | auto logoff = ParseEAPoLLogoffFrame(packet.data); | ||
| 217 | |||
| 218 | network_info.total_nodes = logoff.connected_nodes; | ||
| 219 | network_info.max_nodes = logoff.max_nodes; | ||
| 220 | |||
| 221 | connection_status.network_node_id = logoff.assigned_node_id; | ||
| 222 | connection_status.total_nodes = logoff.connected_nodes; | ||
| 223 | connection_status.max_nodes = logoff.max_nodes; | ||
| 224 | |||
| 225 | node_info.clear(); | ||
| 226 | node_info.reserve(network_info.max_nodes); | ||
| 227 | for (size_t index = 0; index < logoff.connected_nodes; ++index) { | ||
| 228 | connection_status.node_bitmask |= 1 << index; | ||
| 229 | connection_status.changed_nodes |= 1 << index; | ||
| 230 | connection_status.nodes[index] = logoff.nodes[index].network_node_id; | ||
| 231 | |||
| 232 | node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index])); | ||
| 233 | } | ||
| 234 | |||
| 235 | // We're now connected, signal the application | ||
| 236 | connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient); | ||
| 237 | // Some games require ConnectToNetwork to block, for now it doesn't | ||
| 238 | // If blocking is implemented this lock needs to be changed, | ||
| 239 | // otherwise it might cause deadlocks | ||
| 240 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 241 | connection_status_event->Signal(); | ||
| 242 | } | ||
| 158 | } | 243 | } |
| 159 | 244 | ||
| 160 | /* | 245 | /* |
| @@ -238,6 +323,17 @@ void HandleAuthenticationFrame(const Network::WifiPacket& packet) { | |||
| 238 | } | 323 | } |
| 239 | } | 324 | } |
| 240 | 325 | ||
| 326 | static void HandleDataFrame(const Network::WifiPacket& packet) { | ||
| 327 | switch (GetFrameEtherType(packet.data)) { | ||
| 328 | case EtherType::EAPoL: | ||
| 329 | HandleEAPoLPacket(packet); | ||
| 330 | break; | ||
| 331 | case EtherType::SecureData: | ||
| 332 | // TODO(B3N30): Handle SecureData packets | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 241 | /// Callback to parse and handle a received wifi packet. | 337 | /// Callback to parse and handle a received wifi packet. |
| 242 | void OnWifiPacketReceived(const Network::WifiPacket& packet) { | 338 | void OnWifiPacketReceived(const Network::WifiPacket& packet) { |
| 243 | switch (packet.type) { | 339 | switch (packet.type) { |
| @@ -250,6 +346,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { | |||
| 250 | case Network::WifiPacket::PacketType::AssociationResponse: | 346 | case Network::WifiPacket::PacketType::AssociationResponse: |
| 251 | HandleAssociationResponseFrame(packet); | 347 | HandleAssociationResponseFrame(packet); |
| 252 | break; | 348 | break; |
| 349 | case Network::WifiPacket::PacketType::Data: | ||
| 350 | HandleDataFrame(packet); | ||
| 351 | break; | ||
| 253 | } | 352 | } |
| 254 | } | 353 | } |
| 255 | 354 | ||
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 3ef2a84b6..4b389710f 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <cstring> | 6 | #include <cstring> |
| 6 | #include <cryptopp/aes.h> | 7 | #include <cryptopp/aes.h> |
| 7 | #include <cryptopp/ccm.h> | 8 | #include <cryptopp/ccm.h> |
| @@ -277,10 +278,10 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | |||
| 277 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { | 278 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { |
| 278 | EAPoLStartPacket eapol_start{}; | 279 | EAPoLStartPacket eapol_start{}; |
| 279 | eapol_start.association_id = association_id; | 280 | eapol_start.association_id = association_id; |
| 280 | eapol_start.friend_code_seed = node_info.friend_code_seed; | 281 | eapol_start.node.friend_code_seed = node_info.friend_code_seed; |
| 281 | 282 | ||
| 282 | for (int i = 0; i < node_info.username.size(); ++i) | 283 | std::copy(node_info.username.begin(), node_info.username.end(), |
| 283 | eapol_start.username[i] = node_info.username[i]; | 284 | eapol_start.node.username.begin()); |
| 284 | 285 | ||
| 285 | // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. | 286 | // 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 | // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in |
| @@ -295,5 +296,78 @@ std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node | |||
| 295 | return buffer; | 296 | return buffer; |
| 296 | } | 297 | } |
| 297 | 298 | ||
| 299 | EtherType GetFrameEtherType(const std::vector<u8>& frame) { | ||
| 300 | LLCHeader header; | ||
| 301 | std::memcpy(&header, frame.data(), sizeof(header)); | ||
| 302 | |||
| 303 | u16 ethertype = header.protocol; | ||
| 304 | return static_cast<EtherType>(ethertype); | ||
| 305 | } | ||
| 306 | |||
| 307 | u16 GetEAPoLFrameType(const std::vector<u8>& frame) { | ||
| 308 | // Ignore the LLC header | ||
| 309 | u16_be eapol_type; | ||
| 310 | std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type)); | ||
| 311 | return eapol_type; | ||
| 312 | } | ||
| 313 | |||
| 314 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) { | ||
| 315 | EAPoLStartPacket eapol_start; | ||
| 316 | |||
| 317 | // Skip the LLC header | ||
| 318 | std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start)); | ||
| 319 | |||
| 320 | NodeInfo node{}; | ||
| 321 | node.friend_code_seed = eapol_start.node.friend_code_seed; | ||
| 322 | |||
| 323 | std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(), | ||
| 324 | node.username.begin()); | ||
| 325 | |||
| 326 | return node; | ||
| 327 | } | ||
| 328 | |||
| 329 | NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) { | ||
| 330 | NodeInfo node_info{}; | ||
| 331 | node_info.friend_code_seed = node.friend_code_seed; | ||
| 332 | node_info.network_node_id = node.network_node_id; | ||
| 333 | |||
| 334 | std::copy(node.username.begin(), node.username.end(), node_info.username.begin()); | ||
| 335 | |||
| 336 | return node_info; | ||
| 337 | } | ||
| 338 | |||
| 339 | std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, | ||
| 340 | const NodeList& nodes, u8 max_nodes, u8 total_nodes) { | ||
| 341 | EAPoLLogoffPacket eapol_logoff{}; | ||
| 342 | eapol_logoff.assigned_node_id = network_node_id; | ||
| 343 | eapol_logoff.connected_nodes = total_nodes; | ||
| 344 | eapol_logoff.max_nodes = max_nodes; | ||
| 345 | |||
| 346 | for (size_t index = 0; index < total_nodes; ++index) { | ||
| 347 | const auto& node_info = nodes[index]; | ||
| 348 | auto& node = eapol_logoff.nodes[index]; | ||
| 349 | |||
| 350 | node.friend_code_seed = node_info.friend_code_seed; | ||
| 351 | node.network_node_id = node_info.network_node_id; | ||
| 352 | |||
| 353 | std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin()); | ||
| 354 | } | ||
| 355 | |||
| 356 | std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket)); | ||
| 357 | std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff)); | ||
| 358 | |||
| 359 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); | ||
| 360 | buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); | ||
| 361 | return buffer; | ||
| 362 | } | ||
| 363 | |||
| 364 | EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) { | ||
| 365 | EAPoLLogoffPacket eapol_logoff; | ||
| 366 | |||
| 367 | // Skip the LLC header | ||
| 368 | std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff)); | ||
| 369 | return eapol_logoff; | ||
| 370 | } | ||
| 371 | |||
| 298 | } // namespace NWM | 372 | } // namespace NWM |
| 299 | } // namespace Service | 373 | } // namespace Service |
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 76e8f546b..76bccb1bf 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hle/service/nwm/uds_beacon.h" | ||
| 11 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 12 | 13 | ||
| 13 | namespace Service { | 14 | namespace Service { |
| @@ -67,6 +68,16 @@ struct DataFrameCryptoCTR { | |||
| 67 | 68 | ||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 69 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 69 | 70 | ||
| 71 | struct EAPoLNodeInfo { | ||
| 72 | u64_be friend_code_seed; | ||
| 73 | std::array<u16_be, 10> username; | ||
| 74 | INSERT_PADDING_BYTES(4); | ||
| 75 | u16_be network_node_id; | ||
| 76 | INSERT_PADDING_BYTES(6); | ||
| 77 | }; | ||
| 78 | |||
| 79 | static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); | ||
| 80 | |||
| 70 | constexpr u16 EAPoLStartMagic = 0x201; | 81 | constexpr u16 EAPoLStartMagic = 0x201; |
| 71 | 82 | ||
| 72 | /* | 83 | /* |
| @@ -78,15 +89,27 @@ struct EAPoLStartPacket { | |||
| 78 | // This value is hardcoded to 1 in the NWM module. | 89 | // This value is hardcoded to 1 in the NWM module. |
| 79 | u16_be unknown = 1; | 90 | u16_be unknown = 1; |
| 80 | INSERT_PADDING_BYTES(2); | 91 | INSERT_PADDING_BYTES(2); |
| 92 | EAPoLNodeInfo node; | ||
| 93 | }; | ||
| 81 | 94 | ||
| 82 | u64_be friend_code_seed; | 95 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); |
| 83 | std::array<u16_be, 10> username; | 96 | |
| 84 | INSERT_PADDING_BYTES(4); | 97 | constexpr u16 EAPoLLogoffMagic = 0x202; |
| 85 | u16_be network_node_id; | 98 | |
| 99 | struct EAPoLLogoffPacket { | ||
| 100 | u16_be magic = EAPoLLogoffMagic; | ||
| 101 | INSERT_PADDING_BYTES(2); | ||
| 102 | u16_be assigned_node_id; | ||
| 103 | MacAddress client_mac_address; | ||
| 86 | INSERT_PADDING_BYTES(6); | 104 | INSERT_PADDING_BYTES(6); |
| 105 | u8 connected_nodes; | ||
| 106 | u8 max_nodes; | ||
| 107 | INSERT_PADDING_BYTES(4); | ||
| 108 | |||
| 109 | std::array<EAPoLNodeInfo, UDSMaxNodes> nodes; | ||
| 87 | }; | 110 | }; |
| 88 | 111 | ||
| 89 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | 112 | static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size"); |
| 90 | 113 | ||
| 91 | /** | 114 | /** |
| 92 | * Generates an unencrypted 802.11 data payload. | 115 | * Generates an unencrypted 802.11 data payload. |
| @@ -102,5 +125,40 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | |||
| 102 | */ | 125 | */ |
| 103 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | 126 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); |
| 104 | 127 | ||
| 128 | /* | ||
| 129 | * Returns the EtherType of the specified 802.11 frame. | ||
| 130 | */ | ||
| 131 | EtherType GetFrameEtherType(const std::vector<u8>& frame); | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame. | ||
| 135 | * Note: The frame *must* be an EAPoL frame. | ||
| 136 | */ | ||
| 137 | u16 GetEAPoLFrameType(const std::vector<u8>& frame); | ||
| 138 | |||
| 139 | /* | ||
| 140 | * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet | ||
| 141 | * encapsulated in an 802.11 data frame. | ||
| 142 | */ | ||
| 143 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame); | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. | ||
| 147 | */ | ||
| 148 | NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node); | ||
| 149 | |||
| 150 | /* | ||
| 151 | * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS | ||
| 152 | * communication. | ||
| 153 | * @returns The generated frame body. | ||
| 154 | */ | ||
| 155 | std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, | ||
| 156 | const NodeList& nodes, u8 max_nodes, u8 total_nodes); | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame. | ||
| 160 | */ | ||
| 161 | EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame); | ||
| 162 | |||
| 105 | } // namespace NWM | 163 | } // namespace NWM |
| 106 | } // namespace Service | 164 | } // namespace Service |