Merge pull request #2831 from Subv/uds_auth

Services/UDS: Handle beacon frames and the basic AP connection sequence frames.
merge-requests/60/head
Weiyi Wang 2017-09-05 15:03:56 +07:00 committed by GitHub
commit 617b6974b9
7 changed files with 289 additions and 53 deletions

@ -145,6 +145,7 @@ set(SRCS
hle/service/nwm/nwm_tst.cpp
hle/service/nwm/nwm_uds.cpp
hle/service/nwm/uds_beacon.cpp
hle/service/nwm/uds_connection.cpp
hle/service/nwm/uds_data.cpp
hle/service/pm_app.cpp
hle/service/ptm/ptm.cpp
@ -344,6 +345,7 @@ set(HEADERS
hle/service/nwm/nwm_tst.h
hle/service/nwm/nwm_uds.h
hle/service/nwm/uds_beacon.h
hle/service/nwm/uds_connection.h
hle/service/nwm/uds_data.h
hle/service/pm_app.h
hle/service/ptm/ptm.h

@ -4,6 +4,7 @@
#include <array>
#include <cstring>
#include <mutex>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
@ -15,8 +16,10 @@
#include "core/hle/result.h"
#include "core/hle/service/nwm/nwm_uds.h"
#include "core/hle/service/nwm/uds_beacon.h"
#include "core/hle/service/nwm/uds_connection.h"
#include "core/hle/service/nwm/uds_data.h"
#include "core/memory.h"
#include "network/network.h"
namespace Service {
namespace NWM {
@ -51,6 +54,135 @@ static NetworkInfo network_info;
// Event that will generate and send the 802.11 beacon frames.
static int beacon_broadcast_event;
// Mutex to synchronize access to the list of received beacons between the emulation thread and the
// network thread.
static std::mutex beacon_mutex;
// Number of beacons to store before we start dropping the old ones.
// TODO(Subv): Find a more accurate value for this limit.
constexpr size_t MaxBeaconFrames = 15;
// List of the last <MaxBeaconFrames> beacons received from the network.
static std::deque<Network::WifiPacket> received_beacons;
/**
* Returns a list of received 802.11 beacon frames from the specified sender since the last call.
*/
std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
std::lock_guard<std::mutex> lock(beacon_mutex);
// TODO(Subv): Filter by sender.
return std::move(received_beacons);
}
/// Sends a WifiPacket to the room we're currently connected to.
void SendPacket(Network::WifiPacket& packet) {
// TODO(Subv): Implement.
}
// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
// limit is exceeded.
void HandleBeaconFrame(const Network::WifiPacket& packet) {
std::lock_guard<std::mutex> lock(beacon_mutex);
received_beacons.emplace_back(packet);
// Discard old beacons if the buffer is full.
if (received_beacons.size() > MaxBeaconFrames)
received_beacons.pop_front();
}
/*
* Returns an available index in the nodes array for the
* currently-hosted UDS network.
*/
static u16 GetNextAvailableNodeId() {
ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
"Can not accept clients if we're not hosting a network");
for (u16 index = 0; index < connection_status.max_nodes; ++index) {
if ((connection_status.node_bitmask & (1 << index)) == 0)
return index;
}
// Any connection attempts to an already full network should have been refused.
ASSERT_MSG(false, "No available connection slots in the network");
}
/*
* Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
* authentication frame with SEQ1.
*/
void StartConnectionSequence(const MacAddress& server) {
ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
// TODO(Subv): Handle timeout.
// Send an authentication frame with SEQ1
using Network::WifiPacket;
WifiPacket auth_request;
auth_request.channel = network_channel;
auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
auth_request.destination_address = server;
auth_request.type = WifiPacket::PacketType::Authentication;
SendPacket(auth_request);
}
/// Sends an Association Response frame to the specified mac address
void SendAssociationResponseFrame(const MacAddress& address) {
ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
using Network::WifiPacket;
WifiPacket assoc_response;
assoc_response.channel = network_channel;
// TODO(Subv): This will cause multiple clients to end up with the same association id, but
// we're not using that for anything.
u16 association_id = 1;
assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
network_info.network_id);
assoc_response.destination_address = address;
assoc_response.type = WifiPacket::PacketType::AssociationResponse;
SendPacket(assoc_response);
}
/*
* Handles the authentication request frame and sends the authentication response and association
* response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
* with an Authentication frame containing SEQ2, and immediately sends an Association response frame
* containing the details of the access point and the assigned association id for the new client.
*/
void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
// Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
// Respond with an authentication response frame with SEQ2
using Network::WifiPacket;
WifiPacket auth_request;
auth_request.channel = network_channel;
auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
auth_request.destination_address = packet.transmitter_address;
auth_request.type = WifiPacket::PacketType::Authentication;
SendPacket(auth_request);
SendAssociationResponseFrame(packet.transmitter_address);
}
}
/// Callback to parse and handle a received wifi packet.
void OnWifiPacketReceived(const Network::WifiPacket& packet) {
switch (packet.type) {
case Network::WifiPacket::PacketType::Beacon:
HandleBeaconFrame(packet);
break;
case Network::WifiPacket::PacketType::Authentication:
HandleAuthenticationFrame(packet);
break;
}
}
/**
* NWM_UDS::Shutdown service function
* Inputs:
@ -111,8 +243,7 @@ static void RecvBeaconBroadcastData(Interface* self) {
u32 total_size = sizeof(BeaconDataReplyHeader);
// Retrieve all beacon frames that were received from the desired mac address.
std::deque<WifiPacket> beacons =
GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
auto beacons = GetReceivedBeacons(mac_address);
BeaconDataReplyHeader data_reply_header{};
data_reply_header.total_entries = beacons.size();
@ -193,6 +324,9 @@ static void InitializeWithVersion(Interface* self) {
rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
// TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
// the room we're currently in.
LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
sharedmem_size, version, sharedmem_handle);
}
@ -610,31 +744,22 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
return;
// TODO(Subv): Actually send the beacon.
std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
using Network::WifiPacket;
WifiPacket packet;
packet.type = WifiPacket::PacketType::Beacon;
packet.data = std::move(frame);
packet.destination_address = Network::BroadcastMac;
packet.channel = network_channel;
SendPacket(packet);
// Start broadcasting the network, send a beacon frame every 102.4ms.
CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
beacon_broadcast_event, 0);
}
/*
* Returns an available index in the nodes array for the
* currently-hosted UDS network.
*/
static u32 GetNextAvailableNodeId() {
ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
"Can not accept clients if we're not hosting a network");
for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
if ((connection_status.node_bitmask & (1 << index)) == 0)
return index;
}
// Any connection attempts to an already full network should have been refused.
ASSERT_MSG(false, "No available connection slots in the network");
}
/*
* Called when a client connects to an UDS network we're hosting,
* updates the connection status and signals the update event.

@ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>;
enum class NetworkStatus {
NotConnected = 3,
ConnectedAsHost = 6,
Connecting = 7,
ConnectedAsClient = 9,
ConnectedAsSpectator = 10,
};
@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron
static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
/// Additional block tag ids in the Beacon and Association Response frames
enum class TagId : u8 {
SSID = 0,
SupportedRates = 1,
DSParameterSet = 2,
TrafficIndicationMap = 5,
CountryInformation = 7,
ERPInformation = 42,
VendorSpecific = 221
};
class NWM_UDS final : public Interface {
public:
NWM_UDS();

@ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL
return buffer;
}
std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
return {};
}
} // namespace NWM
} // namespace Service

@ -17,17 +17,6 @@ namespace NWM {
using MacAddress = std::array<u8, 6>;
constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
/// Additional block tag ids in the Beacon frames
enum class TagId : u8 {
SSID = 0,
SupportedRates = 1,
DSParameterSet = 2,
TrafficIndicationMap = 5,
CountryInformation = 7,
ERPInformation = 42,
VendorSpecific = 221
};
/**
* Internal vendor-specific tag ids as stored inside
* VendorSpecific blocks in the Beacon frames.
@ -135,20 +124,6 @@ struct BeaconData {
static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
/// Information about a received WiFi packet.
/// Acts as our own 802.11 header.
struct WifiPacket {
enum class PacketType { Beacon, Data };
PacketType type; ///< The type of 802.11 frame, Beacon / Data.
/// Raw 802.11 frame data, starting at the management frame header for management frames.
std::vector<u8> data;
MacAddress transmitter_address; ///< Mac address of the transmitter.
MacAddress destination_address; ///< Mac address of the receiver.
u8 channel; ///< WiFi channel where this frame was transmitted.
};
/**
* Decrypts the beacon data buffer for the network described by `network_info`.
*/
@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer)
*/
std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
/**
* Returns a list of received 802.11 frames from the specified sender
* matching the type since the last call.
*/
std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
} // namespace NWM
} // namespace Service

@ -0,0 +1,79 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/nwm/nwm_uds.h"
#include "core/hle/service/nwm/uds_connection.h"
#include "fmt/format.h"
namespace Service {
namespace NWM {
// Note: These values were taken from a packet capture of an o3DS XL
// broadcasting a Super Smash Bros. 4 lobby.
constexpr u16 DefaultExtraCapabilities = 0x0431;
std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
AuthenticationFrame frame{};
frame.auth_seq = static_cast<u16>(seq);
std::vector<u8> data(sizeof(frame));
std::memcpy(data.data(), &frame, sizeof(frame));
return data;
}
AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
AuthenticationFrame frame;
std::memcpy(&frame, body.data(), sizeof(frame));
return static_cast<AuthenticationSeq>(frame.auth_seq);
}
/**
* Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
* specified network id as the SSID value.
* @param network_id The network id to use.
* @returns A buffer with the SSID tag.
*/
static std::vector<u8> GenerateSSIDTag(u32 network_id) {
constexpr u8 SSIDSize = 8;
struct {
u8 id = static_cast<u8>(TagId::SSID);
u8 size = SSIDSize;
} tag_header;
std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
std::string network_name = fmt::format("{0:08X}", network_id);
std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
return buffer;
}
std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
AssociationResponseFrame frame{};
frame.capabilities = DefaultExtraCapabilities;
frame.status_code = static_cast<u16>(status);
// The association id is ORed with this magic value (0xC000)
constexpr u16 AssociationIdMagic = 0xC000;
frame.assoc_id = association_id | AssociationIdMagic;
std::vector<u8> data(sizeof(frame));
std::memcpy(data.data(), &frame, sizeof(frame));
auto ssid_tag = GenerateSSIDTag(network_id);
data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
// TODO(Subv): Add the SupportedRates tag.
// TODO(Subv): Add the DSParameterSet tag.
// TODO(Subv): Add the ERPInformation tag.
return data;
}
} // namespace NWM
} // namespace Service

@ -0,0 +1,51 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/service.h"
namespace Service {
namespace NWM {
/// Sequence number of the 802.11 authentication frames.
enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
enum class AuthAlgorithm : u16 { OpenSystem = 0 };
enum class AuthStatus : u16 { Successful = 0 };
enum class AssocStatus : u16 { Successful = 0 };
struct AuthenticationFrame {
u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
u16_le auth_seq;
u16_le status_code = static_cast<u16>(AuthStatus::Successful);
};
static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
struct AssociationResponseFrame {
u16_le capabilities;
u16_le status_code;
u16_le assoc_id;
};
static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
/// Generates an 802.11 authentication frame, starting at the frame body.
std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
/// Returns the sequence number from the body of an Authentication frame.
AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
/// Generates an 802.11 association response frame with the specified status, association id and
/// network id, starting at the frame body.
std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
} // namespace NWM
} // namespace Service