summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp121
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp80
-rw-r--r--src/core/hle/service/nwm/uds_data.h68
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 */
108static 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.
105void HandleBeaconFrame(const Network::WifiPacket& packet) { 120void 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/* 161static 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 */
150static 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
326static 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.
242void OnWifiPacketReceived(const Network::WifiPacket& packet) { 338void 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
277std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { 278std::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
299EtherType 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
307u16 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
314NodeInfo 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
329NodeInfo 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
339std::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
364EAPoLLogoffPacket 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
13namespace Service { 14namespace Service {
@@ -67,6 +68,16 @@ struct DataFrameCryptoCTR {
67 68
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 70
71struct 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
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
70constexpr u16 EAPoLStartMagic = 0x201; 81constexpr 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; 95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
83 std::array<u16_be, 10> username; 96
84 INSERT_PADDING_BYTES(4); 97constexpr u16 EAPoLLogoffMagic = 0x202;
85 u16_be network_node_id; 98
99struct 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
89static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); 112static_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 */
103std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); 126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
104 127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType 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 */
137u16 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 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo 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 */
155std::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 */
161EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame);
162
105} // namespace NWM 163} // namespace NWM
106} // namespace Service 164} // namespace Service