DEV9: Handle adapters that provide FCS in capture

This commit is contained in:
TheLastRar 2024-05-17 21:39:15 +01:00 committed by lightningterror
parent 6a37353565
commit 6aa57b7e87
4 changed files with 96 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0+
#include "common/Assertions.h"
#include <algorithm>
#include <memory>
#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 <interface> rx-fcs on` on supported devices.
* Support for capturing FCS can be checked with `ethtool -k <interface> | 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<u16>(EtherType::IPv4)) // IP
{
PayloadPtr* payload = frame.GetPayload();
IP_Packet ippkt(payload->data, payload->GetLength());
payloadSize = ippkt.GetLength();
}
if (frame.GetProtocol() == static_cast<u16>(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<u8>(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;
}

View File

@ -42,4 +42,7 @@ private:
void SetMACBridgedRecv(NetPacket* pkt);
void SetMACBridgedSend(NetPacket* pkt);
void HandleFrameCheckSequence(NetPacket* pkt);
bool ValidateEtherFrame(NetPacket* pkt);
};