diff --git a/pcsx2/DEV9/pcap_io.cpp b/pcsx2/DEV9/pcap_io.cpp index 9cf5c38006..57a59ae857 100644 --- a/pcsx2/DEV9/pcap_io.cpp +++ b/pcsx2/DEV9/pcap_io.cpp @@ -30,7 +30,6 @@ #include "DEV9.h" #include "AdapterUtils.h" #include "net.h" -#include "DEV9/PacketReader/MAC_Address.h" #ifndef PCAP_NETMASK_UNKNOWN #define PCAP_NETMASK_UNKNOWN 0xffffffff #endif @@ -39,103 +38,8 @@ #define PCAPPREFIX "\\Device\\NPF_" #endif -pcap_t* adhandle; -pcap_dumper_t* dump_pcap = nullptr; -char errbuf[PCAP_ERRBUF_SIZE]; - -int pcap_io_running = 0; -bool pcap_io_switched; -bool pcap_io_blocking; - -extern u8 eeprom[]; - -char namebuff[256]; - -ip_address ps2_ip; -mac_address ps2_mac; -mac_address host_mac; - -int pcap_io_init(const std::string& adapter, bool switched, mac_address virtual_mac) -{ - struct bpf_program fp; - char filter[1024] = "ether broadcast or ether dst "; - int dlt; - char* dlt_name; - Console.WriteLn("DEV9: Opening adapter '%s'...", adapter.c_str()); - - pcap_io_switched = switched; - -#ifdef _WIN32 - std::string pcapAdapter = PCAPPREFIX + adapter; -#else - std::string pcapAdapter = adapter; -#endif - /* Open the adapter */ - if ((adhandle = pcap_open_live(pcapAdapter.c_str(), // name of the device - 65536, // portion of the packet to capture. - // 65536 grants that the whole packet will be captured on all the MACs. - switched ? 1 : 0, - 1, // read timeout - errbuf // error buffer - )) == NULL) - { - Console.Error("DEV9: %s", errbuf); - Console.Error("DEV9: Unable to open the adapter. %s is not supported by pcap", adapter.c_str()); - return -1; - } - if (switched) - { - char virtual_mac_str[18]; - sprintf(virtual_mac_str, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", virtual_mac.bytes[0], virtual_mac.bytes[1], virtual_mac.bytes[2], virtual_mac.bytes[3], virtual_mac.bytes[4], virtual_mac.bytes[5]); - strcat(filter, virtual_mac_str); - // fprintf(stderr, "Trying pcap filter: %s\n", filter); - - if (pcap_compile(adhandle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) == -1) - { - Console.Error("DEV9: Error calling pcap_compile: %s", pcap_geterr(adhandle)); - return -1; - } - - if (pcap_setfilter(adhandle, &fp) == -1) - { - Console.Error("DEV9: Error setting filter: %s", pcap_geterr(adhandle)); - return -1; - } - } - - if (pcap_setnonblock(adhandle, 1, errbuf) == -1) - { - Console.Error("DEV9: Error setting non-blocking: %s", pcap_geterr(adhandle)); - Console.Error("DEV9: Continuing in blocking mode"); - pcap_io_blocking = true; - } - else - pcap_io_blocking = false; - - dlt = pcap_datalink(adhandle); - dlt_name = (char*)pcap_datalink_val_to_name(dlt); - - Console.Error("DEV9: Device uses DLT %d: %s", dlt, dlt_name); - switch (dlt) - { - case DLT_EN10MB: - //case DLT_IEEE802_11: - break; - default: - Console.Error("ERROR: Unsupported DataLink Type (%d): %s", dlt, dlt_name); - pcap_close(adhandle); - return -1; - } - -#ifdef DEBUG - const std::string plfile(s_strLogPath + "/pkt_log.pcap"); - dump_pcap = pcap_dump_open(adhandle, plfile.c_str()); -#endif - - pcap_io_running = 1; - Console.WriteLn("DEV9: Adapter Ok."); - return 0; -} +using namespace PacketReader; +using namespace PacketReader::IP; #ifdef _WIN32 int gettimeofday(struct timeval* tv, void* tz) @@ -149,94 +53,6 @@ int gettimeofday(struct timeval* tv, void* tz) } #endif -int pcap_io_send(void* packet, int plen) -{ - if (pcap_io_running <= 0) - return -1; - - if (!pcap_io_switched) - { - if (((ethernet_header*)packet)->protocol == 0x0008) //IP - { - ps2_ip = ((ip_header*)((u8*)packet + sizeof(ethernet_header)))->src; - } - if (((ethernet_header*)packet)->protocol == 0x0608) //ARP - { - ps2_ip = ((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->p_src; - ((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->h_src = host_mac; - } - ((ethernet_header*)packet)->src = host_mac; - } - - if (dump_pcap) - { - static struct pcap_pkthdr ph; - gettimeofday(&ph.ts, NULL); - ph.caplen = plen; - ph.len = plen; - pcap_dump((u_char*)dump_pcap, &ph, (u_char*)packet); - } - - return pcap_sendpacket(adhandle, (u_char*)packet, plen); -} - -int pcap_io_recv(void* packet, int max_len) -{ - static struct pcap_pkthdr* header; - static const u_char* pkt_data1; - - if (pcap_io_running <= 0) - return -1; - - if ((pcap_next_ex(adhandle, &header, &pkt_data1)) > 0) - { - if ((int)header->len > max_len) - return -1; - - memcpy(packet, pkt_data1, header->len); - - if (!pcap_io_switched) - { - { - if (((ethernet_header*)packet)->protocol == 0x0008) - { - ip_header* iph = ((ip_header*)((u8*)packet + sizeof(ethernet_header))); - if (ip_compare(iph->dst, ps2_ip) == 0) - { - ((ethernet_header*)packet)->dst = ps2_mac; - } - } - if (((ethernet_header*)packet)->protocol == 0x0608) - { - arp_packet* aph = ((arp_packet*)((u8*)packet + sizeof(ethernet_header))); - if (ip_compare(aph->p_dst, ps2_ip) == 0) - { - ((ethernet_header*)packet)->dst = ps2_mac; - ((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->h_dst = ps2_mac; - } - } - } - } - - if (dump_pcap) - pcap_dump((u_char*)dump_pcap, header, (u_char*)packet); - - return header->len; - } - - return -1; -} - -void pcap_io_close() -{ - if (dump_pcap) - pcap_dump_close(dump_pcap); - if (adhandle) - pcap_close(adhandle); - pcap_io_running = 0; -} - - PCAPAdapter::PCAPAdapter() : NetAdapter() { @@ -247,44 +63,64 @@ PCAPAdapter::PCAPAdapter() return; #endif - AdapterUtils::Adapter adapter; - AdapterUtils::AdapterBuffer buffer; - if (AdapterUtils::GetAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer)) - { - std::optional adMAC = AdapterUtils::GetAdapterMAC(&adapter); - if (adMAC.has_value()) - { - mac_address hostMAC = *(mac_address*)&adMAC.value(); - mac_address newMAC; - memcpy(&newMAC, &ps2MAC, 6); +#ifdef _WIN32 + std::string pcapAdapter = PCAPPREFIX + EmuConfig.DEV9.EthDevice; +#else + std::string pcapAdapter = EmuConfig.DEV9.EthDevice; +#endif - //Lets take the hosts last 2 bytes to make it unique on Xlink - newMAC.bytes[5] = hostMAC.bytes[4]; - newMAC.bytes[4] = hostMAC.bytes[5]; + switched = EmuConfig.DEV9.EthApi == Pcsx2Config::DEV9Options::NetApi::PCAP_Switched; - SetMACAddress((PacketReader::MAC_Address*)&newMAC); - host_mac = hostMAC; - ps2_mac = newMAC; //Needed outside of this class - } - else - { - Console.Error("DEV9: Failed to get MAC address for adapter"); - return; - } - } - else - { - Console.Error("DEV9: Failed to get adapter information"); - return; - } - - if (pcap_io_init(EmuConfig.DEV9.EthDevice, EmuConfig.DEV9.EthApi == Pcsx2Config::DEV9Options::NetApi::PCAP_Switched, ps2_mac) == -1) + if (!InitPCAP(pcapAdapter, switched)) { Console.Error("DEV9: Can't open Device '%s'", EmuConfig.DEV9.EthDevice.c_str()); return; } - InitInternalServer(&adapter); + AdapterUtils::Adapter adapter; + AdapterUtils::AdapterBuffer buffer; + std::optional adMAC = std::nullopt; + const bool foundAdapter = AdapterUtils::GetAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer); + if (foundAdapter) + adMAC = AdapterUtils::GetAdapterMAC(&adapter); + else + Console.Error("DEV9: Failed to get adapter information"); + + if (adMAC.has_value()) + { + hostMAC = adMAC.value(); + MAC_Address newMAC = ps2MAC; + + //Lets take the hosts last 2 bytes to make it unique on Xlink + newMAC.bytes[5] = hostMAC.bytes[4]; + newMAC.bytes[4] = hostMAC.bytes[5]; + + SetMACAddress(&newMAC); + } + else if (switched) + Console.Error("DEV9: Failed to get MAC address for adapter, proceeding with hardcoded MAC address"); + else + { + Console.Error("DEV9: Failed to get MAC address for adapter"); + pcap_close(hpcap); + hpcap = nullptr; + return; + } + + if (switched && !SetMACSwitchedFilter(ps2MAC)) + { + pcap_close(hpcap); + hpcap = nullptr; + Console.Error("DEV9: Can't open Device '%s'", EmuConfig.DEV9.EthDevice.c_str()); + return; + } + + if (foundAdapter) + InitInternalServer(&adapter); + else + InitInternalServer(nullptr); + + InitPCAPDumper(); } AdapterOptions PCAPAdapter::GetAdapterOptions() { @@ -292,21 +128,46 @@ AdapterOptions PCAPAdapter::GetAdapterOptions() } bool PCAPAdapter::blocks() { - pxAssert(pcap_io_running); - return pcap_io_blocking; + pxAssert(hpcap); + return blocking; } bool PCAPAdapter::isInitialised() { - return !!pcap_io_running; + return hpcap != nullptr; } //gets a packet.rv :true success bool PCAPAdapter::recv(NetPacket* pkt) { - if (!pcap_io_blocking && NetAdapter::recv(pkt)) + pxAssert(hpcap); + + if (!blocking && NetAdapter::recv(pkt)) return true; - int size = pcap_io_recv(pkt->buffer, sizeof(pkt->buffer)); - if (size > 0 && VerifyPkt(pkt, size)) + pcap_pkthdr* header; + const u_char* pkt_data; + + if (pcap_next_ex(hpcap, &header, &pkt_data) > 0) + { + if (header->len > sizeof(pkt->buffer)) + { + Console.Error("DEV9: Dropped jumbo frame of size: %u", header->len); + return false; + } + + pxAssert(header->len == header->caplen); + + memcpy(pkt->buffer, pkt_data, header->len); + + if (!switched) + SetMACBridgedRecv(pkt); + + if (hpcap_dumper) + pcap_dump((u_char*)hpcap_dumper, header, pkt_data); + } + else + return false; + + if (VerifyPkt(pkt, header->len)) { InspectRecv(pkt); return true; @@ -317,18 +178,29 @@ bool PCAPAdapter::recv(NetPacket* pkt) //sends the packet .rv :true success bool PCAPAdapter::send(NetPacket* pkt) { + pxAssert(hpcap); + InspectSend(pkt); if (NetAdapter::send(pkt)) return true; - if (pcap_io_send(pkt->buffer, pkt->size)) + if (hpcap_dumper) { + pcap_pkthdr ph; + gettimeofday(&ph.ts, NULL); + ph.caplen = pkt->size; + ph.len = pkt->size; + pcap_dump((u_char*)hpcap_dumper, &ph, (u_char*)pkt->buffer); + } + + // TODO: loopback broadcast packets to host pc in switched mode. + if (!switched) + SetMACBridgedSend(pkt); + + if (pcap_sendpacket(hpcap, (u_char*)pkt->buffer, pkt->size)) return false; - } else - { return true; - } } void PCAPAdapter::reloadSettings() @@ -343,7 +215,16 @@ void PCAPAdapter::reloadSettings() PCAPAdapter::~PCAPAdapter() { - pcap_io_close(); + if (hpcap_dumper) + { + pcap_dump_close(hpcap_dumper); + hpcap_dumper = nullptr; + } + if (hpcap) + { + pcap_close(hpcap); + hpcap = nullptr; + } } std::vector PCAPAdapter::GetAdapters() @@ -355,6 +236,7 @@ std::vector PCAPAdapter::GetAdapters() return nic; #endif + char errbuf[PCAP_ERRBUF_SIZE]; pcap_if_t* alldevs; pcap_if_t* d; @@ -410,3 +292,127 @@ std::vector PCAPAdapter::GetAdapters() return nic; } + +// Opens device for capture and sets non-blocking. +bool PCAPAdapter::InitPCAP(const std::string& adapter, bool promiscuous) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + Console.WriteLn("DEV9: Opening adapter '%s'...", adapter.c_str()); + + // Open the adapter. + if ((hpcap = pcap_open_live(adapter.c_str(), // Name of the device. + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + promiscuous ? 1 : 0, + 1, // Read timeout. + errbuf // Error buffer. + )) == nullptr) + { + Console.Error("DEV9: %s", errbuf); + Console.Error("DEV9: Unable to open the adapter. %s is not supported by pcap", adapter.c_str()); + return false; + } + + if (pcap_setnonblock(hpcap, 1, errbuf) == -1) + { + Console.Error("DEV9: Error setting non-blocking: %s", pcap_geterr(hpcap)); + Console.Error("DEV9: Continuing in blocking mode"); + blocking = true; + } + else + blocking = false; + + // Validate. + const int dlt = pcap_datalink(hpcap); + const char* dlt_name = pcap_datalink_val_to_name(dlt); + + Console.Error("DEV9: Device uses DLT %d: %s", dlt, dlt_name); + switch (dlt) + { + case DLT_EN10MB: + //case DLT_IEEE802_11: + break; + default: + Console.Error("ERROR: Unsupported DataLink Type (%d): %s", dlt, dlt_name); + pcap_close(hpcap); + hpcap = nullptr; + return false; + } + + Console.WriteLn("DEV9: Adapter Ok."); + return true; +} + +void PCAPAdapter::InitPCAPDumper() +{ +#ifdef DEBUG + const std::string plfile(s_strLogPath + "/pkt_log.pcap"); + hpcap_dumper = pcap_dump_open(hpcap, plfile.c_str()); +#endif +} + +bool PCAPAdapter::SetMACSwitchedFilter(MAC_Address mac) +{ + bpf_program fp; + char filter[1024] = "ether broadcast or ether dst "; + + char virtual_mac_str[18]; + sprintf(virtual_mac_str, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac.bytes[0], mac.bytes[1], mac.bytes[2], mac.bytes[3], mac.bytes[4], mac.bytes[5]); + strcat(filter, virtual_mac_str); + + if (pcap_compile(hpcap, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) == -1) + { + Console.Error("DEV9: Error calling pcap_compile: %s", pcap_geterr(hpcap)); + return false; + } + + int setFilterRet; + if ((setFilterRet = pcap_setfilter(hpcap, &fp)) == -1) + Console.Error("DEV9: Error setting filter: %s", pcap_geterr(hpcap)); + + pcap_freecode(&fp); + return setFilterRet != -1; +} + +void PCAPAdapter::SetMACBridgedRecv(NetPacket* pkt) +{ + ethernet_header* eth = (ethernet_header*)pkt->buffer; + if (eth->protocol == 0x0008) // IP + { + // Compare DEST IP in IP with the PS2's IP, if they match, change DEST MAC to ps2MAC. + const ip_header* iph = ((ip_header*)(pkt->buffer + sizeof(ethernet_header))); + + if (*(IP_Address*)&iph->dst == ps2IP) + eth->dst = *(mac_address*)&ps2MAC; + } + + if (eth->protocol == 0x0608) // ARP + { + // Compare DEST IP in ARP with the PS2's IP, if they match, DEST MAC to ps2MAC on both ARP and ETH Packet headers. + arp_packet* aph = ((arp_packet*)(pkt->buffer + sizeof(ethernet_header))); + + if (*(IP_Address*)&aph->p_dst == ps2IP) + { + eth->dst = *(mac_address*)&ps2MAC; + ((arp_packet*)(pkt->buffer + sizeof(ethernet_header)))->h_dst = *(mac_address*)&ps2MAC; + } + } +} + +void PCAPAdapter::SetMACBridgedSend(NetPacket* pkt) +{ + ethernet_header* eth = (ethernet_header*)pkt->buffer; + if (eth->protocol == 0x0008) // IP + { + const ip_address ps2_ip = ((ip_header*)((u8*)pkt->buffer + sizeof(ethernet_header)))->src; + ps2IP = *(IP_Address*)&ps2_ip; + } + if (eth->protocol == 0x0608) // ARP + { + const ip_address ps2_ip = ((arp_packet*)((u8*)pkt->buffer + sizeof(ethernet_header)))->p_src; + ps2IP = *(IP_Address*)&ps2_ip; + + ((arp_packet*)(pkt->buffer + sizeof(ethernet_header)))->h_src = *(mac_address*)&hostMAC; + } + eth->src = *(mac_address*)&hostMAC; +} diff --git a/pcsx2/DEV9/pcap_io.h b/pcsx2/DEV9/pcap_io.h index 2a03c2e4f3..34e9122916 100644 --- a/pcsx2/DEV9/pcap_io.h +++ b/pcsx2/DEV9/pcap_io.h @@ -16,6 +16,7 @@ #pragma once #include "pcap.h" #include "net.h" +#include "PacketReader/MAC_Address.h" #ifdef __cplusplus extern "C" { @@ -173,6 +174,16 @@ void unload_pcap(); class PCAPAdapter : public NetAdapter { +private: + pcap_t* hpcap = nullptr; + pcap_dumper_t* hpcap_dumper = nullptr; + + bool switched; + bool blocking; + + PacketReader::IP::IP_Address ps2IP{}; + PacketReader::MAC_Address hostMAC; + public: PCAPAdapter(); virtual bool blocks(); @@ -185,4 +196,12 @@ public: virtual ~PCAPAdapter(); static std::vector GetAdapters(); static AdapterOptions GetAdapterOptions(); + +private: + bool InitPCAP(const std::string& adapter, bool promiscuous); + void InitPCAPDumper(); + bool SetMACSwitchedFilter(PacketReader::MAC_Address mac); + + void SetMACBridgedRecv(NetPacket* pkt); + void SetMACBridgedSend(NetPacket* pkt); };