diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj index 301e31e6..573a9e0b 100644 --- a/AppleWinExpress2019.vcxproj +++ b/AppleWinExpress2019.vcxproj @@ -114,6 +114,7 @@ + @@ -222,6 +223,7 @@ + @@ -467,7 +469,7 @@ Windows true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies) "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 @@ -495,7 +497,7 @@ Windows true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies) "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 @@ -522,7 +524,7 @@ Windows true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies) "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 @@ -553,7 +555,7 @@ true true true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 @@ -586,7 +588,7 @@ true true true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 @@ -619,7 +621,7 @@ true true true - htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies) + iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration "type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" 5.01 diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters index 617a7d87..c3ce4b15 100644 --- a/AppleWinExpress2019.vcxproj.filters +++ b/AppleWinExpress2019.vcxproj.filters @@ -259,6 +259,9 @@ Source Files\Uthernet + + Source Files\Uthernet + @@ -594,6 +597,9 @@ Source Files\Uthernet + + Source Files\Uthernet + diff --git a/source/Tfe/IPRaw.cpp b/source/Tfe/IPRaw.cpp new file mode 100644 index 00000000..12a0ac4c --- /dev/null +++ b/source/Tfe/IPRaw.cpp @@ -0,0 +1,162 @@ +/* +AppleWin : An Apple //e emulator for Windows + +Copyright (C) 2022, Andrea Odetti + +AppleWin is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +AppleWin is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with AppleWin; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "StdAfx.h" + +#include "IPRaw.h" + +#ifndef _MSC_VER +#include +#endif + +#define IPV4 0x04 + +namespace +{ + +#pragma pack(push) +#pragma pack(1) // Ensure struct is packed + struct IP4Header + { + uint8_t ihl : 4; + uint8_t version : 4; + uint8_t tos; + uint16_t len; + uint16_t id; + uint16_t flags : 3; + uint16_t fragmentOffset : 13; + uint8_t ttl; + uint8_t proto; + uint16_t checksum; + uint32_t sourceAddress; + uint32_t destinationAddress; + }; + + struct ETH2Frame + { + uint8_t destinationMac[6]; + uint8_t sourceMac[6]; + uint16_t type; + }; +#pragma pack(pop) + + uint32_t sum_every_16bits(const void *addr, int count) + { + uint32_t sum = 0; + const uint16_t *ptr = reinterpret_cast(addr); + + while (count > 1) + { + /* This is the inner loop */ + sum += *ptr++; + count -= 2; + } + + /* Add left-over byte, if any */ + if (count > 0) + sum += *reinterpret_cast(ptr); + + return sum; + } + + uint16_t checksum(const void *addr, int count) + { + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + * Taken from https://tools.ietf.org/html/rfc1071 + */ + uint32_t sum = sum_every_16bits(addr, count); + + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; + } + + // get the minimum size of a ETH Frame that contains a IP payload + // 34 = 14 bytes for ETH2 + 20 bytes IPv4 (minimum) + int getIPMinimumSize() + { + const int minimumSize = sizeof(ETH2Frame) + sizeof(IP4Header) + 0; // 0 len + return minimumSize; + } + +} + +std::vector createETH2Frame(const std::vector &data, + const MACAddress *sourceMac, const MACAddress *destinationMac, + const uint8_t ttl, const uint8_t tos, const uint8_t protocol, + const uint32_t sourceAddress, const uint32_t destinationAddress) +{ + const size_t total = sizeof(ETH2Frame) + sizeof(IP4Header) + data.size(); + std::vector frame(total); + ETH2Frame *eth2frame = reinterpret_cast(frame.data() + 0); + memcpy(eth2frame->destinationMac, destinationMac, sizeof(eth2frame->destinationMac)); + memcpy(eth2frame->sourceMac, sourceMac, sizeof(eth2frame->destinationMac)); + eth2frame->type = htons(0x0800); + IP4Header *ip4header = reinterpret_cast(frame.data() + sizeof(ETH2Frame)); + + ip4header->version = IPV4; + ip4header->ihl = 0x05; // minimum size = 20 bytes, without any extra option + ip4header->tos = tos; + ip4header->len = htons(static_cast(sizeof(IP4Header) + data.size())); + ip4header->id = 0; + ip4header->fragmentOffset = 0; + ip4header->ttl = ttl; + ip4header->proto = protocol; + ip4header->sourceAddress = sourceAddress; + ip4header->destinationAddress = destinationAddress; + ip4header->checksum = checksum(ip4header, sizeof(IP4Header)); + + memcpy(frame.data() + sizeof(ETH2Frame) + sizeof(IP4Header), data.data(), data.size()); + + return frame; +} + +void getIPPayload(const int lengthOfFrame, const uint8_t *frame, + size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol) +{ + const int minimumSize = getIPMinimumSize(); + if (lengthOfFrame > minimumSize) + { + const ETH2Frame *eth2Frame = reinterpret_cast(frame); + const IP4Header *ip4header = reinterpret_cast(frame + sizeof(ETH2Frame)); + if (eth2Frame->type == htons(0x0800) && ip4header->version == IPV4) + { + const uint16_t ipv4HeaderSize = ip4header->ihl * 4; + const uint16_t ipPacketSize = ntohs(ip4header->len); + const int expectedSize = sizeof(ETH2Frame) + ipPacketSize; + if (ipPacketSize > ipv4HeaderSize && lengthOfFrame >= expectedSize) + { + protocol = ip4header->proto; + payload = frame + sizeof(ETH2Frame) + ipv4HeaderSize; + lengthOfPayload = ipPacketSize - ipv4HeaderSize; + destination = ip4header->destinationAddress; + return; + } + } + } + // not a good packet + protocol = 0xFF; // reserved protocol + payload = nullptr; + lengthOfPayload = 0; + destination = 0; +} diff --git a/source/Tfe/IPRaw.h b/source/Tfe/IPRaw.h new file mode 100644 index 00000000..cd0c534e --- /dev/null +++ b/source/Tfe/IPRaw.h @@ -0,0 +1,11 @@ +#pragma once + +struct MACAddress; + +std::vector createETH2Frame(const std::vector &data, + const MACAddress *sourceMac, const MACAddress *destinationMac, + const uint8_t ttl, const uint8_t tos, const uint8_t protocol, + const uint32_t sourceAddress, const uint32_t destinationAddress); + +void getIPPayload(const int lengthOfFrame, const uint8_t *frame, + size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol); diff --git a/source/Tfe/NetworkBackend.h b/source/Tfe/NetworkBackend.h index e883c9cb..3bfda5dd 100644 --- a/source/Tfe/NetworkBackend.h +++ b/source/Tfe/NetworkBackend.h @@ -6,6 +6,14 @@ #define MAX_RXLENGTH 1518 #define MIN_RXLENGTH 64 +#pragma pack(push) +#pragma pack(1) // Ensure struct is packed +struct MACAddress +{ + uint8_t address[6]; +}; +#pragma pack(pop) + class NetworkBackend { public: @@ -26,6 +34,9 @@ public: // process pending packets virtual void update(const ULONG nExecutedCycles) = 0; + // get MAC for IPRAW (it is only supposed to handle addresses on the local network) + virtual void getMACAddress(const uint32_t address, MACAddress & mac) = 0; + // if the backend is usable virtual bool isValid() = 0; }; diff --git a/source/Tfe/PCapBackend.cpp b/source/Tfe/PCapBackend.cpp index c2f7c5aa..58d349da 100644 --- a/source/Tfe/PCapBackend.cpp +++ b/source/Tfe/PCapBackend.cpp @@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../Common.h" #include "../Registry.h" +#ifdef _MSC_VER +#include +#endif + std::string PCapBackend::tfe_interface; PCapBackend::PCapBackend(const std::string & pcapInterface) @@ -71,6 +75,16 @@ void PCapBackend::update(const ULONG /* nExecutedCycles */) // nothing to do } +void PCapBackend::getMACAddress(const uint32_t address, MACAddress & mac) +{ + // this is only expected to be called for IP addresses on the same network +#ifdef _MSC_VER + const DWORD dwSourceAddress = INADDR_ANY; + ULONG len = sizeof(MACAddress::address); + SendARP(address, dwSourceAddress, mac.address, &len); +#endif +} + int PCapBackend::tfe_enumadapter_open(void) { return tfe_arch_enumadapter_open(); diff --git a/source/Tfe/PCapBackend.h b/source/Tfe/PCapBackend.h index 01952b90..a94023d7 100644 --- a/source/Tfe/PCapBackend.h +++ b/source/Tfe/PCapBackend.h @@ -29,6 +29,9 @@ public: // process pending packets virtual bool isValid(); + // get MAC for IPRAW (it is only supposed to handle addresses on the local network) + virtual void getMACAddress(const uint32_t address, MACAddress & mac); + static void tfe_SetRegistryInterface(UINT slot, const std::string& name); static void get_disabled_state(int * param); diff --git a/source/Uthernet2.cpp b/source/Uthernet2.cpp index 74b32aec..74d709d0 100644 --- a/source/Uthernet2.cpp +++ b/source/Uthernet2.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Interface.h" #include "Tfe/NetworkBackend.h" #include "Tfe/PCapBackend.h" +#include "Tfe/IPRaw.h" #include "W5100.h" // Linux uses EINPROGRESS while Windows returns WSAEWOULDBLOCK @@ -73,6 +74,9 @@ typedef int socklen_t; #define SOCK_NONBLOCK O_NONBLOCK #endif +// Dest MAC + Source MAC + Ether Type +#define ETH_MINIMUM_SIZE (6 + 6 + 2) + // #define U2_LOG_VERBOSE // #define U2_LOG_TRAFFIC // #define U2_LOG_STATE @@ -95,6 +99,12 @@ namespace return host; } + uint32_t readAddress(const uint8_t *ptr) + { + const uint32_t address = *reinterpret_cast(ptr); + return address; + } + uint8_t getIByte(const uint16_t value, const size_t shift) { return (value >> shift) & 0xFF; @@ -141,6 +151,13 @@ namespace writeData(socket, memory, data, len); } + void writeDataIPRaw(Socket &socket, std::vector &memory, const uint8_t *data, const size_t len, const uint32_t destination) + { + writeAny(socket, memory, destination); + write16(socket, memory, static_cast(len)); + writeData(socket, memory, data, len); + } + void writeDataForProtocol(Socket &socket, std::vector &memory, const uint8_t *data, const size_t len, const sockaddr_in &source) { if (socket.sn_sr == W5100_SN_SR_SOCK_UDP) @@ -266,12 +283,18 @@ bool Socket::LoadSnapshot(YamlLoadHelper &yamlLoadHelper) // transmit and receive sizes are restored from the card common registers - if (sn_sr != W5100_SN_SR_SOCK_MACRAW) + switch (sn_sr) { + case W5100_SN_SR_SOCK_MACRAW: + case W5100_SN_SR_SOCK_IPRAW: + // we can restore RAW sockets + break; + default: // no point in restoring a broken UDP or TCP connection // just reset the socket sn_sr = W5100_SN_SR_CLOSED; // for the same reason there is no point in saving myFD and myErrno + break; } return true; @@ -434,7 +457,7 @@ void Uthernet2::updateRSR(const size_t i) socket.sn_rx_rsr = dataPresent; } -int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data) +int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data, PacketDestination & packetDestination) { const uint8_t * mac = myMemory.data() + W5100_SHAR0; @@ -442,15 +465,9 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_ int len; while ((len = myNetworkBackend->receive(size, data)) > 0) { - // minimum valid Ethernet frame is actually 64 bytes - // 12 is the minimum to ensure valid MAC Address logging later - if (len >= 12) + // smaller frames are not good anyway + if (len >= ETH_MINIMUM_SIZE) { - if (acceptAll) - { - return len; - } - if (data[0] == mac[0] && data[1] == mac[1] && data[2] == mac[2] && @@ -458,6 +475,7 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_ data[4] == mac[4] && data[5] == mac[5]) { + packetDestination = HOST; return len; } @@ -468,8 +486,16 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_ data[4] == 0xFF && data[5] == 0xFF) { + packetDestination = BROADCAST; return len; } + + if (acceptAll) + { + packetDestination = OTHER; + return len; + } + } // skip this frame and try with another one } @@ -477,34 +503,110 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_ return len; } -void Uthernet2::receiveOnePacketMacRaw(const size_t i) +void Uthernet2::receiveOnePacketRaw() +{ + bool acceptAll = false; + int macRawSocket = -1; // to which IPRaw soccket to send to (can only be 0) + + Socket & socket0 = mySockets[0]; + if (socket0.sn_sr == W5100_SN_SR_SOCK_MACRAW) + { + macRawSocket = 0; // the only MAC Raw socket is open, packet will go there as a fallback + const uint8_t mr = myMemory[socket0.registerAddress + W5100_SN_MR]; + + // see if MAC RAW filters or not + const bool filterMAC = mr & W5100_SN_MR_MF; + if (!filterMAC) + { + acceptAll = true; + } + } + + uint8_t buffer[MAX_RXLENGTH]; + PacketDestination packetDestination; + const int len = receiveForMacAddress(acceptAll, sizeof(buffer), buffer, packetDestination); + if (len > 0) + { + const uint8_t * payload; + size_t lengthOfPayload; + uint32_t destination; + uint8_t packetProtocol; + getIPPayload(len, buffer, lengthOfPayload, payload, destination, packetProtocol); + + // see if there is a IPRAW socket that should accept thi spacket + int ipRawSocket = -1; + if (packetDestination != OTHER) // IPRaw always filters (HOST or BROADCAST, never OTHER) + { + for (size_t i = 0; i < mySockets.size(); ++i) + { + Socket & socket = mySockets[i]; + + if (socket.sn_sr == W5100_SN_SR_SOCK_IPRAW) + { + // IP only accepts by protocol & always filters MAC + const uint8_t socketProtocol = myMemory[socket.registerAddress + W5100_SN_PROTO]; + if (payload && packetProtocol == socketProtocol) + { + ipRawSocket = i; + break; // a valid IPRAW socket has been found + } + } + // we should probably check for UDP & TCP sockets and filter these packets too + } + } + + // priority to IPRAW + if (ipRawSocket >= 0) + { + receiveOnePacketIPRaw(ipRawSocket, lengthOfPayload, payload, destination, packetProtocol, len); + } + // fallback to MACRAW (if open) + else if (macRawSocket >= 0) + { + receiveOnePacketMacRaw(macRawSocket, len, buffer); + } + // else packet is dropped + } +} + +void Uthernet2::receiveOnePacketMacRaw(const size_t i, const int size, uint8_t * data) { Socket &socket = mySockets[i]; - uint8_t buffer[MAX_RXLENGTH]; - - const uint8_t mr = myMemory[socket.registerAddress + W5100_SN_MR]; - const bool filterMAC = mr & W5100_SN_MR_MF; - - const int len = receiveForMacAddress(!filterMAC, sizeof(buffer), buffer); - if (len > 0) + if (socket.isThereRoomFor(size, sizeof(uint16_t))) { - // we know the packet is at least 12 bytes, and logging is ok - if (socket.isThereRoomFor(len, sizeof(uint16_t))) - { - writeDataMacRaw(socket, myMemory, buffer, len); + writeDataMacRaw(socket, myMemory, data, size); #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: Read MACRAW[%" SIZE_T_FMT "]: " MAC_FMT " -> " MAC_FMT ": +%d -> %d bytes\n", i, MAC_SOURCE(buffer), MAC_DEST(buffer), - len, socket.sn_rx_rsr); + LogFileOutput("U2: Read MACRAW[%" SIZE_T_FMT "]: " MAC_FMT " -> " MAC_FMT ": +%d -> %d bytes\n", i, MAC_SOURCE(data), MAC_DEST(data), + size, socket.sn_rx_rsr); #endif - } - else - { - // drop it + } + else + { + // drop it #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: Skip MACRAW[%" SIZE_T_FMT "]: %d bytes\n", i, len); + LogFileOutput("U2: Skip MACRAW[%" SIZE_T_FMT "]: %d bytes\n", i, size); +#endif + } +} + +void Uthernet2::receiveOnePacketIPRaw(const size_t i, const size_t lengthOfPayload, const uint8_t * payload, const uint32_t destination, const uint8_t protocol, const int len) +{ + Socket &socket = mySockets[i]; + + if (socket.isThereRoomFor(lengthOfPayload, 4 + sizeof(uint16_t))) + { + writeDataIPRaw(socket, myMemory, payload, lengthOfPayload, destination); +#ifdef U2_LOG_TRAFFIC + LogFileOutput("U2: Read IPRAW[%" SIZE_T_FMT "]: +%" SIZE_T_FMT " (%d) -> %d bytes\n", i, lengthOfPayload, len, socket.sn_rx_rsr); +#endif + } + else + { + // drop it +#ifdef U2_LOG_TRAFFIC + LogFileOutput("U2: Skip IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " (%d) bytes \n", i, lengthOfPayload, len); #endif - } } } @@ -557,7 +659,8 @@ void Uthernet2::receiveOnePacket(const size_t i) switch (socket.sn_sr) { case W5100_SN_SR_SOCK_MACRAW: - receiveOnePacketMacRaw(i); + case W5100_SN_SR_SOCK_IPRAW: + receiveOnePacketRaw(); break; case W5100_SN_SR_ESTABLISHED: case W5100_SN_SR_SOCK_UDP: @@ -572,6 +675,29 @@ void Uthernet2::receiveOnePacket(const size_t i) }; } +void Uthernet2::sendDataIPRaw(const size_t i, std::vector &payload) +{ + const Socket &socket = mySockets[i]; + + const uint8_t ttl = myMemory[socket.registerAddress + W5100_SN_TTL]; + const uint8_t tos = myMemory[socket.registerAddress + W5100_SN_TOS]; + const uint8_t protocol = myMemory[socket.registerAddress + W5100_SN_PROTO]; + const uint32_t source = readAddress(myMemory.data() + W5100_SIPR0); + const uint32_t destination = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); + + const MACAddress * sourceMac = reinterpret_cast(myMemory.data() + W5100_SHAR0); + const MACAddress * destinationMac; + getMACAddress(destination, destinationMac); + + std::vector packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, destination); + +#ifdef U2_LOG_TRAFFIC + LogFileOutput("U2: Send IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " (%" SIZE_T_FMT ") bytes\n", i, payload.size(), packet.size()); +#endif + + myNetworkBackend->transmit(packet.size(), packet.data()); +} + void Uthernet2::sendDataMacRaw(const size_t i, std::vector &packet) const { #ifdef U2_LOG_TRAFFIC @@ -656,6 +782,9 @@ void Uthernet2::sendData(const size_t i) case W5100_SN_SR_SOCK_MACRAW: sendDataMacRaw(i, data); break; + case W5100_SN_SR_SOCK_IPRAW: + sendDataIPRaw(i, data); + break; case W5100_SN_SR_ESTABLISHED: case W5100_SN_SR_SOCK_UDP: sendDataToSocket(i, data); @@ -854,7 +983,7 @@ uint8_t Uthernet2::readSocketRegister(const uint16_t address) break; default: #ifdef U2_LOG_UNKNOWN - LogFileOutput("U2: Get unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address); + LogFileOutput("U2: Get unknown socket register[%d]: %04x\n", i, address); #endif value = myMemory[address]; break; @@ -988,7 +1117,7 @@ void Uthernet2::writeSocketRegister(const uint16_t address, const uint8_t value) break; #ifdef U2_LOG_UNKNOWN default: - LogFileOutput("U2: Set unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address); + LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address); break; #endif }; @@ -1073,6 +1202,7 @@ void Uthernet2::Reset(const bool powerCycle) // dataAddress is NOT reset, see page 10 of Uthernet II myDataAddress = 0; myNetworkBackend = GetFrame().CreateNetworkBackend(); + myARPCache.clear(); } mySockets.clear(); @@ -1084,14 +1214,25 @@ void Uthernet2::Reset(const bool powerCycle) { resetRXTXBuffers(i); mySockets[i].clearFD(); - mySockets[i].registerAddress = static_cast(W5100_S0_BASE + (i << 8)); + const uint16_t registerAddress = static_cast(W5100_S0_BASE + (i << 8)); + mySockets[i].registerAddress = registerAddress; + + myMemory[registerAddress + W5100_SN_DHAR0] = 0xFF; + myMemory[registerAddress + W5100_SN_DHAR1] = 0xFF; + myMemory[registerAddress + W5100_SN_DHAR2] = 0xFF; + myMemory[registerAddress + W5100_SN_DHAR3] = 0xFF; + myMemory[registerAddress + W5100_SN_DHAR4] = 0xFF; + myMemory[registerAddress + W5100_SN_DHAR5] = 0xFF; + myMemory[registerAddress + W5100_SN_TTL] = 0x80; } // initial values - myMemory[W5100_RTR0] = 0x07; - myMemory[W5100_RTR1] = 0xD0; + myMemory[W5100_RTR0] = 0x07; + myMemory[W5100_RTR1] = 0xD0; + myMemory[W5100_RCR] = 0x08; setRXSizes(W5100_RMSR, 0x55); setTXSizes(W5100_TMSR, 0x55); + myMemory[W5100_PTIMER] = 0x28; } BYTE Uthernet2::IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) @@ -1162,6 +1303,46 @@ void Uthernet2::InitializeIO(LPBYTE pCxRomPeripheral) RegisterIoHandler(m_slot, u2_C0, u2_C0, nullptr, nullptr, this, nullptr); } +void Uthernet2::getMACAddress(const uint32_t address, const MACAddress * & mac) +{ + const auto it = myARPCache.find(address); + if (it != myARPCache.end()) + { + mac = &it->second; + } + else + { + MACAddress & macAddr = myARPCache[address]; + const uint32_t source = readAddress(myMemory.data() + W5100_SIPR0); + + if (address == source) + { + const uint8_t * sourceMac = myMemory.data() + W5100_SHAR0; + memcpy(macAddr.address, sourceMac, sizeof(macAddr.address)); + } + else + { + memset(macAddr.address, 0xFF, sizeof(macAddr.address)); // fallback to broadcast + if (address != INADDR_BROADCAST) + { + const uint32_t subnet = readAddress(myMemory.data() + W5100_SUBR0); + if ((address & subnet) == (source & subnet)) + { + // same network: send ARP request + myNetworkBackend->getMACAddress(address, macAddr); + } + else + { + const uint32_t gateway = readAddress(myMemory.data() + W5100_GAR0); + // different network: go via gateway + myNetworkBackend->getMACAddress(gateway, macAddr); + } + } + } + mac = &macAddr; + } +} + void Uthernet2::Update(const ULONG nExecutedCycles) { myNetworkBackend->update(nExecutedCycles); diff --git a/source/Uthernet2.h b/source/Uthernet2.h index 5e7952fa..752f0b5d 100644 --- a/source/Uthernet2.h +++ b/source/Uthernet2.h @@ -3,8 +3,10 @@ #include "Card.h" #include +#include class NetworkBackend; +struct MACAddress; struct Socket { @@ -47,6 +49,7 @@ struct Socket * Documentation from * http://dserver.macgui.com/Uthernet%20II%20manual%2017%20Nov%2018.pdf * https://www.wiznet.io/wp-content/uploads/wiznethome/Chip/W5100/Document/W5100_DS_V128E.pdf +* https://www.wiznet.io/wp-content/uploads/wiznethome/Chip/W5100/Document/3150Aplus_5100_ES_V260E.pdf */ class Uthernet2 : public Card @@ -54,6 +57,8 @@ class Uthernet2 : public Card public: static const std::string& GetSnapshotCardName(); + enum PacketDestination { HOST, BROADCAST, OTHER }; + Uthernet2(UINT slot); virtual void Destroy(void) {} @@ -72,6 +77,13 @@ private: uint16_t myDataAddress; std::shared_ptr myNetworkBackend; + // the real Uthernet II card does not have a ARP Cache + // but in the interest of speeding up the emulator + // we introduce one + std::map myARPCache; + + void getMACAddress(const uint32_t address, const MACAddress * & mac); + void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value); void setTXSizes(const uint16_t address, uint8_t value); void setRXSizes(const uint16_t address, uint8_t value); @@ -79,11 +91,14 @@ private: uint8_t getTXFreeSizeRegister(const size_t i, const size_t shift) const; uint8_t getRXDataSizeRegister(const size_t i, const size_t shift) const; - void receiveOnePacketMacRaw(const size_t i); + void receiveOnePacketRaw(); + void receiveOnePacketIPRaw(const size_t i, const size_t lengthOfPayload, const uint8_t * payload, const uint32_t destination, const uint8_t protocol, const int len); + void receiveOnePacketMacRaw(const size_t i, const int size, uint8_t * data); void receiveOnePacketFromSocket(const size_t i); void receiveOnePacket(const size_t i); - int receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data); + int receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data, PacketDestination & packetDestination); + void sendDataIPRaw(const size_t i, std::vector &data); void sendDataMacRaw(const size_t i, std::vector &data) const; void sendDataToSocket(const size_t i, std::vector &data); void sendData(const size_t i); diff --git a/source/W5100.h b/source/W5100.h index c9f35ec0..c07a8502 100644 --- a/source/W5100.h +++ b/source/W5100.h @@ -20,8 +20,10 @@ #define W5100_SIPR3 0x0012 #define W5100_RTR0 0x0017 #define W5100_RTR1 0x0018 +#define W5100_RCR 0x0019 #define W5100_RMSR 0x001A #define W5100_TMSR 0x001B +#define W5100_PTIMER 0x0028 #define W5100_UPORT1 0x002F #define W5100_S0_BASE 0x0400 #define W5100_S3_MAX 0x07FF @@ -58,6 +60,12 @@ #define W5100_SN_SR 0x03 #define W5100_SN_PORT0 0x04 #define W5100_SN_PORT1 0x05 +#define W5100_SN_DHAR0 0x06 +#define W5100_SN_DHAR1 0x07 +#define W5100_SN_DHAR2 0x08 +#define W5100_SN_DHAR3 0x09 +#define W5100_SN_DHAR4 0x0A +#define W5100_SN_DHAR5 0x0B #define W5100_SN_DIPR0 0x0C #define W5100_SN_DIPR1 0x0D #define W5100_SN_DIPR2 0x0E