mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-04 07:38:47 +00:00 
			
		
		
		
	Merge pull request #4299 from B3n30/uds
NWM_UDS: Fix some issues, cleanups, better PacketHandling
This commit is contained in:
		
						commit
						0b7b9a51d6
					
				
					 2 changed files with 123 additions and 47 deletions
				
			
		| 
						 | 
				
			
			@ -11,6 +11,7 @@
 | 
			
		|||
#include <mutex>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +80,11 @@ static u8 network_channel = DefaultNetworkChannel;
 | 
			
		|||
static NetworkInfo network_info;
 | 
			
		||||
 | 
			
		||||
// Mapping of mac addresses to their respective node_ids.
 | 
			
		||||
static std::map<MacAddress, u16> node_map;
 | 
			
		||||
struct Node {
 | 
			
		||||
    bool connected;
 | 
			
		||||
    u16 node_id;
 | 
			
		||||
};
 | 
			
		||||
static std::map<MacAddress, Node> node_map;
 | 
			
		||||
 | 
			
		||||
// Event that will generate and send the 802.11 beacon frames.
 | 
			
		||||
static CoreTiming::EventType* beacon_broadcast_event;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +112,9 @@ static std::list<Network::WifiPacket> received_beacons;
 | 
			
		|||
// Network node id used when a SecureData packet is addressed to every connected node.
 | 
			
		||||
constexpr u16 BroadcastNetworkNodeId = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
// The Host has always dest_node_id 1
 | 
			
		||||
constexpr u16 HostDestNodeId = 1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -159,16 +167,20 @@ static void BroadcastNodeMap() {
 | 
			
		|||
    packet.channel = network_channel;
 | 
			
		||||
    packet.type = Network::WifiPacket::PacketType::NodeMap;
 | 
			
		||||
    packet.destination_address = Network::BroadcastMac;
 | 
			
		||||
    std::size_t size = node_map.size();
 | 
			
		||||
    std::size_t num_entries = std::count_if(node_map.begin(), node_map.end(),
 | 
			
		||||
                                            [](const auto& node) { return node.second.connected; });
 | 
			
		||||
    using node_t = decltype(node_map)::value_type;
 | 
			
		||||
    packet.data.resize(sizeof(size) + (sizeof(node_t::first) + sizeof(node_t::second)) * size);
 | 
			
		||||
    std::memcpy(packet.data.data(), &size, sizeof(size));
 | 
			
		||||
    std::size_t offset = sizeof(size);
 | 
			
		||||
    packet.data.resize(sizeof(num_entries) +
 | 
			
		||||
                       (sizeof(node_t::first) + sizeof(node_t::second.node_id)) * num_entries);
 | 
			
		||||
    std::memcpy(packet.data.data(), &num_entries, sizeof(num_entries));
 | 
			
		||||
    std::size_t offset = sizeof(num_entries);
 | 
			
		||||
    for (const auto& node : node_map) {
 | 
			
		||||
        std::memcpy(packet.data.data() + offset, node.first.data(), sizeof(node.first));
 | 
			
		||||
        std::memcpy(packet.data.data() + offset + sizeof(node.first), &node.second,
 | 
			
		||||
                    sizeof(node.second));
 | 
			
		||||
        offset += sizeof(node.first) + sizeof(node.second);
 | 
			
		||||
        if (node.second.connected) {
 | 
			
		||||
            std::memcpy(packet.data.data() + offset, node.first.data(), sizeof(node.first));
 | 
			
		||||
            std::memcpy(packet.data.data() + offset + sizeof(node.first), &node.second.node_id,
 | 
			
		||||
                        sizeof(node.second.node_id));
 | 
			
		||||
            offset += sizeof(node.first) + sizeof(node.second.node_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SendPacket(packet);
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +188,11 @@ static void BroadcastNodeMap() {
 | 
			
		|||
 | 
			
		||||
static void HandleNodeMapPacket(const Network::WifiPacket& packet) {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(connection_status_mutex);
 | 
			
		||||
    if (connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
 | 
			
		||||
        LOG_DEBUG(Service_NWM, "Ignored NodeMapPacket since connection_status is host");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    node_map.clear();
 | 
			
		||||
    std::size_t num_entries;
 | 
			
		||||
    Network::MacAddress address;
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +202,8 @@ static void HandleNodeMapPacket(const Network::WifiPacket& packet) {
 | 
			
		|||
    for (std::size_t i = 0; i < num_entries; ++i) {
 | 
			
		||||
        std::memcpy(&address, packet.data.data() + offset, sizeof(address));
 | 
			
		||||
        std::memcpy(&id, packet.data.data() + offset + sizeof(address), sizeof(id));
 | 
			
		||||
        node_map[address] = id;
 | 
			
		||||
        node_map[address].connected = true;
 | 
			
		||||
        node_map[address].node_id = id;
 | 
			
		||||
        offset += sizeof(address) + sizeof(id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +236,12 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
 | 
			
		|||
               "Could not join network");
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(connection_status_mutex);
 | 
			
		||||
        ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
 | 
			
		||||
        if (connection_status.status != static_cast<u32>(NetworkStatus::Connecting)) {
 | 
			
		||||
            LOG_DEBUG(Service_NWM,
 | 
			
		||||
                      "Ignored AssociationResponseFrame because connection status is {}",
 | 
			
		||||
                      connection_status.status);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send the EAPoL-Start packet to the server.
 | 
			
		||||
| 
						 | 
				
			
			@ -245,14 +268,21 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
 | 
			
		|||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto node = DeserializeNodeInfoFromFrame(packet.data);
 | 
			
		||||
 | 
			
		||||
        if (connection_status.max_nodes == connection_status.total_nodes) {
 | 
			
		||||
            // Reject connection attempt
 | 
			
		||||
            LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
 | 
			
		||||
            // TODO(B3N30): Figure out what packet is sent here
 | 
			
		||||
        auto node_it = node_map.find(packet.transmitter_address);
 | 
			
		||||
        if (node_it == node_map.end()) {
 | 
			
		||||
            LOG_DEBUG(Service_NWM, "Connection sequence aborted, because the AuthenticationFrame "
 | 
			
		||||
                                   "of the client wasn't recieved");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (node_it->second.connected) {
 | 
			
		||||
            LOG_DEBUG(Service_NWM,
 | 
			
		||||
                      "Connection sequence aborted, because the client is already connected");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ASSERT(connection_status.max_nodes != connection_status.total_nodes);
 | 
			
		||||
 | 
			
		||||
        auto node = DeserializeNodeInfoFromFrame(packet.data);
 | 
			
		||||
 | 
			
		||||
        // Get an unused network node id
 | 
			
		||||
        u16 node_id = GetNextAvailableNodeId();
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +298,8 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
 | 
			
		|||
 | 
			
		||||
        network_info.total_nodes++;
 | 
			
		||||
 | 
			
		||||
        node_map[packet.transmitter_address] = node.network_node_id;
 | 
			
		||||
        node_map[packet.transmitter_address].node_id = node.network_node_id;
 | 
			
		||||
        node_map[packet.transmitter_address].connected = true;
 | 
			
		||||
 | 
			
		||||
        BroadcastNodeMap();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,6 +352,7 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
 | 
			
		|||
        connection_status_event->Signal();
 | 
			
		||||
        connection_event->Signal();
 | 
			
		||||
    } else if (connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsClient)) {
 | 
			
		||||
        // TODO(B3N30): Remove that section and send/receive a proper connection_status packet
 | 
			
		||||
        // On a 3ds this packet wouldn't be addressed to already connected clients
 | 
			
		||||
        // We use this information because in the current implementation the host
 | 
			
		||||
        // isn't broadcasting the node information
 | 
			
		||||
| 
						 | 
				
			
			@ -349,6 +381,14 @@ static void HandleSecureDataPacket(const Network::WifiPacket& packet) {
 | 
			
		|||
    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) &&
 | 
			
		||||
        connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient)) {
 | 
			
		||||
        // TODO(B3N30): Handle spectators
 | 
			
		||||
        LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket, because connection status is {}",
 | 
			
		||||
                  connection_status.status);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (secure_data.src_node_id == connection_status.network_node_id) {
 | 
			
		||||
        // Ignore packets that came from ourselves.
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -464,12 +504,24 @@ void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
 | 
			
		|||
                          connection_status.status);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (node_map.find(packet.transmitter_address) != node_map.end()) {
 | 
			
		||||
                LOG_ERROR(Service_NWM, "Connection sequence aborted, because there is already a "
 | 
			
		||||
                                       "connected client with that MAC-Adress");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (connection_status.max_nodes == connection_status.total_nodes) {
 | 
			
		||||
                // Reject connection attempt
 | 
			
		||||
                LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
 | 
			
		||||
                // TODO(B3N30): Figure out what packet is sent here
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // Respond with an authentication response frame with SEQ2
 | 
			
		||||
            auth_request.channel = network_channel;
 | 
			
		||||
            auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
 | 
			
		||||
            auth_request.destination_address = packet.transmitter_address;
 | 
			
		||||
            auth_request.type = WifiPacket::PacketType::Authentication;
 | 
			
		||||
            node_map[packet.transmitter_address].connected = false;
 | 
			
		||||
        }
 | 
			
		||||
        SendPacket(auth_request);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -492,17 +544,29 @@ void HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
 | 
			
		|||
        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;
 | 
			
		||||
    });
 | 
			
		||||
    ASSERT(node != node_info.end());
 | 
			
		||||
    Node node = node_map[packet.transmitter_address];
 | 
			
		||||
    node_map.erase(packet.transmitter_address);
 | 
			
		||||
 | 
			
		||||
    connection_status.node_bitmask &= ~(1 << (node_id - 1));
 | 
			
		||||
    connection_status.changed_nodes |= 1 << (node_id - 1);
 | 
			
		||||
    if (!node.connected) {
 | 
			
		||||
        LOG_DEBUG(Service_NWM, "Received DeauthenticationFrame from a not connected MAC Address");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto node_it = std::find_if(node_info.begin(), node_info.end(), [&node](const NodeInfo& info) {
 | 
			
		||||
        return info.network_node_id == node.node_id;
 | 
			
		||||
    });
 | 
			
		||||
    ASSERT(node_it != node_info.end());
 | 
			
		||||
 | 
			
		||||
    connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
 | 
			
		||||
    connection_status.changed_nodes |= 1 << (node.node_id - 1);
 | 
			
		||||
    connection_status.total_nodes--;
 | 
			
		||||
    connection_status.nodes[node.node_id - 1] = 0;
 | 
			
		||||
 | 
			
		||||
    network_info.total_nodes--;
 | 
			
		||||
    // TODO(B3N30): broadcast new connection_status to clients
 | 
			
		||||
 | 
			
		||||
    node_it->Reset();
 | 
			
		||||
 | 
			
		||||
    connection_status_event->Signal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -541,6 +605,26 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static boost::optional<Network::MacAddress> GetNodeMacAddress(u16 dest_node_id, u8 flags) {
 | 
			
		||||
    constexpr u8 BroadcastFlag = 0x2;
 | 
			
		||||
    if ((flags & BroadcastFlag) || dest_node_id == BroadcastNetworkNodeId) {
 | 
			
		||||
        // Broadcast
 | 
			
		||||
        return Network::BroadcastMac;
 | 
			
		||||
    } else if (dest_node_id == HostDestNodeId) {
 | 
			
		||||
        // Destination is host
 | 
			
		||||
        return network_info.host_mac_address;
 | 
			
		||||
    }
 | 
			
		||||
    // Destination is a specific client
 | 
			
		||||
    auto destination =
 | 
			
		||||
        std::find_if(node_map.begin(), node_map.end(), [dest_node_id](const auto& node) {
 | 
			
		||||
            return node.second.node_id == dest_node_id && node.second.connected;
 | 
			
		||||
        });
 | 
			
		||||
    if (destination == node_map.end()) {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
    return destination->first;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NWM_UDS::Shutdown(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x03, 0, 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -656,6 +740,7 @@ void NWM_UDS::InitializeWithVersion(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
        connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
 | 
			
		||||
        node_info.clear();
 | 
			
		||||
        node_info.push_back(current_node);
 | 
			
		||||
        channel_data.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
 | 
			
		||||
| 
						 | 
				
			
			@ -1000,30 +1085,15 @@ void NWM_UDS::SendTo(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Network::MacAddress dest_address;
 | 
			
		||||
 | 
			
		||||
    if (flags >> 2) {
 | 
			
		||||
        LOG_ERROR(Service_NWM, "Unexpected flags 0x{:02X}", flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((flags & (0x1 << 1)) || dest_node_id == 0xFFFF) {
 | 
			
		||||
        // Broadcast
 | 
			
		||||
        dest_address = Network::BroadcastMac;
 | 
			
		||||
    } else if (dest_node_id != 1) {
 | 
			
		||||
        // Send to specific client
 | 
			
		||||
        auto destination =
 | 
			
		||||
            std::find_if(node_map.begin(), node_map.end(),
 | 
			
		||||
                         [dest_node_id](const auto& node) { return node.second == dest_node_id; });
 | 
			
		||||
        if (destination == node_map.end()) {
 | 
			
		||||
            LOG_ERROR(Service_NWM, "tried to send packet to unknown dest id {}", dest_node_id);
 | 
			
		||||
            rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
 | 
			
		||||
                               ErrorSummary::WrongArgument, ErrorLevel::Status));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        dest_address = destination->first;
 | 
			
		||||
    } else {
 | 
			
		||||
        // Send message to host
 | 
			
		||||
        dest_address = network_info.host_mac_address;
 | 
			
		||||
    auto dest_address = GetNodeMacAddress(dest_node_id, flags);
 | 
			
		||||
    if (!dest_address) {
 | 
			
		||||
        rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
 | 
			
		||||
                           ErrorSummary::WrongArgument, ErrorLevel::Status));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr std::size_t MaxSize = 0x5C6;
 | 
			
		||||
| 
						 | 
				
			
			@ -1039,12 +1109,12 @@ void NWM_UDS::SendTo(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
        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): Use the MAC address of the dest_node_id and our own to encrypt
 | 
			
		||||
    // and encapsulate the payload.
 | 
			
		||||
 | 
			
		||||
    Network::WifiPacket packet;
 | 
			
		||||
 | 
			
		||||
    packet.destination_address = dest_address;
 | 
			
		||||
    packet.destination_address = *dest_address;
 | 
			
		||||
    packet.channel = network_channel;
 | 
			
		||||
    packet.data = std::move(data_payload);
 | 
			
		||||
    packet.type = Network::WifiPacket::PacketType::Data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,12 @@ struct NodeInfo {
 | 
			
		|||
    INSERT_PADDING_BYTES(4);
 | 
			
		||||
    u16_le network_node_id;
 | 
			
		||||
    INSERT_PADDING_BYTES(6);
 | 
			
		||||
 | 
			
		||||
    void Reset() {
 | 
			
		||||
        friend_code_seed = 0;
 | 
			
		||||
        username.fill(0);
 | 
			
		||||
        network_node_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size.");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue