From 6aa57b7e8764637bae6ca3d3dfaf5c836a8642f8 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Fri, 17 May 2024 21:39:15 +0100 Subject: [PATCH] DEV9: Handle adapters that provide FCS in capture --- .../PacketReader/ARP/ARP_PacketEditor.cpp | 5 ++ .../DEV9/PacketReader/ARP/ARP_PacketEditor.h | 3 +- pcsx2/DEV9/pcap_io.cpp | 89 ++++++++++++++++++- pcsx2/DEV9/pcap_io.h | 3 + 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.cpp b/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.cpp index 0c758e353e..4d87d83319 100644 --- a/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.cpp +++ b/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.cpp @@ -62,4 +62,9 @@ namespace PacketReader::ARP int offset = 8 + 2 * GetHardwareAddressLength() + GetProtocolAddressLength(); return &basePkt->data[offset]; } + + int ARP_PacketEditor::GetLength() + { + return 8 + 2 * GetHardwareAddressLength() + 2 * GetProtocolAddressLength(); + } } // namespace PacketReader::ARP diff --git a/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.h b/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.h index 93c33b0efc..2428f4184c 100644 --- a/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.h +++ b/pcsx2/DEV9/PacketReader/ARP/ARP_PacketEditor.h @@ -13,7 +13,6 @@ namespace PacketReader::ARP PayloadPtr* basePkt; public: - ARP_PacketEditor(PayloadPtr* pkt); u16 GetHardwareType(); @@ -26,5 +25,7 @@ namespace PacketReader::ARP u8* SenderProtocolAddress(); u8* TargetHardwareAddress(); u8* TargetProtocolAddress(); + + int GetLength(); }; } // namespace PacketReader::ARP diff --git a/pcsx2/DEV9/pcap_io.cpp b/pcsx2/DEV9/pcap_io.cpp index 903872a7ae..9603d62ce1 100644 --- a/pcsx2/DEV9/pcap_io.cpp +++ b/pcsx2/DEV9/pcap_io.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0+ #include "common/Assertions.h" +#include #include #ifdef _WIN32 @@ -124,9 +125,9 @@ bool PCAPAdapter::recv(NetPacket* pkt) // This delays getting packets we need, so instead loop untill a valid packet, or no packet, is returned from pcap_next_ex. while (pcap_next_ex(hpcap, &header, &pkt_data) > 0) { - // 1514 is the largest etherframe we can get with an MTU of 1500 (assuming no VLAN tagging). - // We don't (typically?) get the FCS, and we might need to strip it if we do. - if (header->len > 1514) + // 1518 is the largest Ethernet frame we can get using an MTU of 1500 (assuming no VLAN tagging). + // This includes the FCS, which should be trimmed (PS2 SDK dosn't allow extra space for this). + if (header->len > 1518) { Console.Error("DEV9: Dropped jumbo frame of size: %u", header->len); continue; @@ -142,6 +143,15 @@ bool PCAPAdapter::recv(NetPacket* pkt) if (VerifyPkt(pkt, header->len)) { + HandleFrameCheckSequence(pkt); + + // FCS (if present) has been removed, apply correct limit + if (pkt->size > 1514) + { + Console.Error("DEV9: Dropped jumbo frame of size: %u", pkt->size); + continue; + } + InspectRecv(pkt); return true; } @@ -366,3 +376,76 @@ void PCAPAdapter::SetMACBridgedSend(NetPacket* pkt) } frame.SetSourceMAC(hostMAC); } + +/* + * Strips the Frame Check Sequence if we manage to capture it. + * + * On Windows, (some?) Intel NICs can be configured to capture FCS. + * + * Linux can be configure to capture FCS, using `ethtool -K rx-fcs on` on supported devices. + * Support for capturing FCS can be checked with `ethtool -k | grep rx-fcs`. + * if it's `off [Fixed]`, then the interface/driver dosn't support capturing FCS. + * + * BSD based systems might capture FCS by default. + * + * Packets sent by host won't have FCS, We identify these packets by checking the source MAC address. + * Packets sent by another application via packet injection also won't have FCS and may not match the adapter MAC. + */ +void PCAPAdapter::HandleFrameCheckSequence(NetPacket* pkt) +{ + EthernetFrameEditor frame(pkt); + if (frame.GetSourceMAC() == hostMAC) + return; + + // There is a (very) low chance of the last 4 bytes of payload somehow acting as a valid checksum for the whole Ethernet frame. + // For EtherTypes we already can parse, trim the Ethernet frame based on the payload length. + + int payloadSize = -1; + if (frame.GetProtocol() == static_cast(EtherType::IPv4)) // IP + { + PayloadPtr* payload = frame.GetPayload(); + IP_Packet ippkt(payload->data, payload->GetLength()); + payloadSize = ippkt.GetLength(); + } + if (frame.GetProtocol() == static_cast(EtherType::ARP)) // ARP + { + ARP_PacketEditor arpPkt(frame.GetPayload()); + payloadSize = arpPkt.GetLength(); + } + + if (payloadSize != -1) + { + // Minumum frame size is 60 + 4 byte FCS. + // Virtual NICs may omit this padding, so check we arn't increasing pkt size. + payloadSize = std::min(std::max(payloadSize, 60 - frame.headerLength), pkt->size); + + pkt->size = payloadSize + frame.headerLength; + return; + } + + // Ethertype unknown, rely on checking for a FCS. + if (ValidateEtherFrame(pkt)) + pkt->size -= 4; +} + +bool PCAPAdapter::ValidateEtherFrame(NetPacket* pkt) +{ + u32 crc = 0xFFFFFFFF; + + for (int i = 0; i < pkt->size; i++) + { + // Neads unsigned value + crc = crc ^ static_cast(pkt->buffer[i]); + for (int bit = 0; bit < 8; bit++) + { + if ((crc & 1) != 0) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + } + + crc = ~crc; + + return crc == 0x2144DF1C; +} diff --git a/pcsx2/DEV9/pcap_io.h b/pcsx2/DEV9/pcap_io.h index f90a65234d..466862836b 100644 --- a/pcsx2/DEV9/pcap_io.h +++ b/pcsx2/DEV9/pcap_io.h @@ -42,4 +42,7 @@ private: void SetMACBridgedRecv(NetPacket* pkt); void SetMACBridgedSend(NetPacket* pkt); + + void HandleFrameCheckSequence(NetPacket* pkt); + bool ValidateEtherFrame(NetPacket* pkt); };