Merge pull request #3070 from B3n30/uds_connect_disconnect

NWM_UDS: change to Service Framework
This commit is contained in:
Weiyi Wang 2018-02-22 16:55:59 +02:00 committed by GitHub
commit e2eab46535
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 559 additions and 376 deletions

View file

@ -21,7 +21,10 @@ void Init() {
AddService(new NWM_SAP); AddService(new NWM_SAP);
AddService(new NWM_SOC); AddService(new NWM_SOC);
AddService(new NWM_TST); AddService(new NWM_TST);
AddService(new NWM_UDS); }
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<NWM_UDS>()->InstallAsService(service_manager);
} }
} // namespace NWM } // namespace NWM

View file

@ -4,11 +4,14 @@
#pragma once #pragma once
#include "core/hle/service/service.h"
namespace Service { namespace Service {
namespace NWM { namespace NWM {
/// Initialize all NWM services /// Initialize all NWM services
void Init(); void Init();
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace NWM } // namespace NWM
} // namespace Service } // namespace Service

View file

@ -7,6 +7,7 @@
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
#include <list> #include <list>
#include <map>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -31,6 +32,7 @@ namespace NWM {
namespace ErrCodes { namespace ErrCodes {
enum { enum {
NotInitialized = 2, NotInitialized = 2,
WrongStatus = 490,
}; };
} // namespace ErrCodes } // namespace ErrCodes
@ -75,13 +77,21 @@ static u8 network_channel = DefaultNetworkChannel;
// Information about the network that we're currently connected to. // Information about the network that we're currently connected to.
static NetworkInfo network_info; static NetworkInfo network_info;
// Mapping of mac addresses to their respective node_ids.
static std::map<MacAddress, u32> node_map;
// Event that will generate and send the 802.11 beacon frames. // Event that will generate and send the 802.11 beacon frames.
static CoreTiming::EventType* beacon_broadcast_event; static CoreTiming::EventType* beacon_broadcast_event;
// Callback identifier for the OnWifiPacketReceived event.
static Network::RoomMember::CallbackHandle<Network::WifiPacket> wifi_packet_received;
// Mutex to synchronize access to the connection status between the emulation thread and the // Mutex to synchronize access to the connection status between the emulation thread and the
// network thread. // network thread.
static std::mutex connection_status_mutex; static std::mutex connection_status_mutex;
static Kernel::SharedPtr<Kernel::Event> connection_event;
// 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;
@ -119,7 +129,12 @@ std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
/// Sends a WifiPacket to the room we're currently connected to. /// Sends a WifiPacket to the room we're currently connected to.
void SendPacket(Network::WifiPacket& packet) { void SendPacket(Network::WifiPacket& packet) {
// TODO(Subv): Implement. if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->GetState() == Network::RoomMember::State::Joined) {
packet.transmitter_address = room_member->GetMacAddress();
room_member->SendWifiPacket(packet);
}
}
} }
/* /*
@ -214,6 +229,8 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
network_info.total_nodes++; network_info.total_nodes++;
node_map[packet.transmitter_address] = node_id;
// Send the EAPoL-Logoff packet. // Send the EAPoL-Logoff packet.
using Network::WifiPacket; using Network::WifiPacket;
WifiPacket eapol_logoff; WifiPacket eapol_logoff;
@ -237,6 +254,7 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
} }
auto logoff = ParseEAPoLLogoffFrame(packet.data); auto logoff = ParseEAPoLLogoffFrame(packet.data);
network_info.host_mac_address = packet.transmitter_address;
network_info.total_nodes = logoff.connected_nodes; network_info.total_nodes = logoff.connected_nodes;
network_info.max_nodes = logoff.max_nodes; network_info.max_nodes = logoff.max_nodes;
@ -260,6 +278,7 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
// If blocking is implemented this lock needs to be changed, // If blocking is implemented this lock needs to be changed,
// otherwise it might cause deadlocks // otherwise it might cause deadlocks
connection_status_event->Signal(); connection_status_event->Signal();
connection_event->Signal();
} }
} }
@ -397,6 +416,35 @@ void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
} }
} }
/// Handles the deauthentication frames sent from clients to hosts, when they leave a session
void HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
LOG_DEBUG(Service_NWM, "called");
std::unique_lock<std::recursive_mutex> hle_lock(HLE::g_hle_lock, std::defer_lock);
std::unique_lock<std::mutex> lock(connection_status_mutex, std::defer_lock);
std::lock(hle_lock, lock);
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
LOG_ERROR(Service_NWM, "Got deauthentication frame but we are not the host");
return;
}
if (node_map.find(packet.transmitter_address) == node_map.end()) {
LOG_ERROR(Service_NWM, "Got deauthentication frame from unknown node");
return;
}
u16 node_id = node_map[packet.transmitter_address];
auto node = std::find_if(node_info.begin(), node_info.end(), [&node_id](const NodeInfo& info) {
return info.network_node_id == node_id + 1;
});
ASSERT(node != node_info.end());
connection_status.node_bitmask &= ~(1 << node_id);
connection_status.changed_nodes |= 1 << node_id;
connection_status.total_nodes--;
network_info.total_nodes--;
connection_status_event->Signal();
}
static void HandleDataFrame(const Network::WifiPacket& packet) { static void HandleDataFrame(const Network::WifiPacket& packet) {
switch (GetFrameEtherType(packet.data)) { switch (GetFrameEtherType(packet.data)) {
case EtherType::EAPoL: case EtherType::EAPoL:
@ -423,47 +471,32 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) {
case Network::WifiPacket::PacketType::Data: case Network::WifiPacket::PacketType::Data:
HandleDataFrame(packet); HandleDataFrame(packet);
break; break;
case Network::WifiPacket::PacketType::Deauthentication:
HandleDeauthenticationFrame(packet);
break;
} }
} }
/** void NWM_UDS::Shutdown(Kernel::HLERequestContext& ctx) {
* NWM_UDS::Shutdown service function IPC::RequestParser rp(ctx, 0x03, 0, 0);
* Inputs:
* 1 : None
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void Shutdown(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(purpasmart): Verify return header on HW if (auto room_member = Network::GetRoomMember().lock())
room_member->Unbind(wifi_packet_received);
cmd_buff[1] = RESULT_SUCCESS.raw; for (auto bind_node : channel_data) {
bind_node.second.event->Signal();
}
channel_data.clear();
LOG_WARNING(Service_NWM, "(STUBBED) called"); recv_buffer_memory.reset();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NWM, "called");
} }
/** void NWM_UDS::RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx) {
* NWM_UDS::RecvBeaconBroadcastData service function IPC::RequestParser rp(ctx, 0x0F, 16, 4);
* Returns the raw beacon data for nearby networks that match the supplied WlanCommId.
* Inputs:
* 1 : Output buffer max size
* 2-3 : Unknown
* 4-5 : Host MAC address.
* 6-14 : Unused
* 15 : WLan Comm Id
* 16 : Id
* 17 : Value 0
* 18 : Input handle
* 19 : (Size<<4) | 12
* 20 : Output buffer ptr
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void RecvBeaconBroadcastData(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 16, 4);
u32 out_buffer_size = rp.Pop<u32>(); u32 out_buffer_size = rp.Pop<u32>();
u32 unk1 = rp.Pop<u32>(); u32 unk1 = rp.Pop<u32>();
@ -476,14 +509,16 @@ static void RecvBeaconBroadcastData(Interface* self) {
u32 wlan_comm_id = rp.Pop<u32>(); u32 wlan_comm_id = rp.Pop<u32>();
u32 id = rp.Pop<u32>(); u32 id = rp.Pop<u32>();
Kernel::Handle input_handle = rp.PopHandle(); // From 3dbrew:
// 'Official user processes create a new event handle which is then passed to this command.
// However, those user processes don't save that handle anywhere afterwards.'
// So we don't save/use that event too.
Kernel::SharedPtr<Kernel::Event> input_event = rp.PopObject<Kernel::Event>();
size_t desc_size; Kernel::MappedBuffer out_buffer = rp.PopMappedBuffer();
const VAddr out_buffer_ptr = rp.PopMappedBuffer(&desc_size); ASSERT(out_buffer.GetSize() == out_buffer_size);
ASSERT(desc_size == out_buffer_size);
VAddr current_buffer_pos = out_buffer_ptr; size_t cur_buffer_size = sizeof(BeaconDataReplyHeader);
u32 total_size = sizeof(BeaconDataReplyHeader);
// Retrieve all beacon frames that were received from the desired mac address. // Retrieve all beacon frames that were received from the desired mac address.
auto beacons = GetReceivedBeacons(mac_address); auto beacons = GetReceivedBeacons(mac_address);
@ -492,9 +527,6 @@ static void RecvBeaconBroadcastData(Interface* self) {
data_reply_header.total_entries = static_cast<u32>(beacons.size()); data_reply_header.total_entries = static_cast<u32>(beacons.size());
data_reply_header.max_output_size = out_buffer_size; data_reply_header.max_output_size = out_buffer_size;
Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
current_buffer_pos += sizeof(BeaconDataReplyHeader);
// Write each of the received beacons into the buffer // Write each of the received beacons into the buffer
for (const auto& beacon : beacons) { for (const auto& beacon : beacons) {
BeaconEntryHeader entry{}; BeaconEntryHeader entry{};
@ -505,45 +537,30 @@ static void RecvBeaconBroadcastData(Interface* self) {
entry.header_size = sizeof(BeaconEntryHeader); entry.header_size = sizeof(BeaconEntryHeader);
entry.mac_address = beacon.transmitter_address; entry.mac_address = beacon.transmitter_address;
ASSERT(current_buffer_pos < out_buffer_ptr + out_buffer_size); ASSERT(cur_buffer_size < out_buffer_size);
Memory::WriteBlock(current_buffer_pos, &entry, sizeof(BeaconEntryHeader)); out_buffer.Write(&entry, cur_buffer_size, sizeof(BeaconEntryHeader));
current_buffer_pos += sizeof(BeaconEntryHeader); cur_buffer_size += sizeof(BeaconEntryHeader);
const unsigned char* beacon_data = beacon.data.data();
Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); out_buffer.Write(beacon_data, cur_buffer_size, beacon.data.size());
current_buffer_pos += static_cast<VAddr>(beacon.data.size()); cur_buffer_size += beacon.data.size();
total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
} }
// Update the total size in the structure and write it to the buffer again. // Update the total size in the structure and write it to the buffer again.
data_reply_header.total_size = total_size; data_reply_header.total_size = cur_buffer_size;
Memory::WriteBlock(out_buffer_ptr, &data_reply_header, sizeof(BeaconDataReplyHeader)); out_buffer.Write(&data_reply_header, 0, sizeof(BeaconDataReplyHeader));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 1);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(out_buffer);
LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X," LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X,"
"input_handle=0x%08X, out_buffer_ptr=0x%08X, unk1=0x%08X, unk2=0x%08X", "unk1=0x%08X, unk2=0x%08X, offset=%zu",
out_buffer_size, wlan_comm_id, id, input_handle, out_buffer_ptr, unk1, unk2); out_buffer_size, wlan_comm_id, id, unk1, unk2, cur_buffer_size);
} }
/** void NWM_UDS::InitializeWithVersion(Kernel::HLERequestContext& ctx) {
* NWM_UDS::Initialize service function IPC::RequestParser rp(ctx, 0x1B, 12, 2);
* Inputs:
* 1 : Shared memory size
* 2-11 : Input NodeInfo Structure
* 12 : 2-byte Version
* 13 : Value 0
* 14 : Shared memory handle
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Value 0
* 3 : Output event handle
*/
static void InitializeWithVersion(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 12, 2);
u32 sharedmem_size = rp.Pop<u32>(); u32 sharedmem_size = rp.Pop<u32>();
@ -552,14 +569,18 @@ static void InitializeWithVersion(Interface* self) {
u16 version = rp.Pop<u16>(); u16 version = rp.Pop<u16>();
Kernel::Handle sharedmem_handle = rp.PopHandle(); recv_buffer_memory = rp.PopObject<Kernel::SharedMemory>();
recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
initialized = true; initialized = true;
ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
if (auto room_member = Network::GetRoomMember().lock()) {
wifi_packet_received = room_member->BindOnWifiPacketReceived(OnWifiPacketReceived);
} else {
LOG_ERROR(Service_NWM, "Network isn't initalized");
}
{ {
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);
@ -567,33 +588,19 @@ static void InitializeWithVersion(Interface* self) {
// except for the actual status value. // except for the actual status value.
connection_status = {}; connection_status = {};
connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
node_info.clear();
node_info.push_back(current_node);
} }
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); rb.PushCopyObjects(connection_status_event);
// TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X", sharedmem_size, version);
// 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);
} }
/** void NWM_UDS::GetConnectionStatus(Kernel::HLERequestContext& ctx) {
* NWM_UDS::GetConnectionStatus service function. IPC::RequestParser rp(ctx, 0xB, 0, 0);
* Returns the connection status structure for the currently open network connection.
* This structure contains information about the connection,
* like the number of connected nodes, etc.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2-13 : Channel of the current WiFi network connection.
*/
static void GetConnectionStatus(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 0, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -611,19 +618,8 @@ static void GetConnectionStatus(Interface* self) {
LOG_DEBUG(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
} }
/** void NWM_UDS::GetNodeInformation(Kernel::HLERequestContext& ctx) {
* NWM_UDS::GetNodeInformation service function. IPC::RequestParser rp(ctx, 0xD, 1, 0);
* Returns the node inforamtion structure for the currently connected node.
* Inputs:
* 0 : Command header.
* 1 : Node ID.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2-11 : NodeInfo structure.
*/
static void GetNodeInformation(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0);
u16 network_node_id = rp.Pop<u16>(); u16 network_node_id = rp.Pop<u16>();
if (!initialized) { if (!initialized) {
@ -653,22 +649,8 @@ static void GetNodeInformation(Interface* self) {
LOG_DEBUG(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
} }
/** void NWM_UDS::Bind(Kernel::HLERequestContext& ctx) {
* NWM_UDS::Bind service function. IPC::RequestParser rp(ctx, 0x12, 4, 0);
* Binds a BindNodeId to a data channel and retrieves a data event.
* Inputs:
* 1 : BindNodeId
* 2 : Receive buffer size.
* 3 : u8 Data channel to bind to.
* 4 : Network node id.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Copy handle descriptor.
* 3 : Data available event handle.
*/
static void Bind(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 0);
u32 bind_node_id = rp.Pop<u32>(); u32 bind_node_id = rp.Pop<u32>();
u32 recv_buffer_size = rp.Pop<u32>(); u32 recv_buffer_size = rp.Pop<u32>();
@ -681,6 +663,8 @@ static void Bind(Interface* self) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
ErrorSummary::WrongArgument, ErrorLevel::Usage)); ErrorSummary::WrongArgument, ErrorLevel::Usage));
LOG_WARNING(Service_NWM, "data_channel = %d, bind_node_id = %d", data_channel,
bind_node_id);
return; return;
} }
@ -689,6 +673,7 @@ static void Bind(Interface* self) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::OutOfMemory, ErrorModule::UDS, rb.Push(ResultCode(ErrorDescription::OutOfMemory, ErrorModule::UDS,
ErrorSummary::OutOfResource, ErrorLevel::Status)); ErrorSummary::OutOfResource, ErrorLevel::Status));
LOG_WARNING(Service_NWM, "max bind nodes");
return; return;
} }
@ -697,6 +682,7 @@ static void Bind(Interface* self) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
ErrorSummary::WrongArgument, ErrorLevel::Usage)); ErrorSummary::WrongArgument, ErrorLevel::Usage));
LOG_WARNING(Service_NWM, "MinRecvBufferSize");
return; return;
} }
@ -711,20 +697,11 @@ static void Bind(Interface* self) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); rb.PushCopyObjects(event);
} }
/** void NWM_UDS::Unbind(Kernel::HLERequestContext& ctx) {
* NWM_UDS::Unbind service function. IPC::RequestParser rp(ctx, 0x12, 1, 0);
* Unbinds a BindNodeId from a data channel.
* Inputs:
* 1 : BindNodeId
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void Unbind(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 1, 0);
u32 bind_node_id = rp.Pop<u32>(); u32 bind_node_id = rp.Pop<u32>();
if (bind_node_id == 0) { if (bind_node_id == 0) {
@ -754,27 +731,15 @@ static void Unbind(Interface* self) {
rb.Push<u32>(0); rb.Push<u32>(0);
} }
/** void NWM_UDS::BeginHostingNetwork(Kernel::HLERequestContext& ctx) {
* NWM_UDS::BeginHostingNetwork service function. IPC::RequestParser rp(ctx, 0x1D, 1, 4);
* Creates a network and starts broadcasting its presence.
* Inputs:
* 1 : Passphrase buffer size.
* 3 : VAddr of the NetworkInfo structure.
* 5 : VAddr of the passphrase.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void BeginHostingNetwork(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 1, 4);
const u32 passphrase_size = rp.Pop<u32>(); const u32 passphrase_size = rp.Pop<u32>();
size_t desc_size; const std::vector<u8> network_info_buffer = rp.PopStaticBuffer();
const VAddr network_info_address = rp.PopStaticBuffer(&desc_size); ASSERT(network_info_buffer.size() == sizeof(NetworkInfo));
ASSERT(desc_size == sizeof(NetworkInfo)); const std::vector<u8> passphrase = rp.PopStaticBuffer();
const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size); ASSERT(passphrase.size() == passphrase_size);
ASSERT(desc_size == passphrase_size);
// TODO(Subv): Store the passphrase and verify it when attempting a connection. // TODO(Subv): Store the passphrase and verify it when attempting a connection.
@ -782,8 +747,7 @@ static void BeginHostingNetwork(Interface* self) {
{ {
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);
std::memcpy(&network_info, network_info_buffer.data(), sizeof(NetworkInfo));
Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo));
// 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.");
@ -845,59 +809,94 @@ static void BeginHostingNetwork(Interface* self) {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
/** void NWM_UDS::UpdateNetworkAttribute(Kernel::HLERequestContext& ctx) {
* NWM_UDS::DestroyNetwork service function. IPC::RequestParser rp(ctx, 0x07, 2, 0);
* Closes the network that we're currently hosting. rp.Skip(2, false);
* Inputs: IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
* 0 : Command header. rb.Push(RESULT_SUCCESS);
* Outputs: }
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void DestroyNetwork(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 0, 0);
// TODO(Subv): Find out what happens if this is called while void NWM_UDS::DestroyNetwork(Kernel::HLERequestContext& ctx) {
// no network is being hosted. IPC::RequestParser rp(ctx, 0x08, 0, 0);
// Unschedule the beacon broadcast event. // Unschedule the beacon broadcast event.
CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
{ // Only a host can destroy
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);
if (connection_status.status != static_cast<u8>(NetworkStatus::ConnectedAsHost)) {
// TODO(Subv): Check if connection_status is indeed reset after this call. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
connection_status = {}; rb.Push(ResultCode(ErrCodes::WrongStatus, ErrorModule::UDS, ErrorSummary::InvalidState,
connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); ErrorLevel::Status));
LOG_WARNING(Service_NWM, "called with status %u", connection_status.status);
return;
} }
// TODO(B3N30): Send 3 Deauth packets
u16_le tmp_node_id = connection_status.network_node_id;
connection_status = {};
connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
connection_status.network_node_id = tmp_node_id;
connection_status_event->Signal(); connection_status_event->Signal();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
for (auto bind_node : channel_data) {
bind_node.second.event->Signal();
}
channel_data.clear();
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
} }
/** void NWM_UDS::DisconnectNetwork(Kernel::HLERequestContext& ctx) {
* NWM_UDS::SendTo service function. IPC::RequestParser rp(ctx, 0xA, 0, 0);
* Sends a data frame to the UDS network we're connected to. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
* Inputs:
* 0 : Command header. using Network::WifiPacket;
* 1 : Unknown. WifiPacket deauth;
* 2 : u16 Destination network node id. {
* 3 : u8 Data channel. std::lock_guard<std::mutex> lock(connection_status_mutex);
* 4 : Buffer size >> 2 if (connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
* 5 : Data size // A real 3ds makes strange things here. We do the same
* 6 : Flags u16_le tmp_node_id = connection_status.network_node_id;
* 7 : Input buffer descriptor connection_status = {};
* 8 : Input buffer address connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
* Outputs: connection_status.network_node_id = tmp_node_id;
* 0 : Return header LOG_DEBUG(Service_NWM, "called as a host");
* 1 : Result of function, 0 on success, otherwise error code rb.Push(ResultCode(ErrCodes::WrongStatus, ErrorModule::UDS, ErrorSummary::InvalidState,
*/ ErrorLevel::Status));
static void SendTo(Interface* self) { return;
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2); }
u16_le tmp_node_id = connection_status.network_node_id;
connection_status = {};
connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
connection_status.network_node_id = tmp_node_id;
connection_status_event->Signal();
deauth.channel = network_channel;
// TODO(B3N30): Add disconnect reason
deauth.data = {};
deauth.destination_address = network_info.host_mac_address;
deauth.type = WifiPacket::PacketType::Deauthentication;
}
SendPacket(deauth);
for (auto bind_node : channel_data) {
bind_node.second.event->Signal();
}
channel_data.clear();
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NWM, "called");
}
void NWM_UDS::SendTo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x17, 6, 2);
rp.Skip(1, false); rp.Skip(1, false);
u16 dest_node_id = rp.Pop<u16>(); u16 dest_node_id = rp.Pop<u16>();
@ -906,9 +905,9 @@ static void SendTo(Interface* self) {
u32 data_size = rp.Pop<u32>(); u32 data_size = rp.Pop<u32>();
u32 flags = rp.Pop<u32>(); u32 flags = rp.Pop<u32>();
size_t desc_size; std::vector<u8> input_buffer = rp.PopStaticBuffer();
const VAddr input_address = rp.PopStaticBuffer(&desc_size); ASSERT(input_buffer.size() >= data_size);
ASSERT(desc_size >= data_size); input_buffer.resize(data_size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -935,13 +934,11 @@ static void SendTo(Interface* self) {
return; return;
} }
std::vector<u8> data(data_size);
Memory::ReadBlock(input_address, data.data(), data.size());
// TODO(B3N30): Increment the sequence number after each sent packet. // TODO(B3N30): 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(input_buffer, data_channel, dest_node_id,
connection_status.network_node_id, sequence_number);
// TODO(B3N30): Retrieve the MAC address of the dest_node_id and our own to encrypt // TODO(B3N30): Retrieve the MAC address of the dest_node_id and our own to encrypt
// and encapsulate the payload. // and encapsulate the payload.
@ -962,34 +959,15 @@ static void SendTo(Interface* self) {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
/** void NWM_UDS::PullPacket(Kernel::HLERequestContext& ctx) {
* NWM_UDS::PullPacket service function. IPC::RequestParser rp(ctx, 0x14, 3, 0);
* Receives a data frame from the specified bind node id
* Inputs:
* 0 : Command header.
* 1 : Bind node id.
* 2 : Max out buff size >> 2.
* 3 : Max out buff size.
* 64 : Output buffer descriptor
* 65 : Output buffer address
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Received data size
* 3 : u16 Source network node id
* 4 : Buffer descriptor
* 5 : Buffer address
*/
static void PullPacket(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 3, 0);
u32 bind_node_id = rp.Pop<u32>(); u32 bind_node_id = rp.Pop<u32>();
u32 max_out_buff_size_aligned = rp.Pop<u32>(); u32 max_out_buff_size_aligned = rp.Pop<u32>();
u32 max_out_buff_size = rp.Pop<u32>(); u32 max_out_buff_size = rp.Pop<u32>();
size_t desc_size; // This size is hard coded into the uds module. We don't know the meaning yet.
const VAddr output_address = rp.PeekStaticBuffer(0, &desc_size); u32 buff_size = std::min<u32>(max_out_buff_size_aligned, 0x172) << 2;
ASSERT(desc_size == max_out_buff_size);
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost) && if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost) &&
@ -1014,12 +992,12 @@ static void PullPacket(Interface* self) {
} }
if (channel->second.received_packets.empty()) { if (channel->second.received_packets.empty()) {
Memory::ZeroBlock(output_address, desc_size); std::vector<u8> output_buffer(buff_size, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); rb.Push<u32>(0);
rb.Push<u16>(0); rb.Push<u16>(0);
rb.PushStaticBuffer(output_address, desc_size, 0); rb.PushStaticBuffer(output_buffer, 0);
return; return;
} }
@ -1036,32 +1014,22 @@ static void PullPacket(Interface* self) {
} }
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
Memory::ZeroBlock(output_address, desc_size);
std::vector<u8> output_buffer(buff_size, 0);
// Write the actual data. // Write the actual data.
Memory::WriteBlock(output_address, std::memcpy(output_buffer.data(),
next_packet.data() + sizeof(LLCHeader) + sizeof(SecureDataHeader), next_packet.data() + sizeof(LLCHeader) + sizeof(SecureDataHeader), data_size);
data_size);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push<u32>(data_size); rb.Push<u32>(data_size);
rb.Push<u16>(secure_data.src_node_id); rb.Push<u16>(secure_data.src_node_id);
rb.PushStaticBuffer(output_address, desc_size, 0); rb.PushStaticBuffer(output_buffer, 0);
channel->second.received_packets.pop_front(); channel->second.received_packets.pop_front();
} }
/** void NWM_UDS::GetChannel(Kernel::HLERequestContext& ctx) {
* NWM_UDS::GetChannel service function. IPC::RequestParser rp(ctx, 0x1A, 0, 0);
* Returns the WiFi channel in which the network we're connected to is transmitting.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Channel of the current WiFi network connection.
*/
static void GetChannel(Interface* self) {
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); std::lock_guard<std::mutex> lock(connection_status_mutex);
@ -1075,26 +1043,46 @@ static void GetChannel(Interface* self) {
LOG_DEBUG(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
} }
/** void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx) {
* NWM_UDS::SetApplicationData service function. IPC::RequestParser rp(ctx, 0x1E, 2, 4);
* Updates the application data that is being broadcast in the beacon frames
* for the network that we're hosting. u8 connection_type = rp.Pop<u8>();
* Inputs: u32 passphrase_size = rp.Pop<u32>();
* 1 : Data size.
* 3 : VAddr of the data. const std::vector<u8> network_struct_buffer = rp.PopStaticBuffer();
* Outputs: ASSERT(network_struct_buffer.size() == sizeof(NetworkInfo));
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code const std::vector<u8> passphrase = rp.PopStaticBuffer();
* 2 : Channel of the current WiFi network connection.
*/ std::memcpy(&network_info, network_struct_buffer.data(), sizeof(network_info));
static void SetApplicationData(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 2); // Start the connection sequence
StartConnectionSequence(network_info.host_mac_address);
// 300 ms
// Since this timing is handled by core_timing it could differ from the 'real world' time
static constexpr std::chrono::nanoseconds UDSConnectionTimeout{300000000};
connection_event = ctx.SleepClientThread(
Kernel::GetCurrentThread(), "uds::ConnectToNetwork", UDSConnectionTimeout,
[](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
ThreadWakeupReason reason) {
// TODO(B3N30): Add error handling for host full and timeout
IPC::RequestBuilder rb(ctx, 0x1E, 1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NWM, "connection sequence finished");
});
LOG_DEBUG(Service_NWM, "called");
}
void NWM_UDS::SetApplicationData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x10, 1, 2);
u32 size = rp.Pop<u32>(); u32 size = rp.Pop<u32>();
size_t desc_size; const std::vector<u8> application_data = rp.PopStaticBuffer();
const VAddr address = rp.PopStaticBuffer(&desc_size); ASSERT(application_data.size() == size);
ASSERT(desc_size == size);
LOG_DEBUG(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
@ -1107,73 +1095,43 @@ static void SetApplicationData(Interface* self) {
} }
network_info.application_data_size = size; network_info.application_data_size = size;
Memory::ReadBlock(address, network_info.application_data.data(), size); std::memcpy(network_info.application_data.data(), application_data.data(), size);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
/** void NWM_UDS::DecryptBeaconData(Kernel::HLERequestContext& ctx) {
* NWM_UDS::DecryptBeaconData service function. IPC::RequestParser rp(ctx, 0x1F, 0, 6);
* Decrypts the encrypted data tags contained in the 802.11 beacons.
* Inputs:
* 1 : Input network struct buffer descriptor.
* 2 : Input network struct buffer ptr.
* 3 : Input tag0 encrypted buffer descriptor.
* 4 : Input tag0 encrypted buffer ptr.
* 5 : Input tag1 encrypted buffer descriptor.
* 6 : Input tag1 encrypted buffer ptr.
* 64 : Output buffer descriptor.
* 65 : Output buffer ptr.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
static void DecryptBeaconData(Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 0, 6);
size_t desc_size; const std::vector<u8> network_struct_buffer = rp.PopStaticBuffer();
const VAddr network_struct_addr = rp.PopStaticBuffer(&desc_size); ASSERT(network_struct_buffer.size() == sizeof(NetworkInfo));
ASSERT(desc_size == sizeof(NetworkInfo));
size_t data0_size; const std::vector<u8> encrypted_data0_buffer = rp.PopStaticBuffer();
const VAddr encrypted_data0_addr = rp.PopStaticBuffer(&data0_size); const std::vector<u8> encrypted_data1_buffer = rp.PopStaticBuffer();
size_t data1_size; LOG_DEBUG(Service_NWM, "called");
const VAddr encrypted_data1_addr = rp.PopStaticBuffer(&data1_size);
size_t output_buffer_size;
const VAddr output_buffer_addr = rp.PeekStaticBuffer(0, &output_buffer_size);
// This size is hardcoded in the 3DS UDS code.
ASSERT(output_buffer_size == sizeof(NodeInfo) * UDSMaxNodes);
LOG_WARNING(Service_NWM, "called in0=%08X in1=%08X out=%08X", encrypted_data0_addr,
encrypted_data1_addr, output_buffer_addr);
NetworkInfo net_info; NetworkInfo net_info;
Memory::ReadBlock(network_struct_addr, &net_info, sizeof(net_info)); std::memcpy(&net_info, network_struct_buffer.data(), sizeof(net_info));
// Read the encrypted data. // Read the encrypted data.
// The first 4 bytes should be the OUI and the OUI Type of the tags. // The first 4 bytes should be the OUI and the OUI Type of the tags.
std::array<u8, 3> oui; std::array<u8, 3> oui;
Memory::ReadBlock(encrypted_data0_addr, oui.data(), oui.size()); std::memcpy(oui.data(), encrypted_data0_buffer.data(), oui.size());
ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
Memory::ReadBlock(encrypted_data1_addr, oui.data(), oui.size());
ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI"); ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
ASSERT_MSG(Memory::Read8(encrypted_data0_addr + 3) == ASSERT_MSG(encrypted_data0_buffer[3] == static_cast<u8>(NintendoTagId::EncryptedData0),
static_cast<u8>(NintendoTagId::EncryptedData0),
"Unexpected tag id");
ASSERT_MSG(Memory::Read8(encrypted_data1_addr + 3) ==
static_cast<u8>(NintendoTagId::EncryptedData1),
"Unexpected tag id"); "Unexpected tag id");
std::vector<u8> beacon_data(data0_size + data1_size); std::vector<u8> beacon_data(encrypted_data0_buffer.size() - 4 + encrypted_data1_buffer.size() -
Memory::ReadBlock(encrypted_data0_addr + 4, beacon_data.data(), data0_size); 4);
Memory::ReadBlock(encrypted_data1_addr + 4, beacon_data.data() + data0_size, data1_size); std::memcpy(beacon_data.data(), encrypted_data0_buffer.data() + 4,
encrypted_data0_buffer.size() - 4);
std::memcpy(beacon_data.data() + encrypted_data0_buffer.size() - 4,
encrypted_data1_buffer.data() + 4, encrypted_data1_buffer.size() - 4);
// Decrypt the data // Decrypt the data
DecryptBeaconData(net_info, beacon_data); DecryptBeacon(net_info, beacon_data);
// The beacon data header contains the MD5 hash of the data. // The beacon data header contains the MD5 hash of the data.
BeaconData beacon_header; BeaconData beacon_header;
@ -1200,12 +1158,12 @@ static void DecryptBeaconData(Interface* self) {
nodes.push_back(node); nodes.push_back(node);
} }
Memory::ZeroBlock(output_buffer_addr, sizeof(NodeInfo) * UDSMaxNodes);
Memory::WriteBlock(output_buffer_addr, nodes.data(), sizeof(NodeInfo) * nodes.size());
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.PushStaticBuffer(output_buffer_addr, output_buffer_size, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
std::vector<u8> output_buffer(sizeof(NodeInfo) * UDSMaxNodes, 0);
std::memcpy(output_buffer.data(), nodes.data(), sizeof(NodeInfo) * nodes.size());
rb.PushStaticBuffer(output_buffer, 0);
} }
// Sends a 802.11 beacon frame with information about the current network. // Sends a 802.11 beacon frame with information about the current network.
@ -1230,63 +1188,42 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
beacon_broadcast_event, 0); beacon_broadcast_event, 0);
} }
/* NWM_UDS::NWM_UDS() : ServiceFramework("nwm::UDS") {
* Called when a client connects to an UDS network we're hosting, static const FunctionInfo functions[] = {
* updates the connection status and signals the update event. {0x000102C2, nullptr, "Initialize (deprecated)"},
* @param network_node_id Network Node Id of the connecting client. {0x00020000, nullptr, "Scrap"},
*/ {0x00030000, &NWM_UDS::Shutdown, "Shutdown"},
void OnClientConnected(u16 network_node_id) { {0x00040402, nullptr, "CreateNetwork (deprecated)"},
std::lock_guard<std::mutex> lock(connection_status_mutex); {0x00050040, nullptr, "EjectClient"},
ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), {0x00060000, nullptr, "EjectSpectator"},
"Can not accept clients if we're not hosting a network"); {0x00070080, &NWM_UDS::UpdateNetworkAttribute, "UpdateNetworkAttribute"},
ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, {0x00080000, &NWM_UDS::DestroyNetwork, "DestroyNetwork"},
"Can not accept connections on a full network"); {0x00090442, nullptr, "ConnectNetwork (deprecated)"},
{0x000A0000, &NWM_UDS::DisconnectNetwork, "DisconnectNetwork"},
u32 node_id = GetNextAvailableNodeId(); {0x000B0000, &NWM_UDS::GetConnectionStatus, "GetConnectionStatus"},
connection_status.node_bitmask |= 1 << node_id; {0x000D0040, &NWM_UDS::GetNodeInformation, "GetNodeInformation"},
connection_status.changed_nodes |= 1 << node_id; {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
connection_status.nodes[node_id] = network_node_id; {0x000F0404, &NWM_UDS::RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
connection_status.total_nodes++; {0x00100042, &NWM_UDS::SetApplicationData, "SetApplicationData"},
connection_status_event->Signal(); {0x00110040, nullptr, "GetApplicationData"},
} {0x00120100, &NWM_UDS::Bind, "Bind"},
{0x00130040, &NWM_UDS::Unbind, "Unbind"},
const Interface::FunctionInfo FunctionTable[] = { {0x001400C0, &NWM_UDS::PullPacket, "PullPacket"},
{0x000102C2, nullptr, "Initialize (deprecated)"}, {0x00150080, nullptr, "SetMaxSendDelay"},
{0x00020000, nullptr, "Scrap"}, {0x00170182, &NWM_UDS::SendTo, "SendTo"},
{0x00030000, Shutdown, "Shutdown"}, {0x001A0000, &NWM_UDS::GetChannel, "GetChannel"},
{0x00040402, nullptr, "CreateNetwork (deprecated)"}, {0x001B0302, &NWM_UDS::InitializeWithVersion, "InitializeWithVersion"},
{0x00050040, nullptr, "EjectClient"}, {0x001D0044, &NWM_UDS::BeginHostingNetwork, "BeginHostingNetwork"},
{0x00060000, nullptr, "EjectSpectator"}, {0x001E0084, &NWM_UDS::ConnectToNetwork, "ConnectToNetwork"},
{0x00070080, nullptr, "UpdateNetworkAttribute"}, {0x001F0006, &NWM_UDS::DecryptBeaconData, "DecryptBeaconData"},
{0x00080000, DestroyNetwork, "DestroyNetwork"}, {0x00200040, nullptr, "Flush"},
{0x00090442, nullptr, "ConnectNetwork (deprecated)"}, {0x00210080, nullptr, "SetProbeResponseParam"},
{0x000A0000, nullptr, "DisconnectNetwork"}, {0x00220402, nullptr, "ScanOnConnection"},
{0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, };
{0x000D0040, GetNodeInformation, "GetNodeInformation"},
{0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
{0x00100042, SetApplicationData, "SetApplicationData"},
{0x00110040, nullptr, "GetApplicationData"},
{0x00120100, Bind, "Bind"},
{0x00130040, Unbind, "Unbind"},
{0x001400C0, PullPacket, "PullPacket"},
{0x00150080, nullptr, "SetMaxSendDelay"},
{0x00170182, SendTo, "SendTo"},
{0x001A0000, GetChannel, "GetChannel"},
{0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
{0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
{0x001E0084, nullptr, "ConnectToNetwork"},
{0x001F0006, DecryptBeaconData, "DecryptBeaconData"},
{0x00200040, nullptr, "Flush"},
{0x00210080, nullptr, "SetProbeResponseParam"},
{0x00220402, nullptr, "ScanOnConnection"},
};
NWM_UDS::NWM_UDS() {
connection_status_event = connection_status_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event"); Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event");
Register(FunctionTable); RegisterHandlers(functions);
beacon_broadcast_event = beacon_broadcast_event =
CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback); CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback);
@ -1305,6 +1242,9 @@ NWM_UDS::~NWM_UDS() {
connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
} }
if (auto room_member = Network::GetRoomMember().lock())
room_member->Unbind(wifi_packet_received);
CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
} }

View file

@ -97,14 +97,250 @@ enum class TagId : u8 {
VendorSpecific = 221 VendorSpecific = 221
}; };
class NWM_UDS final : public Interface { class NWM_UDS final : public ServiceFramework<NWM_UDS> {
public: public:
NWM_UDS(); NWM_UDS();
~NWM_UDS() override; ~NWM_UDS();
std::string GetPortName() const override { private:
return "nwm::UDS"; void UpdateNetworkAttribute(Kernel::HLERequestContext& ctx);
}
/**
* NWM_UDS::Shutdown service function
* Inputs:
* 1 : None
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void Shutdown(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::DestroyNetwork service function.
* Closes the network that we're currently hosting.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void DestroyNetwork(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::DisconnectNetwork service function.
* This disconnects this device from the network.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void DisconnectNetwork(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::GetConnectionStatus service function.
* Returns the connection status structure for the currently open network connection.
* This structure contains information about the connection,
* like the number of connected nodes, etc.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2-13 : Channel of the current WiFi network connection.
*/
void GetConnectionStatus(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::GetNodeInformation service function.
* Returns the node inforamtion structure for the currently connected node.
* Inputs:
* 0 : Command header.
* 1 : Node ID.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2-11 : NodeInfo structure.
*/
void GetNodeInformation(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::RecvBeaconBroadcastData service function
* Returns the raw beacon data for nearby networks that match the supplied WlanCommId.
* Inputs:
* 1 : Output buffer max size
* 2-3 : Unknown
* 4-5 : Host MAC address.
* 6-14 : Unused
* 15 : WLan Comm Id
* 16 : Id
* 17 : Value 0
* 18 : Input handle
* 19 : (Size<<4) | 12
* 20 : Output buffer ptr
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2, 3: output buffer return descriptor & ptr
*/
void RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::SetApplicationData service function.
* Updates the application data that is being broadcast in the beacon frames
* for the network that we're hosting.
* Inputs:
* 1 : Data size.
* 3 : VAddr of the data.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Channel of the current WiFi network connection.
*/
void SetApplicationData(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::Bind service function.
* Binds a BindNodeId to a data channel and retrieves a data event.
* Inputs:
* 1 : BindNodeId
* 2 : Receive buffer size.
* 3 : u8 Data channel to bind to.
* 4 : Network node id.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Copy handle descriptor.
* 3 : Data available event handle.
*/
void Bind(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::Unbind service function.
* Unbinds a BindNodeId from a data channel.
* Inputs:
* 1 : BindNodeId
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void Unbind(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::PullPacket service function.
* Receives a data frame from the specified bind node id
* Inputs:
* 0 : Command header.
* 1 : Bind node id.
* 2 : Max out buff size >> 2.
* 3 : Max out buff size.
* 64 : Output buffer descriptor
* 65 : Output buffer address
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Received data size
* 3 : u16 Source network node id
* 4 : Buffer descriptor
* 5 : Buffer address
*/
void PullPacket(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::SendTo service function.
* Sends a data frame to the UDS network we're connected to.
* Inputs:
* 0 : Command header.
* 1 : Unknown.
* 2 : u16 Destination network node id.
* 3 : u8 Data channel.
* 4 : Buffer size >> 2
* 5 : Data size
* 6 : Flags
* 7 : Input buffer descriptor
* 8 : Input buffer address
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void SendTo(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::GetChannel service function.
* Returns the WiFi channel in which the network we're connected to is transmitting.
* Inputs:
* 0 : Command header.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Channel of the current WiFi network connection.
*/
void GetChannel(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::Initialize service function
* Inputs:
* 1 : Shared memory size
* 2-11 : Input NodeInfo Structure
* 12 : 2-byte Version
* 13 : Value 0
* 14 : Shared memory handle
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Value 0
* 3 : Output event handle
*/
void InitializeWithVersion(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::BeginHostingNetwork service function.
* Creates a network and starts broadcasting its presence.
* Inputs:
* 1 : Passphrase buffer size.
* 3 : VAddr of the NetworkInfo structure.
* 5 : VAddr of the passphrase.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void BeginHostingNetwork(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::ConnectToNetwork service function.
* This connects to the specified network
* Inputs:
* 0 : Command header
* 1 : Connection type: 0x1 = Client, 0x2 = Spectator.
* 2 : Passphrase buffer size
* 3 : (NetworkStructSize<<12) | 0x402
* 4 : Network struct buffer ptr
* 5 : (PassphraseSize<<12) | 2
* 6 : Input passphrase buffer ptr
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
*/
void ConnectToNetwork(Kernel::HLERequestContext& ctx);
/**
* NWM_UDS::DecryptBeaconData service function.
* Decrypts the encrypted data tags contained in the 802.11 beacons.
* Inputs:
* 1 : Input network struct buffer descriptor.
* 2 : Input network struct buffer ptr.
* 3 : Input tag0 encrypted buffer descriptor.
* 4 : Input tag0 encrypted buffer ptr.
* 5 : Input tag1 encrypted buffer descriptor.
* 6 : Input tag1 encrypted buffer ptr.
* 64 : Output buffer descriptor.
* 65 : Output buffer ptr.
* Outputs:
* 0 : Return header
* 1 : Result of function, 0 on success, otherwise error code
* 2, 3: output buffer return descriptor & ptr
*/
void DecryptBeaconData(Kernel::HLERequestContext& ctx);
}; };
} // namespace NWM } // namespace NWM

View file

@ -224,7 +224,7 @@ std::vector<u8> GeneratedEncryptedData(const NetworkInfo& network_info, const No
return buffer; return buffer;
} }
void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) { void DecryptBeacon(const NetworkInfo& network_info, std::vector<u8>& buffer) {
// Decrypt the data using AES-CTR and the NWM beacon key. // Decrypt the data using AES-CTR and the NWM beacon key.
using CryptoPP::AES; using CryptoPP::AES;
std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info); std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info);

View file

@ -127,7 +127,7 @@ static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
/** /**
* Decrypts the beacon data buffer for the network described by `network_info`. * Decrypts the beacon data buffer for the network described by `network_info`.
*/ */
void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer); void DecryptBeacon(const NetworkInfo& network_info, std::vector<u8>& buffer);
/** /**
* Generates an 802.11 beacon frame starting at the management frame header. * Generates an 802.11 beacon frame starting at the management frame header.

View file

@ -273,6 +273,7 @@ void Init() {
AC::InstallInterfaces(*SM::g_service_manager); AC::InstallInterfaces(*SM::g_service_manager);
LDR::InstallInterfaces(*SM::g_service_manager); LDR::InstallInterfaces(*SM::g_service_manager);
MIC::InstallInterfaces(*SM::g_service_manager); MIC::InstallInterfaces(*SM::g_service_manager);
NWM::InstallInterfaces(*SM::g_service_manager);
FS::ArchiveInit(); FS::ArchiveInit();
ACT::Init(); ACT::Init();